Files
Harlekin e0b03ea336
Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s
Gedankenstriche durch Bindestriche ersetzt
375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die
README; an keiner Stelle Bedeutung, nur Zeichensetzung.
2026-09-04 08:54:52 +02:00

80 lines
2.7 KiB
TypeScript

import Link from 'next/link'
import { signOut } from '@/auth'
import { requireUser } from '@/lib/auth/current'
import { de } from '@/lib/i18n/de'
import { PaletteTrigger } from '@/components/palette/PaletteTrigger'
import { NavLinks } from '@/components/NavLinks'
import { ProfileMenu } from '@/components/ProfileMenu'
import { Avatar } from '@/components/Avatar'
import { SearchBar } from '@/components/SearchBar'
import { Vitals } from '@/components/Vitals'
import { hasAdminAccess } from './admin/sections'
/**
* Das Rahmenwerk der angemeldeten Anwendung.
*
* Die Kopfzeile trägt links die Bereiche der Arbeit - Übersicht, Fallakten,
* Personalakten, Verwaltung - und rechts alles, was den Nutzer selbst
* betrifft: Suchbalken und Profilbild. Thema, Benachrichtigungen und Abmelden
* standen früher zwischen den Aktenbereichen und nahmen dort den Platz weg,
* den die Arbeit braucht; sie sitzen jetzt hinter dem Profilbild.
*/
export default async function AppLayout({ children }: { children: React.ReactNode }) {
const user = await requireUser()
async function abmelden() {
'use server'
await signOut({ redirectTo: '/anmelden' })
}
const darfPersonal =
user.permissions.isAdmin || user.permissions.permissions.has('personnel.read')
return (
<div className="min-h-dvh">
{/* Strg+K. Winzig; die Palette selbst wird erst beim Öffnen geholt. */}
<PaletteTrigger />
<Vitals />
<header className="border-b border-line">
<div className="mx-auto flex max-w-5xl flex-wrap items-center gap-x-5 gap-y-3 px-6 py-3">
<Link href="/uebersicht" className="font-condensed text-text no-underline">
{de.app.shortName}
</Link>
<NavLinks
links={[
{ href: '/uebersicht', label: de.nav.dashboard },
{ href: '/faelle', label: de.nav.cases },
...(darfPersonal
? [{ href: '/personalakten', label: de.nav.personnel }]
: []),
{ href: '/abteilungen', label: de.nav.departments },
...(hasAdminAccess(user.permissions)
? [{ href: '/admin', label: de.nav.admin }]
: []),
]}
/>
<div className="ml-auto flex items-center gap-3">
<SearchBar />
<ProfileMenu
displayName={user.displayName}
username={user.username}
signOutAction={abmelden}
avatar={
<Avatar
userId={user.id}
avatarHash={user.avatarHash}
displayName={user.displayName}
/>
}
/>
</div>
</div>
</header>
<div id="inhalt">{children}</div>
</div>
)
}