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 ?? '')}
>
)}
Workspace
{NAV_ITEMS.map(({ to, labelKey, Icon }) => (
}
active={location.pathname.startsWith(to)}
onClick={close}
/>
))}
{m.common_brand_tagline()}
{children}
)
}