From 489390a4a3b2bf383bc23299e426ffe0769ecddc Mon Sep 17 00:00:00 2001 From: kelle Date: Mon, 20 Jul 2026 22:36:48 +0200 Subject: [PATCH] Effektive Werte als eingebettete Ansicht statt Pop-up Klick auf "Effektive Werte" in der Sidebar oeffnet neu -- analog zu "Szenarien" -- eine Liste im Hauptbereich statt eines Modals. Neuer Plan-Tab "actuals"; ActualsDialog bekommt einen embedded-Modus (ohne Overlay, Backdrop und Schliessen-Knopf). Der Modal-Weg ueber den Matrix-Knopf bleibt bestehen. Co-Authored-By: Claude Opus 4.8 --- src/components/ActualsDialog.tsx | 28 ++++++++++++++++++----- src/components/AppShell.tsx | 39 ++++++++++++++++++++++++-------- 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/src/components/ActualsDialog.tsx b/src/components/ActualsDialog.tsx index 6befa61..d0b117e 100644 --- a/src/components/ActualsDialog.tsx +++ b/src/components/ActualsDialog.tsx @@ -43,6 +43,7 @@ export function ActualsDialog({ planName, scenarioMetas, initial, + embedded = false, onClose, onChanged, }: { @@ -52,6 +53,9 @@ export function ActualsDialog({ scenarioMetas: { id: string; name: string; isBase: boolean }[]; // Das bereits geöffnete Szenario, damit der Dialog sofort etwas anzeigen kann. initial: LoadedScenario; + // Eingebettet in den Hauptbereich (Plan-Ansicht) statt als Pop-up -- ohne Overlay, + // ohne Backdrop, ohne Schliessen-Knopf. + embedded?: boolean; onClose: () => void; onChanged: () => void; }) { @@ -252,9 +256,8 @@ export function ActualsDialog({ const setRow = (rootId: string, patch: { value?: number; mortgage?: number }) => setValues((prev) => ({ ...prev, [rootId]: { ...prev[rootId], ...patch } })); - return ( -
-
e.stopPropagation()} className="ui-pop flex w-full max-w-4xl flex-col gap-4 rounded-2xl border border-border bg-surface p-6 shadow-xl"> + const body = ( + <>

@@ -264,9 +267,11 @@ export function ActualsDialog({ Plan «{planName}». Was tatsächlich eingetreten ist – der Plan selbst bleibt unverändert.

- + {!embedded && ( + + )}
{mode === "list" && ( @@ -435,6 +440,17 @@ export function ActualsDialog({
)} + + ); + + // Eingebettet: schlichter Container im Hauptbereich. Sonst: Pop-up mit Overlay. + if (embedded) { + return
{body}
; + } + return ( +
+
e.stopPropagation()} className="ui-pop flex w-full max-w-4xl flex-col gap-4 rounded-2xl border border-border bg-surface p-6 shadow-xl"> + {body}
); diff --git a/src/components/AppShell.tsx b/src/components/AppShell.tsx index 165964b..266197a 100644 --- a/src/components/AppShell.tsx +++ b/src/components/AppShell.tsx @@ -105,7 +105,7 @@ function AppShellInner({ username }: { username: string }) { const [showHistory, setShowHistory] = useState(false); const [showActuals, setShowActuals] = useState(false); // Plan-Ebene: Dashboard / Szenarien-Liste / Analysen. Null = kein Plan-View aktiv. - const [planNav, setPlanNav] = useState<{ planId: string; tab: "dashboard" | "scenarios" | "analyses" } | null>(null); + const [planNav, setPlanNav] = useState<{ planId: string; tab: "dashboard" | "scenarios" | "actuals" | "analyses" } | null>(null); // Welche Szenario-Bäume in der Seitenleiste aufgeklappt sind. Standard: eingeklappt. const [expandedTrees, setExpandedTrees] = useState>({}); const [savedAnalysisId, setSavedAnalysisId] = useState(null); @@ -182,7 +182,7 @@ function AppShellInner({ username }: { username: string }) { // Plan-Ebene öffnen (Dashboard / Szenarien / Analysen). Räumt die Szenario- und Wissens- // Ansichten weg -- es kann immer nur eine Hauptansicht aktiv sein. - function openPlanTab(planId: string, tab: "dashboard" | "scenarios" | "analyses") { + function openPlanTab(planId: string, tab: "dashboard" | "scenarios" | "actuals" | "analyses") { setPlanNav({ planId, tab }); setSelectedScenarioId(null); setShowSpec(false); @@ -204,13 +204,13 @@ function AppShellInner({ username }: { username: string }) { else setShowLiveSim(true); } - // Effektive Werte von der Plan-Ebene aus öffnen (braucht das Basisszenario geladen). - async function openActualsForPlan(planId: string) { + // Effektive-Werte-Ansicht (Plan-Ebene, eingebettet). Lädt das Basisszenario, das der + // eingebettete Dialog als Ausgangspunkt braucht, und öffnet dann den Tab. + async function openActualsTab(planId: string) { const plan = plans.find((p) => p.id === planId); const baseId = plan?.scenarios.find((s) => s.isBase)?.id ?? plan?.scenarios[0]?.id; - if (!baseId) return; - if (detail?.meta.id !== baseId) await loadDetail(baseId, true); - setShowActuals(true); + if (baseId && detail?.meta.id !== baseId) await loadDetail(baseId, true); + openPlanTab(planId, "actuals"); } async function handleDeletePlan(id: string) { @@ -347,7 +347,7 @@ function AppShellInner({ username }: { username: string }) { {plans.map((p) => { const navHere = planNav?.planId === p.id; - const subItem = (tab: "dashboard" | "scenarios" | "analyses", label: string, Icon: typeof FolderKanban) => ( + const subItem = (tab: "dashboard" | "scenarios" | "actuals" | "analyses", label: string, Icon: typeof FolderKanban) => (