Files
betreuungs-cockpit/src/components/benutzer-menue.tsx
T
Florian Wilde 0e7da42f27 docs: Bedienungsanleitung und Roadmap
Bis jetzt gab es Code und Betriebsdokumentation, aber nichts, was einem
beim Arbeiten hilft.

Die Anleitung ist kein Handbuch zum Durchlesen, sondern die zwoelf
Handgriffe, die im Alltag vorkommen - vom Behoerdenschreiben aufnehmen
bis zum Vertrag kuendigen. Dazu der Jahresrhythmus, die Berichte, was am
Handy anders ist, und ein Abschnitt "wenn etwas schiefgeht" mit den
Faellen, die wirklich passieren: Handy verloren, Code wird nicht
angenommen, Frist stimmt nicht, Bericht zeigt eine Differenz.

Ein eigener Abschnitt sagt, was die Anwendung NICHT tut. Wer sich auf
etwas verlaesst, das sie nicht leistet, faellt sonst genau dann darauf
herein, wenn es darauf ankommt.

Sie ist unter /anleitung auch in der Anwendung lesbar - dieselbe Datei,
kein zweiter Text, der auseinanderlaeuft. Wer im Heim steht und nicht
weiterweiss, hat sie auf dem Telefon dabei, und der Druckknopf legt sie
zu den Betreuungsunterlagen.

Dafuer ein schlichter Markdown-Baustein statt einer Bibliothek: die
Anwendung zeigt genau zwei solche Dokumente, beide schreibe ich selbst.
Rohes HTML wird nicht durchgereicht. Die Datenschutzseite benutzt ihn
jetzt ebenfalls, statt eine zweite Umsetzung zu pflegen.

Beide Dateien werden ueber outputFileTracingIncludes in den
Standalone-Build gezogen - der Pfad entsteht erst zur Laufzeit, die
Dateiverfolgung von Next.js kann ihn nicht erkennen. Ohne den Eintrag
waeren beide Seiten im Behaelter leer.

Die Roadmap sortiert die 44 Schema-Wuensche der Etappen nach einer
einzigen Frage zuerst: Kann daraus ein falscher Wert in einem Bericht
ans Gericht werden? Was das kann, steht in 1.1. Dazu ein Abschnitt
"bewusst nicht geplant" mit Begruendung - damit klar ist, worauf
niemand wartet.

Etappe: 8
Phase: Haertung
2026-08-22 10:51:28 +02:00

144 lines
4.3 KiB
TypeScript

'use client'
import * as React from 'react'
import Link from 'next/link'
import { signOut } from 'next-auth/react'
import {
BookOpen,
LogOut,
Monitor,
Moon,
Settings,
ShieldCheck,
Sun,
User as UserIcon,
} from 'lucide-react'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { Button } from '@/components/ui/button'
import { initialen } from '@/lib/utils'
type Thema = 'hell' | 'dunkel' | 'system'
/**
* Benutzermenue mit Themenwahl und Abmelden.
*
* Das Thema wird im localStorage gemerkt und sofort auf <html> gesetzt -
* dieselbe Logik wie im Startskript im Layout, damit es beim naechsten
* Laden nicht flackert.
*/
export function BenutzerMenue({
name,
email,
rolle,
}: {
name: string
email: string
rolle: string
}) {
const [thema, setThema] = React.useState<Thema>('system')
React.useEffect(() => {
const gespeichert = (localStorage.getItem('bc-thema') as Thema | null) ?? 'system'
setThema(gespeichert)
}, [])
function themaSetzen(neu: Thema) {
setThema(neu)
try {
localStorage.setItem('bc-thema', neu)
} catch {
/* Speicher gesperrt - dann gilt die Wahl nur fuer diese Sitzung. */
}
const dunkel =
neu === 'dunkel' ||
(neu === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches)
document.documentElement.classList.toggle('dark', dunkel)
document.documentElement.style.colorScheme = dunkel ? 'dark' : 'light'
}
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="w-full justify-start gap-3 px-2">
<span
aria-hidden="true"
className="grid size-8 shrink-0 place-items-center rounded-full bg-secondary text-xs font-semibold text-secondary-foreground"
>
{initialen(name)}
</span>
<span className="min-w-0 flex-1 text-left lg:block">
<span className="block truncate text-sm font-medium">{name}</span>
<span className="block truncate text-xs text-muted-foreground">{rolle}</span>
</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-64">
<DropdownMenuLabel>
<div className="truncate font-medium">{name}</div>
<div className="truncate text-xs font-normal text-muted-foreground">{email}</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link href="/konto">
<UserIcon className="size-4" />
Mein Konto
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link href="/konto/zwei-faktor">
<ShieldCheck className="size-4" />
Zweiter Faktor
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link href="/einstellungen">
<Settings className="size-4" />
Einstellungen
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link href="/anleitung">
<BookOpen className="size-4" />
Anleitung
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuLabel className="text-xs font-normal text-muted-foreground">
Darstellung
</DropdownMenuLabel>
<DropdownMenuItem onSelect={() => themaSetzen('hell')}>
<Sun className="size-4" />
Hell
{thema === 'hell' && <span className="ml-auto text-xs">✓</span>}
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => themaSetzen('dunkel')}>
<Moon className="size-4" />
Dunkel
{thema === 'dunkel' && <span className="ml-auto text-xs">✓</span>}
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => themaSetzen('system')}>
<Monitor className="size-4" />
Wie das System
{thema === 'system' && <span className="ml-auto text-xs">✓</span>}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onSelect={() => void signOut({ callbackUrl: '/anmelden' })}>
<LogOut className="size-4" />
Abmelden
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}