feat: Stammdaten, Ersteinrichtung, Zweitfaktor und Protokollansicht
Damit ist Etappe 1 fachlich vollstaendig. Ersteinrichtung in drei Schritten, bewusst ausserhalb des Anwendungsrahmens: der setzt Benutzer, zweiten Faktor UND eine betreute Person voraus - beim allerersten Start gibt es nichts davon. Die Aktion fuer den ersten Zugang sperrt sich selbst, sobald ein Benutzer existiert. Zweiter Faktor: Das Geheimnis wird zuerst nur abgelegt, ohne totpAktiviertAm. Erst ein echter Code aus der App schaltet ihn scharf. Wer sich beim Abtippen vertut oder die falsche App nimmt, wuerde sich sonst dauerhaft aussperren. Erst danach gibt es die Wiederherstellungscodes - einmal angezeigt, danach nur noch als Hash. Betreuungsjahr (src/lib/stammdaten/betreuungsjahr.ts): laeuft ab dem Tag der Bestellung, nicht ab dem Kalenderjahr. Schrittweise vorwaerts gerechnet statt ueber eine Differenz, damit Schaltjahre und der 29. Februar von selbst stimmen. Daran haengen Jahresbericht, Rechnungslegung und die Ausschlussfrist der Aufwandspauschale. Aendert sich das Bestellungsdatum, werden die Pflichtfristen neu abgeleitet - ueber einen dynamischen Import, damit die Stammdaten auch dann speicherbar bleiben, wenn das Fristenmodul fehlt. Protokollansicht zeigt je Eintrag die geaenderten Felder als alt -> neu. Die Aenderungen liegen verschluesselt, weil dort Diagnosen und Kontostaende stehen koennen. Seed legt bewusst keine echten Personendaten an, nur den ersten Zugang. Mit SEED_DEMO=true kommt ein vollstaendiger Beispielfall dazu - Erika Musterfrau, COPD GOLD III, Pflegegrad 3, Heimeinzug Januar 2026, leerstehendes Haus, zwei Versicherungen, laufender Antrag beim Bezirk mit acht Nachforderungen. Nur ausserhalb der Produktion. Docker Compose ohne Reverse Proxy - der ist auf dem Zielserver vorhanden. Die Datenbank hat keine Portfreigabe und ist nur ueber das interne Netz erreichbar; die Anwendung horcht auf 127.0.0.1. Etappe: 1 Phase: Fundament
This commit is contained in:
@@ -0,0 +1,433 @@
|
||||
'use client'
|
||||
|
||||
import { useActionState } from 'react'
|
||||
import { useFormStatus } from 'react-dom'
|
||||
import Link from 'next/link'
|
||||
import { AlertCircle, Loader2, Save } from 'lucide-react'
|
||||
import { betreuteAendern, type BetreuteZustand } from '@/server/actions/betreute'
|
||||
import { AUFGABENKREISE, AUFGABENKREIS_BESCHRIFTUNG } from '@/lib/validierung/betreute'
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Feld } from '@/components/feld'
|
||||
import { Rechtshinweis } from '@/components/rechtshinweis'
|
||||
|
||||
/** Prisma liefert Date, das Datumsfeld im Browser will "JJJJ-MM-TT". */
|
||||
function alsDatumswert(datum: string | null | undefined): string {
|
||||
if (!datum) return ''
|
||||
return datum.slice(0, 10)
|
||||
}
|
||||
|
||||
export type StammdatenWerte = {
|
||||
id: string
|
||||
vorname: string
|
||||
nachname: string
|
||||
geburtsname: string | null
|
||||
geburtsdatum: string
|
||||
geburtsort: string | null
|
||||
anrede: string | null
|
||||
heimName: string | null
|
||||
heimStrasse: string | null
|
||||
heimPlz: string | null
|
||||
heimOrt: string | null
|
||||
heimZimmer: string | null
|
||||
heimTelefon: string | null
|
||||
heimEinzugAm: string | null
|
||||
fruehereStrasse: string | null
|
||||
frueherePlz: string | null
|
||||
fruehererOrt: string | null
|
||||
krankenkasse: string | null
|
||||
krankenversichertennummer: string | null
|
||||
pflegekasse: string | null
|
||||
pflegegrad: number | null
|
||||
pflegegradSeit: string | null
|
||||
pflegegradBescheidVom: string | null
|
||||
schwerbehinderungGdB: number | null
|
||||
schwerbehinderungMerkzeichen: string | null
|
||||
diagnosen: string | null
|
||||
ermittelterWille: string | null
|
||||
patientenverfuegungVorhanden: boolean
|
||||
vorsorgevollmachtVorhanden: boolean
|
||||
betreuungsgericht: string | null
|
||||
aktenzeichenGericht: string | null
|
||||
bestellungAm: string | null
|
||||
bestellungBis: string | null
|
||||
aufgabenkreise: string[]
|
||||
aufgabenkreiseFreitext: string | null
|
||||
befreiterBetreuer: boolean
|
||||
ehrenamtlich: boolean
|
||||
aktenzeichenBezirk: string | null
|
||||
aktenzeichenSozialamt: string | null
|
||||
rentenversicherungsnummer: string | null
|
||||
steuerId: string | null
|
||||
notizen: string | null
|
||||
}
|
||||
|
||||
function Speichern() {
|
||||
const { pending } = useFormStatus()
|
||||
return (
|
||||
<Button type="submit" size="lg" disabled={pending}>
|
||||
{pending ? <Loader2 className="animate-spin" aria-hidden="true" /> : <Save aria-hidden="true" />}
|
||||
{pending ? 'Wird gespeichert …' : 'Speichern'}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export function StammdatenFormular({ werte }: { werte: StammdatenWerte }) {
|
||||
const [zustand, aktion] = useActionState<BetreuteZustand, FormData>(betreuteAendern, {})
|
||||
|
||||
/** Nach einem Fehlversuch gilt die Eingabe, sonst der gespeicherte Wert. */
|
||||
const wert = (feld: keyof StammdatenWerte): string => {
|
||||
const ausFehlversuch = zustand.werte?.[feld as string]
|
||||
if (ausFehlversuch !== undefined) return ausFehlversuch
|
||||
const gespeichert = werte[feld]
|
||||
if (gespeichert === null || gespeichert === undefined) return ''
|
||||
return String(gespeichert)
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={aktion} className="space-y-6" noValidate>
|
||||
<input type="hidden" name="betreuteId" value={werte.id} />
|
||||
|
||||
{zustand.fehler && (
|
||||
<Alert variant="fehler">
|
||||
<AlertCircle aria-hidden="true" />
|
||||
<AlertDescription>{zustand.fehler}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* --- Person --------------------------------------------------------- */}
|
||||
<Card>
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-base">Person</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="grid gap-4 sm:grid-cols-2">
|
||||
<Feld id="anrede" beschriftung="Anrede" fehler={zustand.felder?.anrede}>
|
||||
<Input name="anrede" defaultValue={wert('anrede')} placeholder="Frau" />
|
||||
</Feld>
|
||||
<div className="hidden sm:block" />
|
||||
<Feld id="vorname" beschriftung="Vorname" fehler={zustand.felder?.vorname} pflicht>
|
||||
<Input name="vorname" defaultValue={wert('vorname')} required />
|
||||
</Feld>
|
||||
<Feld id="nachname" beschriftung="Nachname" fehler={zustand.felder?.nachname} pflicht>
|
||||
<Input name="nachname" defaultValue={wert('nachname')} required />
|
||||
</Feld>
|
||||
<Feld id="geburtsname" beschriftung="Geburtsname" fehler={zustand.felder?.geburtsname}>
|
||||
<Input name="geburtsname" defaultValue={wert('geburtsname')} />
|
||||
</Feld>
|
||||
<Feld
|
||||
id="geburtsdatum"
|
||||
beschriftung="Geburtsdatum"
|
||||
fehler={zustand.felder?.geburtsdatum}
|
||||
pflicht
|
||||
>
|
||||
<Input
|
||||
name="geburtsdatum"
|
||||
type="date"
|
||||
defaultValue={alsDatumswert(wert('geburtsdatum'))}
|
||||
required
|
||||
/>
|
||||
</Feld>
|
||||
<Feld id="geburtsort" beschriftung="Geburtsort" fehler={zustand.felder?.geburtsort}>
|
||||
<Input name="geburtsort" defaultValue={wert('geburtsort')} />
|
||||
</Feld>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* --- Aufenthalt ------------------------------------------------------ */}
|
||||
<Card>
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-base">Aufenthalt und Einrichtung</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="grid gap-4 sm:grid-cols-2">
|
||||
<Feld id="heimName" beschriftung="Name der Einrichtung" fehler={zustand.felder?.heimName}>
|
||||
<Input name="heimName" defaultValue={wert('heimName')} />
|
||||
</Feld>
|
||||
<Feld id="heimZimmer" beschriftung="Zimmer" fehler={zustand.felder?.heimZimmer}>
|
||||
<Input name="heimZimmer" defaultValue={wert('heimZimmer')} />
|
||||
</Feld>
|
||||
<Feld id="heimStrasse" beschriftung="Straße und Hausnummer" fehler={zustand.felder?.heimStrasse}>
|
||||
<Input name="heimStrasse" defaultValue={wert('heimStrasse')} />
|
||||
</Feld>
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
<Feld id="heimPlz" beschriftung="PLZ" fehler={zustand.felder?.heimPlz}>
|
||||
<Input name="heimPlz" defaultValue={wert('heimPlz')} inputMode="numeric" />
|
||||
</Feld>
|
||||
<Feld id="heimOrt" beschriftung="Ort" className="col-span-2" fehler={zustand.felder?.heimOrt}>
|
||||
<Input name="heimOrt" defaultValue={wert('heimOrt')} />
|
||||
</Feld>
|
||||
</div>
|
||||
<Feld id="heimTelefon" beschriftung="Telefon der Einrichtung" fehler={zustand.felder?.heimTelefon}>
|
||||
<Input name="heimTelefon" type="tel" defaultValue={wert('heimTelefon')} />
|
||||
</Feld>
|
||||
<Feld id="heimEinzugAm" beschriftung="Eingezogen am" fehler={zustand.felder?.heimEinzugAm}>
|
||||
<Input name="heimEinzugAm" type="date" defaultValue={alsDatumswert(wert('heimEinzugAm'))} />
|
||||
</Feld>
|
||||
|
||||
<Feld
|
||||
id="fruehereStrasse"
|
||||
beschriftung="Frühere Straße"
|
||||
erklaerung="Die letzte eigene Wohnanschrift. Behörden fragen danach."
|
||||
fehler={zustand.felder?.fruehereStrasse}
|
||||
>
|
||||
<Input name="fruehereStrasse" defaultValue={wert('fruehereStrasse')} />
|
||||
</Feld>
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
<Feld id="frueherePlz" beschriftung="PLZ" fehler={zustand.felder?.frueherePlz}>
|
||||
<Input name="frueherePlz" defaultValue={wert('frueherePlz')} inputMode="numeric" />
|
||||
</Feld>
|
||||
<Feld id="fruehererOrt" beschriftung="Ort" className="col-span-2" fehler={zustand.felder?.fruehererOrt}>
|
||||
<Input name="fruehererOrt" defaultValue={wert('fruehererOrt')} />
|
||||
</Feld>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* --- Betreuungsgericht ---------------------------------------------- */}
|
||||
<Card>
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-base">Betreuungsgericht</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<Feld id="betreuungsgericht" beschriftung="Gericht" fehler={zustand.felder?.betreuungsgericht}>
|
||||
<Input name="betreuungsgericht" defaultValue={wert('betreuungsgericht')} />
|
||||
</Feld>
|
||||
<Feld id="aktenzeichenGericht" beschriftung="Aktenzeichen" fehler={zustand.felder?.aktenzeichenGericht}>
|
||||
<Input name="aktenzeichenGericht" defaultValue={wert('aktenzeichenGericht')} />
|
||||
</Feld>
|
||||
<Feld
|
||||
id="bestellungAm"
|
||||
beschriftung="Bestellt am"
|
||||
erklaerung="Steuert das Betreuungsjahr. Wird dieses Datum geändert, berechnet die Anwendung alle gesetzlichen Fristen neu."
|
||||
fehler={zustand.felder?.bestellungAm}
|
||||
>
|
||||
<Input name="bestellungAm" type="date" defaultValue={alsDatumswert(wert('bestellungAm'))} />
|
||||
</Feld>
|
||||
<Feld id="bestellungBis" beschriftung="Befristet bis" fehler={zustand.felder?.bestellungBis}>
|
||||
<Input name="bestellungBis" type="date" defaultValue={alsDatumswert(wert('bestellungBis'))} />
|
||||
</Feld>
|
||||
</div>
|
||||
|
||||
<fieldset className="space-y-2">
|
||||
<legend className="text-sm font-medium">Aufgabenkreise</legend>
|
||||
<div className="grid gap-2 sm:grid-cols-2">
|
||||
{AUFGABENKREISE.map((kreis) => (
|
||||
<label
|
||||
key={kreis}
|
||||
className="flex cursor-pointer items-center gap-2.5 rounded-md border p-2.5 hover:bg-accent"
|
||||
>
|
||||
<Checkbox
|
||||
name="aufgabenkreise"
|
||||
value={kreis}
|
||||
defaultChecked={werte.aufgabenkreise.includes(kreis)}
|
||||
/>
|
||||
<span className="text-sm">{AUFGABENKREIS_BESCHRIFTUNG[kreis]}</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<Feld
|
||||
id="aufgabenkreiseFreitext"
|
||||
beschriftung="Ergänzung zu den Aufgabenkreisen"
|
||||
erklaerung="Falls die Bestellungsurkunde einen Bereich anders oder enger fasst."
|
||||
fehler={zustand.felder?.aufgabenkreiseFreitext}
|
||||
>
|
||||
<Textarea name="aufgabenkreiseFreitext" defaultValue={wert('aufgabenkreiseFreitext')} rows={2} />
|
||||
</Feld>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="flex cursor-pointer items-start gap-2.5 rounded-md border p-3 hover:bg-accent">
|
||||
<Checkbox name="ehrenamtlich" defaultChecked={werte.ehrenamtlich} className="mt-0.5" />
|
||||
<span className="text-sm">
|
||||
<span className="font-medium">Ehrenamtliche Betreuung</span>
|
||||
<span className="mt-0.5 block text-muted-foreground">
|
||||
Begründet den Anspruch auf die jährliche Aufwandspauschale.
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
<label className="flex cursor-pointer items-start gap-2.5 rounded-md border p-3 hover:bg-accent">
|
||||
<Checkbox name="befreiterBetreuer" defaultChecked={werte.befreiterBetreuer} className="mt-0.5" />
|
||||
<span className="text-sm">
|
||||
<span className="font-medium">Befreiter Betreuer</span>
|
||||
<span className="mt-0.5 block text-muted-foreground">
|
||||
Dann genügt statt der vollständigen Rechnungslegung eine Vermögensübersicht.
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<Rechtshinweis rechtsgrundlage="§§ 1859, 1863, 1878 BGB" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* --- Gesundheit ------------------------------------------------------ */}
|
||||
<Card>
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-base">Gesundheit und Pflege</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<Alert variant="hinweis">
|
||||
<AlertCircle aria-hidden="true" />
|
||||
<AlertDescription>
|
||||
Diagnosen, Versichertennummer und der ermittelte Wille sind besondere Daten nach
|
||||
Art. 9 DSGVO. Sie werden zusätzlich verschlüsselt gespeichert und sind ohne den
|
||||
Schlüssel aus der Serverumgebung auch aus einem Backup nicht lesbar.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<Feld id="krankenkasse" beschriftung="Krankenkasse" fehler={zustand.felder?.krankenkasse}>
|
||||
<Input name="krankenkasse" defaultValue={wert('krankenkasse')} />
|
||||
</Feld>
|
||||
<Feld
|
||||
id="krankenversichertennummer"
|
||||
beschriftung="Versichertennummer"
|
||||
fehler={zustand.felder?.krankenversichertennummer}
|
||||
>
|
||||
<Input name="krankenversichertennummer" defaultValue={wert('krankenversichertennummer')} />
|
||||
</Feld>
|
||||
<Feld id="pflegekasse" beschriftung="Pflegekasse" fehler={zustand.felder?.pflegekasse}>
|
||||
<Input name="pflegekasse" defaultValue={wert('pflegekasse')} />
|
||||
</Feld>
|
||||
<Feld id="pflegegrad" beschriftung="Pflegegrad" erklaerung="1 bis 5" fehler={zustand.felder?.pflegegrad}>
|
||||
<Input name="pflegegrad" type="number" min={1} max={5} defaultValue={wert('pflegegrad')} />
|
||||
</Feld>
|
||||
<Feld id="pflegegradSeit" beschriftung="Pflegegrad seit" fehler={zustand.felder?.pflegegradSeit}>
|
||||
<Input name="pflegegradSeit" type="date" defaultValue={alsDatumswert(wert('pflegegradSeit'))} />
|
||||
</Feld>
|
||||
<Feld
|
||||
id="pflegegradBescheidVom"
|
||||
beschriftung="Bescheid vom"
|
||||
fehler={zustand.felder?.pflegegradBescheidVom}
|
||||
>
|
||||
<Input
|
||||
name="pflegegradBescheidVom"
|
||||
type="date"
|
||||
defaultValue={alsDatumswert(wert('pflegegradBescheidVom'))}
|
||||
/>
|
||||
</Feld>
|
||||
<Feld
|
||||
id="schwerbehinderungGdB"
|
||||
beschriftung="Grad der Behinderung"
|
||||
erklaerung="20 bis 100"
|
||||
fehler={zustand.felder?.schwerbehinderungGdB}
|
||||
>
|
||||
<Input
|
||||
name="schwerbehinderungGdB"
|
||||
type="number"
|
||||
min={20}
|
||||
max={100}
|
||||
step={10}
|
||||
defaultValue={wert('schwerbehinderungGdB')}
|
||||
/>
|
||||
</Feld>
|
||||
<Feld
|
||||
id="schwerbehinderungMerkzeichen"
|
||||
beschriftung="Merkzeichen"
|
||||
erklaerung="z. B. G, aG, B, H, Bl, Gl"
|
||||
fehler={zustand.felder?.schwerbehinderungMerkzeichen}
|
||||
>
|
||||
<Input
|
||||
name="schwerbehinderungMerkzeichen"
|
||||
defaultValue={wert('schwerbehinderungMerkzeichen')}
|
||||
/>
|
||||
</Feld>
|
||||
</div>
|
||||
|
||||
<Feld
|
||||
id="diagnosen"
|
||||
beschriftung="Diagnosen"
|
||||
erklaerung="Eine Diagnose je Zeile, gern mit Datum der Feststellung."
|
||||
fehler={zustand.felder?.diagnosen}
|
||||
>
|
||||
<Textarea name="diagnosen" defaultValue={wert('diagnosen')} rows={4} />
|
||||
</Feld>
|
||||
|
||||
<Feld
|
||||
id="ermittelterWille"
|
||||
beschriftung="Ermittelter Wille der betreuten Person"
|
||||
erklaerung="Was möchte sie selbst? Seit der Reform 2023 ist das die Leitschnur deines Handelns — auch dann, wenn du es anders entscheiden würdest."
|
||||
fehler={zustand.felder?.ermittelterWille}
|
||||
>
|
||||
<Textarea name="ermittelterWille" defaultValue={wert('ermittelterWille')} rows={4} />
|
||||
</Feld>
|
||||
<Rechtshinweis rechtsgrundlage="§ 1821 BGB" />
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="flex cursor-pointer items-center gap-2.5 rounded-md border p-3 hover:bg-accent">
|
||||
<Checkbox
|
||||
name="patientenverfuegungVorhanden"
|
||||
defaultChecked={werte.patientenverfuegungVorhanden}
|
||||
/>
|
||||
<span className="text-sm font-medium">Patientenverfügung liegt vor</span>
|
||||
</label>
|
||||
<label className="flex cursor-pointer items-center gap-2.5 rounded-md border p-3 hover:bg-accent">
|
||||
<Checkbox
|
||||
name="vorsorgevollmachtVorhanden"
|
||||
defaultChecked={werte.vorsorgevollmachtVorhanden}
|
||||
/>
|
||||
<span className="text-sm font-medium">Vorsorgevollmacht liegt vor</span>
|
||||
</label>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* --- Behörden -------------------------------------------------------- */}
|
||||
<Card>
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-base">Behörden und Kennzeichen</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="grid gap-4 sm:grid-cols-2">
|
||||
<Feld
|
||||
id="aktenzeichenBezirk"
|
||||
beschriftung="Aktenzeichen Bezirk"
|
||||
erklaerung="Hilfe zur Pflege nach dem 7. Kapitel SGB XII."
|
||||
fehler={zustand.felder?.aktenzeichenBezirk}
|
||||
>
|
||||
<Input name="aktenzeichenBezirk" defaultValue={wert('aktenzeichenBezirk')} />
|
||||
</Feld>
|
||||
<Feld
|
||||
id="aktenzeichenSozialamt"
|
||||
beschriftung="Aktenzeichen Sozialamt"
|
||||
fehler={zustand.felder?.aktenzeichenSozialamt}
|
||||
>
|
||||
<Input name="aktenzeichenSozialamt" defaultValue={wert('aktenzeichenSozialamt')} />
|
||||
</Feld>
|
||||
<Feld
|
||||
id="rentenversicherungsnummer"
|
||||
beschriftung="Rentenversicherungsnummer"
|
||||
fehler={zustand.felder?.rentenversicherungsnummer}
|
||||
>
|
||||
<Input name="rentenversicherungsnummer" defaultValue={wert('rentenversicherungsnummer')} />
|
||||
</Feld>
|
||||
<Feld id="steuerId" beschriftung="Steuer-Identifikationsnummer" fehler={zustand.felder?.steuerId}>
|
||||
<Input name="steuerId" inputMode="numeric" defaultValue={wert('steuerId')} />
|
||||
</Feld>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-base">Notizen</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Feld id="notizen" beschriftung="Freitext" fehler={zustand.felder?.notizen}>
|
||||
<Textarea name="notizen" defaultValue={wert('notizen')} rows={5} />
|
||||
</Feld>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
|
||||
<Button asChild variant="outline" size="lg">
|
||||
<Link href="/stammdaten">Abbrechen</Link>
|
||||
</Button>
|
||||
<Speichern />
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import type { Metadata } from 'next'
|
||||
import { SeitenKopf } from '@/components/seiten-kopf'
|
||||
import { aktiveBetreute, erfordereBenutzer, erfordereZugang } from '@/lib/auth/sitzung'
|
||||
import { oeffnen } from '@/lib/crypto/feld'
|
||||
import { StammdatenFormular, type StammdatenWerte } from './formular'
|
||||
|
||||
export const metadata: Metadata = { title: 'Stammdaten bearbeiten' }
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
/** Date → "JJJJ-MM-TT" für das Datumsfeld im Browser. */
|
||||
function alsText(datum: Date | null): string | null {
|
||||
return datum ? datum.toISOString().slice(0, 10) : null
|
||||
}
|
||||
|
||||
export default async function StammdatenBearbeitenSeite() {
|
||||
const benutzer = await erfordereBenutzer()
|
||||
const aktive = await aktiveBetreute(benutzer)
|
||||
if (!aktive) return null
|
||||
|
||||
// Schreibrecht wird hier geprüft, nicht erst beim Absenden.
|
||||
const { betreute } = await erfordereZugang(aktive.id, 'STAMMDATEN', 'schreiben')
|
||||
const offen = oeffnen('Betreute', betreute as unknown as Record<string, unknown>)
|
||||
|
||||
const werte: StammdatenWerte = {
|
||||
id: betreute.id,
|
||||
vorname: betreute.vorname,
|
||||
nachname: betreute.nachname,
|
||||
geburtsname: betreute.geburtsname,
|
||||
geburtsdatum: alsText(betreute.geburtsdatum) ?? '',
|
||||
geburtsort: betreute.geburtsort,
|
||||
anrede: betreute.anrede,
|
||||
heimName: betreute.heimName,
|
||||
heimStrasse: betreute.heimStrasse,
|
||||
heimPlz: betreute.heimPlz,
|
||||
heimOrt: betreute.heimOrt,
|
||||
heimZimmer: betreute.heimZimmer,
|
||||
heimTelefon: betreute.heimTelefon,
|
||||
heimEinzugAm: alsText(betreute.heimEinzugAm),
|
||||
fruehereStrasse: betreute.fruehereStrasse,
|
||||
frueherePlz: betreute.frueherePlz,
|
||||
fruehererOrt: betreute.fruehererOrt,
|
||||
krankenkasse: betreute.krankenkasse,
|
||||
krankenversichertennummer: offen.krankenversichertennummer,
|
||||
pflegekasse: betreute.pflegekasse,
|
||||
pflegegrad: betreute.pflegegrad,
|
||||
pflegegradSeit: alsText(betreute.pflegegradSeit),
|
||||
pflegegradBescheidVom: alsText(betreute.pflegegradBescheidVom),
|
||||
schwerbehinderungGdB: betreute.schwerbehinderungGdB,
|
||||
schwerbehinderungMerkzeichen: betreute.schwerbehinderungMerkzeichen,
|
||||
diagnosen: offen.diagnosen,
|
||||
ermittelterWille: offen.ermittelterWille,
|
||||
patientenverfuegungVorhanden: betreute.patientenverfuegungVorhanden,
|
||||
vorsorgevollmachtVorhanden: betreute.vorsorgevollmachtVorhanden,
|
||||
betreuungsgericht: betreute.betreuungsgericht,
|
||||
aktenzeichenGericht: betreute.aktenzeichenGericht,
|
||||
bestellungAm: alsText(betreute.bestellungAm),
|
||||
bestellungBis: alsText(betreute.bestellungBis),
|
||||
aufgabenkreise: betreute.aufgabenkreise,
|
||||
aufgabenkreiseFreitext: betreute.aufgabenkreiseFreitext,
|
||||
befreiterBetreuer: betreute.befreiterBetreuer,
|
||||
ehrenamtlich: betreute.ehrenamtlich,
|
||||
aktenzeichenBezirk: betreute.aktenzeichenBezirk,
|
||||
aktenzeichenSozialamt: betreute.aktenzeichenSozialamt,
|
||||
rentenversicherungsnummer: offen.rentenversicherungsnummer,
|
||||
steuerId: offen.steuerId,
|
||||
notizen: offen.notizen,
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<SeitenKopf
|
||||
titel="Stammdaten bearbeiten"
|
||||
beschreibung="Was hier steht, taucht in jedem Bericht und in jedem Anschreiben wieder auf. Es lohnt sich, es einmal in Ruhe vollständig einzutragen."
|
||||
pfad={[
|
||||
{ titel: 'Stammdaten', href: '/stammdaten' },
|
||||
{ titel: 'Bearbeiten' },
|
||||
]}
|
||||
/>
|
||||
<StammdatenFormular werte={werte} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user