Files
FPT/src/components/SpecView.tsx
T
admGitAICDS 4791dccf93
Deploy App / deploy (push) Successful in 57s
Detailansichten, Wasserfaelle und vollstaendige Rechenweg-Offenlegung
Roadmap Nr. 43 (Detailansichten) und Nr. 41 (Berechnungslogiken offenlegen).

Systemparameter-Ansicht:
- SYSTEM_PARAMETERS in constants.ts: Wert, Bedeutung, Herleitung, Quelle,
  Stand -- aus derselben Datei, aus der gerechnet wird

Detailansichten (nur lesen, per Expand-Icon):
- Element: Verlauf ueber ALLE Planjahre (neu ElementPhaseComputed.yearly),
  bei Immobilien Verkehrswert/Restschuld/Eigenkapital getrennt
- Phase: Vermoegensaufteilung + zwei Wasserfaelle

Zwei getrennte Wasserfaelle (WealthBridge / CashBridge):
- Sparraten, Amortisationen und Investitionen sind UMBUCHUNGEN und erscheinen
  nur im Cash-Wasserfall -- als Vermoegensabgang gezeichnet wuerden sie einen
  Verlust vortaeuschen, den es nicht gibt
- PK-Beitraege dagegen sind ein echter Vermoegenszugang (belasten kein Cash),
  Verrentung ein echter Abgang (Kapital verlaesst die Bilanz)
- residual als Kontrollgroesse fuer die Vollstaendigkeit der Zerlegung

Rechenweg-Protokoll, vollstaendige Abdeckung:
- computePlan(plan, sample?, { explain }) protokolliert die Schritte, die es
  ohnehin ausfuehrt -- die Erklaerung IST die Rechnung, statt einer zweiten
  Formel-Implementierung im UI, die still abdriften koennte
- standardmaessig aus (Monte Carlo bleibt unberuehrt)
- Arithmetik nicht umgestellt: Zwischengroessen werden als Differenz
  abgeleitet, damit die 43 Golden Tests bitgleich bleiben
- jeder Trace verlinkt in die SPEZIFIKATION; ein Test prueft gegen die echte
  Datei, dass alle Verweise eine existierende Ueberschrift treffen

SPEZIFIKATION auf 0.11: neue Kapitel 3.6.7, 3.6.8, 4.14, 9.20, 9.21.
12 Tests ergaenzt (80 -> 92). Keine DB-Aenderung.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 17:12:55 +02:00

59 lines
2.1 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import rehypeSlug from "rehype-slug";
import { FileText } from "lucide-react";
import { api } from "@/lib/api-client";
// Rendert SPEZIFIKATION.md (via /api/spec) als lesbares Dokument. Das Styling laeuft ueber
// die Klasse .md-doc in globals.css und folgt damit dem gewaehlten Farbschema.
export function SpecView({ anchor }: { anchor?: string | null }) {
const [markdown, setMarkdown] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
api
.get<{ markdown: string }>("/api/spec")
.then((data) => setMarkdown(data.markdown))
.catch((e) => setError(e instanceof Error ? e.message : "Laden fehlgeschlagen."));
}, []);
// Sprungmarke aus einem Rechenweg: erst nach dem Rendern des Markdowns existiert die
// Ueberschrift mit der von rehype-slug erzeugten id.
useEffect(() => {
if (!anchor || markdown === null) return;
const el = document.getElementById(anchor);
if (el) el.scrollIntoView({ behavior: "smooth", block: "start" });
}, [anchor, markdown]);
if (error) return <p className="text-sm text-danger">{error}</p>;
if (markdown === null) return <p className="text-sm text-muted">Laedt</p>;
return (
<div className="flex flex-col gap-4">
<div className="flex items-center gap-2">
<FileText className="h-5 w-5 text-accent" />
<h2 className="text-lg font-semibold text-fg">Spezifikation</h2>
</div>
<article className="md-doc rounded-xl border border-border bg-surface p-6 shadow-sm lg:p-8">
<ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeSlug]}
components={{
// Breite Tabellen scrollen in ihrem eigenen Container statt die Seite zu dehnen.
table: ({ children, ...props }) => (
<div className="md-table-wrap">
<table {...props}>{children}</table>
</div>
),
}}
>
{markdown}
</ReactMarkdown>
</article>
</div>
);
}