From a9cb60e852a26a59c1f5b9119774e37f4d60234e Mon Sep 17 00:00:00 2001 From: Frederic Cabassut Date: Sun, 8 Mar 2026 21:30:36 +0100 Subject: [PATCH] feat(layout): expose user data to app shell Add layout.server.ts to pass event.locals.user to the client. Header now shows user avatar initial with dropdown menu (Settings link, Log out button). Co-Authored-By: Claude Opus 4.6 --- src/lib/components/Layout/Header.svelte | 53 ++++++++++++++++++++++++- src/routes/(app)/+layout.server.ts | 10 +++-- src/routes/(app)/+layout.svelte | 2 +- src/routes/+layout.server.ts | 5 +++ 4 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 src/routes/+layout.server.ts diff --git a/src/lib/components/Layout/Header.svelte b/src/lib/components/Layout/Header.svelte index 6d83dc5..2f2bd7f 100644 --- a/src/lib/components/Layout/Header.svelte +++ b/src/lib/components/Layout/Header.svelte @@ -6,12 +6,28 @@ interface Props { onMenuToggle: () => void; + user: { id: number; email: string; displayName: string; role: 'admin' | 'user' } | null; } - let { onMenuToggle }: Props = $props(); + let { onMenuToggle, user }: Props = $props(); let mobileSearchOpen = $state(false); + let dropdownOpen = $state(false); + + function getInitial(user: Props['user']): string { + if (!user) return '?'; + if (user.displayName) return user.displayName.charAt(0).toUpperCase(); + if (user.email) return user.email.charAt(0).toUpperCase(); + return '?'; + } + + async function handleLogout() { + await fetch('/api/auth/logout', { method: 'POST' }); + window.location.href = '/login'; + } + (dropdownOpen = false)} /> +
{#if mobileSearchOpen} @@ -48,6 +64,41 @@
+ + {#if user} +
+ + + {#if dropdownOpen} +
+
+

{user.displayName || user.email}

+

{user.email}

+
+ + Settings + + +
+ {/if} +
+ {/if}
{/if}
diff --git a/src/routes/(app)/+layout.server.ts b/src/routes/(app)/+layout.server.ts index 93a7c7c..cefd6f2 100644 --- a/src/routes/(app)/+layout.server.ts +++ b/src/routes/(app)/+layout.server.ts @@ -1,5 +1,9 @@ import { version } from '../../../package.json'; +import type { LayoutServerLoad } from './$types'; -export function load() { - return { appVersion: version }; -} +export const load: LayoutServerLoad = (event) => { + return { + appVersion: version, + user: event.locals.user + }; +}; diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte index 658b9a8..6599f93 100644 --- a/src/routes/(app)/+layout.svelte +++ b/src/routes/(app)/+layout.svelte @@ -21,7 +21,7 @@
-
(sidebarOpen = !sidebarOpen)} /> +
(sidebarOpen = !sidebarOpen)} user={data.user} /> (sidebarOpen = false)} appVersion={data.appVersion} />
diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts new file mode 100644 index 0000000..840bbb0 --- /dev/null +++ b/src/routes/+layout.server.ts @@ -0,0 +1,5 @@ +import type { LayoutServerLoad } from './$types.js'; + +export const load: LayoutServerLoad = ({ locals }) => { + return { user: locals.user }; +};