From 36a8620e708adecd60167d2c598fc67e8a9b5096 Mon Sep 17 00:00:00 2001 From: Josh Creek <8179928+jcreek@users.noreply.github.com> Date: Tue, 15 Oct 2024 19:20:40 +0100 Subject: [PATCH 1/4] feat(#6): Add a basic product page using the db rather than stripe --- src/lib/utils/supabase/admin.ts | 37 +++++++++++++++++++++++++++++++- src/routes/products/+page.svelte | 9 ++++---- src/routes/products/+server.ts | 26 +++++++++++----------- 3 files changed, 52 insertions(+), 20 deletions(-) diff --git a/src/lib/utils/supabase/admin.ts b/src/lib/utils/supabase/admin.ts index 563b9d2..79725be 100644 --- a/src/lib/utils/supabase/admin.ts +++ b/src/lib/utils/supabase/admin.ts @@ -409,6 +409,40 @@ const getUserCredits = async (userId: string) => { return creditData.credits_remaining; }; +type ProductWithPrices = Product & { + prices: Price[]; + actualPrice?: number; +}; + +const getActiveProductsWithPrices = async (limit = 10, offset = 0) => { + const { + data: products, + error, + count + } = await supabaseAdmin + .from('products') + .select('*, prices(*)', { count: 'exact' }) + .eq('active', true) + .order('name', { ascending: true }) + .range(offset, offset + limit - 1); + + if (error) { + throw new Error(`Error fetching products: ${error.message}`); + } + + const typedProducts: ProductWithPrices[] = products; + + for (const product of typedProducts) { + if (product.prices && product.prices.length > 0) { + // Assuming the default price is the first one + const defaultPrice = product.prices[0]; + product.actualPrice = defaultPrice.unit_amount! / 100; + } + } + + return { products: typedProducts, count }; // Return products and total count +}; + export { upsertProductRecord, upsertPriceRecord, @@ -420,5 +454,6 @@ export { hasProductAccess, addCredits, deductCredits, - getUserCredits + getUserCredits, + getActiveProductsWithPrices }; diff --git a/src/routes/products/+page.svelte b/src/routes/products/+page.svelte index ad1edd7..18feb8c 100644 --- a/src/routes/products/+page.svelte +++ b/src/routes/products/+page.svelte @@ -6,12 +6,11 @@ let enablePagination = false; onMount(async () => { - const response = await fetch('/products'); + const response = await fetch('/products?limit=20&offset=0'); const responseJson = await response.json(); - products = responseJson.data; - if (responseJson.has_more) { - enablePagination = true; - } + products = responseJson.products; + var count = responseJson.count; + // enablePagination needs to be set to true if there are more than 20 products }); diff --git a/src/routes/products/+server.ts b/src/routes/products/+server.ts index 5668d47..f3e2530 100644 --- a/src/routes/products/+server.ts +++ b/src/routes/products/+server.ts @@ -1,21 +1,19 @@ import { json } from '@sveltejs/kit'; -import { stripe as stripeClient } from '$lib/utils/stripe'; - -export const GET = async () => { - const products = await stripeClient.products.list({ - limit: 100 - }); +import { getActiveProductsWithPrices } from '$lib/utils/supabase/admin'; +export const GET = async ({ url }) => { try { - // Get the price for each product - for (const product of products.data) { - const price = await stripeClient.prices.retrieve(product.default_price as string); - if (price) { - product.actualPrice = price.unit_amount / 100; - } - } + const limitParam = url.searchParams.get('limit'); + const offsetParam = url.searchParams.get('offset'); + + const limit = limitParam ? parseInt(limitParam, 10) : 10; + const offset = offsetParam ? parseInt(offsetParam, 10) : 0; + + const { products, count } = await getActiveProductsWithPrices(limit, offset); + + return json({ products, count }); } catch (error) { console.error(error); + return json({ error: error.message }, { status: 500 }); } - return json(products); }; From 8221224255e8c918777423053fadf9ad5055e6e4 Mon Sep 17 00:00:00 2001 From: Josh Creek <8179928+jcreek@users.noreply.github.com> Date: Tue, 15 Oct 2024 19:31:37 +0100 Subject: [PATCH 2/4] feat(#6): Add proper pagination support --- package.json | 3 +- pnpm-lock.yaml | 8 ++ src/routes/products/+page.svelte | 180 +++++++++++++++++++++++++++---- 3 files changed, 169 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index 51e89f3..6dd387c 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,8 @@ "dependencies": { "@supabase/ssr": "^0.4.0", "@supabase/supabase-js": "^2.44.2", - "stripe": "^15.4.0" + "stripe": "^15.4.0", + "ts-debounce": "^4.0.0" }, "engines": { "node": ">=18.13.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 93412db..437f249 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: stripe: specifier: ^15.4.0 version: 15.12.0 + ts-debounce: + specifier: ^4.0.0 + version: 4.0.0 devDependencies: '@sveltejs/adapter-auto': specifier: ^3.0.0 @@ -3051,6 +3054,9 @@ packages: peerDependencies: typescript: '>=4.2.0' + ts-debounce@4.0.0: + resolution: {integrity: sha512-+1iDGY6NmOGidq7i7xZGA4cm8DAa6fqdYcvO5Z6yBevH++Bdo9Qt/mN0TzHUgcCcKv1gmh9+W5dHqz8pMWbCbg==} + ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} @@ -6580,6 +6586,8 @@ snapshots: dependencies: typescript: 5.6.2 + ts-debounce@4.0.0: {} + ts-interface-checker@0.1.13: {} tslib@2.7.0: {} diff --git a/src/routes/products/+page.svelte b/src/routes/products/+page.svelte index 18feb8c..f7112f2 100644 --- a/src/routes/products/+page.svelte +++ b/src/routes/products/+page.svelte @@ -1,17 +1,108 @@
@@ -21,20 +112,67 @@ > Available Products -
- {#each products as product (product.id)} - - {/each} -
- {#if enablePagination} -
-
- - - - -
+ + {#if isLoading} +

Loading products...

+ {:else if errorMessage} +

{errorMessage}

+ {:else} +
+ {#each products as product (product.id)} + + {/each}
+ + {#if enablePagination} +
+
+ + + + + {#each getPageNumbers() as pageNumber} + {#if pageNumber === '...'} + ... + {:else} + + {/if} + {/each} + + + +
+
+ {/if} {/if}
+ + From 79e2dcb744d4babe12da744d64e41d08ff1d3dfe Mon Sep 17 00:00:00 2001 From: Josh Creek <8179928+jcreek@users.noreply.github.com> Date: Tue, 15 Oct 2024 19:54:11 +0100 Subject: [PATCH 3/4] refactor(*): Cast instead of declaring a new const --- src/lib/utils/supabase/admin.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib/utils/supabase/admin.ts b/src/lib/utils/supabase/admin.ts index 79725be..cad3b0a 100644 --- a/src/lib/utils/supabase/admin.ts +++ b/src/lib/utils/supabase/admin.ts @@ -430,9 +430,7 @@ const getActiveProductsWithPrices = async (limit = 10, offset = 0) => { throw new Error(`Error fetching products: ${error.message}`); } - const typedProducts: ProductWithPrices[] = products; - - for (const product of typedProducts) { + for (const product of products as ProductWithPrices[]) { if (product.prices && product.prices.length > 0) { // Assuming the default price is the first one const defaultPrice = product.prices[0]; @@ -440,7 +438,7 @@ const getActiveProductsWithPrices = async (limit = 10, offset = 0) => { } } - return { products: typedProducts, count }; // Return products and total count + return { products, count }; // Return products and total count }; export { From f46b6359ab7b0c3a29532c0499be6fe98ba8bbe4 Mon Sep 17 00:00:00 2001 From: Josh Creek <8179928+jcreek@users.noreply.github.com> Date: Tue, 15 Oct 2024 20:06:51 +0100 Subject: [PATCH 4/4] refactor(*): Strongly type the return object --- src/lib/utils/supabase/admin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/utils/supabase/admin.ts b/src/lib/utils/supabase/admin.ts index cad3b0a..63f5c0b 100644 --- a/src/lib/utils/supabase/admin.ts +++ b/src/lib/utils/supabase/admin.ts @@ -438,7 +438,7 @@ const getActiveProductsWithPrices = async (limit = 10, offset = 0) => { } } - return { products, count }; // Return products and total count + return { products: products as ProductWithPrices[], count }; }; export {