import { Link, useLocation, useNavigate } from '@tanstack/react-router' import { AppShell, Avatar, Burger, Group, Menu, NavLink, Stack, Text, UnstyledButton, } from '@pikku/mantine/core' import { useDisclosure } from '@mantine/hooks' import { useQueryClient } from '@tanstack/react-query' import { m, mKey } from '@/i18n/messages' import { useLocale } from '@/i18n/config' import { asI18n } from '@pikku/react' import { createContext, useContext, useEffect, useState } from 'react' import { Building2, LayoutDashboard } from 'lucide-react' const PageTitleContext = createContext<((title: string | undefined) => void) | null>(null) export function PageTitle({ title }: { title: string }) { usePageTitle(title) return null } export function usePageTitle(title: string | undefined) { const setTitle = useContext(PageTitleContext) useEffect(() => { if (!setTitle) return setTitle(title) return () => setTitle(undefined) }, [setTitle, title]) } const NAV_ITEMS = [ { to: '/companies', labelKey: 'common.nav.items.companies', Icon: Building2 }, { to: '/portfolio', labelKey: 'common.nav.items.portfolio', Icon: LayoutDashboard }, ] as const const initials = (name: string) => name .split(/\s+/) .filter(Boolean) .slice(0, 2) .map((p) => p[0]?.toUpperCase() ?? '') .join('') function QSeal() { return ( Q ) } export function AppLayout({ user, children, }: { user: { userId: string; email: string; displayName: string } children: React.ReactNode }) { useLocale() const [opened, { toggle, close }] = useDisclosure() const [pageTitle, setPageTitle] = useState(undefined) const location = useLocation() const navigate = useNavigate() const qc = useQueryClient() const API_URL = import.meta.env.VITE_API_URL ?? 'http://localhost:4003' const handleLogout = async () => { try { const csrfRes = await fetch(`${API_URL}/auth/csrf`, { credentials: 'include' }) const { csrfToken } = (await csrfRes.json()) as { csrfToken: string } const body = new URLSearchParams({ csrfToken, callbackUrl: '/login' }) await fetch(`${API_URL}/auth/signout`, { method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, body, credentials: 'include', redirect: 'manual', }) qc.clear() navigate({ to: '/login' }) } catch { navigate({ to: '/logout' }) } } return ( {/* Logo area matches sidebar width */} {m.common_brand_name()} {/* Right: breadcrumb + user */} {pageTitle && ( <> {m.common_brand_name()} / {asI18n(pageTitle ?? '')} )} {asI18n(user.displayName)} {asI18n(user.email)} {asI18n(initials(user.displayName))} {asI18n(user.email)} {m.common_nav_logout()}

Workspace

{NAV_ITEMS.map(({ to, labelKey, Icon }) => ( } active={location.pathname.startsWith(to)} onClick={close} /> ))}
{m.common_brand_tagline()}
{children}
) }