Neuer Knopf auf Plan-Ebene: Liste plus Wizard in zwei Schritten. Ein
Ist-Satz haengt am PLAN, nicht am Szenario -- die Zuordnung laeuft ueber
die Herkunfts-Kette sourceElementId.
computePlan nimmt neu { actuals }: Die Werte schnappen in jedem erfassten
Jahr auf die Realitaet und laufen von dort planmaessig weiter. Luecken
fallen auf die Plandaten zurueck. Ohne die Option unveraendert -- die 43
Golden Tests laufen durch.
Der Sprung ist keine Rendite: eigene Brueckenposition actualsCorrection
in Vermoegens- und Cash-Bruecke, sonst ginge die Zerlegung nicht auf.
Matrix: Umschalter Plan/Effektiv, im Ist-Modus mit farbiger Abweichung
statt acht Zahlen je Zelle. Zeitachse: Marker je Jahr, juengster farbig.
Vier Analysewerkzeuge mit einheitlicher Leiste (nominal/real als
Einfachauswahl, Plan/Effektiv). MC: Zielbetrag dreht mit, Startjahr
abgeleitet statt eingebbar.
Neue Tabelle ActualsSet (gegen echtes Postgres verifiziert), Module
actuals.ts und dataview.ts. Spezifikation 0.21, 27 Tests (181 -> 208).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,9 @@ export interface TimelineSeries {
|
||||
label: string;
|
||||
color: string;
|
||||
computed: PlanComputed;
|
||||
// Plandaten werden gestrichelt gezeichnet, effektive Daten durchgezogen. Das Stilbudget
|
||||
// geht bewusst an Plan/Ist statt an nominal/real (siehe SPEZIFIKATION 9.29).
|
||||
dashed?: boolean;
|
||||
}
|
||||
|
||||
// Datenpunkte je Serie: JEDES Planjahr (nicht nur die Phasengrenzen), verortet auf dem Alter
|
||||
@@ -35,7 +38,13 @@ function pointsFor(computed: PlanComputed) {
|
||||
|
||||
// Liniendiagramm: Gesamtvermögen (nominal + real) über das Alter. Unterstützt mehrere
|
||||
// überlagerte Pläne für den Szenario-Vergleich.
|
||||
export function WealthChart({ series }: { series: TimelineSeries[] }) {
|
||||
export function WealthChart({
|
||||
series,
|
||||
metric = "nominal",
|
||||
}: {
|
||||
series: TimelineSeries[];
|
||||
metric?: "nominal" | "real";
|
||||
}) {
|
||||
if (series.length === 0 || series[0].computed.phases.length === 0) {
|
||||
return <p className="text-sm text-muted">Noch keine Phasen vorhanden.</p>;
|
||||
}
|
||||
@@ -47,8 +56,7 @@ export function WealthChart({ series }: { series: TimelineSeries[] }) {
|
||||
const row: Record<string, number | null> = { age };
|
||||
for (const s of withPoints) {
|
||||
const pt = s.points.find((p) => p.age === age);
|
||||
row[`${s.label} (nominal)`] = pt ? pt.nominal : null;
|
||||
row[`${s.label} (real)`] = pt ? pt.real : null;
|
||||
row[s.label] = pt ? (metric === "real" ? pt.real : pt.nominal) : null;
|
||||
}
|
||||
return row;
|
||||
});
|
||||
@@ -76,25 +84,15 @@ export function WealthChart({ series }: { series: TimelineSeries[] }) {
|
||||
<Legend wrapperStyle={{ fontSize: 12 }} />
|
||||
{withPoints.map((s) => (
|
||||
<Line
|
||||
key={`${s.label}-nominal`}
|
||||
key={s.label}
|
||||
type="monotone"
|
||||
dataKey={`${s.label} (nominal)`}
|
||||
dataKey={s.label}
|
||||
stroke={s.color}
|
||||
strokeWidth={2}
|
||||
strokeDasharray={s.dashed ? "5 3" : undefined}
|
||||
dot={false}
|
||||
connectNulls
|
||||
/>
|
||||
))}
|
||||
{withPoints.map((s) => (
|
||||
<Line
|
||||
key={`${s.label}-real`}
|
||||
type="monotone"
|
||||
dataKey={`${s.label} (real)`}
|
||||
stroke={s.color}
|
||||
strokeWidth={2}
|
||||
strokeDasharray="5 3"
|
||||
dot={false}
|
||||
connectNulls
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
))}
|
||||
</LineChart>
|
||||
|
||||
Reference in New Issue
Block a user