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:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { Flag } from "lucide-react";
|
||||
import { CalendarCheck, Flag } from "lucide-react";
|
||||
import type { PhaseComputed } from "@/lib/calculations";
|
||||
|
||||
interface PersonAxis {
|
||||
@@ -19,11 +19,15 @@ export function Timeline({
|
||||
persons,
|
||||
ruinAge,
|
||||
startYear,
|
||||
actualYears = [],
|
||||
}: {
|
||||
phases: PhaseComputed[];
|
||||
persons: PersonAxis[];
|
||||
ruinAge?: number | null;
|
||||
startYear?: number | null;
|
||||
// Kalenderjahre, für die effektive Werte erfasst sind (aufsteigend). Der jüngste Satz
|
||||
// wird hervorgehoben, ältere bleiben blass -- sie sind überholt, aber nicht bedeutungslos.
|
||||
actualYears?: number[];
|
||||
}) {
|
||||
if (phases.length === 0 || persons.length === 0) return null;
|
||||
|
||||
@@ -91,6 +95,42 @@ export function Timeline({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Marker für erfasste effektive Werte. Nur mit bekanntem Planstartjahr platzierbar --
|
||||
ohne Kalenderbezug gäbe es keine Position auf der Achse. */}
|
||||
{startYear &&
|
||||
actualYears.map((y) => {
|
||||
const age = minAge + (y - startYear);
|
||||
if (age < minAge || age > maxAge) return null;
|
||||
const isLatest = y === actualYears[actualYears.length - 1];
|
||||
return (
|
||||
<div
|
||||
key={y}
|
||||
className="absolute top-0 flex -translate-x-1/2 flex-col items-center"
|
||||
style={{ left: pct(age), opacity: isLatest ? 1 : 0.35 }}
|
||||
title={
|
||||
isLatest
|
||||
? `Effektive Werte erfasst für ${y} (aktuellster Stand)`
|
||||
: `Effektive Werte erfasst für ${y} (überholt)`
|
||||
}
|
||||
>
|
||||
<CalendarCheck
|
||||
className="h-3.5 w-3.5"
|
||||
style={{ color: isLatest ? "var(--attention)" : "var(--muted)" }}
|
||||
/>
|
||||
<span
|
||||
className="whitespace-nowrap text-[10px] font-medium"
|
||||
style={{ color: isLatest ? "var(--attention)" : "var(--muted)" }}
|
||||
>
|
||||
{y}
|
||||
</span>
|
||||
<div
|
||||
className="mt-0.5 h-3 w-px"
|
||||
style={{ backgroundColor: isLatest ? "var(--attention)" : "var(--muted)" }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Phasen-Segmente: Breite proportional zur Dauer, Einfärbung nach Phasentyp. */}
|
||||
<div className="flex w-full overflow-hidden rounded-lg border border-border">
|
||||
{segments.map((s, i) => {
|
||||
|
||||
Reference in New Issue
Block a user