From 22b3a9b9e952874910519806354a160ee082e0fa Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Sat, 22 Aug 2026 02:16:22 +0200 Subject: [PATCH] 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 --- daten/hinweise.json | 338 ++ docker-compose.dev.yml | 50 + docker-compose.yml | 154 + docs/SCHNITTSTELLEN.md | 266 ++ docs/schema-wuensche/substanz.md | 180 + package-lock.json | 3088 ++++++++++++++++- package.json | 15 +- .../20260822000640_fundament/migration.sql | 1150 ++++++ prisma/migrations/migration_lock.toml | 3 + prisma/seed.ts | 545 +++ src/app/(app)/fristen/[id]/page.tsx | 264 ++ src/app/(app)/fristen/kalender/page.tsx | 108 + src/app/(app)/fristen/neu/page.tsx | 41 + src/app/(app)/fristen/page.tsx | 232 ++ src/app/(app)/heute/page.tsx | 84 + src/app/(app)/page.tsx | 9 + src/app/(app)/protokoll/page.tsx | 209 ++ .../(app)/stammdaten/bearbeiten/formular.tsx | 433 +++ src/app/(app)/stammdaten/bearbeiten/page.tsx | 82 + src/app/(app)/stammdaten/page.tsx | 302 ++ src/app/(app)/vorgaenge/neu/page.tsx | 175 + src/app/(app)/vorgaenge/page.tsx | 136 + .../ersteinrichtung/betreuung-formular.tsx | 150 + src/app/(auth)/ersteinrichtung/page.tsx | 82 + .../ersteinrichtung/zugang-formular.tsx | 86 + .../zwei-faktor/einrichten/formular.tsx | 178 + .../(auth)/zwei-faktor/einrichten/page.tsx | 33 + src/app/api/gesundheit/route.ts | 24 + src/app/api/kalender/[signatur]/route.ts | 140 + src/app/page.tsx | 9 + src/components/dashboard/belege-kachel.tsx | 107 + src/components/dashboard/fristen-ampel.tsx | 172 + src/components/dashboard/kachel.tsx | 106 + src/components/dashboard/kennzahlen.tsx | 191 + .../dashboard/nachforderungen-kachel.tsx | 119 + .../dashboard/posteingang-kachel.tsx | 91 + .../dashboard/wartet-auf-andere.tsx | 90 + src/components/fristen/erledigen-knopf.tsx | 71 + src/components/fristen/filterleiste.tsx | 134 + src/components/fristen/frist-aktionen.tsx | 169 + src/components/fristen/frist-formular.tsx | 288 ++ src/components/fristen/fristenliste.tsx | 72 + src/components/fristen/fristkarte.tsx | 111 + src/components/fristen/kalender-abo.tsx | 62 + src/components/fristen/kalender-monat.tsx | 256 ++ .../fristen/pflichtfristen-knopf.tsx | 64 + src/components/fristen/typen.ts | 55 + src/components/kontakte/kontakt-formular.tsx | 157 + .../kontakte/protokoll-erfassung.tsx | 368 ++ src/components/navigation.tsx | 2 + src/components/ui/dialog.tsx | 106 + src/components/ui/fortschritt.tsx | 50 + src/components/vorgaenge/aufgaben-liste.tsx | 182 + src/components/vorgaenge/faelligkeit.tsx | 33 + .../vorgaenge/genehmigung-anlegen.tsx | 109 + .../vorgaenge/genehmigungs-kette.tsx | 289 ++ .../vorgaenge/sachstandsanfrage-knopf.tsx | 115 + src/components/vorgaenge/status-wechsler.tsx | 77 + .../vorgaenge/vorgaenge-ansicht.tsx | 210 ++ src/components/vorgaenge/vorgang-loeschen.tsx | 70 + .../vorgaenge/vorgangs-formular.tsx | 227 ++ src/components/vorgaenge/vorlagen-kacheln.tsx | 79 + .../vorgaenge/zeitstrahl-anzeige.tsx | 110 + src/lib/crypto/feld.ts | 6 +- src/lib/dashboard/bewertung.ts | 383 ++ src/lib/dashboard/laden.ts | 336 ++ src/lib/dashboard/typen.ts | 209 ++ src/lib/dokumente/erkennung.ts | 826 +++++ src/lib/dokumente/suche.ts | 375 ++ src/lib/finanzen/betrag.ts | 189 + src/lib/finanzen/betreuungsjahr.ts | 130 + src/lib/finanzen/camt.ts | 357 ++ src/lib/finanzen/datum.ts | 100 + src/lib/finanzen/iban.ts | 149 + src/lib/finanzen/import.ts | 572 +++ src/lib/finanzen/kassenbuch.ts | 282 ++ src/lib/finanzen/kategorien.ts | 219 ++ src/lib/finanzen/rechnungslegung.ts | 356 ++ src/lib/finanzen/vermoegen.ts | 269 ++ src/lib/fristen/ampel.ts | 115 + src/lib/fristen/datum.ts | 218 ++ src/lib/fristen/ics.ts | 242 ++ src/lib/fristen/kontext.ts | 36 + src/lib/fristen/pflichtfristen.ts | 528 +++ src/lib/fristen/wiederholung.ts | 189 + src/lib/hinweise/laden.ts | 203 ++ src/lib/immobilie/inventar.ts | 88 + src/lib/immobilie/konstanten.ts | 192 + src/lib/immobilie/kostenkonto.ts | 119 + src/lib/immobilie/leerstand-checkliste.ts | 245 ++ src/lib/kontakte/auswertung.ts | 47 + src/lib/kontakte/statistik.ts | 299 ++ src/lib/ocr/index.ts | 321 ++ src/lib/ocr/pdf-text.ts | 227 ++ src/lib/pdf/erzeugen.ts | 206 ++ src/lib/pdf/rahmen.tsx | 370 ++ src/lib/pflege/abwesenheit.ts | 109 + src/lib/pflege/konstanten.ts | 181 + src/lib/pflege/leistungszuschlag.ts | 181 + src/lib/pflege/verlauf.ts | 202 ++ src/lib/sozialhilfe/checkliste.ts | 205 ++ src/lib/sozialhilfe/feiertage.ts | 208 ++ src/lib/sozialhilfe/heimkosten.ts | 368 ++ src/lib/sozialhilfe/konstanten.ts | 256 ++ src/lib/sozialhilfe/leistungskatalog.ts | 285 ++ src/lib/sozialhilfe/nachforderungen.ts | 194 ++ src/lib/sozialhilfe/rechtsbehelf.ts | 296 ++ src/lib/sozialhilfe/schonvermoegen.ts | 320 ++ src/lib/sozialhilfe/vermoegensuebersicht.ts | 148 + src/lib/speicher/datei.ts | 141 + src/lib/speicher/dateityp.ts | 172 + src/lib/speicher/index.ts | 203 ++ src/lib/speicher/s3.ts | 104 + src/lib/speicher/schluessel.ts | 204 ++ src/lib/stammdaten/betreuungsjahr.ts | 100 + src/lib/validierung/finanzen.ts | 398 +++ src/lib/validierung/fristen.ts | 152 + src/lib/validierung/kontakte.ts | 284 ++ src/lib/validierung/vorgaenge.ts | 237 ++ src/lib/vertraege/anschreiben.ts | 89 + src/lib/vertraege/konstanten.ts | 128 + src/lib/vertraege/kuendigung.ts | 207 ++ src/lib/vertraege/kuendigungsassistent.ts | 242 ++ src/lib/vertraege/rueckkaufswert.ts | 116 + src/lib/vorgaenge/genehmigungskette.ts | 393 +++ src/lib/vorgaenge/sachstandsanfrage.ts | 145 + src/lib/vorgaenge/vorlagen.ts | 785 +++++ src/lib/vorgaenge/zeitstrahl.ts | 331 ++ src/server/actions/betreute.ts | 225 ++ src/server/actions/fristen.ts | 535 +++ src/server/actions/genehmigungen.ts | 359 ++ src/server/actions/kontakte.ts | 333 ++ src/server/actions/vorgaenge.ts | 468 +++ src/server/actions/zwei-faktor.ts | 155 + 134 files changed, 30753 insertions(+), 17 deletions(-) create mode 100644 daten/hinweise.json create mode 100644 docker-compose.dev.yml create mode 100644 docker-compose.yml create mode 100644 docs/SCHNITTSTELLEN.md create mode 100644 docs/schema-wuensche/substanz.md create mode 100644 prisma/migrations/20260822000640_fundament/migration.sql create mode 100644 prisma/migrations/migration_lock.toml create mode 100644 prisma/seed.ts create mode 100644 src/app/(app)/fristen/[id]/page.tsx create mode 100644 src/app/(app)/fristen/kalender/page.tsx create mode 100644 src/app/(app)/fristen/neu/page.tsx create mode 100644 src/app/(app)/fristen/page.tsx create mode 100644 src/app/(app)/heute/page.tsx create mode 100644 src/app/(app)/page.tsx create mode 100644 src/app/(app)/protokoll/page.tsx create mode 100644 src/app/(app)/stammdaten/bearbeiten/formular.tsx create mode 100644 src/app/(app)/stammdaten/bearbeiten/page.tsx create mode 100644 src/app/(app)/stammdaten/page.tsx create mode 100644 src/app/(app)/vorgaenge/neu/page.tsx create mode 100644 src/app/(app)/vorgaenge/page.tsx create mode 100644 src/app/(auth)/ersteinrichtung/betreuung-formular.tsx create mode 100644 src/app/(auth)/ersteinrichtung/page.tsx create mode 100644 src/app/(auth)/ersteinrichtung/zugang-formular.tsx create mode 100644 src/app/(auth)/zwei-faktor/einrichten/formular.tsx create mode 100644 src/app/(auth)/zwei-faktor/einrichten/page.tsx create mode 100644 src/app/api/gesundheit/route.ts create mode 100644 src/app/api/kalender/[signatur]/route.ts create mode 100644 src/app/page.tsx create mode 100644 src/components/dashboard/belege-kachel.tsx create mode 100644 src/components/dashboard/fristen-ampel.tsx create mode 100644 src/components/dashboard/kachel.tsx create mode 100644 src/components/dashboard/kennzahlen.tsx create mode 100644 src/components/dashboard/nachforderungen-kachel.tsx create mode 100644 src/components/dashboard/posteingang-kachel.tsx create mode 100644 src/components/dashboard/wartet-auf-andere.tsx create mode 100644 src/components/fristen/erledigen-knopf.tsx create mode 100644 src/components/fristen/filterleiste.tsx create mode 100644 src/components/fristen/frist-aktionen.tsx create mode 100644 src/components/fristen/frist-formular.tsx create mode 100644 src/components/fristen/fristenliste.tsx create mode 100644 src/components/fristen/fristkarte.tsx create mode 100644 src/components/fristen/kalender-abo.tsx create mode 100644 src/components/fristen/kalender-monat.tsx create mode 100644 src/components/fristen/pflichtfristen-knopf.tsx create mode 100644 src/components/fristen/typen.ts create mode 100644 src/components/kontakte/kontakt-formular.tsx create mode 100644 src/components/kontakte/protokoll-erfassung.tsx create mode 100644 src/components/ui/dialog.tsx create mode 100644 src/components/ui/fortschritt.tsx create mode 100644 src/components/vorgaenge/aufgaben-liste.tsx create mode 100644 src/components/vorgaenge/faelligkeit.tsx create mode 100644 src/components/vorgaenge/genehmigung-anlegen.tsx create mode 100644 src/components/vorgaenge/genehmigungs-kette.tsx create mode 100644 src/components/vorgaenge/sachstandsanfrage-knopf.tsx create mode 100644 src/components/vorgaenge/status-wechsler.tsx create mode 100644 src/components/vorgaenge/vorgaenge-ansicht.tsx create mode 100644 src/components/vorgaenge/vorgang-loeschen.tsx create mode 100644 src/components/vorgaenge/vorgangs-formular.tsx create mode 100644 src/components/vorgaenge/vorlagen-kacheln.tsx create mode 100644 src/components/vorgaenge/zeitstrahl-anzeige.tsx create mode 100644 src/lib/dashboard/bewertung.ts create mode 100644 src/lib/dashboard/laden.ts create mode 100644 src/lib/dashboard/typen.ts create mode 100644 src/lib/dokumente/erkennung.ts create mode 100644 src/lib/dokumente/suche.ts create mode 100644 src/lib/finanzen/betrag.ts create mode 100644 src/lib/finanzen/betreuungsjahr.ts create mode 100644 src/lib/finanzen/camt.ts create mode 100644 src/lib/finanzen/datum.ts create mode 100644 src/lib/finanzen/iban.ts create mode 100644 src/lib/finanzen/import.ts create mode 100644 src/lib/finanzen/kassenbuch.ts create mode 100644 src/lib/finanzen/kategorien.ts create mode 100644 src/lib/finanzen/rechnungslegung.ts create mode 100644 src/lib/finanzen/vermoegen.ts create mode 100644 src/lib/fristen/ampel.ts create mode 100644 src/lib/fristen/datum.ts create mode 100644 src/lib/fristen/ics.ts create mode 100644 src/lib/fristen/kontext.ts create mode 100644 src/lib/fristen/pflichtfristen.ts create mode 100644 src/lib/fristen/wiederholung.ts create mode 100644 src/lib/hinweise/laden.ts create mode 100644 src/lib/immobilie/inventar.ts create mode 100644 src/lib/immobilie/konstanten.ts create mode 100644 src/lib/immobilie/kostenkonto.ts create mode 100644 src/lib/immobilie/leerstand-checkliste.ts create mode 100644 src/lib/kontakte/auswertung.ts create mode 100644 src/lib/kontakte/statistik.ts create mode 100644 src/lib/ocr/index.ts create mode 100644 src/lib/ocr/pdf-text.ts create mode 100644 src/lib/pdf/erzeugen.ts create mode 100644 src/lib/pdf/rahmen.tsx create mode 100644 src/lib/pflege/abwesenheit.ts create mode 100644 src/lib/pflege/konstanten.ts create mode 100644 src/lib/pflege/leistungszuschlag.ts create mode 100644 src/lib/pflege/verlauf.ts create mode 100644 src/lib/sozialhilfe/checkliste.ts create mode 100644 src/lib/sozialhilfe/feiertage.ts create mode 100644 src/lib/sozialhilfe/heimkosten.ts create mode 100644 src/lib/sozialhilfe/konstanten.ts create mode 100644 src/lib/sozialhilfe/leistungskatalog.ts create mode 100644 src/lib/sozialhilfe/nachforderungen.ts create mode 100644 src/lib/sozialhilfe/rechtsbehelf.ts create mode 100644 src/lib/sozialhilfe/schonvermoegen.ts create mode 100644 src/lib/sozialhilfe/vermoegensuebersicht.ts create mode 100644 src/lib/speicher/datei.ts create mode 100644 src/lib/speicher/dateityp.ts create mode 100644 src/lib/speicher/index.ts create mode 100644 src/lib/speicher/s3.ts create mode 100644 src/lib/speicher/schluessel.ts create mode 100644 src/lib/stammdaten/betreuungsjahr.ts create mode 100644 src/lib/validierung/finanzen.ts create mode 100644 src/lib/validierung/fristen.ts create mode 100644 src/lib/validierung/kontakte.ts create mode 100644 src/lib/validierung/vorgaenge.ts create mode 100644 src/lib/vertraege/anschreiben.ts create mode 100644 src/lib/vertraege/konstanten.ts create mode 100644 src/lib/vertraege/kuendigung.ts create mode 100644 src/lib/vertraege/kuendigungsassistent.ts create mode 100644 src/lib/vertraege/rueckkaufswert.ts create mode 100644 src/lib/vorgaenge/genehmigungskette.ts create mode 100644 src/lib/vorgaenge/sachstandsanfrage.ts create mode 100644 src/lib/vorgaenge/vorlagen.ts create mode 100644 src/lib/vorgaenge/zeitstrahl.ts create mode 100644 src/server/actions/betreute.ts create mode 100644 src/server/actions/fristen.ts create mode 100644 src/server/actions/genehmigungen.ts create mode 100644 src/server/actions/kontakte.ts create mode 100644 src/server/actions/vorgaenge.ts create mode 100644 src/server/actions/zwei-faktor.ts diff --git a/daten/hinweise.json b/daten/hinweise.json new file mode 100644 index 0000000..10556c6 --- /dev/null +++ b/daten/hinweise.json @@ -0,0 +1,338 @@ +{ + "version": 1, + "karten": [ + { + "schluessel": "wille-leitschnur", + "titel": "Der Wille der betreuten Person gibt die Richtung vor", + "text": "Seit der Reform 2023 gilt: Nicht das, was du für vernünftig hältst, ist maßgeblich, sondern das, was die betreute Person will. Du musst ihre Wünsche ermitteln, ihnen entsprechen und sie bei der Umsetzung unterstützen. Abweichen darfst du nur, wenn sie sich dadurch erheblich selbst schädigen würde und das selbst nicht erkennen kann. Halte geäußerte Wünsche im Kontaktprotokoll fest – im Jahresbericht willst du zeigen können, dass du gefragt hast.", + "rechtsgrundlage": "§ 1821 BGB", + "link": "https://www.gesetze-im-internet.de/bgb/__1821.html", + "kontext": ["STAMMDATEN", "KONTAKTE", "BERICHTE", "GESUNDHEIT", "FRIST:jahresbericht"], + "gewicht": 10 + }, + { + "schluessel": "persoenlicher-kontakt", + "titel": "Persönlicher Kontakt ist Pflicht, nicht Kür", + "text": "Du musst die betreute Person in dem erforderlichen Umfang persönlich aufsuchen und ihre Angelegenheiten mit ihr besprechen. Ein Telefonat ersetzt den Besuch nicht. Wie oft „erforderlich“ ist, hängt vom Einzelfall ab; bei einem Heimaufenthalt hält die Praxis in der Regel mindestens einen Besuch im Quartal für nachvollziehbar. Jeder Besuch gehört ins Kontaktprotokoll, denn der Jahresbericht will Zahl und Art der Kontakte wissen.", + "rechtsgrundlage": "§ 1863 Abs. 2 BGB", + "link": "https://www.gesetze-im-internet.de/bgb/__1863.html", + "kontext": ["KONTAKTE", "BERICHTE", "FRIST:jahresbericht"], + "gewicht": 9 + }, + { + "schluessel": "wohnraum-aufgeben", + "titel": "Wohnung kündigen? Erst dem Gericht Bescheid geben", + "text": "Willst du den Wohnraum der betreuten Person aufgeben, musst du das dem Betreuungsgericht vorher mitteilen; die Kündigung selbst braucht die Genehmigung des Gerichts. Kündige nie zuerst und hole die Genehmigung hinterher – eine ohne Genehmigung ausgesprochene Kündigung kann unwirksam sein, und der Wohnraum ist trotzdem weg. Der Grund für die strenge Regel: Die Wohnung ist oft der letzte Rückweg aus dem Heim.", + "rechtsgrundlage": "§ 1833 BGB", + "link": "https://www.gesetze-im-internet.de/bgb/__1833.html", + "kontext": ["IMMOBILIE", "VORGANG:IMMOBILIE", "VORLAGE:wohnungsaufloesung", "VORLAGE:heimeinzug"], + "gewicht": 10 + }, + { + "schluessel": "grundstueck-genehmigung", + "titel": "Grundstücksgeschäfte brauchen das Gericht", + "text": "Verkauf, Belastung mit einer Grundschuld, Bestellung eines Wohnrechts, Aufhebung eines Rechts am Grundstück: All das darfst du nur mit Genehmigung des Betreuungsgerichts. Das gilt auch dann, wenn der Verkauf wirtschaftlich offensichtlich verständig ist. Plane die Genehmigung als eigenen Vorgang mit eigener Frist ein – zwischen Antrag und Rechtskraft vergehen erfahrungsgemäß Wochen bis Monate.", + "rechtsgrundlage": "§ 1850 BGB", + "link": "https://www.gesetze-im-internet.de/bgb/__1850.html", + "kontext": ["IMMOBILIE", "VORGANG:IMMOBILIE", "VORLAGE:hausverkauf", "VORLAGE:darlehen-91-sgb-xii"], + "gewicht": 10 + }, + { + "schluessel": "genehmigung-vor-geschaeft", + "titel": "Die Genehmigung muss vor dem Geschäft vorliegen", + "text": "Ein genehmigungsbedürftiges Rechtsgeschäft wird erst wirksam, wenn die Genehmigung des Gerichts vorliegt und dem Vertragspartner mitgeteilt worden ist. Unterschreibst du vorher, hängt der Vertrag in der Schwebe – und wenn das Gericht nein sagt, ist er endgültig unwirksam. Sag dem Notar oder der Gegenseite vorher, dass eine betreuungsgerichtliche Genehmigung nötig ist, und lass eine entsprechende Klausel aufnehmen.", + "rechtsgrundlage": "§ 1855, § 1858 BGB", + "link": "https://www.gesetze-im-internet.de/bgb/__1858.html", + "kontext": ["VORGAENGE", "IMMOBILIE", "VORGANG:IMMOBILIE", "VORGANG:GERICHT", "VORLAGE:hausverkauf"], + "gewicht": 10 + }, + { + "schluessel": "rechtspfleger-fragen", + "titel": "Im Zweifel vorher beim Rechtspfleger anrufen", + "text": "Für Vermögensangelegenheiten in der Betreuung ist beim Amtsgericht der Rechtspfleger zuständig, nicht der Richter. Ein Anruf vor einem größeren Schritt kostet zehn Minuten und erspart im Zweifel ein unwirksames Geschäft. Halte das Ergebnis schriftlich fest – mit Datum, Namen und dem, was gesagt wurde. Diese Notiz ist später dein Beleg dafür, dass du sorgfältig vorgegangen bist.", + "rechtsgrundlage": "§ 3 Nr. 2 Buchst. b RPflG", + "link": "https://www.gesetze-im-internet.de/rpflg_1969/__3.html", + "kontext": ["VORGAENGE", "VORGANG:GERICHT", "IMMOBILIE", "FINANZEN"], + "gewicht": 8 + }, + { + "schluessel": "vermoegensverzeichnis-frist", + "titel": "Vermögensverzeichnis: drei Monate nach der Bestellung", + "text": "Nach deiner Bestellung musst du dem Gericht ein Verzeichnis des Vermögens der betreuten Person einreichen – Konten, Bargeld, Grundbesitz, Versicherungen, Forderungen und Schulden, jeweils mit Stand zum Tag der Bestellung. Die Frist beträgt drei Monate. Fordere Kontostandsbestätigungen und Rückkaufswertmitteilungen früh an, die Institute brauchen oft mehrere Wochen.", + "rechtsgrundlage": "§ 1835 BGB", + "link": "https://www.gesetze-im-internet.de/bgb/__1835.html", + "kontext": ["FINANZEN", "BERICHTE", "FRIST:vermoegensverzeichnis"], + "gewicht": 10 + }, + { + "schluessel": "anfangsbericht", + "titel": "Anfangsbericht: die Lage zu Beginn schildern", + "text": "Zu Beginn der Betreuung berichtest du dem Gericht über die persönlichen Verhältnisse der betreuten Person, ihre Wünsche und die Ziele, die du in deinen Aufgabenkreisen verfolgst. Auch dafür gilt die Drei-Monats-Frist. Der Anfangsbericht ist zugleich dein Ausgangspunkt: Woran du später im Jahresbericht die Entwicklung misst, muss hier einmal beschrieben sein.", + "rechtsgrundlage": "§ 1863 Abs. 1 BGB", + "link": "https://www.gesetze-im-internet.de/bgb/__1863.html", + "kontext": ["BERICHTE", "FRIST:anfangsbericht"], + "gewicht": 9 + }, + { + "schluessel": "jahresbericht", + "titel": "Jahresbericht: einmal im Betreuungsjahr", + "text": "Einmal jährlich berichtest du dem Betreuungsgericht über die persönlichen Verhältnisse, über Art und Umfang deiner Kontakte, über die Wünsche der betreuten Person und darüber, ob die Betreuung fortzuführen, zu erweitern oder einzuschränken ist. Das Betreuungsjahr läuft ab dem Tag der Bestellung, nicht ab dem 1. Januar. Der Entwurf aus dieser Anwendung ist ein Textvorschlag – lies ihn durch und schreib ihn in deinen eigenen Worten zu Ende.", + "rechtsgrundlage": "§ 1863 Abs. 3 BGB", + "link": "https://www.gesetze-im-internet.de/bgb/__1863.html", + "kontext": ["BERICHTE", "FRIST:jahresbericht"], + "gewicht": 10 + }, + { + "schluessel": "vermoegensuebersicht-befreit", + "titel": "Als befreiter Betreuer: Übersicht statt voller Rechnungslegung", + "text": "Bist du als Angehöriger von der Rechnungslegung befreit, genügt dem Gericht jährlich eine Übersicht über den Bestand des Vermögens – welche Konten mit welchem Stand, welche Vermögenswerte, welche Verbindlichkeiten. Die volle Rechnungslegung mit jeder einzelnen Einnahme und Ausgabe entfällt. Belege solltest du trotzdem sammeln: Das Gericht kann sie anfordern, und am Ende der Betreuung wollen Erben sie sehen.", + "rechtsgrundlage": "§ 1859 BGB", + "link": "https://www.gesetze-im-internet.de/bgb/__1859.html", + "kontext": ["FINANZEN", "BERICHTE", "FRIST:vermoegensuebersicht"], + "gewicht": 8 + }, + { + "schluessel": "getrennte-konten", + "titel": "Niemals eigenes Geld mit dem der Betreuten vermischen", + "text": "Das Geld der betreuten Person gehört auf ihre eigenen, auf ihren Namen lautenden Konten. Zahle Rechnungen nicht „schnell mal“ von deinem Konto und hol es dir später zurück, und lege nie Geld der Betreuten auf dein Konto. Lässt es sich ausnahmsweise nicht vermeiden, dokumentiere Auslage und Erstattung mit Beleg. Vermischtes Vermögen ist der häufigste Vorwurf in Haftungsfällen und im Streit mit Miterben.", + "rechtsgrundlage": "§ 1841 BGB", + "link": "https://www.gesetze-im-internet.de/bgb/__1841.html", + "kontext": ["FINANZEN", "VORGANG:GERICHT"], + "gewicht": 10 + }, + { + "schluessel": "kontoauszuege-aufbewahren", + "titel": "Kontoauszüge lückenlos aufbewahren", + "text": "Jede Bewegung auf den Konten der betreuten Person muss sich später mit einem Beleg erklären lassen. Sammle Kontoauszüge fortlaufend und ohne Lücke, dazu Rechnungen, Quittungen und Heimabrechnungen. Fehlt ein Auszug, fordere ihn nach, solange die Bank ihn noch hat. Die Belegpflicht endet nicht mit dem Jahresbericht: Am Ende der Betreuung ist Schlussrechnung zu legen, und dann liegt die Beweislast bei dir.", + "rechtsgrundlage": "§ 1865, § 1872 BGB", + "link": "https://www.gesetze-im-internet.de/bgb/__1865.html", + "kontext": ["FINANZEN", "DOKUMENTE", "BERICHTE"], + "gewicht": 9 + }, + { + "schluessel": "aufwandspauschale", + "titel": "Aufwandspauschale: jährlich beantragen, sonst verfällt sie", + "text": "Als ehrenamtlicher Betreuer steht dir eine jährliche Aufwandspauschale zu. Sie wird nicht von selbst gezahlt: Du musst sie beantragen, und zwar innerhalb von sechs Monaten nach dem Ende des Betreuungsjahres. Das ist eine Ausschlussfrist – läuft sie ab, ist der Anspruch für dieses Jahr endgültig weg, ohne Nachsicht und ohne Wiedereinsetzung. Setz dir die Frist mit Vorwarnung in den Kalender.", + "rechtsgrundlage": "§ 1878 BGB", + "link": "https://www.gesetze-im-internet.de/bgb/__1878.html", + "kontext": ["FRISTEN", "FINANZEN", "FRIST:aufwandspauschale"], + "gewicht": 10 + }, + { + "schluessel": "betreuerausweis", + "titel": "Betreuerausweis rechtzeitig verlängern lassen", + "text": "Die Bestellungsurkunde ist dein Ausweis gegenüber Banken, Behörden und dem Heim. Sie ist oft befristet, weil auch die Betreuung befristet angeordnet wird. Läuft sie ab, verweigern Banken den Zugriff auf die Konten – mitten in laufenden Zahlungen. Leg dir die Gültigkeit als Wiedervorlage an und beantrage die Verlängerung zwei bis drei Monate vorher beim Betreuungsgericht.", + "rechtsgrundlage": "§ 1875 Abs. 2 BGB", + "link": "https://www.gesetze-im-internet.de/bgb/__1875.html", + "kontext": ["DOKUMENTE", "FRISTEN", "STAMMDATEN"], + "gewicht": 8 + }, + { + "schluessel": "haftung-betreuer", + "titel": "Du haftest für schuldhafte Pflichtverletzungen", + "text": "Verletzt du deine Pflichten und entsteht der betreuten Person dadurch ein Schaden, haftest du dafür – auch als ehrenamtlicher Angehöriger. Dein Schutz heißt Sorgfalt und Dokumentation: Entscheidungen begründen, Rückfragen bei Gericht und Bezirk festhalten, Belege sammeln. Für ehrenamtliche Betreuer besteht über die Länder in der Regel eine Sammelhaftpflichtversicherung; frag beim örtlichen Betreuungsverein nach, was sie abdeckt.", + "rechtsgrundlage": "§ 1826 BGB", + "link": "https://www.gesetze-im-internet.de/bgb/__1826.html", + "kontext": ["STAMMDATEN", "VORGAENGE", "FINANZEN"], + "gewicht": 7 + }, + { + "schluessel": "zwangsmassnahme", + "titel": "Ärztliche Zwangsmaßnahmen: nur mit Genehmigung", + "text": "Eine ärztliche Behandlung gegen den natürlichen Willen der betreuten Person ist nur unter engen Voraussetzungen und nur mit Genehmigung des Betreuungsgerichts zulässig. Vorausgesetzt wird unter anderem, dass zuvor ernsthaft versucht wurde, die Person zu überzeugen, dass die Maßnahme im Rahmen eines stationären Aufenthalts stattfindet und dass der drohende Schaden erheblich ist. Frag im Zweifel sofort beim Gericht nach – Eile ist kein Grund, den Weg abzukürzen.", + "rechtsgrundlage": "§ 1832 BGB", + "link": "https://www.gesetze-im-internet.de/bgb/__1832.html", + "kontext": ["GESUNDHEIT", "VORGANG:HEIM_PFLEGE"], + "gewicht": 9 + }, + { + "schluessel": "unterbringung", + "titel": "Freiheitsentziehende Maßnahmen nur mit Beschluss", + "text": "Eine geschlossene Unterbringung und freiheitsentziehende Maßnahmen wie Bettgitter, Bauchgurt oder ein ruhigstellendes Medikament zur Bewegungsbeschränkung bedürfen der Genehmigung des Betreuungsgerichts, wenn sie regelmäßig oder über längere Zeit erfolgen. Das gilt auch im Pflegeheim. Ohne Beschluss darf die Einrichtung solche Maßnahmen nicht dauerhaft anwenden – frag nach, wenn dir etwas auffällt.", + "rechtsgrundlage": "§ 1831 BGB", + "link": "https://www.gesetze-im-internet.de/bgb/__1831.html", + "kontext": ["GESUNDHEIT", "VORGANG:HEIM_PFLEGE"], + "gewicht": 9 + }, + { + "schluessel": "schonvermoegen", + "titel": "Schonvermögen: 10.000 Euro bleiben geschützt", + "text": "Sozialhilfe – dazu gehört die Hilfe zur Pflege – setzt voraus, dass eigenes Vermögen eingesetzt wird. Geschützt bleibt aber ein Schonbetrag von derzeit 10.000 Euro je leistungsberechtigter Person. Erst was darüber liegt, ist einzusetzen, und auch das nur, soweit es keine Härte bedeutet. Rechne den Betrag nach, bevor du dem Bezirk eine Verwertung zusagst.", + "rechtsgrundlage": "§ 90 Abs. 2 Nr. 9 SGB XII", + "link": "https://www.gesetze-im-internet.de/sgb_12/__90.html", + "kontext": ["SOZIALHILFE", "FINANZEN", "VORGANG:SOZIALHILFE", "VORLAGE:antrag-hilfe-zur-pflege"], + "gewicht": 10 + }, + { + "schluessel": "hausgrundstueck-geschuetzt", + "titel": "Das selbst bewohnte Hausgrundstück ist geschützt", + "text": "Ein angemessenes Hausgrundstück, das die leistungsberechtigte Person allein oder mit Angehörigen ganz oder teilweise bewohnt, darf nicht zur Verwertung verlangt werden. Beim Umzug ins Heim wird das strittig: Der Träger sieht das Objekt dann oft nicht mehr als „bewohnt“ an. Argumentiere mit der konkreten Rückkehrperspektive, mit dem Wohnrecht von Angehörigen und – falls das nicht trägt – mit der Härteregelung.", + "rechtsgrundlage": "§ 90 Abs. 2 Nr. 8 SGB XII", + "link": "https://www.gesetze-im-internet.de/sgb_12/__90.html", + "kontext": ["SOZIALHILFE", "IMMOBILIE", "VORGANG:IMMOBILIE", "VORGANG:SOZIALHILFE"], + "gewicht": 10 + }, + { + "schluessel": "haerte-verwertung", + "titel": "Härteregelung: wenn die Verwertung unzumutbar wäre", + "text": "Auch Vermögen, das nach dem Katalog eigentlich einzusetzen wäre, darf nicht verlangt werden, wenn das für die betroffene Person oder ihre Angehörigen eine Härte bedeuten würde. Das ist der Auffangtatbestand für alles, was der Gesetzgeber nicht vorhergesehen hat – etwa ein Verkauf weit unter Wert oder der Verlust der Wohnung eines pflegenden Angehörigen. Die Härte muss man geltend machen und begründen; von allein prüft sie kaum ein Träger.", + "rechtsgrundlage": "§ 90 Abs. 3 SGB XII", + "link": "https://www.gesetze-im-internet.de/sgb_12/__90.html", + "kontext": ["SOZIALHILFE", "IMMOBILIE", "VORGANG:SOZIALHILFE"], + "gewicht": 9 + }, + { + "schluessel": "bestattungsvorsorge", + "titel": "Bestattungsvorsorge ist geschütztes Vermögen", + "text": "Geld, das zweckgebunden für die eigene Bestattung zurückgelegt ist – ein Bestattungsvorsorgevertrag oder ein treuhänderisch gebundenes Vorsorgekonto in angemessener Höhe –, ist als Härtefall geschützt und muss nicht für die Heimkosten eingesetzt werden. Entscheidend ist die Zweckbindung: Ein normales Sparbuch „für später“ genügt nicht. Lege dem Bezirk den Vertrag oder die Verpfändungserklärung vor.", + "rechtsgrundlage": "§ 90 Abs. 3 SGB XII", + "link": "https://www.gesetze-im-internet.de/sgb_12/__90.html", + "kontext": ["SOZIALHILFE", "FINANZEN", "VERTRAEGE", "VORGANG:SOZIALHILFE"], + "gewicht": 8 + }, + { + "schluessel": "darlehen-statt-verwertung", + "titel": "Darlehen statt sofortigem Verkauf", + "text": "Kann Vermögen – typischerweise eine Immobilie – nicht sofort oder nur mit erheblichem Verlust verwertet werden, ist die Sozialhilfe als Darlehen zu leisten. Der Träger sichert sich dann meist durch eine Grundschuld ab. Das ist oft die bessere Lösung als ein Notverkauf: Die Leistung fließt sofort, das Haus bleibt vorerst, und die Rückzahlung fällt erst später an. Beantrage das ausdrücklich und begründe, warum ein sofortiger Verkauf unzumutbar wäre.", + "rechtsgrundlage": "§ 91 SGB XII", + "link": "https://www.gesetze-im-internet.de/sgb_12/__91.html", + "kontext": ["SOZIALHILFE", "IMMOBILIE", "VORLAGE:darlehen-91-sgb-xii", "VORGANG:SOZIALHILFE"], + "gewicht": 10 + }, + { + "schluessel": "sozialhilfe-kenntnisgrundsatz", + "titel": "Sozialhilfe beginnt mit der Kenntnis des Trägers", + "text": "Anders als andere Sozialleistungen setzt die Sozialhilfe nicht erst mit dem förmlichen Antrag ein, sondern sobald der Träger von der Notlage Kenntnis erlangt. Deshalb gilt: sofort melden, auch wenn noch Unterlagen fehlen. Eine kurze schriftliche Mitteilung mit Datum sichert dir den früheren Leistungsbeginn; die Unterlagen kannst du nachreichen. Bewahre den Sendenachweis auf.", + "rechtsgrundlage": "§ 18 SGB XII", + "link": "https://www.gesetze-im-internet.de/sgb_12/__18.html", + "kontext": ["SOZIALHILFE", "VORLAGE:antrag-hilfe-zur-pflege", "VORGANG:SOZIALHILFE"], + "gewicht": 10 + }, + { + "schluessel": "schenkungsrueckforderung", + "titel": "Früher verschenkt? Danach wird gefragt", + "text": "Wer eine Schenkung gemacht hat und später selbst bedürftig wird, kann sie innerhalb von zehn Jahren nach der Übergabe zurückfordern, soweit er seinen Unterhalt nicht mehr bestreiten kann. Der Sozialhilfeträger lässt sich diesen Anspruch regelmäßig überleiten und wendet sich dann an den Beschenkten. Rechne bei der Antragstellung damit, dass nach Übertragungen der letzten zehn Jahre gefragt wird, und lege sie von sich aus offen.", + "rechtsgrundlage": "§ 528 BGB, § 93 SGB XII", + "link": "https://www.gesetze-im-internet.de/bgb/__528.html", + "kontext": ["SOZIALHILFE", "IMMOBILIE", "FINANZEN", "VORLAGE:antrag-hilfe-zur-pflege"], + "gewicht": 9 + }, + { + "schluessel": "widerspruchsfrist", + "titel": "Widerspruch: ein Monat ab Bekanntgabe", + "text": "Gegen einen Bescheid des Sozialhilfeträgers kannst du innerhalb eines Monats nach Bekanntgabe Widerspruch einlegen. Maßgeblich ist nicht das Datum auf dem Bescheid, sondern der Zugang – notiere deshalb immer das Eingangsdatum. Ein Widerspruch braucht zunächst keine Begründung: „Hiermit lege ich Widerspruch ein, die Begründung reiche ich nach“ wahrt die Frist.", + "rechtsgrundlage": "§ 84 SGG", + "link": "https://www.gesetze-im-internet.de/sgg/__84.html", + "kontext": ["SOZIALHILFE", "FRISTEN", "DOKUMENTE", "VORLAGE:widerspruch-bescheid"], + "gewicht": 10 + }, + { + "schluessel": "fehlende-rechtsbehelfsbelehrung", + "titel": "Fehlt die Rechtsbehelfsbelehrung, hast du ein Jahr Zeit", + "text": "Ist ein Bescheid ohne oder mit falscher Rechtsbehelfsbelehrung ergangen, beträgt die Widerspruchsfrist nicht einen Monat, sondern ein Jahr ab Bekanntgabe. Prüfe deshalb bei jedem Bescheid: Steht am Ende eine Belehrung? Nennt sie die richtige Stelle, die richtige Frist und die richtige Form? Wenn nicht, ist ein scheinbar verfristeter Widerspruch oft noch möglich.", + "rechtsgrundlage": "§ 66 Abs. 2 SGG", + "link": "https://www.gesetze-im-internet.de/sgg/__66.html", + "kontext": ["SOZIALHILFE", "FRISTEN", "DOKUMENTE", "VORLAGE:widerspruch-bescheid"], + "gewicht": 9 + }, + { + "schluessel": "leistungszuschlag", + "titel": "Leistungszuschlag: der Eigenanteil sinkt mit den Jahren", + "text": "Die Pflegekasse zahlt zum einrichtungseinheitlichen Eigenanteil einen Zuschlag, der mit der Dauer des Heimaufenthalts steigt – gestaffelt nach dem ersten, zweiten, dritten und ab dem vierten Jahr. Er wird von der Einrichtung mit der Pflegekasse abgerechnet und muss nicht gesondert beantragt werden. Prüfe die Heimrechnung trotzdem: Der Wechsel in die nächste Stufe wird gelegentlich übersehen.", + "rechtsgrundlage": "§ 43c SGB XI", + "link": "https://www.gesetze-im-internet.de/sgb_11/__43c.html", + "kontext": ["GESUNDHEIT", "SOZIALHILFE", "FINANZEN", "VORGANG:HEIM_PFLEGE"], + "gewicht": 8 + }, + { + "schluessel": "pflegegrad-hoeherstufung", + "titel": "Verschlechtert sich der Zustand: Höherstufung beantragen", + "text": "Verschlechtert sich der Gesundheitszustand dauerhaft, kannst du jederzeit eine erneute Begutachtung durch den Medizinischen Dienst beantragen; ein formloses Schreiben an die Pflegekasse genügt. Bereite die Begutachtung vor: ein Pflegetagebuch über ein bis zwei Wochen, aktuelle Arztberichte, die Einschätzung der Pflegekräfte im Heim. Ein höherer Pflegegrad bedeutet mehr Leistung der Pflegekasse und damit einen geringeren Eigenanteil aus dem Vermögen.", + "rechtsgrundlage": "§ 18 SGB XI", + "link": "https://www.gesetze-im-internet.de/sgb_11/__18.html", + "kontext": ["GESUNDHEIT", "VORLAGE:pflegegrad-hoeherstufung", "FRIST:pflegegrad-pruefung", "VORGANG:HEIM_PFLEGE"], + "gewicht": 9 + }, + { + "schluessel": "heimkosten-abwesenheit", + "titel": "Krankenhaus und Abwesenheit mindern den Heimsatz", + "text": "Ist die betreute Person vorübergehend abwesend – etwa im Krankenhaus oder in Kurzzeitpflege –, darf die Einrichtung nach den vereinbarten Abwesenheitsregeln nur einen geminderten Satz berechnen. Die genauen Abschläge stehen im Heimvertrag und im Rahmenvertrag des Landes. Notiere jeden Abwesenheitstag mit Datum und prüfe die Heimrechnung daraufhin nach; hier liegt häufig Geld auf der Straße.", + "rechtsgrundlage": "§ 87a SGB XI", + "link": "https://www.gesetze-im-internet.de/sgb_11/__87a.html", + "kontext": ["GESUNDHEIT", "FINANZEN", "VORGANG:HEIM_PFLEGE"], + "gewicht": 8 + }, + { + "schluessel": "zuzahlungsbefreiung", + "titel": "Zuzahlungsbefreiung: jedes Jahr neu beantragen", + "text": "Zuzahlungen zu Medikamenten, Heil- und Hilfsmitteln sind auf zwei Prozent der jährlichen Bruttoeinnahmen begrenzt, bei chronisch Kranken auf ein Prozent. Ist die Belastungsgrenze erreicht, stellt die Krankenkasse eine Befreiung für den Rest des Kalenderjahres aus – aber nur auf Antrag und nur für dieses Jahr. Sammle die Quittungen und stelle den Antrag zu Jahresbeginn erneut.", + "rechtsgrundlage": "§ 62 SGB V", + "link": "https://www.gesetze-im-internet.de/sgb_5/__62.html", + "kontext": ["GESUNDHEIT", "FRISTEN", "FRIST:zuzahlungsbefreiung"], + "gewicht": 8 + }, + { + "schluessel": "landespflegegeld", + "titel": "Bayerisches Landespflegegeld: Antrag bis 31. Dezember", + "text": "Bayern zahlt Menschen mit Pflegegrad 2 bis 5 und Hauptwohnsitz in Bayern ein jährliches Landespflegegeld. Es wird unabhängig von Einkommen und Vermögen gezahlt und nicht auf die Sozialhilfe angerechnet. Der Antrag für das laufende Jahr muss bis zum 31. Dezember gestellt sein. Nach der ersten Bewilligung läuft die Zahlung in der Regel weiter – prüfe trotzdem jährlich den Eingang.", + "rechtsgrundlage": "Art. 1 ff. BayLPflGG", + "link": "https://www.gesetze-bayern.de/Content/Document/BayLPflGG", + "kontext": ["GESUNDHEIT", "FINANZEN", "FRISTEN", "FRIST:landespflegegeld"], + "gewicht": 8 + }, + { + "schluessel": "rundfunkbeitragsbefreiung", + "titel": "Rundfunkbeitrag: Befreiung oder Ermäßigung prüfen", + "text": "Wer Leistungen der Hilfe zur Pflege in einer Einrichtung bezieht, kann sich vom Rundfunkbeitrag befreien lassen; bei Merkzeichen RF ermäßigt er sich auf ein Drittel. Für die Wohnung, die nach dem Heimeinzug leer steht, entfällt die Beitragspflicht ohnehin, sobald dort niemand mehr gemeldet ist – das muss man dem Beitragsservice aber mitteilen. Beides läuft nicht von allein.", + "rechtsgrundlage": "§ 4 RBStV", + "link": "https://www.gesetze-bayern.de/Content/Document/RBStV-4", + "kontext": ["SOZIALHILFE", "VERTRAEGE", "IMMOBILIE", "VORLAGE:heimeinzug"], + "gewicht": 6 + }, + { + "schluessel": "leerstand-versicherung", + "titel": "Leerstand dem Gebäudeversicherer melden", + "text": "Steht ein Gebäude leer, ist das eine Gefahrerhöhung im Sinne des Versicherungsvertragsrechts. Meldest du sie nicht, kann der Versicherer im Schadensfall die Leistung kürzen oder ganz verweigern – und ein Wasserschaden im unbeheizten Haus ist teuer. Melde den Leerstand schriftlich, lass dir die Fortgeltung des Schutzes bestätigen und halte die vereinbarten Auflagen ein, etwa regelmäßige Kontrollgänge.", + "rechtsgrundlage": "§ 23 VVG", + "link": "https://www.gesetze-im-internet.de/vvg_2008/__23.html", + "kontext": ["IMMOBILIE", "VERTRAEGE", "VORGANG:VERSICHERUNG", "VORLAGE:wohnungsaufloesung"], + "gewicht": 9 + }, + { + "schluessel": "verkehrssicherungspflicht", + "titel": "Auch ein leeres Haus muss sicher sein", + "text": "Wer ein Grundstück hält, muss dafür sorgen, dass von ihm keine Gefahr ausgeht: Winterdienst, Baumkontrolle, lose Dachziegel, gesicherte Zugänge. Diese Pflicht trifft dich als Betreuer stellvertretend für die Eigentümerin. Organisiere feste Kontrollgänge mit Datum und Fotos, beauftrage Räum- und Streudienst schriftlich und prüfe, ob die Haus- und Grundbesitzerhaftpflicht noch läuft.", + "rechtsgrundlage": "§ 823 BGB", + "link": "https://www.gesetze-im-internet.de/bgb/__823.html", + "kontext": ["IMMOBILIE", "VORGANG:IMMOBILIE", "VERTRAEGE"], + "gewicht": 8 + }, + { + "schluessel": "versicherung-rueckkaufswert", + "titel": "Lebensversicherungen: Rückkaufswerte jährlich abfragen", + "text": "Kapitalbildende Lebens- und Rentenversicherungen zählen mit ihrem Rückkaufswert zum Vermögen und interessieren sowohl das Betreuungsgericht als auch den Sozialhilfeträger. Fordere die Werte einmal jährlich schriftlich an, am besten zum selben Stichtag wie die Kontostände. Vor einer Kündigung prüfen: Ein Verwertungsausschluss oder die Umwandlung in eine Sterbegeldversicherung kann günstiger sein als der Rückkauf.", + "rechtsgrundlage": "§ 90 SGB XII, § 1865 BGB", + "link": "https://www.gesetze-im-internet.de/sgb_12/__90.html", + "kontext": ["VERTRAEGE", "FINANZEN", "SOZIALHILFE", "VORLAGE:versicherungsverwertung", "FRIST:rueckkaufswerte"], + "gewicht": 7 + }, + { + "schluessel": "zugang-eingangsdatum", + "titel": "Bescheide zählen ab Zugang – Eingangsdatum notieren", + "text": "Für jede Rechtsbehelfsfrist kommt es auf den Zugang an, nicht auf das Datum im Briefkopf. Notiere deshalb bei jedem behördlichen Schreiben den Tag, an dem es bei dir angekommen ist, und hefte den Umschlag mit dem Poststempel dazu, wenn es knapp werden könnte. Diese Anwendung fragt das Eingangsdatum beim Erfassen ab – füll es aus, es ist später der Anker jeder Fristberechnung.", + "rechtsgrundlage": "§ 37 SGB X", + "link": "https://www.gesetze-im-internet.de/sgb_10/__37.html", + "kontext": ["DOKUMENTE", "FRISTEN", "SOZIALHILFE"], + "gewicht": 8 + }, + { + "schluessel": "steuererklaerung-pflicht", + "titel": "Steuererklärung gehört zur Vermögenssorge", + "text": "Zur Vermögenssorge gehört auch, die steuerlichen Pflichten der betreuten Person zu erfüllen. Prüfe jährlich, ob eine Erklärung abzugeben ist – bei Renten oberhalb des Grundfreibetrags ist das häufiger der Fall, als man denkt. Umgekehrt lohnt eine freiwillige Erklärung oft: Heimkosten und Pflegeaufwendungen sind als außergewöhnliche Belastung absetzbar und führen nicht selten zu einer Erstattung.", + "rechtsgrundlage": "§ 33, § 33a EStG", + "link": "https://www.gesetze-im-internet.de/estg/__33.html", + "kontext": ["FINANZEN", "VORGANG:STEUERN", "VORLAGE:steuererklaerung"], + "gewicht": 6 + } + ] +} diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..594c45c --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,50 @@ +# ============================================================================= +# Nur fuer die Entwicklung: Postgres und MinIO auf dem eigenen Rechner. +# ============================================================================= +# +# docker compose -f docker-compose.dev.yml up -d +# +# Anders als im Produktivbetrieb sind die Ports hier freigegeben - aber +# ausschliesslich an 127.0.0.1, damit nichts ins Netz hinausgeht. Die +# Anwendung selbst laeuft dabei mit "npm run dev" auf dem Host. +# ============================================================================= + +name: betreuungs-cockpit-dev + +services: + db: + image: postgres:17-alpine + restart: unless-stopped + environment: + POSTGRES_USER: cockpit + POSTGRES_PASSWORD: entwicklung + POSTGRES_DB: betreuungs_cockpit + POSTGRES_INITDB_ARGS: '--encoding=UTF8 --locale=de_DE.UTF-8' + LANG: de_DE.UTF-8 + TZ: Europe/Berlin + ports: + - '127.0.0.1:5433:5432' + volumes: + - dev-postgres:/var/lib/postgresql/data + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U cockpit -d betreuungs_cockpit'] + interval: 5s + timeout: 5s + retries: 20 + + minio: + image: minio/minio:RELEASE.2025-01-20T14-49-07Z + restart: unless-stopped + command: server /daten --console-address ":9001" + environment: + MINIO_ROOT_USER: entwicklung + MINIO_ROOT_PASSWORD: entwicklung-geheim + ports: + - '127.0.0.1:9000:9000' + - '127.0.0.1:9001:9001' + volumes: + - dev-minio:/daten + +volumes: + dev-postgres: + dev-minio: diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..1252677 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,154 @@ +# ============================================================================= +# Betreuungs-Cockpit - Produktivbetrieb +# ============================================================================= +# +# docker compose up -d --build +# +# Kein Reverse Proxy in dieser Datei: der ist auf dem Server bereits +# vorhanden. Die Anwendung horcht nur auf 127.0.0.1:3000 - von aussen +# erreichbar wird sie ausschliesslich ueber den vorhandenen Proxy, der +# auch TLS beendet. Siehe README, Abschnitt "Reverse Proxy". +# +# Die Datenbank hat bewusst KEINE Portfreigabe. Sie ist nur ueber das +# interne Docker-Netz erreichbar. Wer mit psql heran muss, geht ueber +# docker compose exec db psql -U cockpit betreuungs_cockpit +# ============================================================================= + +name: betreuungs-cockpit + +services: + db: + image: postgres:17-alpine + restart: unless-stopped + environment: + POSTGRES_USER: ${POSTGRES_USER:-cockpit} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD fehlt in der .env} + POSTGRES_DB: ${POSTGRES_DB:-betreuungs_cockpit} + # Deutsche Sortierung und Zeichensatz - wichtig fuer Namenslisten + # und fuer die Volltextsuche mit der Konfiguration "german". + POSTGRES_INITDB_ARGS: '--encoding=UTF8 --locale=de_DE.UTF-8' + LANG: de_DE.UTF-8 + volumes: + - postgres-daten:/var/lib/postgresql/data + - ./backups:/backups + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U ${POSTGRES_USER:-cockpit} -d ${POSTGRES_DB:-betreuungs_cockpit}'] + interval: 10s + timeout: 5s + retries: 10 + start_period: 30s + networks: [intern] + # Kein "ports:" - die Datenbank ist von aussen nicht erreichbar. + + minio: + # S3-kompatibler Objektspeicher fuer die Dokumente. Wer einen externen + # Speicher in der EU nutzt (z.B. Hetzner Object Storage), kann diesen + # Dienst weglassen und in der .env S3_ENDPOINT darauf zeigen lassen. + # Auch dann gilt: AV-Vertrag nach Art. 28 DSGVO abschliessen. + image: minio/minio:RELEASE.2025-01-20T14-49-07Z + restart: unless-stopped + command: server /daten --console-address ":9001" + environment: + MINIO_ROOT_USER: ${S3_ACCESS_KEY_ID:?S3_ACCESS_KEY_ID fehlt in der .env} + MINIO_ROOT_PASSWORD: ${S3_SECRET_ACCESS_KEY:?S3_SECRET_ACCESS_KEY fehlt in der .env} + MINIO_BROWSER_REDIRECT_URL: ${MINIO_KONSOLE_URL:-http://127.0.0.1:9001} + volumes: + - minio-daten:/daten + healthcheck: + test: ['CMD', 'mc', 'ready', 'local'] + interval: 15s + timeout: 5s + retries: 10 + start_period: 20s + ports: + # Nur an die Loopback-Adresse gebunden. Die Konsole erreichst du + # ueber einen SSH-Tunnel oder ueber deinen vorhandenen Proxy. + - '127.0.0.1:9001:9001' + networks: [intern] + + app: + build: + context: . + dockerfile: Dockerfile + restart: unless-stopped + depends_on: + db: + condition: service_healthy + minio: + condition: service_healthy + environment: + NODE_ENV: production + DATABASE_URL: postgresql://${POSTGRES_USER:-cockpit}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-betreuungs_cockpit}?schema=public&connection_limit=10 + AUTH_SECRET: ${AUTH_SECRET:?AUTH_SECRET fehlt in der .env} + AUTH_URL: ${AUTH_URL:?AUTH_URL fehlt in der .env} + AUTH_TRUST_HOST: 'true' + ENC_KEY_V1: ${ENC_KEY_V1:?ENC_KEY_V1 fehlt in der .env - ohne ihn sind die verschluesselten Felder nicht lesbar} + ENC_KEY_V2: ${ENC_KEY_V2:-} + ENC_KEY_AKTIV: ${ENC_KEY_AKTIV:-1} + INDEX_KEY: ${INDEX_KEY:?INDEX_KEY fehlt in der .env} + SESSION_MAX_AGE: ${SESSION_MAX_AGE:-1800} + REAUTH_MAX_AGE: ${REAUTH_MAX_AGE:-300} + LOGIN_MAX_VERSUCHE: ${LOGIN_MAX_VERSUCHE:-5} + LOGIN_FENSTER_SEKUNDEN: ${LOGIN_FENSTER_SEKUNDEN:-900} + LOGIN_SPERRE_SEKUNDEN: ${LOGIN_SPERRE_SEKUNDEN:-900} + SPEICHER_TREIBER: s3 + S3_ENDPOINT: ${S3_ENDPOINT:-http://minio:9000} + S3_REGION: ${S3_REGION:-eu-central} + S3_BUCKET: ${S3_BUCKET:-betreuungs-cockpit} + S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID} + S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY} + UPLOAD_MAX_BYTES: ${UPLOAD_MAX_BYTES:-26214400} + OCR_AKTIV: ${OCR_AKTIV:-true} + SMTP_HOST: ${SMTP_HOST:-} + SMTP_PORT: ${SMTP_PORT:-587} + SMTP_USER: ${SMTP_USER:-} + SMTP_PASSWORD: ${SMTP_PASSWORD:-} + SMTP_FROM: ${SMTP_FROM:-} + TZ: Europe/Berlin + ports: + # Nur Loopback. Der vorhandene Reverse Proxy auf dem Host reicht + # von aussen hierher durch und beendet TLS. + - '127.0.0.1:${APP_PORT:-3000}:3000' + networks: [intern] + healthcheck: + test: ['CMD', 'node', '-e', "fetch('http://127.0.0.1:3000/api/gesundheit').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"] + interval: 30s + timeout: 5s + retries: 5 + start_period: 40s + + sicherung: + # Taeglich um 03:15 Uhr ein verschluesseltes Backup nach ./backups. + # Legt zusaetzlich die Pruefsumme ab und raeumt alte Staende auf. + image: postgres:17-alpine + restart: unless-stopped + depends_on: + db: + condition: service_healthy + environment: + PGHOST: db + PGUSER: ${POSTGRES_USER:-cockpit} + PGPASSWORD: ${POSTGRES_PASSWORD} + PGDATABASE: ${POSTGRES_DB:-betreuungs_cockpit} + SICHERUNG_PASSWORT: ${SICHERUNG_PASSWORT:?SICHERUNG_PASSWORT fehlt in der .env} + SICHERUNG_AUFBEWAHRUNG_TAGE: ${SICHERUNG_AUFBEWAHRUNG_TAGE:-30} + TZ: Europe/Berlin + volumes: + - ./backups:/backups + - ./scripts/sicherung.sh:/sicherung.sh:ro + entrypoint: ['/bin/sh', '-c'] + command: + - | + apk add --no-cache openssl coreutils >/dev/null + echo "15 3 * * * /bin/sh /sicherung.sh >> /backups/sicherung.log 2>&1" > /etc/crontabs/root + echo "Sicherungsdienst bereit. Naechster Lauf 03:15 Uhr." + crond -f -l 8 + networks: [intern] + +volumes: + postgres-daten: + minio-daten: + +networks: + intern: + driver: bridge diff --git a/docs/SCHNITTSTELLEN.md b/docs/SCHNITTSTELLEN.md new file mode 100644 index 0000000..1c1bdb5 --- /dev/null +++ b/docs/SCHNITTSTELLEN.md @@ -0,0 +1,266 @@ +# Schnittstellen zwischen den Modulen + +Verträge, auf die sich mehrere Etappen stützen. Wer eine dieser Schnittstellen +implementiert, hält sich exakt an die hier beschriebene Signatur; wer sie +benutzt, darf sich darauf verlassen. + +--- + +## 1. Dateiablage — `src/lib/speicher/index.ts` + +Zwei Treiber hinter einer Schnittstelle: `datei` (lokales Verzeichnis, für +Entwicklung und kleine Installationen) und `s3` (S3-kompatibler Speicher in +der EU). Gesteuert über `SPEICHER_TREIBER` bzw. das Vorhandensein von +`S3_ACCESS_KEY_ID`. + +```ts +export type Ablageziel = { + /** Schlüssel im Speicher, z.B. "betreute//dokumente/.pdf" */ + schluessel: string + groesseBytes: number + /** SHA-256 hex — Nachweis der Unverändertheit gegenüber Gericht. */ + pruefsumme: string +} + +/** Legt eine Datei ab. Der Schlüssel wird erzeugt, nie vom Nutzer bestimmt. */ +export function ablegen( + betreuteId: string, + bereich: 'dokumente' | 'berichte' | 'exporte' | 'vorschau', + dateiname: string, + inhalt: Buffer, + mimeTyp: string, +): Promise + +/** Holt eine Datei zurück in den Speicher des Servers. */ +export function lesen(schluessel: string): Promise + +/** + * Kurzlebige, signierte URL. Gültigkeit in Sekunden, Standard 300. + * Beim Dateitreiber zeigt sie auf eine eigene, ebenfalls signierte Route. + */ +export function signierteUrl(schluessel: string, sekunden?: number): Promise + +export function entfernen(schluessel: string): Promise +export function existiert(schluessel: string): Promise +``` + +**Regeln** + +- Der Schlüssel enthält immer die `betreuteId` als zweites Segment. Jede + Leseoperation prüft vorher, dass der angemeldete Benutzer Zugriff auf + genau diese `betreuteId` hat. +- Uploads werden nie als HTML ausgeliefert. Ausgehende Antworten tragen + `Content-Disposition: attachment` und `X-Content-Type-Options: nosniff`. +- Erlaubte Typen: `application/pdf`, `image/jpeg`, `image/png`, + `image/heic`, `image/webp`, `image/tiff`. Geprüft wird an den **Magic + Bytes**, nicht am gemeldeten MIME-Typ und nicht an der Endung. +- Größenlimit aus `env.uploadMaxBytes` (Standard 25 MB). + +--- + +## 2. PDF-Erzeugung — `src/lib/pdf/` + +Grundlage ist `@react-pdf/renderer` (kein Chromium im Container). + +### `src/lib/pdf/rahmen.tsx` + +```tsx +export type Berichtskopf = { + titel: string + betreute: { name: string; geburtsdatum: Date | null } + /** z.B. "Amtsgericht Bayreuth · Az. XVII 123/25" */ + behoerde?: string + aktenzeichen?: string + zeitraumVon?: Date | null + zeitraumBis?: Date | null + stichtag?: Date | null + rechtsgrundlage?: string + erstelltAm: Date + erstelltVon: string +} + +/** Seitenrahmen mit Kopf, Fußzeile (Seite x von y, Erstelldatum) und + * optionalem Unterschriftenfeld. Jeder Bericht benutzt ihn. */ +export function Berichtsseite(props: { + kopf: Berichtskopf + mitUnterschrift?: boolean + children: React.ReactNode +}): JSX.Element +``` + +### `src/lib/pdf/erzeugen.ts` + +```ts +/** Rendert ein react-pdf-Dokument zu einem Buffer. */ +export function pdfErzeugen(dokument: React.ReactElement): Promise + +/** + * Fügt mehrere PDFs zu einem zusammen (Belegmappe, Unterlagenpaket). + * Bilder werden vorher in einseitige PDFs gewandelt. + * Nutzt pdf-lib. + */ +export function pdfsZusammenfuehren(teile: Buffer[]): Promise + +/** Seitenzahlen und Fußzeile über ein zusammengeführtes PDF legen. */ +export function paginieren(pdf: Buffer, fusszeile: string): Promise +``` + +**Regeln** + +- Schrift: die in `@react-pdf/renderer` eingebauten Standardschriften + (Helvetica). Kein Nachladen von Google Fonts. +- Jeder Bericht trägt Kopf (Betreute, Aktenzeichen, Gericht/Behörde, + Zeitraum), Fußzeile mit Seitenzahl und Erstellungsdatum sowie ein + Unterschriftenfeld. +- Beträge über `formatCent()`, Daten über `formatDatum()`. + +--- + +## 3. OCR — `src/lib/ocr/index.ts` + +```ts +export type OcrErgebnis = { + text: string + status: 'fertig' | 'fehlgeschlagen' | 'nicht_verfuegbar' + seitenzahl?: number + meldung?: string +} + +/** Ruft Tesseract (deutsch) auf. Nie werfen — Status zurückgeben. */ +export function ocrLesen(inhalt: Buffer, mimeTyp: string): Promise + +/** Ist Tesseract auf diesem System vorhanden? */ +export function ocrVerfuegbar(): Promise +``` + +**Regeln** + +- Aufruf über das System-Binary `tesseract` (`env.tesseractBefehl`), + Sprache `deu`. Im Docker-Image ist es installiert, auf einem Windows- + Entwicklungsrechner in der Regel nicht — dann liefert die Funktion + `nicht_verfuegbar`, und der Upload funktioniert trotzdem. +- PDFs werden vorher mit `pdftoppm` in Bilder gewandelt; fehlt auch das, + wird der eingebettete Text des PDFs mit `pdf-lib` gelesen. +- Der OCR-Lauf blockiert den Upload nicht: Dokument wird mit + `ocrStatus = "ausstehend"` gespeichert, die Erkennung läuft danach. + +--- + +## 4. Fristenableitung — `src/lib/fristen/pflichtfristen.ts` + +```ts +export type Pflichtfrist = { + /** Stabiler Schlüssel, verhindert Dubletten: "anfangsbericht", + * "jahresbericht-2027", "aufwandspauschale-2026", … */ + systemSchluessel: string + titel: string + beschreibung: string + datum: Date + rechtsgrundlage: string + typ: 'GESETZLICH' | 'BEHOERDLICH' | 'SELBST_GESETZT' | 'WIEDERVORLAGE' + ausschlussfrist: boolean + vorwarnungTage: number[] + wiederholungRrule?: string +} + +/** Leitet alle Pflichtfristen aus den Stammdaten ab. Rein, ohne Datenbank. */ +export function pflichtfristenAbleiten(stammdaten: { + bestellungAm: Date | null + befreiterBetreuer: boolean + ehrenamtlich: boolean + pflegegrad: number | null + jahreVoraus?: number +}): Pflichtfrist[] + +/** Schreibt sie in die Datenbank, ohne Dubletten (upsert auf + * betreuteId + systemSchluessel). */ +export function pflichtfristenSchreiben(betreuteId: string): Promise +``` + +Abzuleiten sind mindestens: + +| Schlüssel | Frist | Grundlage | +|---|---|---| +| `vermoegensverzeichnis` | 3 Monate nach Bestellung | § 1835 BGB | +| `anfangsbericht` | 3 Monate nach Bestellung | § 1863 Abs. 1 BGB | +| `jahresbericht-` | jährlich zum Betreuungsjahr | § 1863 Abs. 3 BGB | +| `vermoegensuebersicht-` | jährlich, mit dem Jahresbericht | § 1859, § 1865 BGB | +| `aufwandspauschale-` | 6 Monate nach Ende des Betreuungsjahres, **Ausschlussfrist** | § 1878 BGB | +| `landespflegegeld-` | 31.12. | Bayerisches Landespflegegeldgesetz | +| `zuzahlungsbefreiung-` | jährlich | § 62 SGB V | +| `rueckkaufswerte-` | jährlich | selbst gesetzt | +| `pflegegrad-pruefung` | konfigurierbar | § 18 SGB XI | + +**Betreuungsjahr**: läuft ab dem Tag der Bestellung, nicht ab dem +Kalenderjahr. Die Aufwandspauschale verfällt sechs Monate nach dessen Ende +— diese Frist wird als `ausschlussfrist: true` markiert und im Dashboard +rot mit ausdrücklicher Verfallwarnung gezeigt. + +--- + +## 5. Hinweis-Wissensbasis — `daten/hinweise.json` + +Pflegbare JSON-Datei, kein Hardcoding. + +```jsonc +{ + "version": 1, + "karten": [ + { + "schluessel": "wohnraum-aufgeben", + "titel": "Wohnung kündigen? Erst das Gericht fragen.", + "text": "Die Aufgabe von Wohnraum der betreuten Person ist dem Gericht mitzuteilen und bedarf der Genehmigung.", + "rechtsgrundlage": "§ 1833 BGB", + "link": "https://www.gesetze-im-internet.de/bgb/__1833.html", + "kontext": ["IMMOBILIE", "VORGANG:IMMOBILIE", "VORLAGE:wohnungsaufloesung"], + "gewicht": 10 + } + ] +} +``` + +`kontext` ist eine Liste von Auslösern: ein `Modul`, `VORGANG:`, +`VORLAGE:` oder `FRIST:`. Die Oberfläche +zeigt passende Karten, sortiert nach `gewicht` absteigend, jede mit +``. + +--- + +## 6. Vorgangs-Vorlagen — `src/lib/vorgaenge/vorlagen.ts` + +```ts +export type Vorgangsvorlage = { + schluessel: string + titel: string + kategorie: VorgangKategorie + beschreibung: string + genehmigungspflichtig: boolean + rechtsgrundlage?: string + /** Vorbefüllte Aufgabenliste. */ + aufgaben: { titel: string; beschreibung?: string; tageBisFaellig?: number }[] + /** Fristen, die mit angelegt werden. */ + fristen?: { titel: string; tageBisFaellig: number; rechtsgrundlage?: string }[] +} + +export const VORLAGEN: Vorgangsvorlage[] +``` + +Mindestens: `heimeinzug`, `antrag-hilfe-zur-pflege`, `wohnungsaufloesung`, +`hausverkauf`, `darlehen-91-sgb-xii`, `versicherungsverwertung`, +`pflegegrad-hoeherstufung`, `steuererklaerung`, `widerspruch-bescheid`. + +--- + +## 7. E-Mail — `src/lib/mail.ts` + +```ts +export function mailSenden(nachricht: { + an: string + betreff: string + text: string +}): Promise<{ gesendet: boolean; meldung?: string }> +``` + +Ist kein SMTP konfiguriert, wird nichts gesendet und `gesendet: false` +zurückgegeben — ohne Fehler. Erinnerungen sind eine Zugabe, kein +Kernstück; die Fristen stehen auch so im Dashboard. diff --git a/docs/schema-wuensche/substanz.md b/docs/schema-wuensche/substanz.md new file mode 100644 index 0000000..918aca4 --- /dev/null +++ b/docs/schema-wuensche/substanz.md @@ -0,0 +1,180 @@ +# Schema-Wünsche aus Etappe 6 „Substanz“ + +Das Schema wurde nicht angefasst. Hier steht, was beim Bauen von +Immobilie, Verträgen und Pflege gefehlt hat — mit der Umgehung, die +stattdessen gewählt wurde, damit später klar ist, was zu migrieren wäre. + +--- + +## 1. `Buchung.immobilieId` + +**Problem.** Das Kostenkonto der Immobilie (Grundsteuer, Gebäude­versicherung, +Energie, Wasser, Instandhaltung, Schornsteinfeger) kann nur über die +Kategorien `GRUNDSTEUER`, `IMMOBILIE_BETRIEBSKOSTEN`, +`IMMOBILIE_INSTANDHALTUNG` und `MIETEINNAHME` zusammengestellt werden. Eine +Zuordnung „diese Buchung gehört zu diesem Objekt“ gibt es nicht. + +**Folge.** Solange nur ein Objekt geführt wird, ist die Zuordnung eindeutig. +Bei einem zweiten Grundstück wären die Zahlen falsch. Die Oberfläche sagt +das an der Auswertung ausdrücklich dazu. + +**Wunsch.** + +```prisma +model Buchung { + immobilieId String? + immobilie Immobilie? @relation(fields: [immobilieId], references: [id], onDelete: SetNull) +} +``` + +--- + +## 2. Eigene Buchungskategorie für die Gebäudeversicherung + +`BuchungKategorie.VERSICHERUNG` sammelt alle Versicherungen ein, auch +Sterbegeld und Haftpflicht. Für das Kostenkonto eines Hauses ist das zu +grob; die Gebäudeversicherung muss dort erscheinen, die private +Unfallversicherung nicht. + +**Umgehung.** Die Gebäudeversicherung wird als +`IMMOBILIE_BETRIEBSKOSTEN` gebucht. Das steht als Hinweis an der +Auswertung. + +**Wunsch.** `IMMOBILIE_VERSICHERUNG` in `BuchungKategorie`. + +--- + +## 3. `Inventarstueck` — Inventarliste der Wertgegenstände + +**Problem.** Für die Fotodokumentation und die Inventarliste eines +leerstehenden Hauses fehlt ein Modell mit `bezeichnung`, `raum`, +`geschaetzterWertCent` und `verbleib`. `Vermoegensposten` passt fachlich +(Art `WERTGEGENSTAND`, Verknüpfung auf `immobilieId`, Wert über +`Wertstand`, Fotos über `DokumentVerknuepfung`), hat aber keine Felder für +Raum und Verbleib. + +**Umgehung.** Raum, Verbleib und Bemerkung stehen als beschriftete Zeilen +im ohnehin verschlüsselten `notizenVerschluesselt`; gelesen und geschrieben +wird das ausschließlich über `src/lib/immobilie/inventar.ts`. Der +geschätzte Wert liegt als `Wertstand` mit der Quelle „eigene Schätzung“. + +**Wunsch.** + +```prisma +model Inventarstueck { + id String @id @default(cuid()) + betreuteId String + immobilieId String? + bezeichnung String + raum String? + geschaetzterWertCent BigInt? + verbleib String? // "im Haus", "Heimzimmer", "verschenkt", "entsorgt", "verkauft" + bemerkungVerschluesselt String? +} +``` + +--- + +## 4. `Immobilie.checklisteUebernommenAm` + +Ob die Leerstands-Checkliste schon einmal übernommen wurde, lässt sich nur +daran ablesen, ob `ImmobilienCheckpunkt`-Zeilen existieren. Das genügt, +weil die Übernahme über den stabilen Titel abgleicht und keine Dubletten +erzeugt — sauberer wäre ein `systemSchluessel` am Checkpunkt. + +**Wunsch.** + +```prisma +model ImmobilienCheckpunkt { + systemSchluessel String? + @@unique([immobilieId, systemSchluessel]) +} +``` + +Bis dahin dient der `titel` als Schlüssel; er darf deshalb beim +Bearbeiten eines übernommenen Punktes nicht geändert werden, ohne dass die +nächste Übernahme ihn erneut anlegt. + +--- + +## 5. Pflegegrad-Historie als eigenes Modell + +`Betreute` trägt den aktuellen Pflegegrad, seit wann und das +Bescheiddatum. Frühere Grade gehen verloren, sobald der Grad +hochgestuft wird — für den Jahresbericht und für eine Höherstufung ist +aber genau die Kette interessant. + +**Umgehung.** Jede Begutachtung wird als `Gesundheitsereignis` mit der Art +`PFLEGEBEGUTACHTUNG` erfasst; der Verlauf zeigt daraus die Historie. Der +Grad selbst steht im Titel und in der verschlüsselten Beschreibung, ist +also nicht auswertbar. + +**Wunsch.** + +```prisma +model Pflegegradstand { + id String @id @default(cuid()) + betreuteId String + grad Int + gueltigAb DateTime @db.Date + bescheidVom DateTime? @db.Date + antragAm DateTime? @db.Date + ergebnis String? // "hochgestuft", "abgelehnt", "unverändert" +} +``` + +--- + +## 6. Strukturierte Felder am `Gesundheitsereignis` + +Die Schnellerfassung für COPD braucht Zahlen, die man später auswerten +will: Sauerstofffluss in l/min, Stunden pro Tag, Schweregrad einer +Exazerbation, ob stationär behandelt wurde. + +**Umgehung.** Diese Angaben werden als beschriftete Zeilen in die +verschlüsselte Beschreibung geschrieben +(`src/lib/pflege/ereignis-details.ts`). Sie sind damit lesbar, aber nicht +summierbar. Auswertbar bleiben nur Art, Datum, `bisDatum` und +`abwesenheitstage` — das reicht für Exazerbationen je Jahr und +Krankenhaustage je Jahr. + +**Wunsch.** Ein `details Json?`-Feld am `Gesundheitsereignis` wäre falsch +(Gesundheitsdaten gehören verschlüsselt). Besser wären wenige typisierte, +unverschlüsselte Kennzahlen ohne Diagnosebezug: + +```prisma +model Gesundheitsereignis { + stationaer Boolean @default(false) + schweregrad Int? // 1 leicht, 2 mittel, 3 schwer +} +``` + +--- + +## 7. `Vertrag.vertragsnummerVerschluesselt` war nicht registriert + +Das Feld steht im Schema, fehlte aber in `VERSCHLUESSELTE_FELDER` in +`src/lib/crypto/feld.ts` — `sealen('Vertrag', { vertragsnummer })` hätte +geworfen. Der Eintrag wurde ergänzt; das Schema blieb unverändert. + +--- + +## 8. `Vertrag.laufzeitJahre` + +Der Kündigungsrechner braucht die Vertragsdauer, um die Verlängerung zu +bestimmen. Das Schema kennt nur `beginnAm`, `endeAm` und +`kuendigungsfristMonate`. + +**Umgehung.** Fehlt `endeAm`, rechnet +`src/lib/vertraege/kuendigung.ts` mit einer Verlängerung um ein Jahr — +der Regelfall bei Versicherungen — und schreibt diese Annahme als Hinweis +in das Ergebnis. + +**Wunsch.** + +```prisma +model Vertrag { + laufzeitJahre Int? + verlaengertSichAutomatisch Boolean @default(true) +} +``` diff --git a/package-lock.json b/package-lock.json index 1fdbe02..2d2e6f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,29 +9,40 @@ "version": "0.1.0", "hasInstallScript": true, "dependencies": { + "@aws-sdk/client-s3": "3.735.0", + "@aws-sdk/s3-request-presigner": "3.735.0", "@node-rs/argon2": "2.0.2", "@prisma/client": "6.3.1", "@radix-ui/react-dialog": "1.1.4", "@radix-ui/react-dropdown-menu": "2.1.4", "@radix-ui/react-label": "2.1.1", "@radix-ui/react-slot": "1.1.1", + "@react-pdf/renderer": "4.1.6", + "archiver": "7.0.1", "class-variance-authority": "0.7.1", "clsx": "2.1.1", "date-fns": "4.1.0", + "docx": "9.1.1", "lucide-react": "0.474.0", "next": "15.1.6", "next-auth": "5.0.0-beta.25", + "nodemailer": "6.10.0", "otpauth": "9.3.6", + "pdf-lib": "1.17.1", "qrcode": "1.5.4", "react": "19.0.0", "react-dom": "19.0.0", + "rrule": "2.8.1", "server-only": "^0.0.1", "tailwind-merge": "2.6.0", "tailwindcss-animate": "1.0.7", "zod": "3.24.1" }, "devDependencies": { + "@playwright/test": "1.50.1", + "@types/archiver": "6.0.3", "@types/node": "22.13.1", + "@types/nodemailer": "6.4.17", "@types/qrcode": "1.5.5", "@types/react": "19.0.8", "@types/react-dom": "19.0.3", @@ -92,6 +103,824 @@ } } }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.735.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.735.0.tgz", + "integrity": "sha512-6NcxX06c4tnnu6FTFiyS8shoYLy+8TvIDkYjJ5r9tvbaysOptUKQdolOuh7+Lz95QyaqiznpCsNTxsfywLXcqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.734.0", + "@aws-sdk/credential-provider-node": "3.734.0", + "@aws-sdk/middleware-bucket-endpoint": "3.734.0", + "@aws-sdk/middleware-expect-continue": "3.734.0", + "@aws-sdk/middleware-flexible-checksums": "3.735.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-location-constraint": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-sdk-s3": "3.734.0", + "@aws-sdk/middleware-ssec": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.734.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/signature-v4-multi-region": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.734.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.734.0", + "@aws-sdk/xml-builder": "3.734.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.1", + "@smithy/eventstream-serde-browser": "^4.0.1", + "@smithy/eventstream-serde-config-resolver": "^4.0.1", + "@smithy/eventstream-serde-node": "^4.0.1", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-blob-browser": "^4.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/hash-stream-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/md5-js": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.2", + "@smithy/middleware-retry": "^4.0.3", + "@smithy/middleware-serde": "^4.0.1", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.2", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.3", + "@smithy/util-defaults-mode-node": "^4.0.3", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-stream": "^4.0.2", + "@smithy/util-utf8": "^4.0.0", + "@smithy/util-waiter": "^4.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.734.0.tgz", + "integrity": "sha512-oerepp0mut9VlgTwnG5Ds/lb0C0b2/rQ+hL/rF6q+HGKPfGsCuPvFx1GtwGKCXd49ase88/jVgrhcA9OQbz3kg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.734.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.734.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.734.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.734.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.1", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.2", + "@smithy/middleware-retry": "^4.0.3", + "@smithy/middleware-serde": "^4.0.1", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.2", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.3", + "@smithy/util-defaults-mode-node": "^4.0.3", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.734.0.tgz", + "integrity": "sha512-SxnDqf3vobdm50OLyAKfqZetv6zzwnSqwIwd3jrbopxxHKqNIM/I0xcYjD6Tn+mPig+u7iRKb9q3QnEooFTlmg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/core": "^3.1.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/smithy-client": "^4.1.2", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.734.0.tgz", + "integrity": "sha512-gtRkzYTGafnm1FPpiNO8VBmJrYMoxhDlGPYDVcijzx3DlF8dhWnowuSBCxLSi+MJMx5hvwrX2A+e/q0QAeHqmw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.734.0.tgz", + "integrity": "sha512-JFSL6xhONsq+hKM8xroIPhM5/FOhiQ1cov0lZxhzZWj6Ai3UAjucy3zyIFDr9MgP1KfCYNdvyaUq9/o+HWvEDg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/property-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.2", + "@smithy/types": "^4.1.0", + "@smithy/util-stream": "^4.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.734.0.tgz", + "integrity": "sha512-HEyaM/hWI7dNmb4NhdlcDLcgJvrilk8G4DQX6qz0i4pBZGC2l4iffuqP8K6ZQjUfz5/6894PzeFuhTORAMd+cg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.734.0", + "@aws-sdk/credential-provider-env": "3.734.0", + "@aws-sdk/credential-provider-http": "3.734.0", + "@aws-sdk/credential-provider-process": "3.734.0", + "@aws-sdk/credential-provider-sso": "3.734.0", + "@aws-sdk/credential-provider-web-identity": "3.734.0", + "@aws-sdk/nested-clients": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.734.0.tgz", + "integrity": "sha512-9NOSNbkPVb91JwaXOhyfahkzAwWdMsbWHL6fh5/PHlXYpsDjfIfT23I++toepNF2nODAJNLnOEHGYIxgNgf6jQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.734.0", + "@aws-sdk/credential-provider-http": "3.734.0", + "@aws-sdk/credential-provider-ini": "3.734.0", + "@aws-sdk/credential-provider-process": "3.734.0", + "@aws-sdk/credential-provider-sso": "3.734.0", + "@aws-sdk/credential-provider-web-identity": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/credential-provider-imds": "^4.0.1", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.734.0.tgz", + "integrity": "sha512-zvjsUo+bkYn2vjT+EtLWu3eD6me+uun+Hws1IyWej/fKFAqiBPwyeyCgU7qjkiPQSXqk1U9+/HG9IQ6Iiz+eBw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.734.0.tgz", + "integrity": "sha512-cCwwcgUBJOsV/ddyh1OGb4gKYWEaTeTsqaAK19hiNINfYV/DO9r4RMlnWAo84sSBfJuj9shUNsxzyoe6K7R92Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.734.0", + "@aws-sdk/core": "3.734.0", + "@aws-sdk/token-providers": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.734.0.tgz", + "integrity": "sha512-t4OSOerc+ppK541/Iyn1AS40+2vT/qE+MFMotFkhCgCJbApeRF2ozEdnDN6tGmnl4ybcUuxnp9JWLjwDVlR/4g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.734.0", + "@aws-sdk/nested-clients": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-bucket-endpoint": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.734.0.tgz", + "integrity": "sha512-etC7G18aF7KdZguW27GE/wpbrNmYLVT755EsFc8kXpZj8D6AFKxc7OuveinJmiy0bYXAMspJUWsF6CrGpOw6CQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-arn-parser": "3.723.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-expect-continue": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.734.0.tgz", + "integrity": "sha512-P38/v1l6HjuB2aFUewt7ueAW5IvKkFcv5dalPtbMGRhLeyivBOHwbCyuRKgVs7z7ClTpu9EaViEGki2jEQqEsQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.735.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.735.0.tgz", + "integrity": "sha512-Tx7lYTPwQFRe/wQEHMR6Drh/S+X0ToAEq1Ava9QyxV1riwtepzRLojpNDELFb3YQVVYbX7FEiBMCJLMkmIIY+A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.0.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.734.0.tgz", + "integrity": "sha512-LW7RRgSOHHBzWZnigNsDIzu3AiwtjeI2X66v+Wn1P1u+eXssy1+up4ZY/h+t2sU4LU36UvEf+jrZti9c6vRnFw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-location-constraint": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.734.0.tgz", + "integrity": "sha512-EJEIXwCQhto/cBfHdm3ZOeLxd2NlJD+X2F+ZTOxzokuhBtY0IONfC/91hOo5tWQweerojwshSMHRCKzRv1tlwg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.734.0.tgz", + "integrity": "sha512-mUMFITpJUW3LcKvFok176eI5zXAUomVtahb9IQBwLzkqFYOrMJvWAvoV4yuxrJ8TlQBG8gyEnkb9SnhZvjg67w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.734.0.tgz", + "integrity": "sha512-CUat2d9ITsFc2XsmeiRQO96iWpxSKYFjxvj27Hc7vo87YUHRnfMfnc8jw1EpxEwMcvBD7LsRa6vDNky6AjcrFA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.734.0.tgz", + "integrity": "sha512-zeZPenDhkP/RXYMFG3exhNOe2Qukg2l2KpIjxq9o66meELiTULoIXjCmgPoWcM8zzrue06SBdTsaJDHfDl2vdA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-arn-parser": "3.723.0", + "@smithy/core": "^3.1.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/smithy-client": "^4.1.2", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-stream": "^4.0.2", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-ssec": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.734.0.tgz", + "integrity": "sha512-d4yd1RrPW/sspEXizq2NSOUivnheac6LPeLSLnaeTbBG9g1KqIqvCzP1TfXEqv2CrWfHEsWtJpX7oyjySSPvDQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.734.0.tgz", + "integrity": "sha512-MFVzLWRkfFz02GqGPjqSOteLe5kPfElUrXZft1eElnqulqs6RJfVSpOV7mO90gu293tNAeggMWAVSGRPKIYVMg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.734.0", + "@smithy/core": "^3.1.1", + "@smithy/protocol-http": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.734.0.tgz", + "integrity": "sha512-iph2XUy8UzIfdJFWo1r0Zng9uWj3253yvW9gljhtu+y/LNmNvSnJxQk1f3D2BC5WmcoPZqTS3UsycT3mLPSzWA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.734.0", + "@aws-sdk/middleware-host-header": "3.734.0", + "@aws-sdk/middleware-logger": "3.734.0", + "@aws-sdk/middleware-recursion-detection": "3.734.0", + "@aws-sdk/middleware-user-agent": "3.734.0", + "@aws-sdk/region-config-resolver": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-endpoints": "3.734.0", + "@aws-sdk/util-user-agent-browser": "3.734.0", + "@aws-sdk/util-user-agent-node": "3.734.0", + "@smithy/config-resolver": "^4.0.1", + "@smithy/core": "^3.1.1", + "@smithy/fetch-http-handler": "^5.0.1", + "@smithy/hash-node": "^4.0.1", + "@smithy/invalid-dependency": "^4.0.1", + "@smithy/middleware-content-length": "^4.0.1", + "@smithy/middleware-endpoint": "^4.0.2", + "@smithy/middleware-retry": "^4.0.3", + "@smithy/middleware-serde": "^4.0.1", + "@smithy/middleware-stack": "^4.0.1", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/node-http-handler": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.2", + "@smithy/types": "^4.1.0", + "@smithy/url-parser": "^4.0.1", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-body-length-node": "^4.0.0", + "@smithy/util-defaults-mode-browser": "^4.0.3", + "@smithy/util-defaults-mode-node": "^4.0.3", + "@smithy/util-endpoints": "^3.0.1", + "@smithy/util-middleware": "^4.0.1", + "@smithy/util-retry": "^4.0.1", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.734.0.tgz", + "integrity": "sha512-Lvj1kPRC5IuJBr9DyJ9T9/plkh+EfKLy+12s/mykOy1JaKHDpvj+XGy2YO6YgYVOb8JFtaqloid+5COtje4JTQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner": { + "version": "3.735.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.735.0.tgz", + "integrity": "sha512-PzfS4rWDLlp22NORWmezA8ZH6uwz7fAmYfdIbWsPKoy1Rpm+/6Kqn7Nx+Taz6UKNhGPtexutCoJqsMxCy0ZmxQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/signature-v4-multi-region": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@aws-sdk/util-format-url": "3.734.0", + "@smithy/middleware-endpoint": "^4.0.2", + "@smithy/protocol-http": "^5.0.1", + "@smithy/smithy-client": "^4.1.2", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.734.0.tgz", + "integrity": "sha512-GSRP8UH30RIYkcpPILV4pWrKFjRmmNjtUd41HTKWde5GbjJvNYpxqFXw2aIJHjKTw/js3XEtGSNeTaQMVVt3CQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/protocol-http": "^5.0.1", + "@smithy/signature-v4": "^5.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.734.0.tgz", + "integrity": "sha512-2U6yWKrjWjZO8Y5SHQxkFvMVWHQWbS0ufqfAIBROqmIZNubOL7jXCiVdEFekz6MZ9LF2tvYGnOW4jX8OKDGfIw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/nested-clients": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/property-provider": "^4.0.1", + "@smithy/shared-ini-file-loader": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.734.0.tgz", + "integrity": "sha512-o11tSPTT70nAkGV1fN9wm/hAIiLPyWX6SuGf+9JyTp7S/rC2cFWhR26MvA69nplcjNaXVzB0f+QFrLXXjOqCrg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.723.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.723.0.tgz", + "integrity": "sha512-ZhEfvUwNliOQROcAk34WJWVYTlTa4694kSVhDSjW6lE1bMataPnIN8A0ycukEzBXmd8ZSoBcQLn6lKGl7XIJ5w==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.734.0.tgz", + "integrity": "sha512-w2+/E88NUbqql6uCVAsmMxDQKu7vsKV0KqhlQb0lL+RCq4zy07yXYptVNs13qrnuTfyX7uPXkXrlugvK9R1Ucg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "@smithy/util-endpoints": "^3.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.734.0.tgz", + "integrity": "sha512-TxZMVm8V4aR/QkW9/NhujvYpPZjUYqzLwSge5imKZbWFR806NP7RMwc5ilVuHF/bMOln/cVHkl42kATElWBvNw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/querystring-builder": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.965.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.10.tgz", + "integrity": "sha512-ycwH6Zd2GhuSqdXX9ihbCjeGTB6xOJs+O3+Jb8/zDG9978XU80qs75dfkPJRMNKe5MvBZPuNeFpd4JZKPoUF4g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.734.0.tgz", + "integrity": "sha512-xQTCus6Q9LwUuALW+S76OL0jcWtMOVu14q+GoLnWPUM7QeUw963oQcLhF7oq0CtaLLKyl4GOUfcwc773Zmwwng==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.734.0", + "@smithy/types": "^4.1.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.734.0.tgz", + "integrity": "sha512-c6Iinh+RVQKs6jYUFQ64htOU2HUXFQ3TVx+8Tu3EDF19+9vzWi9UukhIMH9rqyyEXIAkk9XL7avt8y2Uyw2dGA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.734.0", + "@aws-sdk/types": "3.734.0", + "@smithy/node-config-provider": "^4.0.1", + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.734.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.734.0.tgz", + "integrity": "sha512-Zrjxi5qwGEcUsJ0ru7fRtW74WcTS0rbLcehoFB+rN1GRi2hbLcFaYs4PwVA5diLeAJH0gszv3x4Hr/S87MfbKQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@emnapi/core": { "version": "1.11.3", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", @@ -1207,6 +2036,96 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -1430,6 +2349,18 @@ "node": ">= 10" } }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@noble/hashes": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.1.tgz", @@ -1757,6 +2688,50 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/@pdf-lib/standard-fonts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", + "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==", + "license": "MIT", + "dependencies": { + "pako": "^1.0.6" + } + }, + "node_modules/@pdf-lib/upng": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz", + "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==", + "license": "MIT", + "dependencies": { + "pako": "^1.0.10" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@playwright/test": { + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.50.1.tgz", + "integrity": "sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.50.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@prisma/client": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.3.1.tgz", @@ -2402,6 +3377,259 @@ "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==", "license": "MIT" }, + "node_modules/@react-pdf/fns": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@react-pdf/fns/-/fns-3.1.3.tgz", + "integrity": "sha512-0I7pApDr1/RLAKbizuLy/IHTEa93LSPy/bEwYniboC3Xqnp6Od8xFJKbKEzGw2wh/5zKFFwl00g4t9RwgIMc3w==", + "license": "MIT" + }, + "node_modules/@react-pdf/font": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@react-pdf/font/-/font-3.1.0.tgz", + "integrity": "sha512-5q+r3DhZK41gVZp2Uw5M69FEVWeoasnM/HscW3kdpYnwjcB2bhCRWmBGCjm8fmuwQstwNPM1ZxyCWZRTRchwnA==", + "license": "MIT", + "dependencies": { + "@react-pdf/types": "^2.8.0", + "fontkit": "^2.0.2", + "is-url": "^1.2.4" + } + }, + "node_modules/@react-pdf/image": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@react-pdf/image/-/image-3.1.1.tgz", + "integrity": "sha512-pbeb2qUs+lDZsvNpWSjoVkAm10WjXsoYd5jIB3utqkfWwwMkh3qo2Iz4By08AptAcRINPXaG6ICT3zsrXBMb6A==", + "license": "MIT", + "dependencies": { + "@react-pdf/svg": "^1.1.0", + "jay-peg": "^1.1.1", + "png-js": "^2.0.0" + } + }, + "node_modules/@react-pdf/layout": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/@react-pdf/layout/-/layout-4.7.1.tgz", + "integrity": "sha512-Aa6hqB2+ytvxkGgWpxFb8PyMKndXW38bOrpdZq6fjtwYRkgDQlG3mng9paqoY+qQl5A2XiHpiNiXFEphGlVAhA==", + "license": "MIT", + "dependencies": { + "@react-pdf/fns": "3.1.3", + "@react-pdf/image": "^3.1.1", + "@react-pdf/primitives": "^4.3.0", + "@react-pdf/stylesheet": "^6.2.3", + "@react-pdf/textkit": "^6.4.1", + "@react-pdf/types": "^2.11.3", + "emoji-regex-xs": "^1.0.0", + "queue": "^6.0.1", + "yoga-layout": "^3.2.1" + } + }, + "node_modules/@react-pdf/pdfkit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@react-pdf/pdfkit/-/pdfkit-4.1.0.tgz", + "integrity": "sha512-Wm/IOAv0h/U5Ra94c/PltFJGcpTUd/fwVMVeFD6X9tTTPCttIwg0teRG1Lqq617J8K4W7jpL/B0HTH0mjp3QpQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@react-pdf/png-js": "^3.0.0", + "browserify-zlib": "^0.2.0", + "crypto-js": "^4.2.0", + "fontkit": "^2.0.2", + "jay-peg": "^1.1.1", + "linebreak": "^1.1.0", + "vite-compatible-readable-stream": "^3.6.1" + } + }, + "node_modules/@react-pdf/png-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@react-pdf/png-js/-/png-js-3.0.0.tgz", + "integrity": "sha512-eSJnEItZ37WPt6Qv5pncQDxLJRK15eaRwPT+gZoujP548CodenOVp49GST8XJvKMFt9YqIBzGBV/j9AgrOQzVA==", + "license": "MIT", + "dependencies": { + "browserify-zlib": "^0.2.0" + } + }, + "node_modules/@react-pdf/primitives": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@react-pdf/primitives/-/primitives-4.3.0.tgz", + "integrity": "sha512-nYXoZ36pvwNzbc54+DbL8RCn15jU7woJ9D/svnh5tpUXekJ+CbI4mZLo6boSv24CvJgychOu6h7gxX03B4ps0A==", + "license": "MIT" + }, + "node_modules/@react-pdf/reconciler": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@react-pdf/reconciler/-/reconciler-1.1.4.tgz", + "integrity": "sha512-oTQDiR/t4Z/Guxac88IavpU2UgN7eR0RMI9DRKvKnvPz2DUasGjXfChAdMqDNmJJxxV26mMy9xQOUV2UU5/okg==", + "license": "MIT", + "dependencies": { + "object-assign": "^4.1.1", + "scheduler": "0.25.0-rc-603e6108-20241029" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@react-pdf/reconciler/node_modules/scheduler": { + "version": "0.25.0-rc-603e6108-20241029", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0-rc-603e6108-20241029.tgz", + "integrity": "sha512-pFwF6H1XrSdYYNLfOcGlM28/j8CGLu8IvdrxqhjWULe2bPcKiKW4CV+OWqR/9fT52mywx65l7ysNkjLKBda7eA==", + "license": "MIT" + }, + "node_modules/@react-pdf/render": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/@react-pdf/render/-/render-4.6.1.tgz", + "integrity": "sha512-jDPxvtrdETDd+pujEPvCWCiBasZxqf9IDEQUYyD9SiFEaP/FoTLh0sCGTJMG3tlzSPi/jM5Hqduop0RMVlMisg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@react-pdf/fns": "3.1.3", + "@react-pdf/primitives": "^4.3.0", + "@react-pdf/textkit": "^6.4.1", + "@react-pdf/types": "^2.11.3", + "abs-svg-path": "^0.1.1", + "color-string": "^2.1.4", + "normalize-svg-path": "^1.1.0", + "parse-svg-path": "^0.1.2", + "svg-arc-to-cubic-bezier": "^3.2.0" + } + }, + "node_modules/@react-pdf/render/node_modules/color-name": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.1.tgz", + "integrity": "sha512-p2FdgwVx1a9yWBHP2wI0VgShkDpgN4kZISkxdNipGBJWpa5G6b04OINlVWCyJj0JmfvcPrgqt95E9k8yvaOJFg==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/@react-pdf/render/node_modules/color-string": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-pdf/renderer": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@react-pdf/renderer/-/renderer-4.1.6.tgz", + "integrity": "sha512-hfQ0PsuVqfoYxkYgmkj+HFkylbB1QTpXY1rnlgnzJlrlSoNXjzPrCa/ty8jcHOwYA2lNoazIAoDatBIsc8K5pw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@react-pdf/font": "^3.0.1", + "@react-pdf/layout": "^4.2.0", + "@react-pdf/pdfkit": "^4.0.0", + "@react-pdf/primitives": "^4.0.0", + "@react-pdf/reconciler": "^1.1.3", + "@react-pdf/render": "^4.0.2", + "@react-pdf/types": "^2.7.0", + "events": "^3.3.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "queue": "^6.0.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@react-pdf/stylesheet": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/@react-pdf/stylesheet/-/stylesheet-6.2.3.tgz", + "integrity": "sha512-To5bbdM5KyOwkY+GqWdZSq6TlgBe+tQc/r00H1gF6RnqucMfkriRRgqXbYNlH2+RBOg5jltq7IhYARyyuge+1g==", + "license": "MIT", + "dependencies": { + "@react-pdf/fns": "3.1.3", + "@react-pdf/types": "^2.11.3", + "color-string": "^2.1.4", + "hsl-to-hex": "^1.0.0", + "media-engine": "^1.0.3", + "postcss-value-parser": "^4.1.0" + } + }, + "node_modules/@react-pdf/stylesheet/node_modules/color-name": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.1.tgz", + "integrity": "sha512-p2FdgwVx1a9yWBHP2wI0VgShkDpgN4kZISkxdNipGBJWpa5G6b04OINlVWCyJj0JmfvcPrgqt95E9k8yvaOJFg==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/@react-pdf/stylesheet/node_modules/color-string": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-pdf/svg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@react-pdf/svg/-/svg-1.1.0.tgz", + "integrity": "sha512-cTIHXiz9x1HrbfqzfxfZP3FRdDwUXG77QWF6Fb5MP/lV3ONxR+g0Z3hwtBatCS9HeGBQCpxX/Lzb8wHE+co1PA==", + "license": "MIT", + "dependencies": { + "@react-pdf/primitives": "^4.3.0" + } + }, + "node_modules/@react-pdf/textkit": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@react-pdf/textkit/-/textkit-6.4.1.tgz", + "integrity": "sha512-wjQu/f1pKxyC2iRPJrnIm4Sc0tEmofsWtHdiyHP4NUlfN9uX4l7yVihPDWUWfoVPWB1QnTOTAIKv1vqRlGgoSg==", + "license": "MIT", + "dependencies": { + "@react-pdf/fns": "3.1.3", + "bidi-js": "^1.0.2", + "hyphen": "^1.6.4", + "unicode-properties": "^1.4.1" + } + }, + "node_modules/@react-pdf/types": { + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/@react-pdf/types/-/types-2.11.3.tgz", + "integrity": "sha512-/3jG6KUOsSERDDmz2zq+pLI2NcHqupzYtLP+hEnO39FgzWUpAnD44eJdoDtARkoqX/Yhahu4Luu8N6XK9u0/pw==", + "license": "MIT", + "dependencies": { + "@react-pdf/font": "^4.0.10", + "@react-pdf/primitives": "^4.3.0", + "@react-pdf/stylesheet": "^6.2.3" + } + }, + "node_modules/@react-pdf/types/node_modules/@react-pdf/font": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@react-pdf/font/-/font-4.0.10.tgz", + "integrity": "sha512-8Bg7PvtjFumVY8RnIpkbgLIwX3ZlMjrQemGl+sVz1Mx/hi8o8Bj+M9jVCWFaNXLbKAA06YxOlAMhfLEPT1+GLg==", + "license": "MIT", + "dependencies": { + "@react-pdf/pdfkit": "^6.0.1", + "@react-pdf/types": "^2.11.3", + "fontkit": "^2.0.2", + "is-url": "^1.2.4" + } + }, + "node_modules/@react-pdf/types/node_modules/@react-pdf/pdfkit": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@react-pdf/pdfkit/-/pdfkit-6.0.1.tgz", + "integrity": "sha512-emZdaG6L/T+QP9rO4n7LAe3lbVXOy4X/C1UpgbCLxDJXsHiYBuZ8unPBv4/Tdte5hPm/Xc2KT1JEL74RSHE/Fw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@noble/ciphers": "^1.0.0", + "@noble/hashes": "^1.6.0", + "fflate": "^0.8.2", + "fontkit": "^2.0.2", + "js-md5": "^0.8.3", + "linebreak": "^1.1.0", + "png-js": "^2.0.0", + "vite-compatible-readable-stream": "^3.6.1" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.62.5", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.5.tgz", @@ -2805,6 +4033,555 @@ "dev": true, "license": "MIT" }, + "node_modules/@smithy/config-resolver": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.7.2.tgz", + "integrity": "sha512-Y1XfSefHIOub9762qm3ShafdlEE/Va8h3kLUeMq765fNeWeNLcOP2YUPr86H1SlyGwZTOqQ67RlBZPZ3k9Djgg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.33.3", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.33.3.tgz", + "integrity": "sha512-CsOeKq/9kA3y6VJHt+/+VTCtBaxJ4OTFpgrjIUhPpDIKxBci1k2bJaQASF2h/ELWrulGp+t97DZ0mevfAD8idg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.5.2.tgz", + "integrity": "sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.5.2.tgz", + "integrity": "sha512-HQ4l596buGUPnypSGfoYKp17GvR0sMMKyc6PQ/NHxLmh8sQjP7h3TRvy+lW/et12UN9BRPAbHJKa4d3XMMVszQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.6.2.tgz", + "integrity": "sha512-GVWpmVKiVAvZDr3E0qRPu+A7TzeeMUctuP9SoFpzGx31lenW3Wk8zq7N97p8sJMwLCyVhXcbv5D0UTURCHdH0w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.5.2.tgz", + "integrity": "sha512-WkWkseIHXv+hr6RnrkpEOv5TOPHWLzzlOsocnPCCKsdfCOkBN7qhBn1/Pij9QZynkloJXYsQp9A5ykdIPVL2tg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.7.2.tgz", + "integrity": "sha512-nZyWTmSpJEXl6VtWVMBJve/7x12DZu6sIX1z1a+ZMaHlQQRs9Zpu6NbTe/gmxYXVRpkjxyDYpZ5gx2IM6f/Wkw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-blob-browser": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.5.2.tgz", + "integrity": "sha512-+fx5GrWB/EElSRl/z+MQXa5RMaGH4ZH/MyZbBgH7CDE053KLD9o4kLGdrBGe8KicU/t22cHzdZs2cVwGL1yt9g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.5.2.tgz", + "integrity": "sha512-OcD8fGClTkP0BWHVEAgUp1RZyCw8cKfqTPQ+DgrSF5jvR8zKkw2Aud79L4G/1Fu3QKLcsHExxRIPQCcKx7+xkg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.5.2.tgz", + "integrity": "sha512-0sx3A9RyCV8taQ1UM9P7K9JFH2bsLXhdIr85vNNXkhq7Q9TqUdm+XxGiBuInq76aA7vBdvUQFvvyBoB95DJf3Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.5.2.tgz", + "integrity": "sha512-VONOgtCxIXtwXrLVZPUdxOELYpkFzNizkpbQE5CrJ/OEh12Osx+LVXsLEAF+JcvVqPODmUoOUaovlOjCQHTOow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.5.2.tgz", + "integrity": "sha512-ErISzsrBz6I4rREFd9l49An0nIuatPWDegdu446Hf4QU3/UTygVaBYrcGbWNOC6sbANf2eyvPun3DyI2aHV+jg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.5.2.tgz", + "integrity": "sha512-EC8CXhy/Y3DHniNBPK63ECkYir91IpFhIbVOo/Z7y75hO3RMFY0v+b6MUPLVp0TBtTRo+A5uU73yqyrTecTJmw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.5.2.tgz", + "integrity": "sha512-43Ixsc2OE5rhruhc9a1vETUCmK3Yh1AIbg2+wwQUgloOIti2FgPHmPLLBkjIqx8vdmpABtsVlhazNr5TMZKJmA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.7.2.tgz", + "integrity": "sha512-7HgK3/pQQHcD5w9lOPtK53/eDMKDp324Nd4KZ8XvxlcKHiWytuM9VwVOB2iy3rutsz/N1WNEWBkaRBayrGnuog==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.8.2.tgz", + "integrity": "sha512-wj0BTZ6SC7YI8hSLMbZCO1BP7k8JOuSUHjRYrZJApqNEiHL6jxaLs5Sr92EeXu5CuVzZOUCFUuewXusWxps0bQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.5.2.tgz", + "integrity": "sha512-5Qeqkw4IdmUQUGR/FRImBruFcnXiwGC8+EOVH377bonkQF66temzxM4HbCnOOQb8U/SClP1IdlFbC7CTmKux4Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.5.2.tgz", + "integrity": "sha512-db1TDSBA03WtomPMLWPy69L85vEaQbbnDHlmCYQ7hIqOdD22K+k8GlsVYdafYKaFkJaJW2OvcgcXOELGfhtptA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.6.2.tgz", + "integrity": "sha512-zMrXu/O5tPa7GLtra8L4wFG6DACcXT9QV4Ay+WEAjUhXm1dVq7c/q9Qv9gkJZNLY8hmQKg08778kDcxpKNMqOA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.11.3.tgz", + "integrity": "sha512-2jY1tSpERfPfWqyBV2pH+iGFaghVsIJszJNsT7hxtQYhVJpWDyc0LqOWI+nXOxOAHaEfZ4PXXtp1wW1TGpHhkA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.5.2.tgz", + "integrity": "sha512-SPJCSCCGpHf5g5b8244ig3WVKIdv2DS+X6cfmy4bpKEYo4VlLYM+bGHpHbfbmp76vegMKQBvs7DEpwQ5YuhKLA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.6.2.tgz", + "integrity": "sha512-Asd04MaxODN6FNY8EPTeCAM4kPNi3jDUAjZU0Y4F9rHvpLUrrUo7KLcxFgSthywFr6dZfIyDLIJda6jxmVTk5w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.5.2.tgz", + "integrity": "sha512-TLuVW4cGFCj+LirJylCVcFwxcUCpZ5Av0I7K9k/xhm6v6zAUeXSNuZ9e2QKuxcwkgrejL8LZvQTJy42pmq5bOg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.7.2.tgz", + "integrity": "sha512-XsIDj5gVG4YRxGS4n4TiBDAogPWRHXKZyor6JW/sEuaa/7BvKADe9j45jI2dPYCnYbKkLBmbZ4qN9ns0sH+kMQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.7.3.tgz", + "integrity": "sha512-7ImGm+FkHRLcBaRttIAMZ6bzJZWb2cJGoYjq46F2UjycujWzrL9GEN9h4w7eQyXJYnltrUhxbbieBAIRrdqpow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.15.2.tgz", + "integrity": "sha512-qweA7dHwcWmq8IN2mIGo7nAfeji1nwWBra+P3nz4R8W8yoQYE6Iy2bN33I0W3d50MN/V5EiDY/nElr4EHITsrg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.17.2.tgz", + "integrity": "sha512-FOKpVZob9MPTn2znRzGrnsMHv7BOsKVw3XiP/cOyYLDVZ9qKp4nifIiSCuUU/fIj5Vu0UOAxCFr+qRAtG0NUkA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.5.2.tgz", + "integrity": "sha512-xqZcyU/YjOt9ofe8WM1y9aCaAmxel2iRiHpkT0x2BN++mRXw0M4VdxPAOtuux279JndY6wWMiOt5BJ2xmAuMYA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.6.2.tgz", + "integrity": "sha512-wTQX1hPfElIqY6AzM/s6c4UgpMxCL4MwJ5u6340ksLPq78lur6Y+keheIDk5gMX4G3KFh4MERcDt6xhRq+ie1Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.5.2.tgz", + "integrity": "sha512-QyeUSin/R2nz0hXZ5ObTvhmCQomsr6+ZyHygab5AH5+fTY5xVDQeX1DFKp9uPEwOs6RW26jEn9yqq+4yqIZDrA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.5.2.tgz", + "integrity": "sha512-SErsW8pBKTCkFEA1RoChwW6/8JYzdcWqWDOsRP0xOtxMYsHOUMcldkcQjtujpKSy2FtlywqygCOxSKarc4TAZw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-buffer-from/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.5.2.tgz", + "integrity": "sha512-bAtInttsiHw9HX/vaNUU6Nj8pWqxMNKD0okEsjr8StgKwRdzN4mpl4/P7/en2JNCWlCyterSDOoveptct/jOGg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.6.2.tgz", + "integrity": "sha512-wENSdyLXVzupl1nyPK7JP/wt6Lkph/psRgocKli5NLh7O936L66FrZQ/I9HBHwZ5N0Z4MJUoj8qNdw+/6cTRgA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.5.2.tgz", + "integrity": "sha512-3915rhdCibo7Do927AFV6pXxsIiaKOZGGexJ3LnT2l5CgJZi3DPqZ2ZMhYTIPjiRA3asHhPe0gaFBtxuqXC8aA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.7.2.tgz", + "integrity": "sha512-16wQS3r4gXctr9/uYB0OUAUOm1LZLUKAsBeG364l64OalFwsRRvgO+AjL3OYxmxt060z1ExchX2ayrNVfM0r9Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.5.2.tgz", + "integrity": "sha512-wmfA04AZqTwu10EWGKUu4dm/3BZfSedqlqAiQPWVr8OkwFh0MovRDBwc48Pzd2YEEjqpI0JwXVk65TBarff3wQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.6.2.tgz", + "integrity": "sha512-q6MXFNu+W4ZCNdNKutzDLP/Hzumd1FU9CQX++P/7ylanYIYiGhgZwSzwZWAw+G1RNcfY+RBYv61XcGlSYhj+BA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.8.2.tgz", + "integrity": "sha512-R+S7jmu8VdbtOygMUSIGfkmOBp0SowT8+8LZdk21oXdmmKfN4G/bwN5IGIi0UH3RwWc9KJWDH1Dzm45DClyUvw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.5.2.tgz", + "integrity": "sha512-7wNWV7SugHpcMA7uzEawJNpE0GrasXM7a9E+1+Wm6NxVuDClESac/AKt+G7jMZNUz5vBLWqKlqVV7Sv7AtUr6Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.6.2.tgz", + "integrity": "sha512-TNNK01i1HiSAR6hBF94J7q338JgEaHPrP8QKRSeQBsbSbw5O4+YGdZYitmsTeEWvHGX+2R/oQ7BzxyAjeZxuWg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.33.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", @@ -2830,6 +4607,16 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/archiver": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-6.0.3.tgz", + "integrity": "sha512-a6wUll6k3zX6qs5KlxIggs1P1JcYJaTCx2gnlr+f0S1yd2DoaEwoIK10HmBaLnZwWneBz+JBm0dwcZu0zECBcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/readdir-glob": "*" + } + }, "node_modules/@types/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", @@ -2861,12 +4648,21 @@ "version": "22.13.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.20.0" } }, + "node_modules/@types/nodemailer": { + "version": "6.4.17", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.17.tgz", + "integrity": "sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/qrcode": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.5.tgz", @@ -2897,6 +4693,16 @@ "@types/react": "^19.0.0" } }, + "node_modules/@types/readdir-glob": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.5.tgz", + "integrity": "sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.67.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.67.0.tgz", @@ -3717,6 +5523,24 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/abs-svg-path": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz", + "integrity": "sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA==", + "license": "MIT" + }, "node_modules/acorn": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", @@ -3800,6 +5624,42 @@ "node": ">= 8" } }, + "node_modules/archiver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.2", + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "license": "MIT", + "dependencies": { + "glob": "^10.0.0", + "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -4012,6 +5872,12 @@ "dev": true, "license": "MIT" }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -4100,7 +5966,120 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz", + "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.8.0.tgz", + "integrity": "sha512-fM+MhCvdQhZ7NV6S95a07gPSqjIYKn6mFaXfx266wN3ajZGl/+1AzH+ubkXQ0fFZvOe2nk9VHkzdYkQE5zMV3Q==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.28.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.1.tgz", + "integrity": "sha512-JprUlveX3QjApC1cTpsUOiscADftCGVWkzitbHsRqv84hzYwYHw2mbluddsq5TvI8mH/8Ov1f4BiMAdcB0oYnQ==", + "license": "Apache-2.0" + }, + "node_modules/bare-stream": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz", + "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.8.1", + "streamx": "^2.25.0", + "teex": "^1.0.1" + }, + "peerDependencies": { + "bare-abort-controller": "*", + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + }, + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-stream/node_modules/b4a": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.5.2.tgz", + "integrity": "sha512-L13PCJzKG8RGvx8V1/DdMi12ERhC3tprr7/8a94BxpmnRsFqxh5XZNdhtMxu5HPkRshYOOWRGY8lDP7ZhpG9Cg==", + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT" }, "node_modules/baseline-browser-mapping": { @@ -4116,6 +6095,15 @@ "node": ">=6.0.0" } }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -4128,6 +6116,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bowser": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.18", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", @@ -4151,6 +6145,24 @@ "node": ">=8" } }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "license": "MIT", + "dependencies": { + "pako": "~1.0.5" + } + }, "node_modules/browserslist": { "version": "4.28.8", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", @@ -4185,6 +6197,39 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -4413,6 +6458,15 @@ "wrap-ansi": "^6.2.0" } }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -4474,6 +6528,22 @@ "node": ">= 6" } }, + "node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4490,11 +6560,41 @@ "node": ">= 0.6" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -4505,6 +6605,13 @@ "node": ">= 8" } }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "deprecated": "Active development of CryptoJS has been discontinued. This library is no longer maintained.", + "license": "MIT" + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -4691,6 +6798,12 @@ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", "license": "MIT" }, + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", + "license": "MIT" + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -4722,6 +6835,41 @@ "node": ">=0.10.0" } }, + "node_modules/docx": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/docx/-/docx-9.1.1.tgz", + "integrity": "sha512-jz941pdz4+gMljZ1pV+95FwuWEouKi4u1Elhv3ptqeytGOSyX+u131hRYg4wgqLU+x2CbGsz9eTYgo2uMMz65Q==", + "license": "MIT", + "dependencies": { + "@types/node": "^22.7.5", + "hash.js": "^1.1.7", + "jszip": "^3.10.1", + "nanoid": "^5.0.4", + "xml": "^1.0.1", + "xml-js": "^1.6.8" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/docx/node_modules/nanoid": { + "version": "5.1.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.16.tgz", + "integrity": "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -4737,6 +6885,12 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, "node_modules/electron-to-chromium": { "version": "1.5.412", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.412.tgz", @@ -4748,7 +6902,12 @@ "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex-xs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", + "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", "license": "MIT" }, "node_modules/es-abstract": { @@ -5437,6 +7596,33 @@ "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, "node_modules/expect-type": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", @@ -5451,7 +7637,12 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, + "license": "MIT" + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", "license": "MIT" }, "node_modules/fast-glob": { @@ -5498,6 +7689,28 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -5507,6 +7720,12 @@ "reusify": "^1.0.4" } }, + "node_modules/fflate": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", + "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", + "license": "MIT" + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -5570,6 +7789,23 @@ "dev": true, "license": "ISC" }, + "node_modules/fontkit": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", + "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", + "license": "MIT", + "dependencies": { + "@swc/helpers": "^0.5.12", + "brotli": "^1.3.2", + "clone": "^2.1.2", + "dfa": "^1.2.0", + "fast-deep-equal": "^3.1.3", + "restructure": "^3.0.0", + "tiny-inflate": "^1.0.3", + "unicode-properties": "^1.4.0", + "unicode-trie": "^2.0.0" + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -5586,6 +7822,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -5755,6 +8007,27 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -5767,6 +8040,30 @@ "node": ">=10.13.0" } }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -5810,6 +8107,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -5891,6 +8194,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, "node_modules/hasown": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", @@ -5903,6 +8216,47 @@ "node": ">= 0.4" } }, + "node_modules/hsl-to-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hsl-to-hex/-/hsl-to-hex-1.0.0.tgz", + "integrity": "sha512-K6GVpucS5wFf44X0h2bLVRDsycgJmf9FF2elg+CrqD8GcFU8c6vYhgXn8NjUkFCwj+xDFb70qgLbTUm6sxwPmA==", + "license": "MIT", + "dependencies": { + "hsl-to-rgb-for-reals": "^1.1.0" + } + }, + "node_modules/hsl-to-rgb-for-reals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/hsl-to-rgb-for-reals/-/hsl-to-rgb-for-reals-1.1.1.tgz", + "integrity": "sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==", + "license": "ISC" + }, + "node_modules/hyphen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/hyphen/-/hyphen-1.14.1.tgz", + "integrity": "sha512-kvL8xYl5QMTh+LwohVN72ciOxC0OEV79IPdJSTwEXok9y9QHebXGdFgrED4sWfiax/ODx++CAMk3hMy4XPJPOw==", + "license": "ISC" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -5913,6 +8267,12 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -5940,6 +8300,12 @@ "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -6300,6 +8666,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-string": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", @@ -6351,6 +8729,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "license": "MIT" + }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", @@ -6408,7 +8792,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/iterator.prototype": { @@ -6429,6 +8812,30 @@ "node": ">= 0.4" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jay-peg": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/jay-peg/-/jay-peg-1.1.1.tgz", + "integrity": "sha512-D62KEuBxz/ip2gQKOEhk/mx14o7eiFRaU+VNNSP4MOiIkwb/D6B3G1Mfas7C/Fit8EsSV2/IWjZElx/Gs6A4ww==", + "license": "MIT", + "dependencies": { + "restructure": "^3.0.0" + } + }, "node_modules/jiti": { "version": "1.21.7", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", @@ -6447,11 +8854,16 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/js-md5": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.8.3.tgz", + "integrity": "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ==", + "license": "MIT" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -6527,6 +8939,54 @@ "node": ">=4.0" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -6557,6 +9017,54 @@ "node": ">=0.10" } }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -6571,6 +9079,15 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -6583,6 +9100,25 @@ "url": "https://github.com/sponsors/antonk52" } }, + "node_modules/linebreak": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz", + "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==", + "license": "MIT", + "dependencies": { + "base64-js": "0.0.8", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/linebreak/node_modules/base64-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", + "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -6605,6 +9141,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -6616,7 +9158,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -6632,6 +9173,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/lucide-react": { "version": "0.474.0", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.474.0.tgz", @@ -6661,6 +9208,12 @@ "node": ">= 0.4" } }, + "node_modules/media-engine": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/media-engine/-/media-engine-1.0.3.tgz", + "integrity": "sha512-aa5tG6sDoK+k70B9iEX1NeyfT8ObCKhNDs6lJVpwF6r8vhUfuKMslIcirq6HIUYuuUYLefcEQOn9bSBOvawtwg==", + "license": "MIT" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -6683,6 +9236,12 @@ "node": ">=8.6" } }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -6706,6 +9265,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -6914,6 +9482,15 @@ "node": ">=18" } }, + "node_modules/nodemailer": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz", + "integrity": "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -6933,6 +9510,15 @@ "node": ">=0.10.0" } }, + "node_modules/normalize-svg-path": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-1.1.0.tgz", + "integrity": "sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg==", + "license": "MIT", + "dependencies": { + "svg-arc-to-cubic-bezier": "^3.0.0" + } + }, "node_modules/oauth4webapi": { "version": "3.8.7", "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.7.tgz", @@ -7163,6 +9749,18 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -7176,6 +9774,12 @@ "node": ">=6" } }, + "node_modules/parse-svg-path": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz", + "integrity": "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==", + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -7189,7 +9793,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -7201,6 +9804,22 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "license": "MIT" }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -7218,6 +9837,24 @@ "node": ">= 14.16" } }, + "node_modules/pdf-lib": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz", + "integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==", + "license": "MIT", + "dependencies": { + "@pdf-lib/standard-fonts": "^1.0.0", + "@pdf-lib/upng": "^1.0.1", + "pako": "^1.0.11", + "tslib": "^1.11.1" + } + }, + "node_modules/pdf-lib/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -7245,6 +9882,60 @@ "node": ">= 6" } }, + "node_modules/playwright": { + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz", + "integrity": "sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.50.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.1.tgz", + "integrity": "sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/png-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/png-js/-/png-js-2.0.0.tgz", + "integrity": "sha512-GdzJuUMc6ZSpxFJWVxtOH1bzYHym+TOnveqUjb+VJIbZWbZzyiRGFiKhbiielfpYbgMlhHVhsJ0FTazfuRFkMA==", + "dependencies": { + "fflate": "^0.8.2" + } + }, "node_modules/pngjs": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", @@ -7465,11 +10156,25 @@ } } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -7504,6 +10209,15 @@ "node": ">=10.13.0" } }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "license": "MIT", + "dependencies": { + "inherits": "~2.0.3" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -7549,7 +10263,6 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, "license": "MIT" }, "node_modules/react-remove-scroll": { @@ -7627,6 +10340,52 @@ "integrity": "sha512-/peqiBB/n07gQGLsWaHho3WfvUyRscw0gYTsEFMhrIe/nWLkYaf5SbKYjGYqtRV3aPwykJgF2VEMo1ac4bnsGA==", "license": "MIT" }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -7692,6 +10451,15 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", @@ -7742,6 +10510,12 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/restructure": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", + "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==", + "license": "MIT" + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -7798,6 +10572,15 @@ "fsevents": "~2.3.2" } }, + "node_modules/rrule": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/rrule/-/rrule-2.8.1.tgz", + "integrity": "sha512-hM3dHSBMeaJ0Ktp7W38BJZ7O1zOgaFEsn41PDk+yHoEtfLV+PoJt9E9xAlZiWgf/iqEqionN0ebHFZIDAp+iGw==", + "license": "BSD-3-Clause", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -7841,6 +10624,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -7876,6 +10679,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sax": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, "node_modules/scheduler": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", @@ -7956,6 +10768,12 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/sharp": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", @@ -8000,7 +10818,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -8013,7 +10830,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -8102,6 +10918,18 @@ "dev": true, "license": "ISC" }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/simple-swizzle": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", @@ -8164,6 +10992,26 @@ "node": ">=10.0.0" } }, + "node_modules/streamx": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", + "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==", + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -8178,6 +11026,27 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, "node_modules/string-width/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -8310,6 +11179,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -8333,6 +11215,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", @@ -8403,6 +11297,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-arc-to-cubic-bezier": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz", + "integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==", + "license": "ISC" + }, "node_modules/tailwind-merge": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", @@ -8543,6 +11443,64 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tar-stream": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz", + "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/tar-stream/node_modules/b4a": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/text-decoder/node_modules/b4a": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -8564,6 +11522,12 @@ "node": ">=0.8" } }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -8851,7 +11815,32 @@ "version": "6.20.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, + "license": "MIT" + }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "license": "MIT", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/unicode-trie/node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", "license": "MIT" }, "node_modules/unrs-resolver": { @@ -9057,6 +12046,20 @@ } } }, + "node_modules/vite-compatible-readable-stream": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/vite-compatible-readable-stream/-/vite-compatible-readable-stream-3.6.1.tgz", + "integrity": "sha512-t20zYkrSf868+j/p31cRIGN28Phrjm3nRSLR2fyc2tiWi4cZGVdv68yNlwnIINTkMTmPoMiSlc0OadaO7DXZaQ==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/vite-node": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.5.tgz", @@ -9664,7 +12667,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -9812,6 +12814,42 @@ "node": ">=8" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", + "license": "MIT" + }, + "node_modules/xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "license": "MIT", + "dependencies": { + "sax": "^1.2.4" + }, + "bin": { + "xml-js": "bin/cli.js" + } + }, "node_modules/y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", @@ -9933,6 +12971,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yoga-layout": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz", + "integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==", + "license": "MIT" + }, + "node_modules/zip-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/zod": { "version": "3.24.1", "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", diff --git a/package.json b/package.json index d42b158..36893dd 100644 --- a/package.json +++ b/package.json @@ -25,35 +25,48 @@ "postinstall": "prisma generate", "db:audit-schutz": "prisma db execute --schema prisma/schema.prisma --file prisma/sql/audit-schutz.sql", "db:sql": "npm run db:volltext && npm run db:audit-schutz", - "protokoll": "node scripts/bauprotokoll.mjs" + "protokoll": "node scripts/bauprotokoll.mjs", + "test:e2e": "playwright test", + "protokoll:md": "node scripts/bauprotokoll.mjs --md" }, "prisma": { "seed": "tsx prisma/seed.ts" }, "dependencies": { + "@aws-sdk/client-s3": "3.735.0", + "@aws-sdk/s3-request-presigner": "3.735.0", "@node-rs/argon2": "2.0.2", "@prisma/client": "6.3.1", "@radix-ui/react-dialog": "1.1.4", "@radix-ui/react-dropdown-menu": "2.1.4", "@radix-ui/react-label": "2.1.1", "@radix-ui/react-slot": "1.1.1", + "@react-pdf/renderer": "4.1.6", + "archiver": "7.0.1", "class-variance-authority": "0.7.1", "clsx": "2.1.1", "date-fns": "4.1.0", + "docx": "9.1.1", "lucide-react": "0.474.0", "next": "15.1.6", "next-auth": "5.0.0-beta.25", + "nodemailer": "6.10.0", "otpauth": "9.3.6", + "pdf-lib": "1.17.1", "qrcode": "1.5.4", "react": "19.0.0", "react-dom": "19.0.0", + "rrule": "2.8.1", "server-only": "^0.0.1", "tailwind-merge": "2.6.0", "tailwindcss-animate": "1.0.7", "zod": "3.24.1" }, "devDependencies": { + "@playwright/test": "1.50.1", + "@types/archiver": "6.0.3", "@types/node": "22.13.1", + "@types/nodemailer": "6.4.17", "@types/qrcode": "1.5.5", "@types/react": "19.0.8", "@types/react-dom": "19.0.3", diff --git a/prisma/migrations/20260822000640_fundament/migration.sql b/prisma/migrations/20260822000640_fundament/migration.sql new file mode 100644 index 0000000..ee68e83 --- /dev/null +++ b/prisma/migrations/20260822000640_fundament/migration.sql @@ -0,0 +1,1150 @@ +-- CreateEnum +CREATE TYPE "Rolle" AS ENUM ('BETREUER', 'MITLESER', 'ADMIN'); + +-- CreateEnum +CREATE TYPE "Modul" AS ENUM ('STAMMDATEN', 'VORGAENGE', 'FRISTEN', 'DOKUMENTE', 'FINANZEN', 'SOZIALHILFE', 'IMMOBILIE', 'VERTRAEGE', 'GESUNDHEIT', 'KONTAKTE', 'BERICHTE'); + +-- CreateEnum +CREATE TYPE "Aufgabenkreis" AS ENUM ('VERMOEGENSSORGE', 'GESUNDHEITSSORGE', 'AUFENTHALTSBESTIMMUNG', 'WOHNUNGSANGELEGENHEITEN', 'BEHOERDENANGELEGENHEITEN', 'POST_UND_FERNMELDEVERKEHR', 'RECHTS_ANTRAGS_UND_BEHOERDENVERTRETUNG', 'ENTSCHEIDUNG_UEBER_FREIHEITSENTZIEHENDE_MASSNAHMEN', 'SONSTIGES'); + +-- CreateEnum +CREATE TYPE "VorgangKategorie" AS ENUM ('GERICHT', 'SOZIALHILFE', 'IMMOBILIE', 'VERSICHERUNG', 'HEIM_PFLEGE', 'VERTRAEGE', 'STEUERN', 'SONSTIGES'); + +-- CreateEnum +CREATE TYPE "VorgangStatus" AS ENUM ('OFFEN', 'IN_ARBEIT', 'WARTET_AUF_DRITTE', 'WARTET_AUF_GENEHMIGUNG', 'ERLEDIGT'); + +-- CreateEnum +CREATE TYPE "GenehmigungStatus" AS ENUM ('GEPLANT', 'MITGETEILT', 'BEANTRAGT', 'ANGEHOERT', 'ERTEILT', 'VERSAGT', 'RECHTSKRAEFTIG', 'VOLLZOGEN'); + +-- CreateEnum +CREATE TYPE "FristTyp" AS ENUM ('GESETZLICH', 'BEHOERDLICH', 'SELBST_GESETZT', 'WIEDERVORLAGE'); + +-- CreateEnum +CREATE TYPE "FristStatus" AS ENUM ('OFFEN', 'ERLEDIGT', 'VERSCHOBEN', 'ENTFALLEN'); + +-- CreateEnum +CREATE TYPE "DokumentRichtung" AS ENUM ('EINGANG', 'AUSGANG', 'INTERN'); + +-- CreateEnum +CREATE TYPE "DokumentArt" AS ENUM ('BESCHEID', 'ANTRAG', 'SCHREIBEN', 'RECHNUNG', 'KONTOAUSZUG', 'VERTRAG', 'BESCHLUSS', 'URKUNDE', 'GUTACHTEN', 'AUSWEIS', 'PROTOKOLL', 'QUITTUNG', 'MAHNUNG', 'VOLLMACHT', 'ATTEST', 'GRUNDBUCHAUSZUG', 'FOTO', 'SONSTIGES'); + +-- CreateEnum +CREATE TYPE "PosteingangStatus" AS ENUM ('NEU', 'GESICHTET', 'ZUGEORDNET', 'BEANTWORTET'); + +-- CreateEnum +CREATE TYPE "KontoArt" AS ENUM ('GIRO', 'SPAR', 'TAGESGELD', 'FESTGELD', 'DEPOT', 'VERWAHRGELD_HEIM', 'BARKASSE', 'SONSTIGES'); + +-- CreateEnum +CREATE TYPE "BuchungRichtung" AS ENUM ('EINNAHME', 'AUSGABE', 'UMBUCHUNG'); + +-- CreateEnum +CREATE TYPE "BuchungKategorie" AS ENUM ('RENTE', 'PFLEGEKASSE', 'SOZIALHILFE', 'LANDESPFLEGEGELD', 'ZINSEN', 'MIETEINNAHME', 'SONSTIGE_EINNAHME', 'HEIMKOSTEN', 'BARBETRAG', 'BEKLEIDUNG', 'ARZT_UND_MEDIKAMENTE', 'ZUZAHLUNG', 'VERSICHERUNG', 'IMMOBILIE_BETRIEBSKOSTEN', 'IMMOBILIE_INSTANDHALTUNG', 'GRUNDSTEUER', 'STEUERN', 'GEBUEHREN_GERICHT', 'PORTO_UND_KOPIEN', 'FAHRTKOSTEN', 'TELEFON_INTERNET', 'RUNDFUNKBEITRAG', 'BESTATTUNGSVORSORGE', 'BANKGEBUEHREN', 'SONSTIGE_AUSGABE', 'UMBUCHUNG'); + +-- CreateEnum +CREATE TYPE "VermoegensArt" AS ENUM ('KONTO', 'IMMOBILIE', 'VERSICHERUNG', 'FAHRZEUG', 'WERTGEGENSTAND', 'FORDERUNG', 'VERBINDLICHKEIT', 'SONSTIGES'); + +-- CreateEnum +CREATE TYPE "SozialhilfeBewertung" AS ENUM ('UNGEPRUEFT', 'VERWERTBAR', 'GESCHUETZT', 'STRITTIG'); + +-- CreateEnum +CREATE TYPE "ImmobilienStatusSozialhilfe" AS ENUM ('UNGEKLAERT', 'SELBST_BEWOHNT_GESCHUETZT', 'HAERTEFALL_GELTEND_GEMACHT', 'DARLEHEN_91_SGB_XII', 'VERKAUF_LAEUFT', 'VERWERTET'); + +-- CreateEnum +CREATE TYPE "VertragArt" AS ENUM ('LEBENSVERSICHERUNG', 'STERBEGELDVERSICHERUNG', 'BESTATTUNGSVORSORGE', 'RENTENVERSICHERUNG_PRIVAT', 'UNFALLVERSICHERUNG', 'HAFTPFLICHT', 'HAUSRAT', 'WOHNGEBAEUDE', 'KRANKENZUSATZ', 'PFLEGEZUSATZ', 'RECHTSSCHUTZ', 'STERBEKASSE', 'STROM', 'GAS', 'WASSER', 'TELEFON_INTERNET', 'RUNDFUNKBEITRAG', 'MITGLIEDSCHAFT', 'ABONNEMENT', 'SONSTIGES'); + +-- CreateEnum +CREATE TYPE "VertragStatus" AS ENUM ('AKTIV', 'BEITRAGSFREI', 'RUHEND', 'GEKUENDIGT', 'ABGETRETEN', 'VERWERTET', 'ABGELAUFEN'); + +-- CreateEnum +CREATE TYPE "ZahlRhythmus" AS ENUM ('MONATLICH', 'VIERTELJAEHRLICH', 'HALBJAEHRLICH', 'JAEHRLICH', 'EINMALIG'); + +-- CreateEnum +CREATE TYPE "KontaktRolle" AS ENUM ('RECHTSPFLEGER', 'RICHTER', 'BETREUUNGSBEHOERDE', 'SACHBEARBEITER_BEZIRK', 'SACHBEARBEITER_SOZIALAMT', 'HEIMLEITUNG', 'PFLEGEDIENSTLEITUNG', 'SOZIALDIENST_HEIM', 'HAUSARZT', 'FACHARZT', 'KRANKENKASSE', 'PFLEGEKASSE', 'RENTENVERSICHERUNG', 'NOTAR', 'MAKLER', 'VERSICHERUNGSMAKLER', 'VERSICHERUNG', 'STEUERBERATER', 'RECHTSANWALT', 'BANK', 'HANDWERKER', 'ANGEHOERIGER', 'SONSTIGES'); + +-- CreateEnum +CREATE TYPE "KontaktArt" AS ENUM ('BESUCH', 'TELEFONAT', 'EMAIL', 'BRIEF', 'VIDEOCALL', 'FAX', 'VORSPRACHE'); + +-- CreateEnum +CREATE TYPE "GesundheitsArt" AS ENUM ('ARZTBESUCH', 'KRANKENHAUS', 'EXAZERBATION', 'STURZ', 'MEDIKATIONSAENDERUNG', 'SAUERSTOFFVERSORGUNG', 'INHALATION', 'HILFSMITTEL', 'IMPFUNG', 'REHA', 'PFLEGEBEGUTACHTUNG', 'HEILMITTEL', 'SONSTIGES'); + +-- CreateEnum +CREATE TYPE "NachforderungStatus" AS ENUM ('OFFEN', 'IN_BESCHAFFUNG', 'UEBERSANDT', 'ANERKANNT', 'NICHT_VORHANDEN'); + +-- CreateEnum +CREATE TYPE "BescheidArt" AS ENUM ('BEWILLIGUNG', 'ABLEHNUNG', 'AENDERUNG', 'AUFHEBUNG', 'ERSTATTUNG', 'VORSCHUSS', 'WIDERSPRUCHSBESCHEID'); + +-- CreateEnum +CREATE TYPE "BescheidPruefStatus" AS ENUM ('UNGEPRUEFT', 'GEPRUEFT_OK', 'WIDERSPRUCH_EINGELEGT', 'WIDERSPRUCH_ERLEDIGT', 'FRIST_VERSTRICHEN'); + +-- CreateEnum +CREATE TYPE "LeistungStatus" AS ENUM ('NICHT_GEPRUEFT', 'PRUEFUNG_LAEUFT', 'BEANTRAGT', 'BEWILLIGT', 'ABGELEHNT', 'LAEUFT', 'NICHT_ZUTREFFEND'); + +-- CreateEnum +CREATE TYPE "BerichtArt" AS ENUM ('VERMOEGENSVERZEICHNIS', 'JAHRESBERICHT', 'VERMOEGENSUEBERSICHT', 'RECHNUNGSLEGUNG', 'BELEGMAPPE', 'UNTERLAGENPAKET_BEZIRK', 'VOLLEXPORT'); + +-- CreateEnum +CREATE TYPE "AuditAktion" AS ENUM ('ERSTELLT', 'GEAENDERT', 'GELOESCHT', 'WIEDERHERGESTELLT', 'ENDGUELTIG_GELOESCHT', 'ANGESEHEN', 'EXPORTIERT', 'DOKUMENT_HERUNTERGELADEN', 'ANMELDUNG', 'ANMELDUNG_FEHLGESCHLAGEN', 'ABMELDUNG', 'ZWEITER_FAKTOR_EINGERICHTET', 'PASSWORT_GEAENDERT', 'ROLLE_GEAENDERT', 'FREIGABE_GEAENDERT'); + +-- CreateTable +CREATE TABLE "benutzer" ( + "id" TEXT NOT NULL, + "email" TEXT NOT NULL, + "name" TEXT NOT NULL, + "passwortHash" TEXT NOT NULL, + "passwortGeaendertAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "totpSecretVerschluesselt" TEXT, + "totpAktiviertAm" TIMESTAMP(3), + "wiederherstellungscodes" TEXT[] DEFAULT ARRAY[]::TEXT[], + "rolle" "Rolle" NOT NULL DEFAULT 'BETREUER', + "aktiv" BOOLEAN NOT NULL DEFAULT true, + "letzteAnmeldung" TIMESTAMP(3), + "sitzungsGeneration" INTEGER NOT NULL DEFAULT 0, + "theme" TEXT NOT NULL DEFAULT 'system', + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + "geloeschtAm" TIMESTAMP(3), + + CONSTRAINT "benutzer_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "betreuungs_zugang" ( + "id" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "rolle" "Rolle" NOT NULL DEFAULT 'BETREUER', + "freigegebeneModule" "Modul"[] DEFAULT ARRAY[]::"Modul"[], + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "betreuungs_zugang_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "login_versuch" ( + "id" TEXT NOT NULL, + "email" TEXT NOT NULL, + "ipHash" TEXT, + "erfolg" BOOLEAN NOT NULL, + "grund" TEXT, + "zeitpunkt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "login_versuch_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "betreute" ( + "id" TEXT NOT NULL, + "vorname" TEXT NOT NULL, + "nachname" TEXT NOT NULL, + "geburtsname" TEXT, + "geburtsdatum" DATE NOT NULL, + "geburtsort" TEXT, + "anrede" TEXT, + "heimName" TEXT, + "heimStrasse" TEXT, + "heimPlz" TEXT, + "heimOrt" TEXT, + "heimZimmer" TEXT, + "heimTelefon" TEXT, + "heimEinzugAm" DATE, + "fruehereStrasse" TEXT, + "frueherePlz" TEXT, + "fruehererOrt" TEXT, + "krankenkasse" TEXT, + "krankenversichertennummerVerschluesselt" TEXT, + "pflegekasse" TEXT, + "pflegegrad" INTEGER, + "pflegegradSeit" DATE, + "pflegegradBescheidVom" DATE, + "schwerbehinderungGdB" INTEGER, + "schwerbehinderungMerkzeichen" TEXT, + "diagnosenVerschluesselt" TEXT, + "ermittelterWilleVerschluesselt" TEXT, + "patientenverfuegungVorhanden" BOOLEAN NOT NULL DEFAULT false, + "vorsorgevollmachtVorhanden" BOOLEAN NOT NULL DEFAULT false, + "betreuungsgericht" TEXT, + "aktenzeichenGericht" TEXT, + "bestellungAm" DATE, + "bestellungBis" DATE, + "aufgabenkreise" "Aufgabenkreis"[] DEFAULT ARRAY[]::"Aufgabenkreis"[], + "aufgabenkreiseFreitext" TEXT, + "befreiterBetreuer" BOOLEAN NOT NULL DEFAULT true, + "ehrenamtlich" BOOLEAN NOT NULL DEFAULT true, + "aktenzeichenBezirk" TEXT, + "aktenzeichenSozialamt" TEXT, + "rentenversicherungsnummerVerschluesselt" TEXT, + "steuerIdVerschluesselt" TEXT, + "notizenVerschluesselt" TEXT, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + "geloeschtAm" TIMESTAMP(3), + + CONSTRAINT "betreute_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "vorgang" ( + "id" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "titel" TEXT NOT NULL, + "beschreibungVerschluesselt" TEXT, + "kategorie" "VorgangKategorie" NOT NULL, + "status" "VorgangStatus" NOT NULL DEFAULT 'OFFEN', + "prioritaet" INTEGER NOT NULL DEFAULT 2, + "gegenstelle" TEXT, + "aktenzeichen" TEXT, + "verantwortlich" TEXT, + "angelegtVonId" TEXT, + "naechsterSchritt" TEXT, + "faelligAm" DATE, + "letzterKontaktAm" DATE, + "genehmigungspflichtig" BOOLEAN NOT NULL DEFAULT false, + "vorlageSchluessel" TEXT, + "erledigtAm" TIMESTAMP(3), + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + "geloeschtAm" TIMESTAMP(3), + + CONSTRAINT "vorgang_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "aufgabe" ( + "id" TEXT NOT NULL, + "vorgangId" TEXT NOT NULL, + "titel" TEXT NOT NULL, + "beschreibung" TEXT, + "erledigt" BOOLEAN NOT NULL DEFAULT false, + "erledigtAm" TIMESTAMP(3), + "faelligAm" DATE, + "reihenfolge" INTEGER NOT NULL DEFAULT 0, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "aufgabe_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "genehmigung" ( + "id" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "vorgangId" TEXT NOT NULL, + "titel" TEXT NOT NULL, + "rechtsgrundlage" TEXT NOT NULL, + "status" "GenehmigungStatus" NOT NULL DEFAULT 'GEPLANT', + "mitteilungAm" DATE, + "antragAm" DATE, + "anhoerungAm" DATE, + "beschlussAm" DATE, + "rechtskraftAm" DATE, + "vollzugAm" DATE, + "beschlussDokumentId" TEXT, + "notizenVerschluesselt" TEXT, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + "geloeschtAm" TIMESTAMP(3), + + CONSTRAINT "genehmigung_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "frist" ( + "id" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "vorgangId" TEXT, + "titel" TEXT NOT NULL, + "beschreibung" TEXT, + "datum" DATE NOT NULL, + "typ" "FristTyp" NOT NULL DEFAULT 'SELBST_GESETZT', + "status" "FristStatus" NOT NULL DEFAULT 'OFFEN', + "rechtsgrundlage" TEXT, + "vorwarnungTage" INTEGER[] DEFAULT ARRAY[60, 30, 7, 1]::INTEGER[], + "wiederholungRrule" TEXT, + "vorgaengerFristId" TEXT, + "ausschlussfrist" BOOLEAN NOT NULL DEFAULT false, + "systemSchluessel" TEXT, + "letzteErinnerungStufe" INTEGER, + "erledigtAm" TIMESTAMP(3), + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + "geloeschtAm" TIMESTAMP(3), + + CONSTRAINT "frist_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "dokument" ( + "id" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "titel" TEXT NOT NULL, + "art" "DokumentArt" NOT NULL DEFAULT 'SONSTIGES', + "richtung" "DokumentRichtung" NOT NULL DEFAULT 'EINGANG', + "schreibenVom" DATE, + "eingangAm" DATE, + "versandAm" DATE, + "absender" TEXT, + "empfaenger" TEXT, + "aktenzeichen" TEXT, + "objektSchluessel" TEXT, + "dateiname" TEXT, + "mimeTyp" TEXT, + "groesseBytes" INTEGER, + "pruefsumme" TEXT, + "seitenzahl" INTEGER, + "ocrText" TEXT, + "ocrStatus" TEXT, + "suchvektor" tsvector, + "posteingangStatus" "PosteingangStatus" NOT NULL DEFAULT 'NEU', + "ablageortPapier" TEXT, + "wiedervorlageAm" DATE, + "ablaufAm" DATE, + "notizenVerschluesselt" TEXT, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + "geloeschtAm" TIMESTAMP(3), + + CONSTRAINT "dokument_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "dokument_verknuepfung" ( + "id" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "dokumentId" TEXT NOT NULL, + "vorgangId" TEXT, + "fristId" TEXT, + "buchungId" TEXT, + "vermoegenspostenId" TEXT, + "immobilieId" TEXT, + "vertragId" TEXT, + "kontaktprotokollId" TEXT, + "gesundheitsereignisId" TEXT, + "genehmigungId" TEXT, + "nachforderungId" TEXT, + "bescheidId" TEXT, + "kontaktId" TEXT, + "bemerkung" TEXT, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "dokument_verknuepfung_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "tag" ( + "id" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "name" TEXT NOT NULL, + "farbe" TEXT NOT NULL DEFAULT 'slate', + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "tag_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "konto" ( + "id" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "bezeichnung" TEXT NOT NULL, + "bank" TEXT, + "art" "KontoArt" NOT NULL DEFAULT 'GIRO', + "ibanVerschluesselt" TEXT, + "ibanIndex" TEXT, + "bic" TEXT, + "istBetreuerkonto" BOOLEAN NOT NULL DEFAULT false, + "eroeffnetAm" DATE, + "geschlossenAm" DATE, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + "geloeschtAm" TIMESTAMP(3), + + CONSTRAINT "konto_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "saldostand" ( + "id" TEXT NOT NULL, + "kontoId" TEXT NOT NULL, + "stichtag" DATE NOT NULL, + "betragCent" BIGINT NOT NULL, + "quelle" TEXT, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "saldostand_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "buchung" ( + "id" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "kontoId" TEXT, + "vorgangId" TEXT, + "datum" DATE NOT NULL, + "wertstellung" DATE, + "betragCent" BIGINT NOT NULL, + "richtung" "BuchungRichtung" NOT NULL, + "kategorie" "BuchungKategorie" NOT NULL DEFAULT 'SONSTIGE_AUSGABE', + "gegenstelle" TEXT, + "verwendungszweckVerschluesselt" TEXT, + "belegId" TEXT, + "belegFehltBegruendung" TEXT, + "betrifftBetreute" BOOLEAN NOT NULL DEFAULT true, + "vermoegenswirksam" BOOLEAN NOT NULL DEFAULT false, + "importReferenz" TEXT, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + "geloeschtAm" TIMESTAMP(3), + + CONSTRAINT "buchung_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "vermoegensposten" ( + "id" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "bezeichnung" TEXT NOT NULL, + "art" "VermoegensArt" NOT NULL, + "kontoId" TEXT, + "immobilieId" TEXT, + "vertragId" TEXT, + "bewertungSozialhilfe" "SozialhilfeBewertung" NOT NULL DEFAULT 'UNGEPRUEFT', + "bewertungBegruendungVerschluesselt" TEXT, + "notizenVerschluesselt" TEXT, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + "geloeschtAm" TIMESTAMP(3), + + CONSTRAINT "vermoegensposten_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "wertstand" ( + "id" TEXT NOT NULL, + "vermoegenspostenId" TEXT NOT NULL, + "stichtag" DATE NOT NULL, + "wertCent" BIGINT NOT NULL, + "wertquelle" TEXT NOT NULL, + "nachweisDokumentId" TEXT, + "bemerkung" TEXT, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "wertstand_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "immobilie" ( + "id" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "bezeichnung" TEXT NOT NULL, + "strasse" TEXT, + "plz" TEXT, + "ort" TEXT, + "grundbuchamt" TEXT, + "grundbuchBlatt" TEXT, + "gemarkung" TEXT, + "flurstueck" TEXT, + "eigentumsanteil" TEXT, + "wohnflaecheQm" INTEGER, + "grundstueckQm" INTEGER, + "baujahr" INTEGER, + "bewohntStatus" TEXT, + "leerstandSeit" DATE, + "statusSozialhilfe" "ImmobilienStatusSozialhilfe" NOT NULL DEFAULT 'UNGEKLAERT', + "statusBegruendungVerschluesselt" TEXT, + "notizenVerschluesselt" TEXT, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + "geloeschtAm" TIMESTAMP(3), + + CONSTRAINT "immobilie_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "belastung" ( + "id" TEXT NOT NULL, + "immobilieId" TEXT NOT NULL, + "art" TEXT NOT NULL, + "abteilung" TEXT, + "laufendeNr" TEXT, + "glaeubiger" TEXT, + "betragCent" BIGINT, + "bemerkung" TEXT, + "geloeschtImGrundbuchAm" DATE, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "belastung_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "immobilien_checkpunkt" ( + "id" TEXT NOT NULL, + "immobilieId" TEXT NOT NULL, + "titel" TEXT NOT NULL, + "beschreibung" TEXT, + "erledigt" BOOLEAN NOT NULL DEFAULT false, + "erledigtAm" DATE, + "faelligAm" DATE, + "wiederholungRrule" TEXT, + "reihenfolge" INTEGER NOT NULL DEFAULT 0, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "immobilien_checkpunkt_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "vertrag" ( + "id" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "bezeichnung" TEXT NOT NULL, + "gesellschaft" TEXT, + "art" "VertragArt" NOT NULL, + "status" "VertragStatus" NOT NULL DEFAULT 'AKTIV', + "vertragsnummerVerschluesselt" TEXT, + "beginnAm" DATE, + "endeAm" DATE, + "kuendigungsfristMonate" INTEGER, + "kuendigungZum" DATE, + "gekuendigtAm" DATE, + "beitragCent" BIGINT, + "rhythmus" "ZahlRhythmus", + "bewertungSozialhilfe" "SozialhilfeBewertung" NOT NULL DEFAULT 'UNGEPRUEFT', + "bewertungBegruendungVerschluesselt" TEXT, + "rueckkaufswertAngefragtAm" DATE, + "notizenVerschluesselt" TEXT, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + "geloeschtAm" TIMESTAMP(3), + + CONSTRAINT "vertrag_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "kontakt" ( + "id" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "name" TEXT NOT NULL, + "organisation" TEXT, + "rolle" "KontaktRolle" NOT NULL DEFAULT 'SONSTIGES', + "telefon" TEXT, + "mobil" TEXT, + "fax" TEXT, + "email" TEXT, + "strasse" TEXT, + "plz" TEXT, + "ort" TEXT, + "aktenzeichen" TEXT, + "notizenVerschluesselt" TEXT, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + "geloeschtAm" TIMESTAMP(3), + + CONSTRAINT "kontakt_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "kontaktprotokoll" ( + "id" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "kontaktId" TEXT, + "vorgangId" TEXT, + "datum" TIMESTAMP(3) NOT NULL, + "art" "KontaktArt" NOT NULL, + "dauerMinuten" INTEGER, + "gespraechspartner" TEXT, + "mitBetreuter" BOOLEAN NOT NULL DEFAULT false, + "inhaltVerschluesselt" TEXT, + "wuenscheVerschluesselt" TEXT, + "folgeaufgabenVerschluesselt" TEXT, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + "geloeschtAm" TIMESTAMP(3), + + CONSTRAINT "kontaktprotokoll_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "gesundheitsereignis" ( + "id" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "datum" DATE NOT NULL, + "bisDatum" DATE, + "art" "GesundheitsArt" NOT NULL, + "titel" TEXT NOT NULL, + "beschreibungVerschluesselt" TEXT, + "behandler" TEXT, + "einrichtung" TEXT, + "abwesenheitstage" INTEGER, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + "geloeschtAm" TIMESTAMP(3), + + CONSTRAINT "gesundheitsereignis_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "heimvertrag" ( + "id" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "einrichtung" TEXT NOT NULL, + "traeger" TEXT, + "strasse" TEXT, + "plz" TEXT, + "ort" TEXT, + "vertragAm" DATE, + "einzugAm" DATE, + "auszugAm" DATE, + "kuendigungsfristTage" INTEGER DEFAULT 14, + "pflegeEigenanteilCent" BIGINT, + "unterkunftVerpflegungCent" BIGINT, + "investitionskostenCent" BIGINT, + "ausbildungsumlageCent" BIGINT, + "notizenVerschluesselt" TEXT, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "heimvertrag_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "heimkostenmonat" ( + "id" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "jahr" INTEGER NOT NULL, + "monat" INTEGER NOT NULL, + "pflegeEigenanteilCent" BIGINT NOT NULL DEFAULT 0, + "unterkunftVerpflegungCent" BIGINT NOT NULL DEFAULT 0, + "investitionskostenCent" BIGINT NOT NULL DEFAULT 0, + "ausbildungsumlageCent" BIGINT NOT NULL DEFAULT 0, + "zusatzleistungenCent" BIGINT NOT NULL DEFAULT 0, + "pflegekasseCent" BIGINT NOT NULL DEFAULT 0, + "leistungszuschlagProzent" INTEGER NOT NULL DEFAULT 0, + "leistungszuschlagCent" BIGINT NOT NULL DEFAULT 0, + "renteCent" BIGINT NOT NULL DEFAULT 0, + "sonstigeEinkuenfteCent" BIGINT NOT NULL DEFAULT 0, + "sozialhilfeCent" BIGINT NOT NULL DEFAULT 0, + "eigenmittelCent" BIGINT NOT NULL DEFAULT 0, + "barbetragCent" BIGINT NOT NULL DEFAULT 0, + "bekleidungspauschaleCent" BIGINT NOT NULL DEFAULT 0, + "abwesenheitstage" INTEGER NOT NULL DEFAULT 0, + "rechnungDokumentId" TEXT, + "bemerkung" TEXT, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "heimkostenmonat_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "sozialhilfe_antrag" ( + "id" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "traeger" TEXT NOT NULL DEFAULT 'Bezirk Oberfranken', + "leistungsart" TEXT NOT NULL DEFAULT 'Hilfe zur Pflege nach dem 7. Kapitel SGB XII', + "aktenzeichen" TEXT, + "sachbearbeiter" TEXT, + "telefon" TEXT, + "email" TEXT, + "antragAm" DATE, + "eingangBeimTraegerAm" DATE, + "bewilligtVon" DATE, + "bewilligtBis" DATE, + "notizenVerschluesselt" TEXT, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + "geloeschtAm" TIMESTAMP(3), + + CONSTRAINT "sozialhilfe_antrag_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "nachforderung" ( + "id" TEXT NOT NULL, + "antragId" TEXT NOT NULL, + "bezeichnung" TEXT NOT NULL, + "erlaeuterung" TEXT, + "status" "NachforderungStatus" NOT NULL DEFAULT 'OFFEN', + "angefordertAm" DATE, + "fristBis" DATE, + "uebersandtAm" DATE, + "begruendungNichtVorhanden" TEXT, + "reihenfolge" INTEGER NOT NULL DEFAULT 0, + "ausCheckliste" BOOLEAN NOT NULL DEFAULT false, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + "geloeschtAm" TIMESTAMP(3), + + CONSTRAINT "nachforderung_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "bescheid" ( + "id" TEXT NOT NULL, + "antragId" TEXT NOT NULL, + "art" "BescheidArt" NOT NULL, + "bescheidVom" DATE NOT NULL, + "zugegangenAm" DATE, + "zeitraumVon" DATE, + "zeitraumBis" DATE, + "betragCent" BIGINT, + "rechtsbehelfsfristMonate" INTEGER NOT NULL DEFAULT 1, + "widerspruchMoeglichBis" DATE, + "pruefStatus" "BescheidPruefStatus" NOT NULL DEFAULT 'UNGEPRUEFT', + "widerspruchEingelegtAm" DATE, + "notizenVerschluesselt" TEXT, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + "geloeschtAm" TIMESTAMP(3), + + CONSTRAINT "bescheid_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "leistung" ( + "id" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "bezeichnung" TEXT NOT NULL, + "traeger" TEXT, + "rechtsgrundlage" TEXT, + "status" "LeistungStatus" NOT NULL DEFAULT 'NICHT_GEPRUEFT', + "beantragtAm" DATE, + "bewilligtAm" DATE, + "gueltigBis" DATE, + "betragCent" BIGINT, + "rhythmus" "ZahlRhythmus", + "jaehrlichNeuBeantragen" BOOLEAN NOT NULL DEFAULT false, + "katalogSchluessel" TEXT, + "notizen" TEXT, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + "geloeschtAm" TIMESTAMP(3), + + CONSTRAINT "leistung_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "bericht" ( + "id" TEXT NOT NULL, + "betreuteId" TEXT NOT NULL, + "art" "BerichtArt" NOT NULL, + "titel" TEXT NOT NULL, + "zeitraumVon" DATE, + "zeitraumBis" DATE, + "stichtag" DATE, + "entwurfVerschluesselt" TEXT, + "objektSchluessel" TEXT, + "dateiname" TEXT, + "pruefsumme" TEXT, + "erstelltVonId" TEXT, + "abgegebenAm" DATE, + "abgegebenAn" TEXT, + "erstelltAm" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "aktualisiertAm" TIMESTAMP(3) NOT NULL, + "geloeschtAm" TIMESTAMP(3), + + CONSTRAINT "bericht_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "audit_log" ( + "id" TEXT NOT NULL, + "zeitpunkt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "userId" TEXT, + "userEmail" TEXT, + "userName" TEXT, + "betreuteId" TEXT, + "aktion" "AuditAktion" NOT NULL, + "entitaet" TEXT NOT NULL, + "entitaetId" TEXT, + "bezeichnung" TEXT, + "aenderungVerschluesselt" TEXT, + "ipHash" TEXT, + "userAgent" TEXT, + + CONSTRAINT "audit_log_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "_DokumentTags" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL, + + CONSTRAINT "_DokumentTags_AB_pkey" PRIMARY KEY ("A","B") +); + +-- CreateTable +CREATE TABLE "_VorgangTags" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL, + + CONSTRAINT "_VorgangTags_AB_pkey" PRIMARY KEY ("A","B") +); + +-- CreateTable +CREATE TABLE "_VorgangKontakte" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL, + + CONSTRAINT "_VorgangKontakte_AB_pkey" PRIMARY KEY ("A","B") +); + +-- CreateIndex +CREATE UNIQUE INDEX "benutzer_email_key" ON "benutzer"("email"); + +-- CreateIndex +CREATE INDEX "benutzer_email_idx" ON "benutzer"("email"); + +-- CreateIndex +CREATE INDEX "betreuungs_zugang_betreuteId_idx" ON "betreuungs_zugang"("betreuteId"); + +-- CreateIndex +CREATE UNIQUE INDEX "betreuungs_zugang_userId_betreuteId_key" ON "betreuungs_zugang"("userId", "betreuteId"); + +-- CreateIndex +CREATE INDEX "login_versuch_email_zeitpunkt_idx" ON "login_versuch"("email", "zeitpunkt"); + +-- CreateIndex +CREATE INDEX "login_versuch_zeitpunkt_idx" ON "login_versuch"("zeitpunkt"); + +-- CreateIndex +CREATE INDEX "betreute_nachname_vorname_idx" ON "betreute"("nachname", "vorname"); + +-- CreateIndex +CREATE INDEX "vorgang_betreuteId_status_idx" ON "vorgang"("betreuteId", "status"); + +-- CreateIndex +CREATE INDEX "vorgang_betreuteId_kategorie_idx" ON "vorgang"("betreuteId", "kategorie"); + +-- CreateIndex +CREATE INDEX "vorgang_faelligAm_idx" ON "vorgang"("faelligAm"); + +-- CreateIndex +CREATE INDEX "aufgabe_vorgangId_idx" ON "aufgabe"("vorgangId"); + +-- CreateIndex +CREATE UNIQUE INDEX "genehmigung_beschlussDokumentId_key" ON "genehmigung"("beschlussDokumentId"); + +-- CreateIndex +CREATE INDEX "genehmigung_betreuteId_status_idx" ON "genehmigung"("betreuteId", "status"); + +-- CreateIndex +CREATE INDEX "genehmigung_vorgangId_idx" ON "genehmigung"("vorgangId"); + +-- CreateIndex +CREATE UNIQUE INDEX "frist_vorgaengerFristId_key" ON "frist"("vorgaengerFristId"); + +-- CreateIndex +CREATE INDEX "frist_betreuteId_status_datum_idx" ON "frist"("betreuteId", "status", "datum"); + +-- CreateIndex +CREATE INDEX "frist_datum_idx" ON "frist"("datum"); + +-- CreateIndex +CREATE UNIQUE INDEX "frist_betreuteId_systemSchluessel_key" ON "frist"("betreuteId", "systemSchluessel"); + +-- CreateIndex +CREATE INDEX "dokument_betreuteId_posteingangStatus_idx" ON "dokument"("betreuteId", "posteingangStatus"); + +-- CreateIndex +CREATE INDEX "dokument_betreuteId_eingangAm_idx" ON "dokument"("betreuteId", "eingangAm"); + +-- CreateIndex +CREATE INDEX "dokument_suchvektor_idx" ON "dokument" USING GIN ("suchvektor"); + +-- CreateIndex +CREATE INDEX "dokument_verknuepfung_dokumentId_idx" ON "dokument_verknuepfung"("dokumentId"); + +-- CreateIndex +CREATE INDEX "dokument_verknuepfung_betreuteId_idx" ON "dokument_verknuepfung"("betreuteId"); + +-- CreateIndex +CREATE INDEX "dokument_verknuepfung_vorgangId_idx" ON "dokument_verknuepfung"("vorgangId"); + +-- CreateIndex +CREATE UNIQUE INDEX "tag_betreuteId_name_key" ON "tag"("betreuteId", "name"); + +-- CreateIndex +CREATE INDEX "konto_betreuteId_idx" ON "konto"("betreuteId"); + +-- CreateIndex +CREATE INDEX "konto_ibanIndex_idx" ON "konto"("ibanIndex"); + +-- CreateIndex +CREATE INDEX "saldostand_kontoId_stichtag_idx" ON "saldostand"("kontoId", "stichtag"); + +-- CreateIndex +CREATE UNIQUE INDEX "saldostand_kontoId_stichtag_key" ON "saldostand"("kontoId", "stichtag"); + +-- CreateIndex +CREATE INDEX "buchung_betreuteId_datum_idx" ON "buchung"("betreuteId", "datum"); + +-- CreateIndex +CREATE INDEX "buchung_betreuteId_kategorie_idx" ON "buchung"("betreuteId", "kategorie"); + +-- CreateIndex +CREATE UNIQUE INDEX "buchung_kontoId_importReferenz_key" ON "buchung"("kontoId", "importReferenz"); + +-- CreateIndex +CREATE INDEX "vermoegensposten_betreuteId_art_idx" ON "vermoegensposten"("betreuteId", "art"); + +-- CreateIndex +CREATE INDEX "wertstand_vermoegenspostenId_stichtag_idx" ON "wertstand"("vermoegenspostenId", "stichtag"); + +-- CreateIndex +CREATE UNIQUE INDEX "wertstand_vermoegenspostenId_stichtag_key" ON "wertstand"("vermoegenspostenId", "stichtag"); + +-- CreateIndex +CREATE INDEX "immobilie_betreuteId_idx" ON "immobilie"("betreuteId"); + +-- CreateIndex +CREATE INDEX "belastung_immobilieId_idx" ON "belastung"("immobilieId"); + +-- CreateIndex +CREATE INDEX "immobilien_checkpunkt_immobilieId_idx" ON "immobilien_checkpunkt"("immobilieId"); + +-- CreateIndex +CREATE INDEX "vertrag_betreuteId_status_idx" ON "vertrag"("betreuteId", "status"); + +-- CreateIndex +CREATE INDEX "kontakt_betreuteId_rolle_idx" ON "kontakt"("betreuteId", "rolle"); + +-- CreateIndex +CREATE INDEX "kontaktprotokoll_betreuteId_datum_idx" ON "kontaktprotokoll"("betreuteId", "datum"); + +-- CreateIndex +CREATE INDEX "gesundheitsereignis_betreuteId_datum_idx" ON "gesundheitsereignis"("betreuteId", "datum"); + +-- CreateIndex +CREATE UNIQUE INDEX "heimvertrag_betreuteId_key" ON "heimvertrag"("betreuteId"); + +-- CreateIndex +CREATE INDEX "heimkostenmonat_betreuteId_jahr_idx" ON "heimkostenmonat"("betreuteId", "jahr"); + +-- CreateIndex +CREATE UNIQUE INDEX "heimkostenmonat_betreuteId_jahr_monat_key" ON "heimkostenmonat"("betreuteId", "jahr", "monat"); + +-- CreateIndex +CREATE INDEX "sozialhilfe_antrag_betreuteId_idx" ON "sozialhilfe_antrag"("betreuteId"); + +-- CreateIndex +CREATE INDEX "nachforderung_antragId_status_idx" ON "nachforderung"("antragId", "status"); + +-- CreateIndex +CREATE INDEX "bescheid_antragId_bescheidVom_idx" ON "bescheid"("antragId", "bescheidVom"); + +-- CreateIndex +CREATE INDEX "leistung_betreuteId_status_idx" ON "leistung"("betreuteId", "status"); + +-- CreateIndex +CREATE UNIQUE INDEX "leistung_betreuteId_katalogSchluessel_key" ON "leistung"("betreuteId", "katalogSchluessel"); + +-- CreateIndex +CREATE INDEX "bericht_betreuteId_art_idx" ON "bericht"("betreuteId", "art"); + +-- CreateIndex +CREATE INDEX "audit_log_betreuteId_zeitpunkt_idx" ON "audit_log"("betreuteId", "zeitpunkt"); + +-- CreateIndex +CREATE INDEX "audit_log_userId_zeitpunkt_idx" ON "audit_log"("userId", "zeitpunkt"); + +-- CreateIndex +CREATE INDEX "audit_log_entitaet_entitaetId_idx" ON "audit_log"("entitaet", "entitaetId"); + +-- CreateIndex +CREATE INDEX "_DokumentTags_B_index" ON "_DokumentTags"("B"); + +-- CreateIndex +CREATE INDEX "_VorgangTags_B_index" ON "_VorgangTags"("B"); + +-- CreateIndex +CREATE INDEX "_VorgangKontakte_B_index" ON "_VorgangKontakte"("B"); + +-- AddForeignKey +ALTER TABLE "betreuungs_zugang" ADD CONSTRAINT "betreuungs_zugang_userId_fkey" FOREIGN KEY ("userId") REFERENCES "benutzer"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "betreuungs_zugang" ADD CONSTRAINT "betreuungs_zugang_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "vorgang" ADD CONSTRAINT "vorgang_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "vorgang" ADD CONSTRAINT "vorgang_angelegtVonId_fkey" FOREIGN KEY ("angelegtVonId") REFERENCES "benutzer"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "aufgabe" ADD CONSTRAINT "aufgabe_vorgangId_fkey" FOREIGN KEY ("vorgangId") REFERENCES "vorgang"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "genehmigung" ADD CONSTRAINT "genehmigung_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "genehmigung" ADD CONSTRAINT "genehmigung_vorgangId_fkey" FOREIGN KEY ("vorgangId") REFERENCES "vorgang"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "genehmigung" ADD CONSTRAINT "genehmigung_beschlussDokumentId_fkey" FOREIGN KEY ("beschlussDokumentId") REFERENCES "dokument"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "frist" ADD CONSTRAINT "frist_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "frist" ADD CONSTRAINT "frist_vorgangId_fkey" FOREIGN KEY ("vorgangId") REFERENCES "vorgang"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "frist" ADD CONSTRAINT "frist_vorgaengerFristId_fkey" FOREIGN KEY ("vorgaengerFristId") REFERENCES "frist"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "dokument" ADD CONSTRAINT "dokument_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "dokument_verknuepfung" ADD CONSTRAINT "dokument_verknuepfung_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "dokument_verknuepfung" ADD CONSTRAINT "dokument_verknuepfung_dokumentId_fkey" FOREIGN KEY ("dokumentId") REFERENCES "dokument"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "dokument_verknuepfung" ADD CONSTRAINT "dokument_verknuepfung_vorgangId_fkey" FOREIGN KEY ("vorgangId") REFERENCES "vorgang"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "dokument_verknuepfung" ADD CONSTRAINT "dokument_verknuepfung_fristId_fkey" FOREIGN KEY ("fristId") REFERENCES "frist"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "dokument_verknuepfung" ADD CONSTRAINT "dokument_verknuepfung_buchungId_fkey" FOREIGN KEY ("buchungId") REFERENCES "buchung"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "dokument_verknuepfung" ADD CONSTRAINT "dokument_verknuepfung_vermoegenspostenId_fkey" FOREIGN KEY ("vermoegenspostenId") REFERENCES "vermoegensposten"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "dokument_verknuepfung" ADD CONSTRAINT "dokument_verknuepfung_immobilieId_fkey" FOREIGN KEY ("immobilieId") REFERENCES "immobilie"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "dokument_verknuepfung" ADD CONSTRAINT "dokument_verknuepfung_vertragId_fkey" FOREIGN KEY ("vertragId") REFERENCES "vertrag"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "dokument_verknuepfung" ADD CONSTRAINT "dokument_verknuepfung_kontaktprotokollId_fkey" FOREIGN KEY ("kontaktprotokollId") REFERENCES "kontaktprotokoll"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "dokument_verknuepfung" ADD CONSTRAINT "dokument_verknuepfung_gesundheitsereignisId_fkey" FOREIGN KEY ("gesundheitsereignisId") REFERENCES "gesundheitsereignis"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "dokument_verknuepfung" ADD CONSTRAINT "dokument_verknuepfung_genehmigungId_fkey" FOREIGN KEY ("genehmigungId") REFERENCES "genehmigung"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "dokument_verknuepfung" ADD CONSTRAINT "dokument_verknuepfung_nachforderungId_fkey" FOREIGN KEY ("nachforderungId") REFERENCES "nachforderung"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "dokument_verknuepfung" ADD CONSTRAINT "dokument_verknuepfung_bescheidId_fkey" FOREIGN KEY ("bescheidId") REFERENCES "bescheid"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "dokument_verknuepfung" ADD CONSTRAINT "dokument_verknuepfung_kontaktId_fkey" FOREIGN KEY ("kontaktId") REFERENCES "kontakt"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "tag" ADD CONSTRAINT "tag_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "konto" ADD CONSTRAINT "konto_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "saldostand" ADD CONSTRAINT "saldostand_kontoId_fkey" FOREIGN KEY ("kontoId") REFERENCES "konto"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "buchung" ADD CONSTRAINT "buchung_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "buchung" ADD CONSTRAINT "buchung_kontoId_fkey" FOREIGN KEY ("kontoId") REFERENCES "konto"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "buchung" ADD CONSTRAINT "buchung_vorgangId_fkey" FOREIGN KEY ("vorgangId") REFERENCES "vorgang"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "buchung" ADD CONSTRAINT "buchung_belegId_fkey" FOREIGN KEY ("belegId") REFERENCES "dokument"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "vermoegensposten" ADD CONSTRAINT "vermoegensposten_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "vermoegensposten" ADD CONSTRAINT "vermoegensposten_kontoId_fkey" FOREIGN KEY ("kontoId") REFERENCES "konto"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "vermoegensposten" ADD CONSTRAINT "vermoegensposten_immobilieId_fkey" FOREIGN KEY ("immobilieId") REFERENCES "immobilie"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "vermoegensposten" ADD CONSTRAINT "vermoegensposten_vertragId_fkey" FOREIGN KEY ("vertragId") REFERENCES "vertrag"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "wertstand" ADD CONSTRAINT "wertstand_vermoegenspostenId_fkey" FOREIGN KEY ("vermoegenspostenId") REFERENCES "vermoegensposten"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "wertstand" ADD CONSTRAINT "wertstand_nachweisDokumentId_fkey" FOREIGN KEY ("nachweisDokumentId") REFERENCES "dokument"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "immobilie" ADD CONSTRAINT "immobilie_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "belastung" ADD CONSTRAINT "belastung_immobilieId_fkey" FOREIGN KEY ("immobilieId") REFERENCES "immobilie"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "immobilien_checkpunkt" ADD CONSTRAINT "immobilien_checkpunkt_immobilieId_fkey" FOREIGN KEY ("immobilieId") REFERENCES "immobilie"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "vertrag" ADD CONSTRAINT "vertrag_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "kontakt" ADD CONSTRAINT "kontakt_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "kontaktprotokoll" ADD CONSTRAINT "kontaktprotokoll_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "kontaktprotokoll" ADD CONSTRAINT "kontaktprotokoll_kontaktId_fkey" FOREIGN KEY ("kontaktId") REFERENCES "kontakt"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "kontaktprotokoll" ADD CONSTRAINT "kontaktprotokoll_vorgangId_fkey" FOREIGN KEY ("vorgangId") REFERENCES "vorgang"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "gesundheitsereignis" ADD CONSTRAINT "gesundheitsereignis_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "heimvertrag" ADD CONSTRAINT "heimvertrag_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "heimkostenmonat" ADD CONSTRAINT "heimkostenmonat_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "sozialhilfe_antrag" ADD CONSTRAINT "sozialhilfe_antrag_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "nachforderung" ADD CONSTRAINT "nachforderung_antragId_fkey" FOREIGN KEY ("antragId") REFERENCES "sozialhilfe_antrag"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "bescheid" ADD CONSTRAINT "bescheid_antragId_fkey" FOREIGN KEY ("antragId") REFERENCES "sozialhilfe_antrag"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "leistung" ADD CONSTRAINT "leistung_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "bericht" ADD CONSTRAINT "bericht_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "bericht" ADD CONSTRAINT "bericht_erstelltVonId_fkey" FOREIGN KEY ("erstelltVonId") REFERENCES "benutzer"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "audit_log" ADD CONSTRAINT "audit_log_userId_fkey" FOREIGN KEY ("userId") REFERENCES "benutzer"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "audit_log" ADD CONSTRAINT "audit_log_betreuteId_fkey" FOREIGN KEY ("betreuteId") REFERENCES "betreute"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_DokumentTags" ADD CONSTRAINT "_DokumentTags_A_fkey" FOREIGN KEY ("A") REFERENCES "dokument"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_DokumentTags" ADD CONSTRAINT "_DokumentTags_B_fkey" FOREIGN KEY ("B") REFERENCES "tag"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_VorgangTags" ADD CONSTRAINT "_VorgangTags_A_fkey" FOREIGN KEY ("A") REFERENCES "tag"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_VorgangTags" ADD CONSTRAINT "_VorgangTags_B_fkey" FOREIGN KEY ("B") REFERENCES "vorgang"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_VorgangKontakte" ADD CONSTRAINT "_VorgangKontakte_A_fkey" FOREIGN KEY ("A") REFERENCES "kontakt"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_VorgangKontakte" ADD CONSTRAINT "_VorgangKontakte_B_fkey" FOREIGN KEY ("B") REFERENCES "vorgang"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..648c57f --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "postgresql" \ No newline at end of file diff --git a/prisma/seed.ts b/prisma/seed.ts new file mode 100644 index 0000000..3297969 --- /dev/null +++ b/prisma/seed.ts @@ -0,0 +1,545 @@ +/** + * Startdaten. + * + * npm run db:seed + * + * Der Seed legt bewusst KEINE echten Personendaten an. Er richtet nur den + * ersten Zugang ein; alles Weitere fragt der Ersteinrichtungs-Assistent + * beim ersten Start ab. So landen keine Gesundheitsdaten im Repository, + * und der Seed bleibt auch dann brauchbar, wenn eine zweite Betreuung + * dazukommt. + * + * Zwei Schalter fuer die Entwicklung: + * + * SEED_DEMO=true legt einen vollstaendigen Beispielfall an + * (Erika Musterfrau). Nur ausserhalb der + * Produktion, und in der Oberflaeche als + * Demo gekennzeichnet. + * SEED_DEV_TOTP_SECRET=... aktiviert den zweiten Faktor mit einem + * bekannten Geheimnis, damit automatisierte + * Tests sich anmelden koennen. Wird in der + * Produktion ignoriert. + * + * Der Seed ist idempotent: mehrfaches Ausfuehren aendert nichts. + */ +import { PrismaClient } from '@prisma/client' +import { hash, Algorithm } from '@node-rs/argon2' +import { createCipheriv, randomBytes } from 'node:crypto' + +const db = new PrismaClient() + +const ARGON2 = { + algorithm: Algorithm.Argon2id, + memoryCost: 19_456, + timeCost: 2, + parallelism: 1, +} as const + +const istProduktion = process.env.NODE_ENV === 'production' + +/** + * Verschluesselt wie src/lib/crypto/feld.ts. Bewusst hier noch einmal + * ausgeschrieben: der Seed laeuft ausserhalb von Next.js und kann das + * Modul mit seinem "server-only"-Import nicht laden. + */ +function verschluesseln(klartext: string | null, kontext: string): string | null { + if (!klartext) return null + + const version = Number.parseInt(process.env.ENC_KEY_AKTIV ?? '1', 10) + const roh = process.env[`ENC_KEY_V${version}`] + if (!roh) throw new Error(`ENC_KEY_V${version} fehlt. Erzeuge ihn mit "npm run schluessel".`) + + const schluessel = Buffer.from(roh, 'base64') + if (schluessel.length !== 32) throw new Error(`ENC_KEY_V${version} muss 32 Bytes lang sein.`) + + const iv = randomBytes(12) + const cipher = createCipheriv('aes-256-gcm', schluessel, iv, { authTagLength: 16 }) + cipher.setAAD(Buffer.from(`bc1:${version}:${kontext}`, 'utf8')) + const geheim = Buffer.concat([cipher.update(klartext, 'utf8'), cipher.final()]) + + return [ + 'bc1', + version, + iv.toString('base64url'), + cipher.getAuthTag().toString('base64url'), + geheim.toString('base64url'), + ].join('.') +} + +function datum(text: string): Date { + return new Date(`${text}T00:00:00.000Z`) +} + +function cent(euro: number): bigint { + return BigInt(Math.round(euro * 100)) +} + +async function ersterZugang() { + const email = (process.env.SEED_ADMIN_EMAIL ?? '').trim().toLowerCase() + const passwort = process.env.SEED_ADMIN_PASSWORT + + if (!email || !passwort) { + console.log( + 'Kein SEED_ADMIN_EMAIL / SEED_ADMIN_PASSWORT gesetzt - der erste Zugang wird beim ' + + 'Aufruf von /ersteinrichtung im Browser angelegt.', + ) + return null + } + + const vorhanden = await db.user.findUnique({ where: { email } }) + if (vorhanden) { + console.log(`Zugang ${email} besteht bereits.`) + return vorhanden + } + + // Der zweite Faktor bleibt normalerweise offen und wird beim ersten + // Anmelden eingerichtet - nur die Entwicklung darf ihn vorbelegen. + const devSecret = !istProduktion ? process.env.SEED_DEV_TOTP_SECRET : undefined + + const benutzer = await db.user.create({ + data: { + email, + name: process.env.SEED_ADMIN_NAME ?? 'Betreuer', + passwortHash: await hash(passwort, ARGON2), + rolle: 'ADMIN', + ...(devSecret + ? { + totpSecretVerschluesselt: verschluesseln(devSecret, 'totpSecretVerschluesselt'), + totpAktiviertAm: new Date(), + } + : {}), + }, + }) + + console.log(`Zugang ${email} angelegt.`) + if (devSecret) { + console.log(' Zweiter Faktor mit dem Entwicklungs-Geheimnis vorbelegt.') + } else { + console.log(' Der zweite Faktor wird beim ersten Anmelden eingerichtet.') + } + + return benutzer +} + +/** + * Vollstaendiger Beispielfall. Nur mit SEED_DEMO=true und nie in der + * Produktion - er soll zeigen, wie die Anwendung mit Inhalt aussieht. + */ +async function demofall(userId: string) { + const vorhanden = await db.betreute.findFirst({ + where: { nachname: 'Musterfrau', vorname: 'Erika' }, + }) + if (vorhanden) { + console.log('Demofall besteht bereits.') + return + } + + const betreute = await db.betreute.create({ + data: { + anrede: 'Frau', + vorname: 'Erika', + nachname: 'Musterfrau', + geburtsname: 'Beispiel', + geburtsdatum: datum('1943-04-17'), + geburtsort: 'Bayreuth', + + heimName: 'Seniorenzentrum St. Katharina', + heimStrasse: 'Kirchweg 12', + heimPlz: '95444', + heimOrt: 'Bayreuth', + heimZimmer: '218', + heimTelefon: '0921 123456-218', + heimEinzugAm: datum('2026-01-19'), + + fruehereStrasse: 'Am Sonnenhang 4', + frueherePlz: '95463', + fruehererOrt: 'Bindlach', + + krankenkasse: 'AOK Bayern', + krankenversichertennummerVerschluesselt: verschluesseln( + 'A123456789', + 'krankenversichertennummerVerschluesselt', + ), + pflegekasse: 'AOK Pflegekasse Bayern', + pflegegrad: 3, + pflegegradSeit: datum('2025-11-01'), + pflegegradBescheidVom: datum('2025-10-22'), + schwerbehinderungGdB: 80, + schwerbehinderungMerkzeichen: 'G, B', + diagnosenVerschluesselt: verschluesseln( + [ + 'COPD GOLD III mit chronischer respiratorischer Insuffizienz (J44.09)', + 'Langzeitsauerstofftherapie seit 03/2025, 2 l/min, 16 h/Tag', + 'Arterielle Hypertonie (I10)', + 'Osteoporose mit Wirbelkörperfraktur BWK 12 (M80.08)', + 'Beginnende vaskuläre Demenz (F01.9)', + ].join('\n'), + 'diagnosenVerschluesselt', + ), + ermittelterWilleVerschluesselt: verschluesseln( + 'Möchte im Seniorenzentrum bleiben, dort ist eine frühere Nachbarin. ' + + 'Will das Haus in Bindlach "nicht einfach hergeben", versteht aber, dass die ' + + 'Pflegekosten gedeckt werden müssen. Wünscht keine künstliche Beatmung und keine ' + + 'Verlegung in eine Klinik, wenn es zum Ende geht — so auch in der Patientenverfügung ' + + 'vom 08.03.2019 festgehalten. Besuche bitte nachmittags, vormittags ist sie erschöpft.', + 'ermittelterWilleVerschluesselt', + ), + patientenverfuegungVorhanden: true, + vorsorgevollmachtVorhanden: false, + + betreuungsgericht: 'Amtsgericht Bayreuth', + aktenzeichenGericht: 'XVII 412/25', + bestellungAm: datum('2026-02-14'), + aufgabenkreise: [ + 'VERMOEGENSSORGE', + 'GESUNDHEITSSORGE', + 'AUFENTHALTSBESTIMMUNG', + 'WOHNUNGSANGELEGENHEITEN', + 'BEHOERDENANGELEGENHEITEN', + ], + befreiterBetreuer: true, + ehrenamtlich: true, + + aktenzeichenBezirk: 'HzP 2026/04711', + aktenzeichenSozialamt: 'SA-4711-26', + rentenversicherungsnummerVerschluesselt: verschluesseln( + '17 170443 M 512', + 'rentenversicherungsnummerVerschluesselt', + ), + notizenVerschluesselt: verschluesseln( + 'Dies ist ein Demofall zum Ausprobieren. Er lässt sich unter ' + + 'Einstellungen jederzeit vollständig löschen.', + 'notizenVerschluesselt', + ), + + zugaenge: { create: { userId, rolle: 'BETREUER' } }, + }, + }) + + // --- Kontakte ----------------------------------------------------------- + await db.kontakt.createMany({ + data: [ + { + betreuteId: betreute.id, + name: 'Frau Hartmann', + organisation: 'Amtsgericht Bayreuth', + rolle: 'RECHTSPFLEGER', + telefon: '0921 504-0', + aktenzeichen: 'XVII 412/25', + }, + { + betreuteId: betreute.id, + name: 'Herr Doblinger', + organisation: 'Bezirk Oberfranken, Sozialverwaltung', + rolle: 'SACHBEARBEITER_BEZIRK', + telefon: '0921 604-1234', + email: 'sozialverwaltung@bezirk-oberfranken.de', + aktenzeichen: 'HzP 2026/04711', + }, + { + betreuteId: betreute.id, + name: 'Frau Kellner', + organisation: 'Seniorenzentrum St. Katharina', + rolle: 'SOZIALDIENST_HEIM', + telefon: '0921 123456-30', + }, + { + betreuteId: betreute.id, + name: 'Dr. med. Reuther', + organisation: 'Hausarztpraxis Bindlach', + rolle: 'HAUSARZT', + telefon: '09208 4477', + }, + ], + }) + + // --- Konten und Vermoegen ---------------------------------------------- + const giro = await db.konto.create({ + data: { + betreuteId: betreute.id, + bezeichnung: 'Girokonto', + bank: 'Sparkasse Bayreuth', + art: 'GIRO', + ibanVerschluesselt: verschluesseln('DE02773501100123456789', 'ibanVerschluesselt'), + saldostaende: { + create: [ + { stichtag: datum('2026-02-14'), betragCent: cent(4218.36), quelle: 'Kontoauszug 02/2026' }, + { stichtag: datum('2026-07-31'), betragCent: cent(2967.11), quelle: 'Kontoauszug 07/2026' }, + ], + }, + }, + }) + + await db.konto.create({ + data: { + betreuteId: betreute.id, + bezeichnung: 'Verwahrgeld im Heim', + bank: 'Seniorenzentrum St. Katharina', + art: 'VERWAHRGELD_HEIM', + saldostaende: { + create: { stichtag: datum('2026-07-31'), betragCent: cent(184.2), quelle: 'Abrechnung Heim' }, + }, + }, + }) + + const immobilie = await db.immobilie.create({ + data: { + betreuteId: betreute.id, + bezeichnung: 'Einfamilienhaus Bindlach', + strasse: 'Am Sonnenhang 4', + plz: '95463', + ort: 'Bindlach', + grundbuchamt: 'Amtsgericht Bayreuth', + grundbuchBlatt: '3241', + gemarkung: 'Bindlach', + flurstueck: '812/3', + eigentumsanteil: '1/1', + wohnflaecheQm: 128, + grundstueckQm: 640, + baujahr: 1972, + bewohntStatus: 'leerstehend', + leerstandSeit: datum('2026-01-19'), + statusSozialhilfe: 'UNGEKLAERT', + statusBegruendungVerschluesselt: verschluesseln( + 'Nicht mehr selbst bewohnt, seit dem Heimeinzug leerstehend. Schutz nach ' + + '§ 90 Abs. 2 Nr. 8 SGB XII greift daher voraussichtlich nicht. Zu prüfen: ' + + 'Härtefall nach § 90 Abs. 3 SGB XII und Darlehen nach § 91 SGB XII statt ' + + 'sofortiger Verwertung. Mit dem Bezirk noch nicht abschließend geklärt.', + 'statusBegruendungVerschluesselt', + ), + belastungen: { + create: { + art: 'Grundschuld', + abteilung: 'III', + laufendeNr: '2', + glaeubiger: 'Sparkasse Bayreuth', + betragCent: cent(30000), + bemerkung: 'Valutiert nach Auskunft der Bank nicht mehr, Löschungsbewilligung angefordert.', + }, + }, + }, + }) + + const lebensversicherung = await db.vertrag.create({ + data: { + betreuteId: betreute.id, + bezeichnung: 'Kapitallebensversicherung', + gesellschaft: 'Musterland Leben AG', + art: 'LEBENSVERSICHERUNG', + status: 'BEITRAGSFREI', + vertragsnummerVerschluesselt: verschluesseln('LV-8842119', 'vertragsnummerVerschluesselt'), + beginnAm: datum('1998-07-01'), + endeAm: datum('2028-07-01'), + bewertungSozialhilfe: 'STRITTIG', + bewertungBegruendungVerschluesselt: verschluesseln( + 'Rückkaufswert liegt über dem Schonvermögen. Verwertung wäre grundsätzlich zumutbar, ' + + 'aber der Vertrag läuft 2028 aus — Kündigung jetzt bedeutet spürbaren Verlust. ' + + 'Härtefall nach § 90 Abs. 3 SGB XII geltend gemacht, Antwort des Bezirks steht aus.', + 'bewertungBegruendungVerschluesselt', + ), + rueckkaufswertAngefragtAm: datum('2026-03-02'), + }, + }) + + await db.vertrag.create({ + data: { + betreuteId: betreute.id, + bezeichnung: 'Sterbegeldversicherung', + gesellschaft: 'Nordheim Sterbekasse VVaG', + art: 'STERBEGELDVERSICHERUNG', + status: 'AKTIV', + vertragsnummerVerschluesselt: verschluesseln('SG-220417', 'vertragsnummerVerschluesselt'), + beitragCent: cent(18.4), + rhythmus: 'MONATLICH', + bewertungSozialhilfe: 'GESCHUETZT', + bewertungBegruendungVerschluesselt: verschluesseln( + 'Angemessene Bestattungsvorsorge. Nach § 90 Abs. 3 SGB XII und ständiger ' + + 'Rechtsprechung regelmäßig geschützt, weil sie der Würde der Bestattung dient.', + 'bewertungBegruendungVerschluesselt', + ), + }, + }) + + await db.vermoegensposten.createMany({ + data: [ + { betreuteId: betreute.id, bezeichnung: 'Girokonto Sparkasse', art: 'KONTO', kontoId: giro.id, bewertungSozialhilfe: 'VERWERTBAR' }, + { betreuteId: betreute.id, bezeichnung: 'Einfamilienhaus Bindlach', art: 'IMMOBILIE', immobilieId: immobilie.id, bewertungSozialhilfe: 'STRITTIG' }, + { betreuteId: betreute.id, bezeichnung: 'Kapitallebensversicherung', art: 'VERSICHERUNG', vertragId: lebensversicherung.id, bewertungSozialhilfe: 'STRITTIG' }, + ], + }) + + const posten = await db.vermoegensposten.findMany({ where: { betreuteId: betreute.id } }) + for (const p of posten) { + const werte: Record = { + IMMOBILIE: { wert: 268000, quelle: 'Bodenrichtwert und Vergleichspreise, eigene Schätzung' }, + VERSICHERUNG: { wert: 14820.55, quelle: 'Rückkaufswertmitteilung vom 26.03.2026' }, + KONTO: { wert: 2967.11, quelle: 'Kontoauszug 07/2026' }, + } + const w = werte[p.art] + if (w) { + await db.wertstand.create({ + data: { + vermoegenspostenId: p.id, + stichtag: datum('2026-07-31'), + wertCent: cent(w.wert), + wertquelle: w.quelle, + }, + }) + } + } + + // --- Sozialhilfeantrag mit Nachforderungen ------------------------------ + const antrag = await db.sozialhilfeAntrag.create({ + data: { + betreuteId: betreute.id, + traeger: 'Bezirk Oberfranken', + leistungsart: 'Hilfe zur Pflege nach dem 7. Kapitel SGB XII', + aktenzeichen: 'HzP 2026/04711', + sachbearbeiter: 'Herr Doblinger', + telefon: '0921 604-1234', + antragAm: datum('2026-02-26'), + eingangBeimTraegerAm: datum('2026-03-02'), + }, + }) + + await db.nachforderung.createMany({ + data: [ + { antragId: antrag.id, bezeichnung: 'Rentenbescheide aller Träger', status: 'ANERKANNT', angefordertAm: datum('2026-03-18'), uebersandtAm: datum('2026-03-26'), ausCheckliste: true, reihenfolge: 1 }, + { antragId: antrag.id, bezeichnung: 'Kontoauszüge der letzten 3 Monate, lückenlos', status: 'ANERKANNT', angefordertAm: datum('2026-03-18'), uebersandtAm: datum('2026-03-26'), ausCheckliste: true, reihenfolge: 2 }, + { antragId: antrag.id, bezeichnung: 'Pflegegradbescheid', status: 'ANERKANNT', angefordertAm: datum('2026-03-18'), uebersandtAm: datum('2026-03-26'), ausCheckliste: true, reihenfolge: 3 }, + { antragId: antrag.id, bezeichnung: 'Heimvertrag', status: 'UEBERSANDT', angefordertAm: datum('2026-03-18'), uebersandtAm: datum('2026-04-08'), ausCheckliste: true, reihenfolge: 4 }, + { antragId: antrag.id, bezeichnung: 'Aktueller Grundbuchauszug', status: 'UEBERSANDT', angefordertAm: datum('2026-04-21'), uebersandtAm: datum('2026-05-06'), ausCheckliste: true, reihenfolge: 5 }, + { antragId: antrag.id, bezeichnung: 'Rückkaufswert Kapitallebensversicherung zum Antragsdatum', status: 'IN_BESCHAFFUNG', angefordertAm: datum('2026-04-21'), fristBis: datum('2026-09-05'), erlaeuterung: 'Zweite Anfrage an die Gesellschaft am 12.08.2026 raus.', ausCheckliste: true, reihenfolge: 6 }, + { antragId: antrag.id, bezeichnung: 'Notarielle Verträge früherer Grundstücksübertragungen', status: 'OFFEN', angefordertAm: datum('2026-04-21'), fristBis: datum('2026-09-05'), erlaeuterung: 'Der Träger prüft eine Rückforderung wegen Verarmung (§ 528 BGB). Notariat Bayreuth angefragt.', ausCheckliste: true, reihenfolge: 7 }, + { antragId: antrag.id, bezeichnung: 'Erklärung zu Unterhaltspflichtigen', status: 'OFFEN', angefordertAm: datum('2026-07-30'), fristBis: datum('2026-08-31'), ausCheckliste: true, reihenfolge: 8 }, + ], + }) + + // --- Vorgaenge ---------------------------------------------------------- + await db.vorgang.create({ + data: { + betreuteId: betreute.id, + titel: 'Antrag Hilfe zur Pflege beim Bezirk Oberfranken', + kategorie: 'SOZIALHILFE', + status: 'WARTET_AUF_DRITTE', + gegenstelle: 'Bezirk Oberfranken', + aktenzeichen: 'HzP 2026/04711', + naechsterSchritt: 'Rückkaufswert und Übertragungsverträge nachreichen, dann Sachstand erfragen', + letzterKontaktAm: datum('2026-07-30'), + angelegtVonId: userId, + beschreibungVerschluesselt: verschluesseln( + 'Antrag vom 26.02.2026. Der Bezirk hat in zwei Wellen nachgefordert. ' + + 'Offen sind noch der Rückkaufswert der Lebensversicherung und die notariellen ' + + 'Übertragungsverträge aus den 1990er-Jahren.', + 'beschreibungVerschluesselt', + ), + }, + }) + + await db.vorgang.create({ + data: { + betreuteId: betreute.id, + titel: 'Haus Bindlach: Verwertung oder Darlehen klären', + kategorie: 'IMMOBILIE', + status: 'IN_ARBEIT', + gegenstelle: 'Bezirk Oberfranken', + genehmigungspflichtig: true, + naechsterSchritt: 'Verkehrswert belastbar ermitteln lassen, dann Gespräch mit dem Bezirk', + angelegtVonId: userId, + beschreibungVerschluesselt: verschluesseln( + 'Das Haus ist seit dem Heimeinzug leerstehend. Ein Verkauf wäre nach § 1850 BGB ' + + 'genehmigungspflichtig. Vorrangig prüfen: Darlehen nach § 91 SGB XII gegen ' + + 'Grundschuld, damit nicht unter Zeitdruck verkauft werden muss.', + 'beschreibungVerschluesselt', + ), + }, + }) + + await db.vorgang.create({ + data: { + betreuteId: betreute.id, + titel: 'Aufwandspauschale für das erste Betreuungsjahr beantragen', + kategorie: 'GERICHT', + status: 'OFFEN', + gegenstelle: 'Amtsgericht Bayreuth', + aktenzeichen: 'XVII 412/25', + naechsterSchritt: 'Antrag nach Ablauf des Betreuungsjahres am 13.02.2027 stellen', + angelegtVonId: userId, + }, + }) + + // --- Kontaktprotokolle -------------------------------------------------- + const besuche: { tag: string; dauer: number; inhalt: string; wunsch?: string }[] = [ + { tag: '2026-06-14', dauer: 75, inhalt: 'Besuch im Heim. Gemeinsam im Garten gesessen. Atmung an diesem Tag ruhig, Sauerstoff 2 l/min. Über das Haus gesprochen.', wunsch: 'Möchte das Haus nicht verkaufen, solange es nicht sein muss.' }, + { tag: '2026-07-05', dauer: 60, inhalt: 'Besuch. Erschöpft, blieb im Zimmer. Pflegedienstleitung berichtet von zwei kurzen Luftnotepisoden in der Vorwoche.' }, + { tag: '2026-07-22', dauer: 30, inhalt: 'Telefonat mit dem Sozialdienst wegen der Heimabrechnung Juli.' }, + { tag: '2026-08-09', dauer: 90, inhalt: 'Besuch mit Enkelin. Deutlich wacher als beim letzten Mal. Wünscht sich häufiger Besuch am Nachmittag.', wunsch: 'Besuche bitte nachmittags, vormittags ist sie zu müde.' }, + ] + + for (const b of besuche) { + await db.kontaktprotokoll.create({ + data: { + betreuteId: betreute.id, + datum: new Date(`${b.tag}T15:00:00.000Z`), + art: b.dauer === 30 ? 'TELEFONAT' : 'BESUCH', + dauerMinuten: b.dauer, + mitBetreuter: b.dauer !== 30, + inhaltVerschluesselt: verschluesseln(b.inhalt, 'inhaltVerschluesselt'), + wuenscheVerschluesselt: b.wunsch ? verschluesseln(b.wunsch, 'wuenscheVerschluesselt') : null, + }, + }) + } + + // --- Gesundheitsverlauf ------------------------------------------------- + await db.gesundheitsereignis.createMany({ + data: [ + { betreuteId: betreute.id, datum: datum('2026-03-11'), art: 'EXAZERBATION', titel: 'Infektexazerbation, ambulant behandelt', behandler: 'Dr. Reuther', beschreibungVerschluesselt: verschluesseln('Antibiotikum und kurzzeitig Prednisolon. Keine Einweisung nötig.', 'beschreibungVerschluesselt') }, + { betreuteId: betreute.id, datum: datum('2026-05-02'), bisDatum: datum('2026-05-09'), art: 'KRANKENHAUS', titel: 'Stationär wegen Luftnot', einrichtung: 'Klinikum Bayreuth', abwesenheitstage: 7, beschreibungVerschluesselt: verschluesseln('Aufnahme über die Notaufnahme. Sauerstoffbedarf danach von 1,5 auf 2 l/min erhöht.', 'beschreibungVerschluesselt') }, + { betreuteId: betreute.id, datum: datum('2026-06-20'), art: 'HILFSMITTEL', titel: 'Mobiler Sauerstoffkonzentrator bewilligt', beschreibungVerschluesselt: verschluesseln('Ermöglicht Ausflüge aus dem Zimmer.', 'beschreibungVerschluesselt') }, + { betreuteId: betreute.id, datum: datum('2026-07-15'), art: 'IMPFUNG', titel: 'Pneumokokken-Impfung', behandler: 'Dr. Reuther' }, + ], + }) + + // --- Heimkosten --------------------------------------------------------- + for (const monat of [5, 6, 7]) { + await db.heimkostenmonat.create({ + data: { + betreuteId: betreute.id, + jahr: 2026, + monat, + pflegeEigenanteilCent: cent(1489.0), + unterkunftVerpflegungCent: cent(878.4), + investitionskostenCent: cent(486.3), + ausbildungsumlageCent: cent(112.5), + pflegekasseCent: cent(1363.0), + leistungszuschlagProzent: 15, + leistungszuschlagCent: cent(223.35), + renteCent: cent(1284.62), + barbetragCent: cent(135.66), + bekleidungspauschaleCent: cent(27.13), + abwesenheitstage: monat === 5 ? 7 : 0, + }, + }) + } + + console.log('Demofall "Erika Musterfrau" angelegt.') +} + +async function main() { + const benutzer = await ersterZugang() + + if (process.env.SEED_DEMO === 'true' && !istProduktion && benutzer) { + await demofall(benutzer.id) + } else if (process.env.SEED_DEMO === 'true' && istProduktion) { + console.log('SEED_DEMO wird in der Produktion nicht ausgefuehrt.') + } + + console.log('\nFertig. Weiter geht es im Browser unter /ersteinrichtung.') +} + +main() + .catch((fehler) => { + console.error('Seed fehlgeschlagen:', fehler) + process.exit(1) + }) + .finally(() => db.$disconnect()) diff --git a/src/app/(app)/fristen/[id]/page.tsx b/src/app/(app)/fristen/[id]/page.tsx new file mode 100644 index 0000000..5ae21d7 --- /dev/null +++ b/src/app/(app)/fristen/[id]/page.tsx @@ -0,0 +1,264 @@ +import Link from 'next/link' +import { notFound } from 'next/navigation' +import { CalendarClock, FolderKanban, Link2, Pencil, Repeat, Scale } from 'lucide-react' +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' +import { Badge } from '@/components/ui/badge' +import { Button } from '@/components/ui/button' +import { ErledigenKnopf } from '@/components/fristen/erledigen-knopf' +import { FristAktionen } from '@/components/fristen/frist-aktionen' +import { FristFormular } from '@/components/fristen/frist-formular' +import { alsAnzeige } from '@/components/fristen/typen' +import { Rechtshinweis } from '@/components/rechtshinweis' +import { SeitenKopf } from '@/components/seiten-kopf' +import { db, nichtGeloescht } from '@/lib/db' +import { ampelText, fristAmpel } from '@/lib/fristen/ampel' +import { ausIsoTag, nichtWerktagHinweis, wochentagName } from '@/lib/fristen/datum' +import { fristenKontext } from '@/lib/fristen/kontext' +import { rruleBeschreiben } from '@/lib/fristen/wiederholung' +import { FRIST_STATUS_BESCHRIFTUNG, FRIST_TYP_BESCHRIFTUNG } from '@/lib/validierung/fristen' +import { formatDatum } from '@/lib/utils' +import { fristAendern } from '@/server/actions/fristen' + +/** + * Eine einzelne Frist. + * + * Das Bearbeitungsformular liegt auf derselben Adresse hinter + * "?bearbeiten=1" statt auf einer eigenen Unterseite: So bleibt der Weg + * zurueck zur Ansicht der Zurueck-Knopf des Browsers. + */ +export default async function FristSeite({ + params, + searchParams, +}: { + params: Promise<{ id: string }> + searchParams: Promise> +}) { + const { id } = await params + const parameter = await searchParams + const { betreute, darfSchreiben } = await fristenKontext('lesen') + + const datensatz = await db.frist.findFirst({ + where: { id, betreuteId: betreute.id, ...nichtGeloescht }, + include: { + vorgang: { select: { id: true, titel: true } }, + vorgaengerFrist: { select: { id: true, titel: true, datum: true, status: true } }, + nachfolgerFrist: { select: { id: true, titel: true, datum: true, status: true } }, + }, + }) + if (!datensatz) notFound() + + const frist = alsAnzeige(datensatz) + const datum = ausIsoTag(frist.datum)! + const ampel = fristAmpel(datum, frist.status) + const werktagshinweis = frist.status === 'OFFEN' ? nichtWerktagHinweis(datum) : null + + const bearbeiten = + darfSchreiben && + (Array.isArray(parameter.bearbeiten) ? parameter.bearbeiten[0] : parameter.bearbeiten) === '1' + + const vorgaenge = bearbeiten + ? await db.vorgang.findMany({ + where: { betreuteId: betreute.id, ...nichtGeloescht }, + select: { id: true, titel: true }, + orderBy: { aktualisiertAm: 'desc' }, + take: 100, + }) + : [] + + return ( +
+ + {ampelText(ampel, frist.ausschlussfrist)} + {FRIST_TYP_BESCHRIFTUNG[frist.typ]} + {FRIST_STATUS_BESCHRIFTUNG[frist.status]} + + } + pfad={[ + { titel: 'Start', href: '/heute' }, + { titel: 'Fristen', href: '/fristen' }, + { titel: frist.titel }, + ]} + aktionen={ + darfSchreiben && !bearbeiten ? ( + + ) : undefined + } + /> + + {frist.ausschlussfrist && frist.status === 'OFFEN' && ( + + Ausschlussfrist + + Läuft dieser Termin ab, ist der Anspruch ersatzlos verloren. Es gibt keine + Nachsicht und keine Wiedereinsetzung — auch nicht bei guten Gründen. + + + )} + + {bearbeiten ? ( + + ) : ( + <> +
+
+
Stichtag
+
+
+
+ +
+
Vorwarnung
+
+ {frist.vorwarnungTage.length === 0 + ? 'Keine' + : frist.vorwarnungTage.map((t) => (t === 1 ? '1 Tag' : `${t} Tage`)).join(', ') + + ' vorher'} +
+
+ + {frist.rechtsgrundlage && ( +
+
Rechtsgrundlage
+
+
+
+ )} + +
+
Wiederholung
+
+
+
+ + {frist.vorgang && ( +
+
Gehört zum Vorgang
+
+ +
+
+ )} + + {frist.erledigtAm && ( +
+
Erledigt am
+
{formatDatum(ausIsoTag(frist.erledigtAm))}
+
+ )} + + {frist.systemSchluessel && ( +
+
Abgeleitete Pflichtfrist
+
+ Diese Frist wird aus dem Bestellungsdatum berechnet (Schlüssel{' '} + {frist.systemSchluessel}). + Ändert sich das Bestellungsdatum, zieht die Anwendung das Datum nach — solange + die Frist noch offen ist. +
+
+ )} +
+ + {werktagshinweis && ( + + Stichtag an einem arbeitsfreien Tag + {werktagshinweis} + + )} + + {frist.beschreibung && ( +
+

Was ist zu tun?

+

+ {frist.beschreibung} +

+
+ )} + + {(datensatz.vorgaengerFrist || datensatz.nachfolgerFrist) && ( +
+

+

+
    + {datensatz.vorgaengerFrist && ( +
  • + Hervorgegangen aus{' '} + + {datensatz.vorgaengerFrist.titel} + {' '} + vom {formatDatum(datensatz.vorgaengerFrist.datum)}. +
  • + )} + {datensatz.nachfolgerFrist && ( +
  • + Nachfolger:{' '} + + {datensatz.nachfolgerFrist.titel} + {' '} + am {formatDatum(datensatz.nachfolgerFrist.datum)}. +
  • + )} +
+
+ )} + + {darfSchreiben && ( +
+

Was möchtest du tun?

+ {frist.status === 'OFFEN' && ( + + )} + +
+ )} + + )} + + {frist.rechtsgrundlage && } +
+ ) +} diff --git a/src/app/(app)/fristen/kalender/page.tsx b/src/app/(app)/fristen/kalender/page.tsx new file mode 100644 index 0000000..b11cb14 --- /dev/null +++ b/src/app/(app)/fristen/kalender/page.tsx @@ -0,0 +1,108 @@ +import Link from 'next/link' +import { List, Plus } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { KalenderAbo } from '@/components/fristen/kalender-abo' +import { KalenderMonat, monatsname } from '@/components/fristen/kalender-monat' +import { alsAnzeige } from '@/components/fristen/typen' +import { Rechtshinweis } from '@/components/rechtshinweis' +import { SeitenKopf } from '@/components/seiten-kopf' +import { db, nichtGeloescht } from '@/lib/db' +import { heuteAlsTag, plusTage, tag } from '@/lib/fristen/datum' +import { feedUrlErzeugen } from '@/lib/fristen/ics' +import { fristenKontext } from '@/lib/fristen/kontext' + +export const metadata = { title: 'Fristenkalender' } + +/** + * Monatsansicht. + * + * Geladen wird ein Puffer von einer Woche vor und nach dem Monat: Das + * Raster zeigt am Rand Tage der Nachbarmonate, und dort sollen keine + * Fristen unsichtbar bleiben. + */ +export default async function KalenderSeite({ + searchParams, +}: { + searchParams: Promise> +}) { + const { betreute, benutzer, darfSchreiben } = await fristenKontext('lesen') + const parameter = await searchParams + + const einzeln = (name: string): string => { + const wert = parameter[name] + return (Array.isArray(wert) ? wert[0] : wert) ?? '' + } + + const heute = heuteAlsTag() + const jahr = ganzzahl(einzeln('jahr'), heute.getUTCFullYear(), 1900, 2200) + const monat = ganzzahl(einzeln('monat'), heute.getUTCMonth() + 1, 1, 12) + + const monatsanfang = tag(jahr, monat, 1) + const monatsende = plusTage(tag(monat === 12 ? jahr + 1 : jahr, monat === 12 ? 1 : monat + 1, 1), -1) + + const datensaetze = await db.frist.findMany({ + where: { + betreuteId: betreute.id, + ...nichtGeloescht, + datum: { gte: plusTage(monatsanfang, -7), lte: plusTage(monatsende, 7) }, + }, + orderBy: [{ datum: 'asc' }, { ausschlussfrist: 'desc' }], + include: { vorgang: { select: { id: true, titel: true } } }, + }) + + const feedUrl = feedUrlErzeugen(betreute.id, benutzer.id) + + return ( +
+ + + {darfSchreiben && ( + + )} + + } + /> + + {(jahr !== heute.getUTCFullYear() || monat !== heute.getUTCMonth() + 1) && ( +
+ +
+ )} + + + + + + +
+ ) +} + +function ganzzahl(text: string, standard: number, min: number, max: number): number { + const zahl = Number.parseInt(text, 10) + if (Number.isNaN(zahl) || zahl < min || zahl > max) return standard + return zahl +} diff --git a/src/app/(app)/fristen/neu/page.tsx b/src/app/(app)/fristen/neu/page.tsx new file mode 100644 index 0000000..827cc1d --- /dev/null +++ b/src/app/(app)/fristen/neu/page.tsx @@ -0,0 +1,41 @@ +import { FristFormular } from '@/components/fristen/frist-formular' +import { SeitenKopf } from '@/components/seiten-kopf' +import { db, nichtGeloescht } from '@/lib/db' +import { fristenKontext } from '@/lib/fristen/kontext' +import { fristAnlegen } from '@/server/actions/fristen' + +export const metadata = { title: 'Frist anlegen' } + +/** + * Neue Frist. + * + * Der Schreibzugriff wird hier geprueft und in der Server Action noch + * einmal - die Seite auszublenden reicht nicht, ein Formular laesst sich + * auch von aussen abschicken. + */ +export default async function NeueFristSeite() { + const { betreute } = await fristenKontext('schreiben') + + const vorgaenge = await db.vorgang.findMany({ + where: { betreuteId: betreute.id, ...nichtGeloescht, erledigtAm: null }, + select: { id: true, titel: true }, + orderBy: { aktualisiertAm: 'desc' }, + take: 100, + }) + + return ( +
+ + + +
+ ) +} diff --git a/src/app/(app)/fristen/page.tsx b/src/app/(app)/fristen/page.tsx new file mode 100644 index 0000000..d9fa07d --- /dev/null +++ b/src/app/(app)/fristen/page.tsx @@ -0,0 +1,232 @@ +import Link from 'next/link' +import type { Prisma } from '@prisma/client' +import { CalendarDays, Plus } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' +import { Filterleiste } from '@/components/fristen/filterleiste' +import { Fristenliste } from '@/components/fristen/fristenliste' +import { PflichtfristenKnopf } from '@/components/fristen/pflichtfristen-knopf' +import { alsAnzeige } from '@/components/fristen/typen' +import { Leerzustand, SeitenKopf } from '@/components/seiten-kopf' +import { Rechtshinweis } from '@/components/rechtshinweis' +import { db, nichtGeloescht } from '@/lib/db' +import { alsIsoTag, heuteAlsTag, plusJahre, plusTage } from '@/lib/fristen/datum' +import { fristenKontext } from '@/lib/fristen/kontext' +import { FRIST_STATUS, FRIST_TYPEN } from '@/lib/validierung/fristen' + +export const metadata = { title: 'Fristen' } + +/** + * Die Fristenliste. + * + * Standardansicht ist "alles Offene", gruppiert nach Dringlichkeit. Wer die + * Seite morgens oeffnet, soll oben sehen, was brennt - nicht eine nach + * Datum sortierte Tabelle, in der das Ueberfaellige zwischen dem Erledigten + * verschwindet. + */ +export default async function FristenSeite({ + searchParams, +}: { + searchParams: Promise> +}) { + const { betreute, benutzer, darfSchreiben } = await fristenKontext('lesen') + const parameter = await searchParams + + const einzeln = (name: string): string => { + const wert = parameter[name] + return (Array.isArray(wert) ? wert[0] : wert) ?? '' + } + + const zeitraum = einzeln('zeitraum') || 'offen' + const kategorie = einzeln('kategorie') + const typ = einzeln('typ') + const status = einzeln('status') + + const heute = heuteAlsTag() + const wo: Prisma.FristWhereInput = { betreuteId: betreute.id, ...nichtGeloescht } + + // --- Zeitraum ------------------------------------------------------------- + switch (zeitraum) { + case 'ueberfaellig': + wo.status = 'OFFEN' + wo.datum = { lt: heute } + break + case 'woche': + wo.status = 'OFFEN' + wo.datum = { lte: plusTage(heute, 7) } + break + case 'monat': + wo.status = 'OFFEN' + wo.datum = { lte: plusTage(heute, 31) } + break + case 'jahr': + wo.status = 'OFFEN' + wo.datum = { lte: plusJahre(heute, 1) } + break + case 'alle': + break + default: + // "offen" ist der Standard: alles, was noch zu tun ist. + wo.status = 'OFFEN' + } + + // Ein ausdruecklich gewaehlter Status schlaegt die Voreinstellung des + // Zeitraums - sonst liesse sich "nur Entfallenes" nie anzeigen. + if (status && (FRIST_STATUS as readonly string[]).includes(status)) { + wo.status = status as (typeof FRIST_STATUS)[number] + } + + if (typ && (FRIST_TYPEN as readonly string[]).includes(typ)) { + wo.typ = typ as (typeof FRIST_TYPEN)[number] + } + + // --- Kategorie ------------------------------------------------------------ + switch (kategorie) { + case 'pflicht': + wo.systemSchluessel = { not: null } + break + case 'eigene': + wo.systemSchluessel = null + break + case 'ausschluss': + wo.ausschlussfrist = true + break + case 'wiederkehrend': + wo.wiederholungRrule = { not: null } + break + default: + break + } + + const [datensaetze, gesamtOffen, ueberfaellig] = await Promise.all([ + db.frist.findMany({ + where: wo, + orderBy: [{ datum: 'asc' }], + include: { vorgang: { select: { id: true, titel: true } } }, + take: 500, + }), + db.frist.count({ where: { betreuteId: betreute.id, status: 'OFFEN', ...nichtGeloescht } }), + db.frist.count({ + where: { + betreuteId: betreute.id, + status: 'OFFEN', + datum: { lt: heute }, + ...nichtGeloescht, + }, + }), + ]) + + const fristen = datensaetze.map(alsAnzeige) + const verfallsgefahr = fristen.filter( + (f) => f.ausschlussfrist && f.status === 'OFFEN' && f.datum <= alsIsoTag(plusTage(heute, 90)), + ) + + return ( +
+ 0 ? `, davon ${ueberfaellig} überfällig.` : '.') + } + pfad={[{ titel: 'Start', href: '/heute' }, { titel: 'Fristen' }]} + aktionen={ + <> + + {darfSchreiben && ( + + )} + + } + /> + + {verfallsgefahr.length > 0 && ( + + + {verfallsgefahr.length === 1 + ? 'Eine Ausschlussfrist läuft' + : `${verfallsgefahr.length} Ausschlussfristen laufen`} + + +

+ Läuft eine Ausschlussfrist ab, ist der Anspruch ersatzlos weg. Bei der + Aufwandspauschale nach § 1878 BGB ist damit die Vergütung für ein ganzes + Betreuungsjahr verloren — ohne Nachsicht, ohne Wiedereinsetzung. +

+
    + {verfallsgefahr.map((f) => ( +
  • + + {f.titel} + +
  • + ))} +
+
+
+ )} + + + + {fristen.length === 0 ? ( + + ) : ( + + ) + } + /> + ) : ( + + )} + + {darfSchreiben && fristen.length > 0 && ( +
+

Gesetzliche Pflichtfristen

+

+ Berechnet Anfangsbericht, Vermögensverzeichnis, Jahresbericht, Vermögensübersicht + und die Ausschlussfrist für die Aufwandspauschale neu aus dem Bestellungsdatum + {betreute.bestellungAm ? '' : ' — das in den Stammdaten noch fehlt'}. Mehrfaches + Ableiten legt nichts doppelt an. +

+
+ +
+
+ )} + + + +

+ Angemeldet als {benutzer.name}. Fristen der betreuten Person {betreute.vorname}{' '} + {betreute.nachname}. +

+
+ ) +} diff --git a/src/app/(app)/heute/page.tsx b/src/app/(app)/heute/page.tsx new file mode 100644 index 0000000..7151ff1 --- /dev/null +++ b/src/app/(app)/heute/page.tsx @@ -0,0 +1,84 @@ +import type { Metadata } from 'next' +import Link from 'next/link' +import { redirect } from 'next/navigation' +import { Button } from '@/components/ui/button' +import { SeitenKopf } from '@/components/seiten-kopf' +import { BelegeKachel } from '@/components/dashboard/belege-kachel' +import { FristenAmpel } from '@/components/dashboard/fristen-ampel' +import { Kennzahlen } from '@/components/dashboard/kennzahlen' +import { NachforderungenKachel } from '@/components/dashboard/nachforderungen-kachel' +import { PosteingangKachel } from '@/components/dashboard/posteingang-kachel' +import { WartetAufAndere } from '@/components/dashboard/wartet-auf-andere' +import { darf } from '@/lib/auth/rollen' +import { aktiveBetreute, erfordereBenutzer, erfordereZugang } from '@/lib/auth/sitzung' +import { dashboardLaden } from '@/lib/dashboard/laden' + +export const metadata: Metadata = { title: 'Heute' } + +/** + * Das Dashboard. + * + * Es beantwortet genau eine Frage: was muss ich heute tun? Deshalb endet + * jede Kachel in einer Handlung, und die Reihenfolge ist Dringlichkeit, + * nicht fachliche Systematik. Am Handy einspaltig von oben nach unten, am + * Schreibtisch zweispaltig - die Fristen-Ampel steht immer zuoberst. + */ +export default async function HeutePage() { + const benutzer = await erfordereBenutzer('/heute') + const aktive = await aktiveBetreute(benutzer) + if (!aktive) redirect('/ersteinrichtung') + + // Das Dashboard fasst mehrere Module zusammen. Der Zugang wird gegen die + // Stammdaten geprueft; jede einzelne Kachel entfaellt, wenn ihr Modul + // nicht freigegeben ist. + const { betreute, zugang } = await erfordereZugang(aktive.id, 'STAMMDATEN', 'lesen') + + const daten = await dashboardLaden(betreute, (modul) => darf(zugang, modul, 'lesen')) + + const heuteText = new Intl.DateTimeFormat('de-DE', { + weekday: 'long', + day: '2-digit', + month: 'long', + year: 'numeric', + }).format(daten.heute) + + return ( +
+ + {heuteText} · Betreuung für {daten.name} + + } + aktionen={ + <> + + + + } + /> + + {/* Die Ampel steht immer oben - in jeder Bildschirmbreite. */} + + + {/* + Am Handy laeuft alles einspaltig in der Reihenfolge der + Dringlichkeit: Nachforderungen (Geld und Fristen des Traegers), + dann Vorgaenge, dann Post, dann Belege. + */} +
+ + + + +
+ + +
+ ) +} diff --git a/src/app/(app)/page.tsx b/src/app/(app)/page.tsx new file mode 100644 index 0000000..5476df0 --- /dev/null +++ b/src/app/(app)/page.tsx @@ -0,0 +1,9 @@ +import { redirect } from 'next/navigation' + +/** + * Die Wurzel der Anwendung hat keinen eigenen Inhalt: Wer angemeldet ist, + * will wissen, was heute zu tun ist. + */ +export default function StartPage() { + redirect('/heute') +} diff --git a/src/app/(app)/protokoll/page.tsx b/src/app/(app)/protokoll/page.tsx new file mode 100644 index 0000000..a311f9f --- /dev/null +++ b/src/app/(app)/protokoll/page.tsx @@ -0,0 +1,209 @@ +import type { Metadata } from 'next' +import { ShieldCheck } from 'lucide-react' +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' +import { Badge } from '@/components/ui/badge' +import { Card, CardContent } from '@/components/ui/card' +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@/components/ui/table' +import { SeitenKopf, Leerzustand } from '@/components/seiten-kopf' +import { aktiveBetreute, erfordereBenutzer, erfordereZugang } from '@/lib/auth/sitzung' +import { db } from '@/lib/db' +import { entschluesselnWennMoeglich } from '@/lib/crypto/feld' +import { formatDatumZeit } from '@/lib/utils' +import type { AuditAktion } from '@prisma/client' + +export const metadata: Metadata = { title: 'Protokoll' } +export const dynamic = 'force-dynamic' + +const AKTION_BESCHRIFTUNG: Record = { + ERSTELLT: 'angelegt', + GEAENDERT: 'geändert', + GELOESCHT: 'in den Papierkorb', + WIEDERHERGESTELLT: 'wiederhergestellt', + ENDGUELTIG_GELOESCHT: 'endgültig gelöscht', + ANGESEHEN: 'angesehen', + EXPORTIERT: 'exportiert', + DOKUMENT_HERUNTERGELADEN: 'Dokument geöffnet', + ANMELDUNG: 'angemeldet', + ANMELDUNG_FEHLGESCHLAGEN: 'Anmeldung fehlgeschlagen', + ABMELDUNG: 'abgemeldet', + ZWEITER_FAKTOR_EINGERICHTET: 'zweiter Faktor eingerichtet', + PASSWORT_GEAENDERT: 'Passwort geändert', + ROLLE_GEAENDERT: 'Rolle geändert', + FREIGABE_GEAENDERT: 'Freigabe geändert', +} + +function variante(aktion: AuditAktion) { + if (aktion === 'ANMELDUNG_FEHLGESCHLAGEN' || aktion === 'ENDGUELTIG_GELOESCHT') return 'rot' + if (aktion === 'GELOESCHT' || aktion === 'EXPORTIERT') return 'orange' + if (aktion === 'ERSTELLT') return 'gruen' + return 'secondary' +} + +type Aenderung = Record + +function alsText(wert: unknown): string { + if (wert === null || wert === undefined || wert === '') return '—' + if (typeof wert === 'boolean') return wert ? 'ja' : 'nein' + if (Array.isArray(wert)) return wert.length ? wert.join(', ') : '—' + return String(wert) +} + +export default async function ProtokollSeite({ + searchParams, +}: { + searchParams: Promise<{ seite?: string }> +}) { + const benutzer = await erfordereBenutzer() + const aktive = await aktiveBetreute(benutzer) + if (!aktive) return null + + await erfordereZugang(aktive.id, 'STAMMDATEN') + + const { seite = '1' } = await searchParams + const proSeite = 50 + const nummer = Math.max(1, Number.parseInt(seite, 10) || 1) + + const [eintraege, gesamt] = await Promise.all([ + db.auditLog.findMany({ + where: { betreuteId: aktive.id }, + orderBy: { zeitpunkt: 'desc' }, + skip: (nummer - 1) * proSeite, + take: proSeite, + }), + db.auditLog.count({ where: { betreuteId: aktive.id } }), + ]) + + const seiten = Math.max(1, Math.ceil(gesamt / proSeite)) + + return ( +
+ + + + + + {eintraege.length === 0 ? ( + + ) : ( + + + + + + Zeitpunkt + Wer + Was + Gegenstand + + + + {eintraege.map((eintrag) => { + const roh = entschluesselnWennMoeglich( + eintrag.aenderungVerschluesselt, + 'aenderungVerschluesselt', + ) + let aenderung: Aenderung | null = null + try { + aenderung = roh ? (JSON.parse(roh) as Aenderung) : null + } catch { + aenderung = null + } + + return ( + + + {formatDatumZeit(eintrag.zeitpunkt)} + + + {eintrag.userName ?? '—'} + + {eintrag.userEmail} + + + + + {AKTION_BESCHRIFTUNG[eintrag.aktion]} + + + + {eintrag.entitaet} + {eintrag.bezeichnung && ( + · {eintrag.bezeichnung} + )} + {aenderung && Object.keys(aenderung).length > 0 && ( +
+ + {Object.keys(aenderung).length} Feld + {Object.keys(aenderung).length === 1 ? '' : 'er'} geändert + +
+ {Object.entries(aenderung).map(([feld, werte]) => ( +
+
+ {feld} +
+
+ + {alsText(werte.alt)} + + + {alsText(werte.neu)} +
+
+ ))} +
+
+ )} +
+
+ ) + })} +
+
+
+
+ )} + + {seiten > 1 && ( + + )} +
+ ) +} diff --git a/src/app/(app)/stammdaten/bearbeiten/formular.tsx b/src/app/(app)/stammdaten/bearbeiten/formular.tsx new file mode 100644 index 0000000..4e0d4c3 --- /dev/null +++ b/src/app/(app)/stammdaten/bearbeiten/formular.tsx @@ -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 ( + + ) +} + +export function StammdatenFormular({ werte }: { werte: StammdatenWerte }) { + const [zustand, aktion] = useActionState(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 ( +
+ + + {zustand.fehler && ( + + + )} + + {/* --- Person --------------------------------------------------------- */} + + + Person + + + + + +
+ + + + + + + + + + + + + + + + + + + {/* --- Aufenthalt ------------------------------------------------------ */} + + + Aufenthalt und Einrichtung + + + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + + +
+ + + + + + +
+
+
+ + {/* --- Betreuungsgericht ---------------------------------------------- */} + + + Betreuungsgericht + + +
+ + + + + + + + + + + + +
+ +
+ Aufgabenkreise +
+ {AUFGABENKREISE.map((kreis) => ( + + ))} +
+
+ + +