diff --git a/apps/frontend/package.json b/apps/frontend/package.json index b9446c7..eb048f5 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -22,6 +22,7 @@ "@eslint/compat": "^1.2.5", "@eslint/js": "^9.18.0", "@inlang/paraglide-js": "^2.2.0", + "@internationalized/date": "^3.10.0", "@langchain/core": "^0.3.78", "@langchain/langgraph-sdk": "^0.1.9", "@lucide/svelte": "^0.554.0", @@ -35,6 +36,7 @@ "@testing-library/jest-dom": "^6.6.3", "@testing-library/svelte": "^5.2.4", "@vitest/coverage-v8": "^3.2.4", + "bits-ui": "^2.14.4", "clsx": "^2.1.1", "eslint": "^9.18.0", "eslint-config-prettier": "^10.0.1", diff --git a/apps/frontend/src/lib/components/ui/avatar/avatar-fallback.svelte b/apps/frontend/src/lib/components/ui/avatar/avatar-fallback.svelte new file mode 100644 index 0000000..b911baf --- /dev/null +++ b/apps/frontend/src/lib/components/ui/avatar/avatar-fallback.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/frontend/src/lib/components/ui/avatar/avatar-image.svelte b/apps/frontend/src/lib/components/ui/avatar/avatar-image.svelte new file mode 100644 index 0000000..7ccc3ce --- /dev/null +++ b/apps/frontend/src/lib/components/ui/avatar/avatar-image.svelte @@ -0,0 +1,17 @@ + + + diff --git a/apps/frontend/src/lib/components/ui/avatar/avatar.svelte b/apps/frontend/src/lib/components/ui/avatar/avatar.svelte new file mode 100644 index 0000000..3fd4dc2 --- /dev/null +++ b/apps/frontend/src/lib/components/ui/avatar/avatar.svelte @@ -0,0 +1,19 @@ + + + diff --git a/apps/frontend/src/lib/components/ui/avatar/index.ts b/apps/frontend/src/lib/components/ui/avatar/index.ts new file mode 100644 index 0000000..9585f8a --- /dev/null +++ b/apps/frontend/src/lib/components/ui/avatar/index.ts @@ -0,0 +1,13 @@ +import Root from './avatar.svelte'; +import Image from './avatar-image.svelte'; +import Fallback from './avatar-fallback.svelte'; + +export { + Root, + Image, + Fallback, + // + Root as Avatar, + Image as AvatarImage, + Fallback as AvatarFallback +}; diff --git a/apps/frontend/src/routes/+layout.svelte b/apps/frontend/src/routes/+layout.svelte index 240b7f4..b1201ab 100644 --- a/apps/frontend/src/routes/+layout.svelte +++ b/apps/frontend/src/routes/+layout.svelte @@ -20,15 +20,38 @@ NavHamburger, Dropdown, DropdownHeader, - DropdownDivider, - Avatar + DropdownDivider } from 'flowbite-svelte'; - + import { Avatar, AvatarImage, AvatarFallback } from '$lib/components/ui/avatar'; import { ModeWatcher, toggleMode, mode } from 'mode-watcher'; import LanguageSwitcher from '$lib/components/LanguageSwitcher.svelte'; import SignInButton from '$lib/auth/components/SignInButton.svelte'; + type SessionUser = { + name?: string | null; + email?: string | null; + }; + + function getInitials(name?: string | null) { + if (!name) return 'U'; + const parts = name.trim().split(/\s+/).filter(Boolean); + if (parts.length === 0) return 'U'; + return parts + .slice(0, 2) + .map((p) => p[0]!.toUpperCase()) + .join(''); + } + + function getDisplayName(user?: SessionUser | null) { + if (user?.name && user.name.trim().length > 0) return user.name; + if (user?.email) { + const localPart = user.email.split('@')[0] ?? user.email; + return localPart || user.email; + } + return m.user_fallback(); + } + let { children } = $props(); @@ -43,20 +66,21 @@
{#if page.data.session} - - diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 565be5e..6ad1d7c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: '@inlang/paraglide-js': specifier: ^2.2.0 version: 2.2.0 + '@internationalized/date': + specifier: ^3.10.0 + version: 3.10.0 '@langchain/core': specifier: ^0.3.78 version: 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0)) @@ -71,6 +74,9 @@ importers: '@vitest/coverage-v8': specifier: ^3.2.4 version: 3.2.4(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.1.0)(jiti@2.5.0)(jsdom@26.1.0)(lightningcss@1.30.1)) + bits-ui: + specifier: ^2.14.4 + version: 2.14.4(@internationalized/date@3.10.0)(@sveltejs/kit@2.25.2(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.36.14)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.0)(lightningcss@1.30.1)))(svelte@5.36.14)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.0)(lightningcss@1.30.1)))(svelte@5.36.14) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -554,6 +560,9 @@ packages: resolution: {integrity: sha512-cvz/C1rF5WBxzHbEoiBoI6Sz6q6M+TdxfWkEGBYTD77opY8i8WN01prUWXEM87GPF4SZcyIySez9U0Ccm12oFQ==} engines: {node: '>=18.0.0'} + '@internationalized/date@3.10.0': + resolution: {integrity: sha512-oxDR/NTEJ1k+UFVQElaNIk65E/Z83HK1z1WI3lQyhTtnNg4R5oVXaPzK3jcpKG8UHKDVuDQHzn+wsxSz8RP3aw==} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -1188,6 +1197,9 @@ packages: peerDependencies: '@svgdotjs/svg.js': ^3.2.4 + '@swc/helpers@0.5.17': + resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} + '@tailwindcss/forms@0.5.10': resolution: {integrity: sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==} peerDependencies: @@ -1569,6 +1581,13 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + bits-ui@2.14.4: + resolution: {integrity: sha512-W6kenhnbd/YVvur+DKkaVJ6GldE53eLewur5AhUCqslYQ0vjZr8eWlOfwZnMiPB+PF5HMVqf61vXBvmyrAmPWg==} + engines: {node: '>=20'} + peerDependencies: + '@internationalized/date': ^3.8.1 + svelte: ^5.33.0 + brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -2905,6 +2924,15 @@ packages: peerDependencies: svelte: ^5.7.0 + runed@0.35.1: + resolution: {integrity: sha512-2F4Q/FZzbeJTFdIS/PuOoPRSm92sA2LhzTnv6FXhCoENb3huf5+fDuNOg1LNvGOouy3u/225qxmuJvcV3IZK5Q==} + peerDependencies: + '@sveltejs/kit': ^2.21.0 + svelte: ^5.7.0 + peerDependenciesMeta: + '@sveltejs/kit': + optional: true + sade@1.8.1: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} engines: {node: '>=6'} @@ -3036,6 +3064,12 @@ packages: peerDependencies: svelte: ^5.1.3 + svelte-toolbelt@0.10.6: + resolution: {integrity: sha512-YWuX+RE+CnWYx09yseAe4ZVMM7e7GRFZM6OYWpBKOb++s+SQ8RBIMMe+Bs/CznBMc0QPLjr+vDBxTAkozXsFXQ==} + engines: {node: '>=18', pnpm: '>=8.7.0'} + peerDependencies: + svelte: ^5.30.2 + svelte-toolbelt@0.7.1: resolution: {integrity: sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ==} engines: {node: '>=18', pnpm: '>=8.7.0'} @@ -3049,6 +3083,9 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + tabbable@6.3.0: + resolution: {integrity: sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==} + tailwind-merge@3.3.1: resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} @@ -3759,6 +3796,10 @@ snapshots: transitivePeerDependencies: - babel-plugin-macros + '@internationalized/date@3.10.0': + dependencies: + '@swc/helpers': 0.5.17 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -4490,6 +4531,10 @@ snapshots: dependencies: '@svgdotjs/svg.js': 3.2.5 + '@swc/helpers@0.5.17': + dependencies: + tslib: 2.8.1 + '@tailwindcss/forms@0.5.10(tailwindcss@4.1.11)': dependencies: mini-svg-data-uri: 1.4.4 @@ -4908,6 +4953,19 @@ snapshots: binary-extensions@2.3.0: {} + bits-ui@2.14.4(@internationalized/date@3.10.0)(@sveltejs/kit@2.25.2(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.36.14)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.0)(lightningcss@1.30.1)))(svelte@5.36.14)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.0)(lightningcss@1.30.1)))(svelte@5.36.14): + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/dom': 1.7.4 + '@internationalized/date': 3.10.0 + esm-env: 1.2.2 + runed: 0.35.1(@sveltejs/kit@2.25.2(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.36.14)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.0)(lightningcss@1.30.1)))(svelte@5.36.14)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.0)(lightningcss@1.30.1)))(svelte@5.36.14) + svelte: 5.36.14 + svelte-toolbelt: 0.10.6(@sveltejs/kit@2.25.2(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.36.14)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.0)(lightningcss@1.30.1)))(svelte@5.36.14)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.0)(lightningcss@1.30.1)))(svelte@5.36.14) + tabbable: 6.3.0 + transitivePeerDependencies: + - '@sveltejs/kit' + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 @@ -6343,6 +6401,15 @@ snapshots: esm-env: 1.2.2 svelte: 5.36.14 + runed@0.35.1(@sveltejs/kit@2.25.2(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.36.14)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.0)(lightningcss@1.30.1)))(svelte@5.36.14)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.0)(lightningcss@1.30.1)))(svelte@5.36.14): + dependencies: + dequal: 2.0.3 + esm-env: 1.2.2 + lz-string: 1.5.0 + svelte: 5.36.14 + optionalDependencies: + '@sveltejs/kit': 2.25.2(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.36.14)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.0)(lightningcss@1.30.1)))(svelte@5.36.14)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.0)(lightningcss@1.30.1)) + sade@1.8.1: dependencies: mri: 1.2.0 @@ -6471,6 +6538,15 @@ snapshots: transitivePeerDependencies: - supports-color + svelte-toolbelt@0.10.6(@sveltejs/kit@2.25.2(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.36.14)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.0)(lightningcss@1.30.1)))(svelte@5.36.14)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.0)(lightningcss@1.30.1)))(svelte@5.36.14): + dependencies: + clsx: 2.1.1 + runed: 0.35.1(@sveltejs/kit@2.25.2(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.36.14)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.0)(lightningcss@1.30.1)))(svelte@5.36.14)(vite@6.3.5(@types/node@24.1.0)(jiti@2.5.0)(lightningcss@1.30.1)))(svelte@5.36.14) + style-to-object: 1.0.12 + svelte: 5.36.14 + transitivePeerDependencies: + - '@sveltejs/kit' + svelte-toolbelt@0.7.1(svelte@5.36.14): dependencies: clsx: 2.1.1 @@ -6497,6 +6573,8 @@ snapshots: symbol-tree@3.2.4: {} + tabbable@6.3.0: {} + tailwind-merge@3.3.1: {} tailwind-variants@3.1.1(tailwind-merge@3.3.1)(tailwindcss@4.1.11):