From e8a2217247a7fdcddc9c79eb9030932da083c4c5 Mon Sep 17 00:00:00 2001 From: pptx704 Date: Thu, 16 Apr 2026 03:25:03 +0600 Subject: [PATCH] Add settings page, forgot/reset password flows, and me API client Adds /dashboard/settings route with profile/password/OAuth/account-deletion management. Adds /forgot-password and /reset-password routes. Enables sidebar settings link. Adds typed me.ts API client. --- frontend/src/lib/api/me.ts | 42 ++ frontend/src/lib/components/Sidebar.svelte | 20 +- .../routes/dashboard/settings/+page.svelte | 673 ++++++++++++++++++ .../src/routes/forgot-password/+page.svelte | 99 +++ frontend/src/routes/login/+page.svelte | 6 +- .../src/routes/reset-password/+page.svelte | 138 ++++ 6 files changed, 969 insertions(+), 9 deletions(-) create mode 100644 frontend/src/lib/api/me.ts create mode 100644 frontend/src/routes/dashboard/settings/+page.svelte create mode 100644 frontend/src/routes/forgot-password/+page.svelte create mode 100644 frontend/src/routes/reset-password/+page.svelte diff --git a/frontend/src/lib/api/me.ts b/frontend/src/lib/api/me.ts new file mode 100644 index 0000000..1396d9d --- /dev/null +++ b/frontend/src/lib/api/me.ts @@ -0,0 +1,42 @@ +import { apiFetch, type ApiResult } from '$lib/api/client'; +import type { AuthResponse } from '$lib/api/auth'; + +export type MeResponse = { + name: string; + email: string; + has_password: boolean; + providers: string[]; +}; + +export type ChangePasswordBody = { + current_password?: string; + new_password: string; + confirm_password?: string; +}; + +export const getMe = (): Promise> => + apiFetch('GET', '/api/v1/me'); + +export const updateName = (name: string): Promise> => + apiFetch('PATCH', '/api/v1/me', { name }); + +export const changePassword = (body: ChangePasswordBody): Promise> => + apiFetch('POST', '/api/v1/me/password', body); + +export const requestPasswordReset = (email: string): Promise> => + apiFetch('POST', '/api/v1/me/password/reset', { email }); + +export const confirmPasswordReset = ( + token: string, + new_password: string +): Promise> => + apiFetch('POST', '/api/v1/me/password/reset/confirm', { token, new_password }); + +export const getProviderConnectURL = (provider: string): Promise> => + apiFetch('GET', `/api/v1/me/providers/${provider}/connect`); + +export const disconnectProvider = (provider: string): Promise> => + apiFetch('DELETE', `/api/v1/me/providers/${provider}`); + +export const deleteAccount = (confirmation: string): Promise> => + apiFetch('DELETE', '/api/v1/me', { confirmation }); diff --git a/frontend/src/lib/components/Sidebar.svelte b/frontend/src/lib/components/Sidebar.svelte index 911f3ac..47d2960 100644 --- a/frontend/src/lib/components/Sidebar.svelte +++ b/frontend/src/lib/components/Sidebar.svelte @@ -280,13 +280,21 @@ {#if !collapsed}Notifications{/if} -
- - {#if !collapsed}Settings{/if} -
+ {#if isActive('/dashboard/settings') && !collapsed} +
+ {/if} + + {#if !collapsed} + + Settings + + {/if} + diff --git a/frontend/src/routes/dashboard/settings/+page.svelte b/frontend/src/routes/dashboard/settings/+page.svelte new file mode 100644 index 0000000..490b8b1 --- /dev/null +++ b/frontend/src/routes/dashboard/settings/+page.svelte @@ -0,0 +1,673 @@ + + + + Wrenn — Settings + + +
+ + +
+
+ +
+
+

Settings

+

+ Manage your account details and security. +

+
+
+
+ + +
+ {#if loadError} +
+ {loadError} +
+ {:else if me} +
+ + +
+
+
+ {initials} +
+
+

Profile

+

How you appear across Wrenn.

+
+
+ +
+
+ + +
+ +
+ + Email + +
+ {me.email} +
+
+ + {#if nameError} +

{nameError}

+ {/if} + +
+ +
+
+
+ +
+ + +
+
+
+ +
+
+

+ {me.has_password ? 'Change password' : 'Add a password'} +

+

+ {me.has_password + ? 'Use a strong, unique password you don\'t use elsewhere.' + : 'Set a password so you can sign in with your email.'} +

+
+
+ +
+ {#if me.has_password} +
+ + +
+ {/if} + +
+ + +
+ + {#if !me.has_password} +
+ + +
+ {/if} + + {#if passwordError} +

{passwordError}

+ {/if} + +
+ {#if me.has_password} + + {:else} + + {/if} + + +
+
+
+ +
+ + +
+
+
+ +
+
+

Connected accounts

+

+ Sign in with a linked account instead of your password. +

+
+
+ +
+ +
+
+ + + + +
+
GitHub
+ {#if me.providers.includes('github')} +
+ + Connected +
+ {:else} +
Not connected
+ {/if} +
+
+ + {#if me.providers.includes('github')} + + {:else} + + {/if} +
+
+
+ +
+ + +
+

Danger zone

+

+ Deleting your account is irreversible. +

+ +
+
+
+
Delete account
+
+ Your account will be deactivated immediately and permanently removed after 15 days. +
+
+ +
+
+
+ +
+ {:else} + +
+
+
+
+
+
+
+
+ {#each [140, 180, 100] as h, i} +
+
+
+ {/each} +
+ {/if} +
+
+
+
+
+ + +{#if showDisconnectConfirm} +
+ +
{ if (!disconnectingGitHub) showDisconnectConfirm = false; }} + onkeydown={(e) => { if (e.key === 'Escape' && !disconnectingGitHub) showDisconnectConfirm = false; }} + >
+
+

Disconnect GitHub

+

+ You won't be able to sign in with GitHub. You can reconnect it later. +

+ + {#if disconnectError} +
+ {disconnectError} +
+ {/if} + +
+ + +
+
+
+{/if} + + +{#if showDeleteConfirm} +
+ +
{ if (!deleting) showDeleteConfirm = false; }} + onkeydown={(e) => { if (e.key === 'Escape' && !deleting) showDeleteConfirm = false; }} + >
+
+

Delete account

+

+ Your account will be deactivated immediately and permanently deleted after 15 days. This cannot be undone. +

+ + {#if deleteError} +
+ {deleteError} +
+ {/if} + +
+ + +
+ +
+ + +
+
+
+{/if} + + diff --git a/frontend/src/routes/forgot-password/+page.svelte b/frontend/src/routes/forgot-password/+page.svelte new file mode 100644 index 0000000..0cd8730 --- /dev/null +++ b/frontend/src/routes/forgot-password/+page.svelte @@ -0,0 +1,99 @@ + + + + Wrenn — Reset password + + +
+
+ +
+ Wrenn + Wrenn +
+ + {#if submitted} +
+

Check your email

+

+ If an account exists for {email}, you'll receive a reset link shortly. The link expires in 15 minutes. +

+ + Back to sign in + +
+ {:else} +
+

Reset your password

+

+ Enter your email and we'll send you a reset link. +

+ + {#if error} +

{error}

+ {/if} + +
+
+ + +
+ + +
+ + + Back to sign in + +
+ {/if} +
+
diff --git a/frontend/src/routes/login/+page.svelte b/frontend/src/routes/login/+page.svelte index 914bc70..3e07c1c 100644 --- a/frontend/src/routes/login/+page.svelte +++ b/frontend/src/routes/login/+page.svelte @@ -285,12 +285,12 @@ {#if mode === 'signin'}
- +
{/if} diff --git a/frontend/src/routes/reset-password/+page.svelte b/frontend/src/routes/reset-password/+page.svelte new file mode 100644 index 0000000..8dfbd12 --- /dev/null +++ b/frontend/src/routes/reset-password/+page.svelte @@ -0,0 +1,138 @@ + + + + Wrenn — Set new password + + +
+
+ +
+ Wrenn + Wrenn +
+ + {#if done} +
+

All set

+

+ Your password has been updated. Sign in to continue. +

+ + Sign in + +
+ {:else} +
+

Set new password

+

Must be at least 8 characters.

+ + {#if error} +

{error}

+ {/if} + +
+
+
+ +
+ +
+ +
+
+ +
+ +
+ + +
+
+ {/if} + + + Back to sign in + +
+