import type { ReactNode } from 'react' import { notFound } from 'next/navigation' import { db } from '@/lib/db' import { Tabs } from '@/components/Tabs' import { requireUser } from '@/lib/auth/current' import { loadVisibleCase } from '@/lib/cases/service' import { de } from '@/lib/i18n/de' import { StampMark, classificationEdgeStyle } from '@/components/StampMark' import { allowedTargets, type CaseStatusName } from '@/lib/cases/lifecycle' import { CaseActions } from './CaseActions' import { WatchToggle } from './WatchToggle' import { isWatching } from '@/lib/cases/watch' import { trackOpen } from '@/lib/recent/track' export const dynamic = 'force-dynamic' /** * Kopf und Reiter einer Fallakte. * * Die Akte ist in Reiter geteilt, und zwar in **eigene Seiten**, nicht in * umgeschaltete Blöcke: nur so wird der Code getrennt geladen. Die Fallakte * war die dichteste Seite der Anwendung und hatte das Bündelbudget aus * docs/09-performance-und-ux.md aufgebraucht — Beteiligte, Verknüpfungen, * Dokumente und Anhänge lagen mit auf der Seite, obwohl man sie selten * gleichzeitig braucht. * * Fachlich passt der Schnitt ohnehin: die drei Ebenen aus * docs/05-akten-und-vorlagen.md — Kopfdaten, Abschnitte, Verlauf — bleiben * zusammen, das Umfeld der Akte bekommt seinen eigenen Platz. */ export default async function CaseLayout({ children, params, }: { children: ReactNode params: Promise<{ caseNumber: string }> }) { const user = await requireUser() const { caseNumber } = await params const akte = await loadVisibleCase(user.permissions, caseNumber) // Kein Zugriff heisst 404, nicht 403: die Existenz ist selbst eine Information. if (!akte) notFound() // Für die eigene Sprungliste vormerken. Steht im Layout und nicht auf einem // Reiter: wer die Beteiligten aufruft, hat die Akte geöffnet. await trackOpen(user.id, 'CASE', akte.id) const [levels, beobachtet] = await Promise.all([ db.clearanceLevelConfig.findMany({ orderBy: { level: 'asc' } }), isWatching(user.permissions, akte.id), ]) const level = levels.find((l) => l.level === akte.classification) const basis = `/faelle/${akte.caseNumber}` const reiter = [ { href: basis, label: de.case.tabFile }, { href: `${basis}/beteiligte`, label: de.case.tabRelations }, { href: `${basis}/dokumente`, label: de.case.tabDocuments }, { href: `${basis}/aenderungen`, label: de.case.changes }, { href: `${basis}/export`, label: de.export.heading }, ] return (
{akte.caseNumber}

{akte.title}

{de.case.status}
{de.case.statusNames[akte.status]}
{de.case.type}
{akte.templateKey}
{akte.dueAt ? (
{de.case.dueAt}
{akte.dueAt.toLocaleDateString('de-DE')}
) : null}
{/* Statuswechsel und Umklassifizierung gehören zum Kopf und stehen deshalb auf jedem Reiter zur Verfügung. */} ({ level: l.level, name: l.name }))} maxLevel={user.permissions.clearanceLevel} />
{children}
) }