Modul-Review 2 Feinschliff: Assistent, Tour, 3-Spalten-Ansicht
Deploy App / deploy (push) Successful in 1m8s
Deploy App / deploy (push) Successful in 1m8s
Sechs Punkte aus der Testrunde: Assistent: - Willkommens-Screen (Gesamtbild der 5 Schritte) + persistente Schritt-Leiste - Rendite-Feld bei PK/3a/Wertschriften ergaenzt (war fest verdrahtet) - Schritt 5: Hypothekarzinsen (falls nicht in Ausgaben) korrekt bei den Ausgaben dazugerechnet; "noch zu verteilen" prominent nach oben; Verteilung nach Gemeinsam/Person A/Person B gruppiert Tour: - Spotlight: Rest abgedunkelt (9999px box-shadow), staerkerer Puls - Karte groesser + springt auf die gegenueberliegende Bildschirmhaelfte - "Ueberspringen"-Knopf Ansicht: - Grundprofil + Zeitachse + Kennzahlen (Endvermoegen nom/real, Ruin) als ein kompakter 3-Spalten-Block (25/50/25%) statt zweier breiter Zeilen - neue Modal-Groesse xwide Kein Eingriff in den Rechenkern; 267 Tests unveraendert. SPEZIFIKATION 0.29. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+63
-31
@@ -343,6 +343,7 @@ export function PlanView({
|
||||
|
||||
const hasPhases = computed.phases.length > 0;
|
||||
const firstPhase = computed.phases[0] ?? null;
|
||||
const lastPhase = computed.phases[computed.phases.length - 1] ?? null;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-5">
|
||||
@@ -360,39 +361,70 @@ export function PlanView({
|
||||
onOpenSensitivity={onOpenSensitivity}
|
||||
/>
|
||||
|
||||
{/* Szenario-Profil */}
|
||||
<div
|
||||
data-tour="profil"
|
||||
className={`flex flex-wrap items-center gap-3 rounded-xl border px-4 py-3 text-sm shadow-sm ${
|
||||
diff?.profileChanged ? "border-diff bg-diff-soft" : "border-border bg-surface"
|
||||
}`}
|
||||
>
|
||||
<span className="text-xs font-semibold uppercase tracking-wide text-faint">
|
||||
Grundprofil (Szenario){diff?.profileChanged ? " · abweichend" : ""}
|
||||
</span>
|
||||
{plan.persons.map((p) => (
|
||||
<span key={p.role} className="text-xs text-muted">
|
||||
{personLabel(p.role)}: {p.age} J., Pension {p.retirementAge}
|
||||
</span>
|
||||
))}
|
||||
<span className="text-xs text-muted">Inflation {plan.inflationRateDefault}%</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setPanel({ kind: "profile" })}
|
||||
className="ml-auto flex items-center gap-1.5 rounded-lg border border-border px-2.5 py-1 text-xs font-medium text-muted hover:bg-surface-2"
|
||||
{/* Kompakter Klotz: Grundprofil (25 %) · Zeitachse (50 %) · Kennzahlen (25 %). Auf
|
||||
schmalen Screens stapeln die drei. */}
|
||||
<div className="grid grid-cols-1 gap-4 lg:grid-cols-4">
|
||||
{/* Grundprofil -- Daten untereinander, «Bearbeiten» oben rechts. */}
|
||||
<div
|
||||
data-tour="profil"
|
||||
className={`flex flex-col gap-2 rounded-xl border px-4 py-3 shadow-sm lg:col-span-1 ${
|
||||
diff?.profileChanged ? "border-diff bg-diff-soft" : "border-border bg-surface"
|
||||
}`}
|
||||
>
|
||||
<Settings2 className="h-3.5 w-3.5" /> Profil bearbeiten
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<span className="text-xs font-semibold uppercase tracking-wide text-faint">
|
||||
Grundprofil{diff?.profileChanged ? " · abweichend" : ""}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setPanel({ kind: "profile" })}
|
||||
className="flex shrink-0 items-center gap-1 rounded-lg border border-border px-2 py-0.5 text-xs font-medium text-muted hover:bg-surface-2"
|
||||
>
|
||||
<Settings2 className="h-3.5 w-3.5" /> Bearbeiten
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
{plan.persons.map((p) => (
|
||||
<span key={p.role} className="text-xs text-muted">
|
||||
{personLabel(p.role)}: {p.age} J., Pension {p.retirementAge}
|
||||
</span>
|
||||
))}
|
||||
<span className="text-xs text-muted">Inflation {plan.inflationRateDefault}%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-tour="timeline">
|
||||
<Timeline
|
||||
phases={computed.phases}
|
||||
persons={personAxes}
|
||||
ruinAge={computed.ruinAge}
|
||||
actualYears={actualYears}
|
||||
startYear={plan.startYear}
|
||||
/>
|
||||
{/* Zeitachse */}
|
||||
<div data-tour="timeline" className="lg:col-span-2">
|
||||
<Timeline
|
||||
phases={computed.phases}
|
||||
persons={personAxes}
|
||||
ruinAge={computed.ruinAge}
|
||||
actualYears={actualYears}
|
||||
startYear={plan.startYear}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Kennzahlen: Endvermögen nominal + real (+ Ruin, falls vorhanden). */}
|
||||
<div className="flex flex-col justify-center gap-2 rounded-xl border border-border bg-surface px-4 py-3 shadow-sm lg:col-span-1">
|
||||
<span className="text-xs font-semibold uppercase tracking-wide text-faint">Endvermögen</span>
|
||||
<div>
|
||||
<div className="text-[11px] text-muted">Nominal</div>
|
||||
<div className="text-lg font-bold text-fg">
|
||||
{lastPhase ? formatChf(Math.round(lastPhase.endWealthNominal)) : "—"}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-[11px] text-muted">Real (heutige Kaufkraft)</div>
|
||||
<div className="text-lg font-bold text-fg">
|
||||
{lastPhase ? formatChf(Math.round(lastPhase.endWealthReal)) : "—"}
|
||||
</div>
|
||||
</div>
|
||||
{computed.ruinAge !== null && (
|
||||
<div className="mt-0.5 rounded-lg bg-danger-soft px-2 py-1 text-xs font-medium text-danger">
|
||||
Kapital reicht nur bis Alter {computed.ruinAge}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Anzeige-Umschalter (nominal/real, Plan/Ist) -- direkt über der Matrix, weil er nur
|
||||
|
||||
Reference in New Issue
Block a user