diff --git a/src/lib/components/UI/dashboard/DataRowItem.svelte b/src/lib/components/UI/dashboard/DataRowItem.svelte index 820d7f88..07d737cd 100644 --- a/src/lib/components/UI/dashboard/DataRowItem.svelte +++ b/src/lib/components/UI/dashboard/DataRowItem.svelte @@ -104,7 +104,7 @@ {text} diff --git a/src/lib/components/charts/Vpd.svelte b/src/lib/components/charts/Vpd.svelte index 79aa67e9..986914bf 100644 --- a/src/lib/components/charts/Vpd.svelte +++ b/src/lib/components/charts/Vpd.svelte @@ -221,14 +221,3 @@
- - diff --git a/src/lib/stores/toast.svelte.ts b/src/lib/stores/toast.svelte.ts index 14f6ca6e..36d25ba0 100644 --- a/src/lib/stores/toast.svelte.ts +++ b/src/lib/stores/toast.svelte.ts @@ -63,29 +63,3 @@ export function info(message: string, options = {}) { export function neutral(message: string, options = {}) { return add({ type: 'neutral', message, ...options }); } - -// USEAGE: -// Toast demo functions -// function showSuccessToast() { -// success('Success! Operation completed successfully.'); -// } - -// function showErrorToast() { -// error('Error! Something went wrong.'); -// } - -// function showWarningToast() { -// warning('Warning! Please be careful with this action.'); -// } - -// function showInfoToast() { -// info('Info: This is an informational message.'); -// } - -// function showNeutralToast() { -// neutral('This is a neutral notification.'); -// } - -// function showCustomToast() { -// success('Custom toast with longer timeout!', { timeout: 10000 }); -// } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 977b7d18..80db095c 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -7,6 +7,135 @@ import { success, error, warning, info, neutral } from '$lib/stores/toast.svelte'; import { onMount } from 'svelte'; + // Dynamic import for RevoGrid to avoid SSR issues + let RevoGrid: any = $state(null); + let ColumnRegular: any = $state(null); + + let { data } = $props(); + + let grid_component_instance: any = $state(); // To bind the component instance if needed + const columns: any[] = $state([ + { + name: '🎰 Ticker', + prop: 'symbol', + sortable: true, + pin: 'colPinStart', + cellTemplate: (h, { model, prop }) => h('strong', null, model[prop]) + }, + { + name: '🔠 Company Name', + prop: 'company_name', + size: 300 + }, + { + name: '', + prop: '📉 graph', + readonly: true, + // Custom cell render + cellTemplate(h) { + const barWidth = 5; + const barSpacing = 5; + const maxHeight = 30; + const bars = []; + + // Draw 5 vertical bars with random heights + for (let i = 0; i < 5; i++) { + const barHeight = Math.random() * maxHeight; + const x = i * (barWidth + barSpacing); + const y = maxHeight - barHeight + 5; + + // Create the rectangle element for the bar + const rect = h('rect', { + key: i, + x, + y, + width: barWidth, + height: barHeight, + fill: 'blue', + stroke: 'black' + }); + + // Append the rectangle to the group + bars.push(rect); + } + return h( + 'svg', + { + width: '100%', + height: maxHeight + 10 + }, + h('g', {}, bars) + ); + } + }, + { + name: '💰 Price', + prop: 'price', + columnType: 'numeric', + sortable: true + }, + { + name: '⬆️ Change', + prop: 'change', + columnType: 'numeric', + sortable: true + }, + { + name: '% Change', + prop: 'percent_change' + } + ]); + + const source = $state([ + { + symbol: 'AAPL', + company_name: 'Apple Inc.', + price: 150.25, + change: 1.5, + percent_change: '1.01%' + }, + { + symbol: 'MSFT', + company_name: 'Microsoft Corp.', + price: 280.75, + change: -0.5, + percent_change: '-0.18%' + }, + { + symbol: 'GOOGL', + company_name: 'Alphabet Inc.', + price: 2700.5, + change: 10.2, + percent_change: '0.38%' + } + ]); + + onMount(() => { + // Dynamically import RevoGrid only on the client side + if (browser) { + import('@revolist/svelte-datagrid').then((module) => { + RevoGrid = module.RevoGrid; + ColumnRegular = module.ColumnRegular; + }); + } + + const interval = setInterval(() => { + source.forEach((item) => { + item.price = parseFloat((Math.random() * 3000).toFixed(2)); + item.change = parseFloat((Math.random() * 20 - 10).toFixed(2)); // Random change between -10 and 10 + const priceForCalc = item.price === 0 ? 1 : item.price; // Avoid division by zero + item.percent_change = `${((item.change / priceForCalc) * 100).toFixed(2)}%`; + }); + // With Svelte 5 runes, direct modification of $state array items should be reactive. + // If the grid doesn't update, uncommenting the next line might be necessary if RevoGrid needs an explicit push. + // source = [...source]; + }, 2000); // Update every 2 seconds + + return () => { + clearInterval(interval); // Clear interval on component destroy + }; + }); + // Define the AlertPoint type locally to match the one in NumberLine.svelte type AlertPoint = { id: number; @@ -20,6 +149,47 @@ let showDataPicker = $state(false); + // Toast demo functions + function showSuccessToast() { + success('Success! Operation completed successfully.'); + } + + function showErrorToast() { + error('Error! Something went wrong.'); + } + + function showWarningToast() { + warning('Warning! Please be careful with this action.'); + } + + function showInfoToast() { + info('Info: This is an informational message.'); + } + + function showNeutralToast() { + neutral('This is a neutral notification.'); + } + + function showCustomToast() { + success('Custom toast with longer timeout!', { timeout: 10000 }); + } + + // Code examples as strings to avoid syntax issues + const importExample = + "import { success, error, warning, info, neutral } from '$lib/stores/toast';"; + const usageExample = `// Basic usage +success('Success message'); +error('Error message'); +warning('Warning message'); +info('Info message'); +neutral('Neutral message'); + +// With custom options +success('Custom message', { + timeout: 8000, // Duration in ms (default: 5000) + dismissible: true // Allow manual dismissal (default: true) +});`; + let points: AlertPoint[] = $state([ // // Way 1: operator > // { id: 1, name: 'Warning High', operator: '>', value: 30, color: '#FFA500' }, @@ -33,6 +203,7 @@
+

Toast Notification Demo

+
+

Click the buttons below to see different types of toast notifications:

+ +
+ + + + + + + + + + + +
+
+

How to Use Toast Notifications

@@ -89,3 +308,67 @@
+ +
+ {#if browser && RevoGrid} + + {:else} +

Loading data grid...

+ {/if} +
+ +
+ +
+ + diff --git a/src/routes/account/update-password/+page.server.ts b/src/routes/account/update-password/+page.server.ts new file mode 100644 index 00000000..6a243be7 --- /dev/null +++ b/src/routes/account/update-password/+page.server.ts @@ -0,0 +1,61 @@ +import { fail, redirect } from '@sveltejs/kit'; +import type { Actions, PageServerLoad } from './$types'; +import { container } from '$lib/server/ioc.config'; +import { TYPES } from '$lib/server/ioc.types'; +import type { ErrorHandlingService } from '$lib/errors/ErrorHandlingService'; +import { AuthService } from '$lib/services/AuthService'; + +// Make sure users are authenticated to access this page +export const load: PageServerLoad = async ({locals: { supabase }}) => { + // Get error handler from container + const errorHandler = container.get(TYPES.ErrorHandlingService); + // Create AuthService with the request's Supabase client + const authService = new AuthService(supabase, errorHandler); + const session = await authService.getSession(); + // If not logged in, redirect to login page + if (!session || (session && !session.user)) { + throw redirect(302, '/auth/login'); + } + + return {}; +}; + +export const actions: Actions = { + default: async ({ request, locals: { supabase } }) => { + try { + // Check if user is authenticated + const errorHandler = container.get(TYPES.ErrorHandlingService); + // Create AuthService with the request's Supabase client + const authService = new AuthService(locals.supabase, errorHandler); + const session = await authService.getSession(); + if (!session) { + return fail(401, { error: 'You must be logged in to update your password' }); + } + + // Parse form data + const formData = await request.formData(); + const newPassword = formData.get('newPassword')?.toString() || ''; + const confirmPassword = formData.get('confirmPassword')?.toString() || ''; + + if (newPassword.length < 8) { + return fail(400, { error: 'New password must be at least 8 characters long' }); + } + + if (newPassword !== confirmPassword) { + return fail(400, { error: 'New passwords don\'t match' }); + } + + // Update the password + const result = await authService.updatePassword(newPassword); + + if (!result.success) { + return fail(400, { error: result.error || 'Failed to update password' }); + } + + return { success: true }; + } catch (error) { + console.error('Password update error:', error); + return fail(500, { error: 'An unexpected error occurred. Please try again later.' }); + } + } +}; \ No newline at end of file diff --git a/src/routes/auth/update-password/+page.svelte b/src/routes/account/update-password/+page.svelte similarity index 87% rename from src/routes/auth/update-password/+page.svelte rename to src/routes/account/update-password/+page.svelte index e577a0fb..76360d31 100644 --- a/src/routes/auth/update-password/+page.svelte +++ b/src/routes/account/update-password/+page.svelte @@ -1,7 +1,7 @@