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/lib/utils/supabase/admin.ts b/src/lib/utils/supabase/admin.ts index 563b9d2..63f5c0b 100644 --- a/src/lib/utils/supabase/admin.ts +++ b/src/lib/utils/supabase/admin.ts @@ -409,6 +409,38 @@ 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}`); + } + + 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]; + product.actualPrice = defaultPrice.unit_amount! / 100; + } + } + + return { products: products as ProductWithPrices[], count }; +}; + export { upsertProductRecord, upsertPriceRecord, @@ -420,5 +452,6 @@ export { hasProductAccess, addCredits, deductCredits, - getUserCredits + getUserCredits, + getActiveProductsWithPrices }; diff --git a/src/routes/products/+page.svelte b/src/routes/products/+page.svelte index ad1edd7..f7112f2 100644 --- a/src/routes/products/+page.svelte +++ b/src/routes/products/+page.svelte @@ -1,18 +1,108 @@
@@ -22,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}
+ + 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); };