Baustein 1: Grundgeruest, Datenbank, Anmeldung, zwei Rollen

Legt das Fundament der Lern-Bar an:

- Next.js 15 (App Router), React 19, TypeScript, ESLint, Vitest
- SQLite ueber Drizzle hinter einer Repository-Schicht
- Eigener Migrationslauf mit Pruefsummen, beim Start angewendet
- Argon2id, serverseitige Sitzungen, HttpOnly-Cookie, SameSite=Lax
- Rollen Ausbilder und Azubi, Rechteregeln als reine Funktionen
- Verwaltung: Azubikonto und Ausbildung anlegen, Passwort, Sperren
- Gestaltung Petrol/Bernstein, helle und dunkle Darstellung
- Dockerfile, docker-compose, Lebenszeichen, update.sh mit Rueckfall
- Sicherung und Wiederherstellung als Shell-Skripte
- 29 Tests fuer Rechte, Zeitrechnung und Migrationslauf

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Florian Wilde
2026-09-10 00:52:44 +02:00
co-authored by Claude Opus 5
commit e856349cd4
51 changed files with 11883 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
'use client';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { ThemaUmschalter } from './ThemaUmschalter';
export type Navigationseintrag = { pfad: string; titel: string };
export function Kopfzeile({
eintraege,
name,
rolle,
abmeldeAktion,
}: {
eintraege: Navigationseintrag[];
name: string;
rolle: string;
abmeldeAktion: () => Promise<void>;
}) {
const pfad = usePathname();
function istAktiv(ziel: string): boolean {
if (ziel === '/') return pfad === '/';
return pfad === ziel || pfad.startsWith(`${ziel}/`);
}
return (
<header className="kopf">
<div className="kopf-innen">
<Link href="/" className="marke">
Lern<span className="marke-akzent">-Bar</span>
</Link>
<nav className="hauptnavigation" aria-label="Hauptbereiche">
{eintraege.map((e) => (
<Link key={e.pfad} href={e.pfad} aria-current={istAktiv(e.pfad) ? 'page' : undefined}>
{e.titel}
</Link>
))}
</nav>
<div className="kopf-rechts">
<span className="leise" style={{ color: '#cfe2e9', fontSize: '0.85rem' }}>
{name} · {rolle}
</span>
<ThemaUmschalter />
<form action={abmeldeAktion}>
<button type="submit" className="kopf-knopf">
Abmelden
</button>
</form>
</div>
</div>
</header>
);
}