UI-Gesamtumbau: Du-Form, Inspector-Panel, Onboarding-Assistent, Tour, Palette
Deploy App / deploy (push) Successful in 1m1s

Rein an der Oberflaeche -- Berechnung, Datenmodell und API-Semantik unveraendert
(103 Tests unveraendert gruen).

Paket A (Fundament):
- durchgehend Du-Form und echte Umlaute in allen sichtbaren Texten,
  inkl. API-Fehlermeldungen (vorher Mix aus Sie/Du und ae/oe/ue)
- neue UI-Primitiven (ui.tsx): Button, Modal mit ESC/Fokus-Falle/Animation,
  Bestaetigungs-Dialog statt window.confirm, Toasts statt alert,
  Skeleton-Loader, EmptyState
- eigene Attention-Farbe (Amber) fuer offene Entscheide, getrennt vom Akzent
- Micro-Interactions mit prefers-reduced-motion-Fallback

Paket B (Onboarding, Roadmap Nr. 10):
- gefuehrter Plan-Assistent in 5 Schritten; Einkommen bewusst pro Person
  (raeumt die 9.9-AHV-Falle aus); reine Orchestrierung bestehender Endpunkte
- Beispielplan mit einem Klick; Uebergaenge absichtlich offen
- interaktive Tour ueber die Planansicht (localStorage, jederzeit neu startbar)
- abgeleitete "Naechste Schritte"-Karte (offene Entscheide, fehlende Elemente,
  fehlende Pensionsphase, Ruin -> Einflussfaktoren)

Paket C (Struktur):
- Inspector-Panel rechts statt Modals fuer alle Einzel-Bearbeitungen;
  Matrix bleibt sichtbar, Zellklick wechselt den Inhalt
- Phasenkopf auf vier Kern-Infos entschlackt (Rest in der 0.11-Detailansicht)
- Matrix mit eigenem Scrollbereich, Koepfe beidachsig fixiert
- Sidebar-Gruppen "Meine Plaene" / "Wissen"; "So rechnet FPT" statt
  SPEZIFIKATION; "Szenario-Profil" statt "Plan-Einstellungen"
- Aktions-Icons ohne Hover sichtbar (Touch)

Paket D (Extras):
- Sparklines je Element-Zeile aus den 0.11-Verlaufswerten
- Befehls-Palette (Ctrl/Cmd+K)
- Ruin-Banner verlinkt auf die Einflussfaktoren

Nebenbei: der ProfileMenu-Lint-Fehler und der Selection-Rest (9.17) sind
behoben -- npm run lint laeuft erstmals fehlerfrei.

SPEZIFIKATION auf 0.13: neue Kapitel 3.2.8, 3.7.6-3.7.9, 9.23, 9.24;
3.6.3 und 3.7.1 ueberarbeitet, 9.17 bereinigt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 07:40:42 +02:00
parent e1f74fca95
commit c2fb82b0de
60 changed files with 2915 additions and 1210 deletions
+22 -22
View File
@@ -1,4 +1,4 @@
// Typ- und Validierungs-Layer fuer die finanziellen Elemente. Die kategorie- und
// Typ- und Validierungs-Layer für die finanziellen Elemente. Die kategorie- und
// kontextspezifischen Felder liegen in der DB als JSON; hier werden sie typisiert und
// (an der API-Grenze) mit Zod validiert.
@@ -19,7 +19,7 @@ export type OwnerRole = "PERSON_A" | "PERSON_B" | "HOUSEHOLD";
// Kategorien, deren Element zwingend genau einer Person zugeordnet ist.
export const PERSON_ONLY_CATEGORIES: ElementCategory[] = ["AHV", "PENSION_FUND", "PILLAR_3A"];
// Kategorien, die gemeinsam ODER pro Person erfasst werden koennen (inkl. Einkommen).
// Kategorien, die gemeinsam ODER pro Person erfasst werden können (inkl. Einkommen).
export const OWNER_OPTIONAL_CATEGORIES: ElementCategory[] = ["INCOME", "EXPENSE", "REAL_ESTATE", "OTHER_ASSET", "OTHER_DEBT"];
export const CATEGORY_LABELS: Record<ElementCategory, string> = {
@@ -27,9 +27,9 @@ export const CATEGORY_LABELS: Record<ElementCategory, string> = {
EXPENSE: "Ausgaben",
AHV: "AHV",
PENSION_FUND: "Pensionskasse",
PILLAR_3A: "Saeule 3a",
PILLAR_3A: "Säule 3a",
REAL_ESTATE: "Immobilie",
OTHER_ASSET: "Sonstiges Vermoegen",
OTHER_ASSET: "Sonstiges Vermögen",
OTHER_DEBT: "Sonstige Schulden",
};
@@ -52,14 +52,14 @@ export const CATEGORY_ORDER: ElementCategory[] = [
export interface PhaseData {
// INCOME / EXPENSE
amount?: number;
// INCOME / EXPENSE: jaehrlicher Teuerungsausgleich (%). Indexiert den Flow ueber die
// INCOME / EXPENSE: jährlicher Teuerungsausgleich (%). Indexiert den Flow über die
// Phasenjahre (Jahr t = Basis x (1+idx)^(t-1)). Eigenes Feld je Element (Einkommen und
// Ausgaben unabhaengig). Default = Phaseninflation.
// Ausgaben unabhängig). Default = Phaseninflation.
teuerungsausgleich?: number;
// AHV
gapYears?: number;
// AHV, nur wenn die Person bei Planbeginn BEREITS pensioniert ist (dann gibt es keinen
// Pensions-Uebergang, an dem die Karriere geprueft werden koennte): Beitragskarriere.
// Pensions-Übergang, an dem die Karriere geprüft werden könnte): Beitragskarriere.
// avgIncomeBefore ist REAL (heutige Kaufkraft).
avgIncomeBefore?: number;
gapYearsBefore?: number;
@@ -68,10 +68,10 @@ export interface PhaseData {
startValue?: number;
expectedReturn?: number;
annualContribution?: number;
// OTHER_ASSET: jaehrliche Bezugsrate (Entnahme). Mindert das Vermoegen und fliesst ins Cash.
// OTHER_ASSET: jährliche Bezugsrate (Entnahme). Mindert das Vermögen und fliesst ins Cash.
annualWithdrawal?: number;
// PENSION_FUND / PILLAR_3A / OTHER_ASSET (ab Phase 2): zusaetzliche Einlage aus dem
// verfuegbaren Kapital der Phase. Der Basis-Startwert wird aus der Vorphase fortgeschrieben.
// PENSION_FUND / PILLAR_3A / OTHER_ASSET (ab Phase 2): zusätzliche Einlage aus dem
// verfügbaren Kapital der Phase. Der Basis-Startwert wird aus der Vorphase fortgeschrieben.
additionalInvestment?: number;
// REAL_ESTATE
purchasePrice?: number;
@@ -79,10 +79,10 @@ export interface PhaseData {
amortization?: number;
// Hypothekarzins in % der Restschuld. Der Zinsbetrag sinkt dadurch mit der Amortisation.
interestRate?: number;
// Steuert die Doppelzaehlung: Sind die Zinsen im Ausgaben-Element bereits enthalten
// (INCLUDED, Default -- Verhalten bisheriger Plaene) oder soll das Tool sie dazurechnen (ADD)?
// Steuert die Doppelzählung: Sind die Zinsen im Ausgaben-Element bereits enthalten
// (INCLUDED, Default -- Verhalten bisheriger Pläne) oder soll das Tool sie dazurechnen (ADD)?
interestHandling?: "INCLUDED" | "ADD";
// Geschaetzte jaehrliche Wertveraenderung der LIEGENSCHAFT (nicht des Eigenkapitals).
// Geschätzte jährliche Wertveränderung der LIEGENSCHAFT (nicht des Eigenkapitals).
valueGrowth?: number;
// OTHER_DEBT
annualRepayment?: number;
@@ -92,36 +92,36 @@ export type TransitionDecision = "HOLD" | "SELL" | "PARTIAL";
export type PkPayoutMode = "CAPITAL" | "PENSION" | "COMBI";
export interface TransitionData {
// AHV (Pensions-Uebergang): Pruefung der Beitragskarriere. Das geplante Durchschnitts-
// AHV (Pensions-Übergang): Prüfung der Beitragskarriere. Das geplante Durchschnitts-
// einkommen kommt aus dem Plan; reicht der Plan nicht bis zum Beitragsbeginn (Alter 21)
// zurueck, ergaenzt der Benutzer die Jahre davor. avgIncomeBefore ist REAL.
// zurück, ergänzt der Benutzer die Jahre davor. avgIncomeBefore ist REAL.
reviewed?: boolean;
avgIncomeBefore?: number;
gapYearsBefore?: number;
// PENSION_FUND / PILLAR_3A (normaler Uebergang): expliziter Bezugs-Entscheid.
// PENSION_FUND / PILLAR_3A (normaler Übergang): expliziter Bezugs-Entscheid.
withdrawalMode?: "NONE" | "AMOUNT";
withdrawal?: number;
// PENSION_FUND (Pensions-Uebergang)
// PENSION_FUND (Pensions-Übergang)
payoutMode?: PkPayoutMode;
capitalAmount?: number;
conversionRate?: number;
// PENSION_FUND (Kapital) / PILLAR_3A (Pensions-Uebergang) / REAL_ESTATE
// PENSION_FUND (Kapital) / PILLAR_3A (Pensions-Übergang) / REAL_ESTATE
capitalTaxRate?: number;
saleTaxRate?: number;
// REAL_ESTATE / OTHER_ASSET
decision?: TransitionDecision;
salePrice?: number;
// OTHER_ASSET (Teilverkauf): Betrag, der am Uebergang ins Cash fliesst; der Rest bleibt aktiv.
// OTHER_ASSET (Teilverkauf): Betrag, der am Übergang ins Cash fliesst; der Rest bleibt aktiv.
partialSaleAmount?: number;
// REAL_ESTATE (Sonderamortisation): Einmaltilgung der Hypothek am Uebergang, aus dem Cash.
// REAL_ESTATE (Sonderamortisation): Einmaltilgung der Hypothek am Übergang, aus dem Cash.
extraAmortization?: number;
// OTHER_DEBT
immediateRepayment?: number;
}
// --- Cash-Uebergang: einmalige Sonderein-/ausgaben ---
// --- Cash-Übergang: einmalige Sonderein-/ausgaben ---
// Entscheid am UEBERGANG zwischen zwei Phasen, direkt auf dem Cash-Konto (Cash ist kein
// Element, der Entscheid haengt darum an der Von-Phase). Erfassungs-Konventionen analog zu
// Element, der Entscheid hängt darum an der Von-Phase). Erfassungs-Konventionen analog zu
// den laufenden Flows: Zufluss NOMINAL (wie Einkommen), Kosten REAL (wie Ausgaben).
export type CashTransitionMode = "NONE" | "INFLOW" | "OUTFLOW" | "BOTH";