feat: Grundgerüst mit Anmeldung, Layout und Design-Tokens

Next.js 16, Prisma 7, Tailwind 4 und Vitest/Playwright, Konfiguration aus
Laufzettel übernommen, weil dort derselbe Stack erprobt ist.

Die Farben der Branding-Vorgabe stehen als Tokens in globals.css, das
helle Theme setzt dieselben Namen neu. Amber und Rot sind dort
abgedunkelt, sonst reicht der Kontrast nicht – ein Test prüft beide
Themes gegen 4,5:1.

Anmeldung mit lokalem Benutzer, Rolle je Anfrage aus der Datenbank,
Audit-Log per Trigger nur anhängbar. Geprüft im gebauten Stand auf
1440 px und 390 px.

Closes #001

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 09:23:41 +02:00
co-authored by Claude Opus 5.5
parent c3211d0fe2
commit 871bb3a77c
75 changed files with 10155 additions and 15 deletions
+35
View File
@@ -0,0 +1,35 @@
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { Seitenleiste } from "@/components/seitenleiste";
import { NAVIGATION } from "@/lib/navigation";
import { THEME_COOKIE, themeAusCookie } from "@/lib/theme";
import { versionText } from "@/lib/version";
import { darf } from "@/server/auth/rechte";
import { aktuellerBenutzer } from "@/server/auth/zugang";
/**
* Hülle aller angemeldeten Seiten. Sie ist das **zweite** Netz – jede Seite prüft ihr Recht
* selbst, weil Next dieses Layout bei Navigation innerhalb der Gruppe nicht neu rendert.
*/
export default async function AppHuelle({ children }: { children: React.ReactNode }) {
const benutzer = await aktuellerBenutzer();
if (!benutzer) redirect("/anmelden");
const theme = themeAusCookie((await cookies()).get(THEME_COOKIE)?.value);
const eintraege = NAVIGATION.filter((eintrag) => !eintrag.ab && darf(benutzer.rolle, eintrag.recht));
return (
<div className="lg:flex">
<Seitenleiste
eintraege={eintraege}
benutzer={{ anzeigename: benutzer.anzeigename, rolle: benutzer.rolle }}
version={versionText()}
theme={theme}
serverUmschalter={null}
/>
<main id="inhalt" className="min-w-0 flex-1 px-4 py-6 sm:px-8 lg:py-10">
<div className="mx-auto max-w-6xl">{children}</div>
</main>
</div>
);
}