From ed17f4baf892da95702e1fdac94ce688833673a8 Mon Sep 17 00:00:00 2001 From: Dominic Ferrando Date: Fri, 17 Jul 2026 10:06:53 -0400 Subject: [PATCH] Implement portal frontend for events --- apps/api/src/server.ts | 1 + apps/api/src/services/events.ts | 2 +- apps/portal/src/routes/(app)/+layout.svelte | 5 +- .../(app)/apparel/activity/+page.svelte | 251 ++++++++++++++++++ .../(app)/apparel/products/+page.svelte | 12 +- 5 files changed, 261 insertions(+), 10 deletions(-) create mode 100644 apps/portal/src/routes/(app)/apparel/activity/+page.svelte diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index 233a609..eb09c79 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -367,3 +367,4 @@ app.listen(3000, async () => { }); export type API = typeof app +export { type EventStatus } from "./services/events"; diff --git a/apps/api/src/services/events.ts b/apps/api/src/services/events.ts index 7788ed7..52a1f20 100644 --- a/apps/api/src/services/events.ts +++ b/apps/api/src/services/events.ts @@ -54,7 +54,7 @@ export class EventsService { } = {}, trx?: Transaction) { return await (trx ?? db).selectFrom("events") .select([ - "id", "type", "source", "started_at", "ended_at", "available_at", + "id", "group", "type", "source", "started_at", "ended_at", "available_at", "errors", "rate_limits", "has_failed", "last_error", "created_at", ]) .$if(opt.filter?.group !== undefined, (qb) => qb.where("group", "=", opt.filter!.group!)) diff --git a/apps/portal/src/routes/(app)/+layout.svelte b/apps/portal/src/routes/(app)/+layout.svelte index ba5a019..2caa853 100644 --- a/apps/portal/src/routes/(app)/+layout.svelte +++ b/apps/portal/src/routes/(app)/+layout.svelte @@ -15,7 +15,10 @@ { name: "Apparel", path: "/apparel", - links: [{ name: "Products", path: "/products" }], + links: [ + { name: "Products", path: "/products" }, + { name: "Activity", path: "/activity" }, + ], }, ]; diff --git a/apps/portal/src/routes/(app)/apparel/activity/+page.svelte b/apps/portal/src/routes/(app)/apparel/activity/+page.svelte new file mode 100644 index 0000000..490f538 --- /dev/null +++ b/apps/portal/src/routes/(app)/apparel/activity/+page.svelte @@ -0,0 +1,251 @@ + + +
+ + + + + + +
+ + + +
+ +{#if loadError} + +{/if} + +
+ + + + + + + + + + + + + + + + + + {#each events as event (event.id)} + + + + + + + + + + + + + + {:else} + + + + {/each} + +
GroupTypeStatusSourceCreatedStartedEndedErrorsRate limitsLast error
{formatLabel(event.group)}{formatLabel(event.type)}
+ {event.status} +
{event.source}{formatDate(event.created_at)}{formatDate(event.started_at)}{formatDate(event.ended_at)}{event.errors}{event.rate_limits} + {#if event.last_error} +
+ + {errorSummary(event.last_error)} + +
{JSON.stringify(
+                                        event.last_error,
+                                        null,
+                                        2,
+                                    )}
+
+ {:else} + — + {/if} +
+ {#if event.status === "failed"} + + {/if} +
+ {loading ? "Loading..." : "No events found"} +
+
diff --git a/apps/portal/src/routes/(app)/apparel/products/+page.svelte b/apps/portal/src/routes/(app)/apparel/products/+page.svelte index 417a132..a7be0b6 100644 --- a/apps/portal/src/routes/(app)/apparel/products/+page.svelte +++ b/apps/portal/src/routes/(app)/apparel/products/+page.svelte @@ -2,18 +2,14 @@ import type { Printful } from "@blade-and-brawn/commerce"; import { onMount } from "svelte"; import { api } from "$lib/api.js"; - - type SyncStatus = - | "waiting" - | "available" - | "processing" - | "failed" - | "fulfilled"; + import type { EventStatus } from "@blade-and-brawn/api"; const { data } = $props(); + type SyncStatus = EventStatus | "none"; + const synchronizer = $state({ - status: "none" as SyncStatus | "none", + status: "none" as SyncStatus, syncingPProductIds: [] as number[], get isInProgress() { return (