From 6da25dc696bb2085ff236be851ef6bd689e727be Mon Sep 17 00:00:00 2001 From: Masahiko AMANO Date: Mon, 6 Apr 2026 23:37:44 +0300 Subject: [PATCH] feat(frontend): implement settings page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Profile editor: name and optional password change with confirm field, saves via PATCH /users/me and updates auth store - Appearance: theme toggle button (dark/light) with sun/moon icon - App cache: PWA reset — unregisters service workers and clears caches - Sessions: list active sessions with parsed user agent, start date, expiry, current badge, and terminate button per session - Add mock handlers: PATCH /users/me, DELETE /auth/sessions/{id} Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/routes/settings/+page.svelte | 527 ++++++++++++++++++++++ frontend/vite-mock-plugin.ts | 13 + 2 files changed, 540 insertions(+) create mode 100644 frontend/src/routes/settings/+page.svelte diff --git a/frontend/src/routes/settings/+page.svelte b/frontend/src/routes/settings/+page.svelte new file mode 100644 index 0000000..6cad9d0 --- /dev/null +++ b/frontend/src/routes/settings/+page.svelte @@ -0,0 +1,527 @@ + + + + Settings | Tanabata + + +
+ +
+

Profile

+ + {#if profileError} + + {/if} + {#if profileSuccess} +

Saved.

+ {/if} + +
+ + +
+ +
+ + +
+ + {#if password} +
+ + +
+ {/if} + +
+ +
+
+ + +
+

Appearance

+
+ + {$themeStore === 'light' ? 'Light theme' : 'Dark theme'} + + +
+
+ + +
+

App cache

+

Clear service worker and cached assets. Useful if the app feels stale after an update.

+ {#if pwaSuccess} +

Cache cleared. Reload the page to fetch fresh assets.

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

+ Active sessions + {#if sessionsTotal > 0}({sessionsTotal}){/if} +

+ + {#if sessionsError} + + {:else if sessionsLoading} +

Loading…

+ {:else if sessions.length === 0} +

No active sessions.

+ {:else} +
    + {#each sessions as session (session.id)} +
  • +
    + {shortUserAgent(session.user_agent)} + {#if session.is_current} + current + {/if} + + Started {formatDate(session.started_at)} + {#if session.expires_at}· Expires {formatDate(session.expires_at)}{/if} + +
    + {#if !session.is_current} + + {/if} +
  • + {/each} +
+ {/if} +
+
+ + \ No newline at end of file diff --git a/frontend/vite-mock-plugin.ts b/frontend/vite-mock-plugin.ts index f50b565..ea4afea 100644 --- a/frontend/vite-mock-plugin.ts +++ b/frontend/vite-mock-plugin.ts @@ -300,6 +300,19 @@ export function mockApiPlugin(): Plugin { return json(res, 200, ME); } + // PATCH /users/me + if (method === 'PATCH' && path === '/users/me') { + const body = (await readBody(req)) as { name?: string; password?: string }; + if (body.name) ME.name = body.name; + return json(res, 200, ME); + } + + // DELETE /auth/sessions/{id} + const sessionDelMatch = path.match(/^\/auth\/sessions\/(\d+)$/); + if (method === 'DELETE' && sessionDelMatch) { + return noContent(res); + } + // GET /files/{id}/thumbnail const thumbMatch = path.match(/^\/files\/([^/]+)\/thumbnail$/); if (method === 'GET' && thumbMatch) {