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} +
+
+ +