From 93f41de1857a6c0c4e31941641afe2db2ad63a6e Mon Sep 17 00:00:00 2001 From: Josh Creek <8179928+jcreek@users.noreply.github.com> Date: Wed, 13 Nov 2024 12:00:59 +0000 Subject: [PATCH 1/8] feat(#54): Add credit usage dashboard --- package.json | 1 + pnpm-lock.yaml | 16 ++++ src/lib/utils/supabase/admin.ts | 22 ++++++ src/routes/(user)/credits/+page.server.ts | 29 ++++++++ src/routes/(user)/credits/+page.svelte | 91 +++++++++++++++++++++++ 5 files changed, 159 insertions(+) create mode 100644 src/routes/(user)/credits/+page.server.ts create mode 100644 src/routes/(user)/credits/+page.svelte diff --git a/package.json b/package.json index a47e5f4..ac9cfe1 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "@getbrevo/brevo": "^2.2.0", "@supabase/ssr": "^0.4.0", "@supabase/supabase-js": "^2.44.2", + "chart.js": "^4.4.6", "stripe": "^15.4.0", "ts-debounce": "^4.0.0", "winston": "^3.15.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fa1b771..88dfa8c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: '@supabase/supabase-js': specifier: ^2.44.2 version: 2.45.4 + chart.js: + specifier: ^4.4.6 + version: 4.4.6 stripe: specifier: ^15.4.0 version: 15.12.0 @@ -937,6 +940,9 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@kurkle/color@0.3.2': + resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1533,6 +1539,10 @@ packages: resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + chart.js@4.4.6: + resolution: {integrity: sha512-8Y406zevUPbbIBA/HRk33khEmQPk5+cxeflWE/2rx1NJsjVWMPw/9mSP9rxHP5eqi6LNoPBVMfZHxbwLSgldYA==} + engines: {pnpm: '>=8'} + chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -4530,6 +4540,8 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@kurkle/color@0.3.2': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -5194,6 +5206,10 @@ snapshots: chalk@5.3.0: {} + chart.js@4.4.6: + dependencies: + '@kurkle/color': 0.3.2 + chokidar@3.6.0: dependencies: anymatch: 3.1.3 diff --git a/src/lib/utils/supabase/admin.ts b/src/lib/utils/supabase/admin.ts index 659f3ed..62497ac 100644 --- a/src/lib/utils/supabase/admin.ts +++ b/src/lib/utils/supabase/admin.ts @@ -451,6 +451,28 @@ const getUserCredits = async (userId: string) => { return creditData.credits_remaining; }; +/** + * Fetches the credit transaction history for a specific user. + * + * @param userId - The ID of the user whose transactions are to be fetched. + * @returns Array of transaction records containing credits_change, description, and created_at. + */ +export const getUserCreditTransactions = async (userId: string) => { + if (!userId) throw new Error('User ID is required'); + + const { data, error } = await supabaseAdmin + .from('credit_transactions') + .select('credits_change, description, created_at') + .eq('user_id', userId) + .order('created_at', { ascending: false }); // Orders transactions with the latest ones first + + if (error) { + throw new Error(`Error fetching transactions: ${error.message}`); + } + + return data || []; +}; + type ProductWithPrices = Product & { prices: Price[]; actualPrice?: number; diff --git a/src/routes/(user)/credits/+page.server.ts b/src/routes/(user)/credits/+page.server.ts new file mode 100644 index 0000000..34df0b8 --- /dev/null +++ b/src/routes/(user)/credits/+page.server.ts @@ -0,0 +1,29 @@ +import { redirect } from '@sveltejs/kit'; +import { getUserCredits, getUserCreditTransactions } from '$lib/utils/supabase/admin'; + +export async function load({ locals: { safeGetSession } }) { + const { session } = await safeGetSession(); + + if (!session) { + redirect(303, '/'); + } + + const userId = session.user?.id; + if (!userId) { + return { error: 'User not authenticated' }; + } + + try { + const creditsRemaining = await getUserCredits(userId); + const transactions = await getUserCreditTransactions(userId); + + return { + creditsRemaining, + transactions + }; + } catch (error) { + return { + error: error.message + }; + } +} diff --git a/src/routes/(user)/credits/+page.svelte b/src/routes/(user)/credits/+page.svelte new file mode 100644 index 0000000..80444ed --- /dev/null +++ b/src/routes/(user)/credits/+page.svelte @@ -0,0 +1,91 @@ + + +
+

Credit Dashboard

+

Credits Remaining: {creditsRemaining}

+ +
+ +
+ +

Transaction History

+
+ {#if noTransactions} +

No transactions found.

+ {:else} + + + + + + + + + + {#each transactions as transaction} + + + + + + {/each} + +
DateDescriptionChange
{new Date(transaction.created_at).toLocaleDateString()}{transaction.description} 0} + class:text-red-600={transaction.credits_change < 0} + > + {transaction.credits_change > 0 ? '+' : ''}{transaction.credits_change} +
+ {/if} +
+
+ + From d62c92e527f053a6bbd85a2cde430f49fb8f4837 Mon Sep 17 00:00:00 2001 From: Josh Creek <8179928+jcreek@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:51:13 +0000 Subject: [PATCH 2/8] feat(#54): Improve display of credit usage and accessibility --- package.json | 1 + pnpm-lock.yaml | 21 ++++ src/routes/(user)/credits/+page.server.ts | 18 +++- src/routes/(user)/credits/+page.svelte | 120 +++++++++++++++++----- 4 files changed, 135 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index ac9cfe1..d1aab9e 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "@supabase/ssr": "^0.4.0", "@supabase/supabase-js": "^2.44.2", "chart.js": "^4.4.6", + "simple-datatables": "^9.2.1", "stripe": "^15.4.0", "ts-debounce": "^4.0.0", "winston": "^3.15.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 88dfa8c..e1e057c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: chart.js: specifier: ^4.4.6 version: 4.4.6 + simple-datatables: + specifier: ^9.2.1 + version: 9.2.1 stripe: specifier: ^15.4.0 version: 15.12.0 @@ -1680,6 +1683,9 @@ packages: resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} engines: {node: '>= 0.4'} + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + debug@4.3.7: resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} engines: {node: '>=6.0'} @@ -1741,6 +1747,9 @@ packages: didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + diff-dom@5.1.4: + resolution: {integrity: sha512-TSEaVdVGictY1KHg7VpVw2nuM02YKC9C8/qBkGiCnkiAybVbu1zQTMj2/dnVLRO7Z62UsqzHGpXweiOj5/jaZg==} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -3003,6 +3012,9 @@ packages: simple-concat@1.0.1: resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + simple-datatables@9.2.1: + resolution: {integrity: sha512-R+KRyZ4GNQdqj5IoW08OYi9FHqCbndbw+MfdOJDnwOGs6me9+BhbNuj2QHZR/GXQ04TLra43Uj2/Lk1KcLiJRw==} + simple-get@4.0.1: resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} @@ -5353,6 +5365,8 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.1 + dayjs@1.11.13: {} + debug@4.3.7: dependencies: ms: 2.1.3 @@ -5402,6 +5416,8 @@ snapshots: didyoumean@1.2.2: {} + diff-dom@5.1.4: {} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -6715,6 +6731,11 @@ snapshots: simple-concat@1.0.1: {} + simple-datatables@9.2.1: + dependencies: + dayjs: 1.11.13 + diff-dom: 5.1.4 + simple-get@4.0.1: dependencies: decompress-response: 6.0.0 diff --git a/src/routes/(user)/credits/+page.server.ts b/src/routes/(user)/credits/+page.server.ts index 34df0b8..5cbc43e 100644 --- a/src/routes/(user)/credits/+page.server.ts +++ b/src/routes/(user)/credits/+page.server.ts @@ -17,9 +17,25 @@ export async function load({ locals: { safeGetSession } }) { const creditsRemaining = await getUserCredits(userId); const transactions = await getUserCreditTransactions(userId); + const monthlyAggregates = transactions.reduce((acc, transaction) => { + const date = new Date(transaction.created_at); + const month = `${date.getFullYear()}-${date.getMonth() + 1}`; + + if (!acc[month]) acc[month] = { credits_added: 0, credits_deducted: 0 }; + + if (transaction.credits_change > 0) { + acc[month].credits_added += transaction.credits_change; + } else { + acc[month].credits_deducted += transaction.credits_change; + } + + return acc; + }, {}); + return { creditsRemaining, - transactions + transactions, + monthlyAggregates }; } catch (error) { return { diff --git a/src/routes/(user)/credits/+page.svelte b/src/routes/(user)/credits/+page.svelte index 80444ed..1d3b8fd 100644 --- a/src/routes/(user)/credits/+page.svelte +++ b/src/routes/(user)/credits/+page.svelte @@ -1,59 +1,120 @@

Credit Dashboard

Credits Remaining: {creditsRemaining}

+
+ + +
+

Transaction History

- {#if noTransactions} + {#if transactions.length === 0}

No transactions found.

{:else} - +
@@ -88,4 +149,15 @@ table { margin-top: 1rem; } + button { + margin: 0 1rem; + padding: 0.5rem 1rem; + border: 1px solid #ccc; + border-radius: 0.5rem; + cursor: pointer; + } + button.font-bold { + font-weight: bold; + background-color: #f0f0f0; + } From 05a50a0ce0b5e88f652bfebe6824509be69e8247 Mon Sep 17 00:00:00 2001 From: Josh Creek <8179928+jcreek@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:05:04 +0000 Subject: [PATCH 3/8] refactor(#54): Improve month string formatting for consistent sorting --- src/routes/(user)/credits/+page.server.ts | 2 +- src/routes/(user)/credits/+page.svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/(user)/credits/+page.server.ts b/src/routes/(user)/credits/+page.server.ts index 5cbc43e..f781a0d 100644 --- a/src/routes/(user)/credits/+page.server.ts +++ b/src/routes/(user)/credits/+page.server.ts @@ -19,7 +19,7 @@ export async function load({ locals: { safeGetSession } }) { const monthlyAggregates = transactions.reduce((acc, transaction) => { const date = new Date(transaction.created_at); - const month = `${date.getFullYear()}-${date.getMonth() + 1}`; + const month = date.toISOString().substring(0, 7); // Returns YYYY-MM format if (!acc[month]) acc[month] = { credits_added: 0, credits_deducted: 0 }; diff --git a/src/routes/(user)/credits/+page.svelte b/src/routes/(user)/credits/+page.svelte index 1d3b8fd..ffafa94 100644 --- a/src/routes/(user)/credits/+page.svelte +++ b/src/routes/(user)/credits/+page.svelte @@ -19,7 +19,7 @@ const groupedTransactions = transactions.reduce((acc, transaction) => { const date = new Date(transaction.created_at); - const month = `${date.getFullYear()}-${date.getMonth() + 1}`; + const month = date.toISOString().substring(0, 7); // Returns YYYY-MM format if (!acc[month]) acc[month] = []; acc[month].push(transaction); return acc; From 42663e70325114a21fd30ceca931601fb06bcf3f Mon Sep 17 00:00:00 2001 From: Josh Creek <8179928+jcreek@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:05:53 +0000 Subject: [PATCH 4/8] refactor(#54): Strongly type page load --- src/routes/(user)/credits/+page.server.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/routes/(user)/credits/+page.server.ts b/src/routes/(user)/credits/+page.server.ts index f781a0d..46a1e05 100644 --- a/src/routes/(user)/credits/+page.server.ts +++ b/src/routes/(user)/credits/+page.server.ts @@ -1,7 +1,8 @@ import { redirect } from '@sveltejs/kit'; +import type { PageServerLoad } from './$types'; import { getUserCredits, getUserCreditTransactions } from '$lib/utils/supabase/admin'; -export async function load({ locals: { safeGetSession } }) { +export const load: PageServerLoad = async ({ locals: { safeGetSession } }) => { const { session } = await safeGetSession(); if (!session) { @@ -42,4 +43,4 @@ export async function load({ locals: { safeGetSession } }) { error: error.message }; } -} +}; From 3b77f926e6c57b93ef12352b0d0197e140e2b773 Mon Sep 17 00:00:00 2001 From: Josh Creek <8179928+jcreek@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:08:14 +0000 Subject: [PATCH 5/8] refactor(#54): Enhance error handling for database operations --- src/routes/(user)/credits/+page.server.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/routes/(user)/credits/+page.server.ts b/src/routes/(user)/credits/+page.server.ts index 46a1e05..6d25a78 100644 --- a/src/routes/(user)/credits/+page.server.ts +++ b/src/routes/(user)/credits/+page.server.ts @@ -1,6 +1,7 @@ import { redirect } from '@sveltejs/kit'; import type { PageServerLoad } from './$types'; import { getUserCredits, getUserCreditTransactions } from '$lib/utils/supabase/admin'; +import logger from '$lib/utils/logger/logger'; export const load: PageServerLoad = async ({ locals: { safeGetSession } }) => { const { session } = await safeGetSession(); @@ -15,8 +16,14 @@ export const load: PageServerLoad = async ({ locals: { safeGetSession } }) => { } try { - const creditsRemaining = await getUserCredits(userId); - const transactions = await getUserCreditTransactions(userId); + const creditsRemaining = await getUserCredits(userId).catch((error) => { + logger.error('Failed to fetch user credits:', error); + throw new Error('Unable to retrieve credit balance'); + }); + const transactions = await getUserCreditTransactions(userId).catch((error) => { + logger.error('Failed to fetch credit transactions:', error); + throw new Error('Unable to retrieve transaction history'); + }); const monthlyAggregates = transactions.reduce((acc, transaction) => { const date = new Date(transaction.created_at); From 13dd60428deced6c7a737a675c63acc0c590e920 Mon Sep 17 00:00:00 2001 From: Josh Creek <8179928+jcreek@users.noreply.github.com> Date: Tue, 19 Nov 2024 20:32:31 +0000 Subject: [PATCH 6/8] refactor(#54): Move account into (user) and improve button styling --- src/routes/{ => (user)}/account/+page.server.ts | 0 src/routes/{ => (user)}/account/+page.svelte | 17 +++++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) rename src/routes/{ => (user)}/account/+page.server.ts (100%) rename src/routes/{ => (user)}/account/+page.svelte (93%) diff --git a/src/routes/account/+page.server.ts b/src/routes/(user)/account/+page.server.ts similarity index 100% rename from src/routes/account/+page.server.ts rename to src/routes/(user)/account/+page.server.ts diff --git a/src/routes/account/+page.svelte b/src/routes/(user)/account/+page.svelte similarity index 93% rename from src/routes/account/+page.svelte rename to src/routes/(user)/account/+page.svelte index 1c1e4fb..56a6c95 100644 --- a/src/routes/account/+page.svelte +++ b/src/routes/(user)/account/+page.svelte @@ -123,6 +123,11 @@ {/if} +
+

Credit Usage

+ +
+

Billing History

@@ -183,12 +188,12 @@

Provide Feedback
- - -
- -
- + +
+ +
+ + From cf5771396ceecd75558c4cccfdfd36452e296cf1 Mon Sep 17 00:00:00 2001 From: Josh Creek <8179928+jcreek@users.noreply.github.com> Date: Tue, 19 Nov 2024 20:33:52 +0000 Subject: [PATCH 7/8] whoops --- src/routes/(user)/account/+page.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/(user)/account/+page.svelte b/src/routes/(user)/account/+page.svelte index 56a6c95..956bed8 100644 --- a/src/routes/(user)/account/+page.svelte +++ b/src/routes/(user)/account/+page.svelte @@ -125,7 +125,7 @@

Credit Usage

- + View details
From 15d2f04754e704b67ad68fdcb3ef84b8e0e521c3 Mon Sep 17 00:00:00 2001 From: Josh Creek <8179928+jcreek@users.noreply.github.com> Date: Tue, 19 Nov 2024 20:39:44 +0000 Subject: [PATCH 8/8] Update src/routes/(user)/account/+page.svelte Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/routes/(user)/account/+page.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/src/routes/(user)/account/+page.svelte b/src/routes/(user)/account/+page.svelte index 956bed8..fc80b8b 100644 --- a/src/routes/(user)/account/+page.svelte +++ b/src/routes/(user)/account/+page.svelte @@ -125,6 +125,7 @@

Credit Usage

+

Monitor your credit balance and view transaction history.

View details
Date