From 99b4a638b93a962dbf7cc408a77be1fd49c722a0 Mon Sep 17 00:00:00 2001 From: Dominic Ferrando Date: Fri, 17 Oct 2025 13:51:58 -0400 Subject: [PATCH] Make parameters per activity --- bun.lock | 6 +- package.json | 6 +- src/lib/components/StandardsTable.svelte | 6 +- src/lib/server/app.ts | 13 ++- src/lib/services/commerce/util/types.ts | 13 +-- src/lib/services/commerce/webflow.ts | 39 ++++++- src/lib/test/scratch.ts | 126 ++++++++++++----------- src/routes/calculator/+page.svelte | 53 +++++----- 8 files changed, 152 insertions(+), 110 deletions(-) diff --git a/bun.lock b/bun.lock index e1e980f..65002b4 100644 --- a/bun.lock +++ b/bun.lock @@ -146,7 +146,7 @@ "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.6", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ=="], - "@sveltejs/kit": ["@sveltejs/kit@2.46.5", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.3.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["@opentelemetry/api"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-7TSvMrCdmig5TMyYDW876C5FljhA0wlGixtvASCiqUqtLfmyEEpaysXjC7GhR5mWcGRrCGF+L2Bl1eEaW1wTCA=="], + "@sveltejs/kit": ["@sveltejs/kit@2.47.1", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.3.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["@opentelemetry/api"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-1v+MbMHxTi6ctQyxmz3owLKqZGaBHyx4EQqTdq/PvDswPFzw3WlqhrOKOh2ZzH23+XpQGEF9G+KDIgYJE+byvg=="], "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@6.2.1", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "debug": "^4.4.1", "deepmerge": "^4.3.1", "magic-string": "^0.30.17", "vitefu": "^1.1.1" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ=="], @@ -214,7 +214,7 @@ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], - "daisyui": ["daisyui@5.3.2", "", {}, "sha512-hw6NmQvFHdZI1Zb94EX+vA0DhuXGfqwQInDQ6XNZ5dgkoDy+e01P4OkJ8umAQAAzVSKGwm6QDMXOw6eJV63OEQ=="], + "daisyui": ["daisyui@5.3.7", "", {}, "sha512-0+8PaSGift0HlIQABCeZzWOBV5Nx/vsI2TihB9hbaEyZENPlZZz+se2JnAH5rz9gBYTyDLB7NJup8hkREr6WBw=="], "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], @@ -322,7 +322,7 @@ "strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="], - "svelte": ["svelte@5.40.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-EdA6LIo1vZVQOvlGfM18lhYM1k5Dkk/e93IlpzK4OVM6c11sUMHPgCtGFKmdy1WT4XqbpAvicq9JH5MTNoGaKw=="], + "svelte": ["svelte@5.40.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-wr/SwBVCVfeHU8FZr48vRrzSpWdBBzGo5mlErjGzeW4reJhK/CWutLZbk/eHwhKqO17ccjeTcvsqjrT4aK3wZA=="], "svelte-adapter-bun": ["svelte-adapter-bun@0.5.2", "", { "dependencies": { "tiny-glob": "^0.2.9" } }, "sha512-xEtFgaal6UgrCwwkSIcapO9kopoFNUYCYqyKCikdqxX9bz2TDYnrWQZ7qBnkunMxi1HOIERUCvTcebYGiarZLA=="], diff --git a/package.json b/package.json index ca68f20..4413fa1 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "type": "module", "devDependencies": { - "@sveltejs/kit": "^2.46.5", + "@sveltejs/kit": "^2.47.1", "@sveltejs/vite-plugin-svelte": "^6.2.1", "@types/bun": "^1.3.0", - "svelte": "^5.40.0", + "svelte": "^5.40.2", "svelte-adapter-bun": "^0.5.2", "svelte-check": "^4.3.3", "typescript": "^5.9.3", @@ -22,7 +22,7 @@ "@elysiajs/cors": "^1.4.0", "@elysiajs/eden": "^1.4.4", "@tailwindcss/vite": "^4.1.14", - "daisyui": "^5.3.2", + "daisyui": "^5.3.7", "elysia": "^1.4.12", "memoirist": "^0.4.0", "tailwindcss": "^4.1.14" diff --git a/src/lib/components/StandardsTable.svelte b/src/lib/components/StandardsTable.svelte index 03e1253..87b9616 100644 --- a/src/lib/components/StandardsTable.svelte +++ b/src/lib/components/StandardsTable.svelte @@ -52,7 +52,7 @@

- {#if !selected.cfg.disableGeneration && allStandards + {#if !selected.cfg.activity[selected.activity].disableGeneration && allStandards .byActivity(selected.activity) .getMetadata().generators.length} (Generated data: {allStandards @@ -87,7 +87,7 @@ ? parseFloat(kgToLb(standard.metrics.weight).toFixed(2)) : "None"} - {#each range(selected.cfg.maxLevel) as lvl} + {#each range(selected.cfg.global.maxLevel) as lvl} {getLvlValue(activity, standard, lvl)} {/each} @@ -99,7 +99,7 @@ Body weight - {#each range(selected.cfg.maxLevel) as lvl} + {#each range(selected.cfg.global.maxLevel) as lvl} {lvl} {/each} diff --git a/src/lib/server/app.ts b/src/lib/server/app.ts index 744a9d6..5c12a22 100644 --- a/src/lib/server/app.ts +++ b/src/lib/server/app.ts @@ -82,7 +82,14 @@ export const app = new Elysia({ prefix: "/api" }) } case Printful.Webhook.Event.PackageShipped: { const webflowOrderId = payload.data.order.external_id; - await WebflowService.Orders.update(webflowOrderId, { status: "fulfilled" }); + const shipInfo = payload.data.shipment; + + await WebflowService.Orders.update(webflowOrderId, { + shippingTrackingURL: shipInfo.tracking_url, + shippingTracking: shipInfo.tracking_number, + shippingProvider: shipInfo.carrier + }); + await WebflowService.Orders.fulfill(webflowOrderId, { sendOrderFulfilledEmail: true }); break; } } @@ -118,10 +125,6 @@ export const app = new Elysia({ prefix: "/api" }) })) }); - // set webflow order to pending - webflowOrder.status = "pending"; - await WebflowService.Orders.update(webflowOrder.orderId, webflowOrder); - console.log("Complete"); break; diff --git a/src/lib/services/commerce/util/types.ts b/src/lib/services/commerce/util/types.ts index c9adcd1..3d94fa6 100644 --- a/src/lib/services/commerce/util/types.ts +++ b/src/lib/services/commerce/util/types.ts @@ -148,24 +148,19 @@ export namespace Printful { export interface PackageShipped extends MetaData { } diff --git a/src/lib/services/commerce/webflow.ts b/src/lib/services/commerce/webflow.ts index f03e5c4..49f3a84 100644 --- a/src/lib/services/commerce/webflow.ts +++ b/src/lib/services/commerce/webflow.ts @@ -125,20 +125,55 @@ export default class WebflowService { } static Orders = class { - static async update(webflowOrderId: string, webflowOrder: DeepPartial) { + static async getAll(opt: { status?: Webflow.Orders.Order["status"] } = {}): Promise { + // TODO: pagination + const params = new URLSearchParams(); + if (opt.status !== undefined) { + params.set("status", opt.status); + } + + const res = await fetch(`${env().API_SITES_URL}/orders?${params.toString()}`, { + method: "GET", + headers: { ...env().AUTH_HEADER }, + }); + + if (!res.ok) { + throw new FetchError("Failed to get all Webflow orders", res); + } + + const payload = await res.json(); + return payload.orders as Webflow.Orders.Order[]; + } + + static async update(webflowOrderId: string, webflowOrderUpdate: { comment?: string, shippingProvider?: string, shippingTracking?: string, shippingTrackingURL: string }) { const res = await fetch(`${env().API_SITES_URL}/orders/${webflowOrderId}`, { method: "PATCH", headers: { "Content-Type": "application/json", ...env().AUTH_HEADER }, - body: JSON.stringify(webflowOrder) + body: JSON.stringify(webflowOrderUpdate) }); if (!res.ok) { throw new FetchError("Failed to update Webflow order", res); } } + + static async fulfill(webflowOrderId: string, opt: { sendOrderFulfilledEmail?: boolean } = {}) { + const res = await fetch(`${env().API_SITES_URL}/orders/${webflowOrderId}/fulfill`, { + method: "POST", + headers: { + "Content-Type": "application/json", + ...env().AUTH_HEADER + }, + body: JSON.stringify(opt) + }); + + if (!res.ok) { + throw new FetchError("Failed to fulfill Webflow order", res); + } + } } static Util = class { diff --git a/src/lib/test/scratch.ts b/src/lib/test/scratch.ts index 867ea16..85b44a3 100644 --- a/src/lib/test/scratch.ts +++ b/src/lib/test/scratch.ts @@ -46,71 +46,75 @@ const computedPerformances: ActivityPerformance[] = [ }, ]; +const wOrders = await WebflowService.Orders.getAll(); + +console.log(wOrders); + // wProduct.product.fieldData.name = "Rogue Title Hoodie [White] / XS"; // // WebflowService.Products.update("68e66dc4633e577c91eda727",); -const printfulProducts = await PrintfulService.Products.getAll(); -const webflowProducts = await WebflowService.Products.getAll(); - -for (const printfulProduct of printfulProducts) { - if (!printfulProduct.name.includes("T-Shirt") || printfulProduct.name.includes("[White]") || printfulProduct.name.includes("[Black]")) { - continue; - } - - console.log(printfulProduct.name); - - const fullPrintfulProduct = await PrintfulService.Products.get(printfulProduct.id); - if (!fullPrintfulProduct) { - console.log("Could not get full product, skipped"); - continue; - } - - const existingWebflowProduct = webflowProducts.find(p => fullPrintfulProduct.sync_product.name.includes(p.product.fieldData.name)) - if (!existingWebflowProduct) { - console.log("Could not get associated webflow product, skipped"); - continue; - } - - const newPrintfulVariants: DeepPartial[] = []; - for (const printfulVariant of fullPrintfulProduct.sync_variants) { - console.log(printfulVariant.name); - const associatedWebflowSku = existingWebflowProduct.skus - .find(sku => sku.fieldData["sku-values"]?.["color"] === SyncService.findColorInProductName(printfulVariant.name) && - sku.fieldData["sku-values"]?.["size"] === printfulVariant.size); - if (associatedWebflowSku) { - console.log('Found associated webflow sku: ' + associatedWebflowSku.fieldData.name); - newPrintfulVariants.push({ - "id": printfulVariant.id, // printful variant id - "external_id": String(associatedWebflowSku.id), // webflow variant id - }); - } - else { - console.log("Could not get associated webflow sku"); - } - } - - if (!newPrintfulVariants.length) { - console.log("No new printful variants, skipped"); - continue; - } - - await sleep(3000); - try { - await PrintfulService.Products.update(printfulProduct.id, { - "sync_product": { - "id": printfulProduct.id, - "external_id": existingWebflowProduct.product.id + "-" + SyncService.findColorInProductName(printfulProduct.name) - }, - "sync_variants": newPrintfulVariants - }); - } catch (err) { - if (err instanceof FetchError) { - console.error(err.message, err.payload); - } - } -} -console.log("DONE"); +// const printfulProducts = await PrintfulService.Products.getAll(); +// const webflowProducts = await WebflowService.Products.getAll(); +// +// for (const printfulProduct of printfulProducts) { +// if (!printfulProduct.name.includes("T-Shirt") || printfulProduct.name.includes("[White]") || printfulProduct.name.includes("[Black]")) { +// continue; +// } +// +// console.log(printfulProduct.name); +// +// const fullPrintfulProduct = await PrintfulService.Products.get(printfulProduct.id); +// if (!fullPrintfulProduct) { +// console.log("Could not get full product, skipped"); +// continue; +// } +// +// const existingWebflowProduct = webflowProducts.find(p => fullPrintfulProduct.sync_product.name.includes(p.product.fieldData.name)) +// if (!existingWebflowProduct) { +// console.log("Could not get associated webflow product, skipped"); +// continue; +// } +// +// const newPrintfulVariants: DeepPartial[] = []; +// for (const printfulVariant of fullPrintfulProduct.sync_variants) { +// console.log(printfulVariant.name); +// const associatedWebflowSku = existingWebflowProduct.skus +// .find(sku => sku.fieldData["sku-values"]?.["color"] === SyncService.findColorInProductName(printfulVariant.name) && +// sku.fieldData["sku-values"]?.["size"] === printfulVariant.size); +// if (associatedWebflowSku) { +// console.log('Found associated webflow sku: ' + associatedWebflowSku.fieldData.name); +// newPrintfulVariants.push({ +// "id": printfulVariant.id, // printful variant id +// "external_id": String(associatedWebflowSku.id), // webflow variant id +// }); +// } +// else { +// console.log("Could not get associated webflow sku"); +// } +// } +// +// if (!newPrintfulVariants.length) { +// console.log("No new printful variants, skipped"); +// continue; +// } +// +// await sleep(3000); +// try { +// await PrintfulService.Products.update(printfulProduct.id, { +// "sync_product": { +// "id": printfulProduct.id, +// "external_id": existingWebflowProduct.product.id + "-" + SyncService.findColorInProductName(printfulProduct.name) +// }, +// "sync_variants": newPrintfulVariants +// }); +// } catch (err) { +// if (err instanceof FetchError) { +// console.error(err.message, err.payload); +// } +// } +// } +// console.log("DONE"); // await WebflowService.Products.update(wProduct?.product.id!, wProduct!); diff --git a/src/routes/calculator/+page.svelte b/src/routes/calculator/+page.svelte index da69665..b8a3c79 100644 --- a/src/routes/calculator/+page.svelte +++ b/src/routes/calculator/+page.svelte @@ -12,7 +12,7 @@ let activeTab = $state("standards"); - let input = $state({ + let inputSelected = $state({ activity: Activity.BenchPress, metrics: { gender: Gender.Male, @@ -28,7 +28,7 @@ activity, { enableGeneration: true, - weightModfier: ".1", + weightModifier: ".1", weightSkew: "0", ageModifier: ".1", }, @@ -38,26 +38,30 @@ }); const selected = $derived({ - activity: input.activity, + activity: inputSelected.activity, metrics: { - gender: input.metrics.gender, - age: +input.metrics.age, - weight: lbToKg(+input.metrics.weight), + gender: inputSelected.metrics.gender, + age: +inputSelected.metrics.age, + weight: lbToKg(+inputSelected.metrics.weight), } as Metrics, cfg: { global: { - maxLevel: +input.cfg.global.maxLevel, + maxLevel: +inputSelected.cfg.global.maxLevel, }, activity: Object.fromEntries( Object.values(Activity).map((activity) => [ activity, { weightModifier: - +input.cfg.activity[activity].weightModfier, - weightSkew: +input.cfg.activity[activity].weightSkew, - ageModifier: +input.cfg.activity[activity].ageModifier, + +inputSelected.cfg.activity[activity] + .weightModifier, + weightSkew: + +inputSelected.cfg.activity[activity].weightSkew, + ageModifier: + +inputSelected.cfg.activity[activity].ageModifier, disableGeneration: - !input.cfg.activity[activity].enableGeneration, + !inputSelected.cfg.activity[activity] + .enableGeneration, }, ]), ), @@ -70,16 +74,16 @@ $effect(() => { console.log(selected); - if (!input.metrics.age) input.metrics.weight = ""; + if (!inputSelected.metrics.age) inputSelected.metrics.weight = ""; }); $effect(() => { const savedParameters = localStorage.getItem("parameters"); - if (savedParameters) input = JSON.parse(savedParameters); + if (savedParameters) inputSelected = JSON.parse(savedParameters); }); $effect(() => { - localStorage.setItem("parameters", JSON.stringify(input)); + localStorage.setItem("parameters", JSON.stringify(inputSelected)); }); @@ -102,7 +106,7 @@ min="1" max="100" placeholder="5" - bind:value={input.cfg.global.maxLevel} + bind:value={inputSelected.cfg.global.maxLevel} />

@@ -118,7 +122,7 @@ {#each Object.values(Activity) as activity}