Phasen-Kopf: Verteil-Prozent bei der Quote, Start->End-Alter je Person, Vermoegen Start->Ende
Deploy App / deploy (push) Successful in 1m0s
Deploy App / deploy (push) Successful in 1m0s
- Sparquote/Verzehr zeigt in Klammern den bereits verteilten/gedeckten Prozentsatz, z. B. "Sparquote 10'000 (100% verteilt)". - Statt einer Alterszeile je Phase nun pro Person "Name Startalter -> Endalter". - Neue Zeile "Vermoegen Startvermoegen -> Endvermoegen" (nominal). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -308,6 +308,7 @@ export function PlanView({
|
||||
<PhaseHeader
|
||||
key={col.phase.id}
|
||||
phase={col.phase}
|
||||
personLabel={personLabel}
|
||||
onClick={() => setSelected({ type: "phase", phaseId: col.phase.id })}
|
||||
active={selected?.type === "phase" && selected.phaseId === col.phase.id}
|
||||
/>
|
||||
@@ -596,9 +597,21 @@ function phaseCellContent(ce: ReturnType<PhaseComputed["elements"]["find"]> | un
|
||||
return ce.summary || "–";
|
||||
}
|
||||
|
||||
function PhaseHeader({ phase, onClick, active }: { phase: PhaseComputed; onClick: () => void; active: boolean }) {
|
||||
function PhaseHeader({
|
||||
phase,
|
||||
personLabel,
|
||||
onClick,
|
||||
active,
|
||||
}: {
|
||||
phase: PhaseComputed;
|
||||
personLabel: (role: string) => string;
|
||||
onClick: () => void;
|
||||
active: boolean;
|
||||
}) {
|
||||
const quotaLabel = phase.isConsumption ? "Verzehr" : "Sparquote";
|
||||
const quotaRemaining = Math.max(0, phase.quotaRemaining);
|
||||
const quotaTarget = Math.abs(phase.quota);
|
||||
const quotaPct = quotaTarget > 0 ? Math.round((phase.quotaAllocated / quotaTarget) * 100) : 100;
|
||||
const distributedWord = phase.isConsumption ? "gedeckt" : "verteilt";
|
||||
return (
|
||||
<th
|
||||
onClick={onClick}
|
||||
@@ -619,14 +632,20 @@ function PhaseHeader({ phase, onClick, active }: { phase: PhaseComputed; onClick
|
||||
{phase.type === "ERWERB" ? "Erwerb" : phase.type === "PENSION" ? "Pension" : "Misch"}
|
||||
</span>
|
||||
<span className="text-[10px] text-faint">{phase.durationYears} J.</span>
|
||||
<span className="text-[10px] text-faint">Alter {phase.persons.map((p) => p.startAge).join("/")}</span>
|
||||
</div>
|
||||
<div className="mt-1 space-y-0.5 text-[10px] leading-tight text-muted">
|
||||
{phase.persons.map((p) => (
|
||||
<div key={p.role} className="text-faint">
|
||||
{personLabel(p.role)} {p.startAge} → {p.endAge}
|
||||
</div>
|
||||
))}
|
||||
<div>Einkommen {formatChf(phase.incomeTotal)}</div>
|
||||
<div>Ausgaben {formatChf(phase.expenseTotal)}</div>
|
||||
<div className={phase.quotaComplete ? "text-success" : "text-danger"}>
|
||||
{quotaLabel} {formatChf(Math.abs(phase.quota))}
|
||||
{!phase.quotaComplete && <span> · offen {formatChf(quotaRemaining)}</span>}
|
||||
{quotaLabel} {formatChf(quotaTarget)} ({quotaPct}% {distributedWord})
|
||||
</div>
|
||||
<div>
|
||||
Vermoegen {formatChf(phase.startWealthNominal)} → {formatChf(phase.endWealthNominal)}
|
||||
</div>
|
||||
<div className={phase.availableCapitalComplete ? "" : "text-danger"}>
|
||||
Kapital {phase.availableCapital === null ? "n.a." : formatChf(phase.availableCapital)}
|
||||
|
||||
Reference in New Issue
Block a user