fb70781e5b
Deploy App / deploy (push) Successful in 1m45s
Sidebar zweistufig: pro Plan die Unterpunkte Szenarien / Effektive Werte / Analysen; Klick auf Plan-Name oeffnet ein Plan-Dashboard. Plan-Dashboard: Kennzahlen, gerechnete Werte ausdruecklich "laut Basisszenario", Ist-Abweichung falls erfasst. Szenario-Liste: Version, Elementzahl, Endvermoegen, Ruinalter + Aktionen Historie und Matrix. Baum in der Sidebar bleibt. Analysen: vier umklappende Kacheln (auch per Antippen). Grafiken oeffnen neu mit Auswahl EINER Grafik. Szenario-Vergleich zu den Grafiken, CSV-Export auf die Matrix. Gespeicherte Analysen: Grafik/MC/Einflussfaktoren als ZAHLEN einfrieren (read-only, nichts wird neu gerechnet) -- druckfaehig fuer den spaeteren PDF-Bericht, ohne finalWealthSorted. Einheitliche generische Ergebnisform. Neue Tabelle SavedAnalysis, Endpunkte /analyses und /dashboard, Module analyses.ts, Komponenten PlanViews/SavedAnalysisView/SaveAnalysisButton. Kein Eingriff in den Rechenkern. Spezifikation 0.24 (3.10 und 9.30 neu). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
94 lines
3.0 KiB
TypeScript
94 lines
3.0 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { Bookmark, Check } from "lucide-react";
|
|
import { useToast } from "@/components/ui";
|
|
import { defaultAnalysisName, saveAnalysis, type AnalysisType, type SaveAnalysisPayload } from "@/lib/analyses";
|
|
|
|
// Einheitlicher Speichern-Knopf für die Analysewerkzeuge. `build` wird erst beim Klick
|
|
// aufgerufen -- so wird das (evtl. grosse) Ergebnis nicht bei jedem Render zusammengebaut,
|
|
// sondern nur, wenn wirklich gespeichert wird.
|
|
export function SaveAnalysisButton({
|
|
planId,
|
|
type,
|
|
scenarioName,
|
|
versionLabel,
|
|
disabled,
|
|
build,
|
|
}: {
|
|
planId: string;
|
|
type: AnalysisType;
|
|
scenarioName: string | null;
|
|
versionLabel: string | null;
|
|
disabled?: boolean;
|
|
build: () => Omit<SaveAnalysisPayload, "name" | "type"> | null;
|
|
}) {
|
|
const [open, setOpen] = useState(false);
|
|
const [name, setName] = useState("");
|
|
const [busy, setBusy] = useState(false);
|
|
const [done, setDone] = useState(false);
|
|
const toast = useToast();
|
|
|
|
function start() {
|
|
setName(defaultAnalysisName(type, scenarioName, versionLabel));
|
|
setOpen(true);
|
|
}
|
|
|
|
async function save() {
|
|
const payload = build();
|
|
if (!payload) {
|
|
toast("error", "Es gibt noch kein Ergebnis zum Speichern.");
|
|
return;
|
|
}
|
|
setBusy(true);
|
|
try {
|
|
await saveAnalysis(planId, { ...payload, name: name.trim() || defaultAnalysisName(type, scenarioName, versionLabel), type });
|
|
setOpen(false);
|
|
setDone(true);
|
|
toast("success", "Analyse gespeichert.");
|
|
setTimeout(() => setDone(false), 2000);
|
|
} catch (e) {
|
|
toast("error", e instanceof Error ? e.message : "Speichern fehlgeschlagen.");
|
|
} finally {
|
|
setBusy(false);
|
|
}
|
|
}
|
|
|
|
if (open) {
|
|
return (
|
|
<div className="flex flex-wrap items-center gap-2 rounded-lg border border-accent bg-accent-soft/20 p-2">
|
|
<input
|
|
autoFocus
|
|
value={name}
|
|
onChange={(e) => setName(e.target.value)}
|
|
onKeyDown={(e) => e.key === "Enter" && save()}
|
|
className="min-w-0 flex-1 rounded border border-border bg-surface px-2 py-1 text-sm text-fg"
|
|
/>
|
|
<button
|
|
type="button"
|
|
disabled={busy}
|
|
onClick={save}
|
|
className="rounded-lg bg-accent px-3 py-1 text-xs font-medium text-accent-fg hover:bg-accent-hover disabled:opacity-50"
|
|
>
|
|
{busy ? "Speichern…" : "Speichern"}
|
|
</button>
|
|
<button type="button" onClick={() => setOpen(false)} className="rounded-lg border border-border px-2 py-1 text-xs text-muted hover:bg-surface-2">
|
|
Abbrechen
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<button
|
|
type="button"
|
|
disabled={disabled}
|
|
onClick={start}
|
|
className="flex items-center gap-1.5 rounded-lg border border-border px-3 py-1.5 text-xs font-medium text-muted transition-colors hover:bg-surface-2 hover:text-fg disabled:opacity-40"
|
|
>
|
|
{done ? <Check className="h-3.5 w-3.5 text-success" /> : <Bookmark className="h-3.5 w-3.5" />}
|
|
{done ? "Gespeichert" : "Analyse speichern"}
|
|
</button>
|
|
);
|
|
}
|