From 25d8f549ecc8a8aea3739b2229bf206ad03accda Mon Sep 17 00:00:00 2001 From: Dominic Ferrando Date: Fri, 17 Jul 2026 15:38:28 -0400 Subject: [PATCH] Make products table more consistent with other tables --- apps/api/src/server.ts | 30 ++- apps/api/src/services/commerce.ts | 9 + apps/portal/src/lib/printful.ts | 7 - apps/portal/src/lib/webflow.ts | 12 - .../routes/(app)/apparel/orders/+page.svelte | 2 +- .../(app)/apparel/products/+page.server.ts | 12 - .../(app)/apparel/products/+page.svelte | 214 ++++++++++++------ 7 files changed, 171 insertions(+), 115 deletions(-) delete mode 100644 apps/portal/src/lib/printful.ts delete mode 100644 apps/portal/src/lib/webflow.ts delete mode 100644 apps/portal/src/routes/(app)/apparel/products/+page.server.ts diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index 55104a4..34d7a46 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -13,7 +13,7 @@ import { import { DEFAULT_NAME, env, log } from "./util"; import serverTiming from "@elysia/server-timing"; import jwt from "@elysia/jwt"; -import { CommerceService } from "./services/commerce"; +import { CommerceService, WOrderStatusSchema } from "./services/commerce"; import cluster from "node:cluster"; import { randomUUIDv7, sleep } from "bun"; import { CalculatorService, CalculatorUnavailableError } from "./services/calculator"; @@ -201,6 +201,25 @@ export const app = new Elysia() // COMMERCE .group("/commerce", { auth: true }, (app) => app .group("/products", (app) => app + .get("/", async ({ query }) => { + const pProducts = await s.Commerce.Printful.Products.list({ + limit: query.limit, + offset: query.offset, + }); + const isSyncedFlags = await Promise.all( + pProducts.map(async (pProduct) => { + const wProductId = pProduct.external_id.split("-")[0]; + if (!wProductId) return false; + return (await s.Commerce.Webflow.Products.get(wProductId)) !== undefined; + }) + ); + return pProducts.map((pProduct, i) => ({ pProduct, isSynced: isSyncedFlags[i] })); + }, { + query: t.Object({ + limit: t.Optional(t.Numeric()), + offset: t.Optional(t.Numeric()), + }) + }) .group("/sync", (app) => app // Sync status .get("/", async ({ sessionId }) => { @@ -251,14 +270,7 @@ export const app = new Elysia() return wOrders.map((wOrder, i) => ({ wOrder, isSynced: isSyncedFlags[i] })); }, { query: t.Object({ - status: t.Optional(t.Union([ - t.Literal("pending"), - t.Literal("unfulfilled"), - t.Literal("fulfilled"), - t.Literal("disputed"), - t.Literal("dispute-lost"), - t.Literal("refunded"), - ])), + status: t.Optional(WOrderStatusSchema), limit: t.Optional(t.Numeric()), offset: t.Optional(t.Numeric()), }) diff --git a/apps/api/src/services/commerce.ts b/apps/api/src/services/commerce.ts index 8327fb7..27f39b0 100644 --- a/apps/api/src/services/commerce.ts +++ b/apps/api/src/services/commerce.ts @@ -9,6 +9,15 @@ import zipcodesUs from "zipcodes-us"; import { secToMs } from "@blade-and-brawn/domain"; import { EventsService } from "./events"; +export const WOrderStatusSchema = t.Union([ + t.Literal("pending"), + t.Literal("unfulfilled"), + t.Literal("fulfilled"), + t.Literal("disputed"), + t.Literal("dispute-lost"), + t.Literal("refunded"), +]); + export class CommerceService { readonly Printful = new PrintfulClient({ token: env.PRINTFUL_AUTH, diff --git a/apps/portal/src/lib/printful.ts b/apps/portal/src/lib/printful.ts deleted file mode 100644 index 783df2f..0000000 --- a/apps/portal/src/lib/printful.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { PrintfulClient } from "@blade-and-brawn/commerce"; -import { env } from "$env/dynamic/private"; - -export const printful = new PrintfulClient({ - token: env.PRINTFUL_AUTH, - storeId: env.PRINTFUL_STORE_ID -}); diff --git a/apps/portal/src/lib/webflow.ts b/apps/portal/src/lib/webflow.ts deleted file mode 100644 index 245866b..0000000 --- a/apps/portal/src/lib/webflow.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { WebflowClient } from "@blade-and-brawn/commerce"; -import { env } from "$env/dynamic/private"; - -export const webflow = new WebflowClient({ - siteId: env.WEBFLOW_SITE_ID, - collectionIds: { - products: env.WEBFLOW_COLLECTION_PRODUCTS_ID, - skus: env.WEBFLOW_COLLECTION_SKUS_ID, - }, - token: env.WEBFLOW_AUTH, - webhookSecret: env.WEBFLOW_WEBHOOK_SECRET, -}); diff --git a/apps/portal/src/routes/(app)/apparel/orders/+page.svelte b/apps/portal/src/routes/(app)/apparel/orders/+page.svelte index a43bc33..ac766ac 100644 --- a/apps/portal/src/routes/(app)/apparel/orders/+page.svelte +++ b/apps/portal/src/routes/(app)/apparel/orders/+page.svelte @@ -6,7 +6,7 @@ Awaited>["data"] >[number]; - const LIMIT = 25; + const LIMIT = 5; let orders = $state([]); let loading = $state(true); diff --git a/apps/portal/src/routes/(app)/apparel/products/+page.server.ts b/apps/portal/src/routes/(app)/apparel/products/+page.server.ts deleted file mode 100644 index 1276095..0000000 --- a/apps/portal/src/routes/(app)/apparel/products/+page.server.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { PageServerLoad } from "./$types"; -import { printful } from "$lib/printful"; -import { webflow } from "$lib/webflow"; - -export const load = async ({ }: Parameters[0]) => { - return { - products: { - printful: printful.Products.list({ forceAll: true }), - webflow: webflow.Products.list({ forceAll: true }), - }, - }; -}; diff --git a/apps/portal/src/routes/(app)/apparel/products/+page.svelte b/apps/portal/src/routes/(app)/apparel/products/+page.svelte index e05af92..72f3ca9 100644 --- a/apps/portal/src/routes/(app)/apparel/products/+page.svelte +++ b/apps/portal/src/routes/(app)/apparel/products/+page.svelte @@ -1,13 +1,58 @@ -{#await data.products.printful} -

Loading...

-{:then pProducts} +
-
- - - - - - - - - - {#each pProducts as pProduct} - - - - - - {/each} - -
NameStatusSync
- {#await isProductSynced(pProduct) then isSynced} - {#if synchronizer.syncingPProductIds.includes(pProduct.id)} -
- Syncing... -
- {:else if isSynced} -
- Synced -
- {:else} -
- Desynced -
- {/if} - {/await} -
- -
+ + + +
+ + + +
+ +{#if loadError} + -{:catch error} -

Something went wrong: {error.message}

-{/await} +{/if} + +
+ + + + + + + + + + {#each products as product (product.pProduct.id)} + + + + + + {:else} + + + + {/each} + +
NameStatusSync
+ {#if synchronizer.syncingPProductIds.includes(product.pProduct.id)} +
Syncing...
+ {:else if product.isSynced} +
Synced
+ {:else} +
Desynced
+ {/if} +
+ +
+ {loading ? "Loading..." : "No products found"} +
+