import React, { useMemo } from "react"; import type { ScenarioDef, PresetDef, SubCaseSpec } from "../scenarios/types"; import { EconGraph } from "../graph/EconGraph"; import { PresetBar } from "./PresetBar"; import { ParamControls } from "./ParamControls"; import { StatCards } from "./StatCards"; import { StepBreakdown } from "./StepBreakdown"; import { ComparisonTable } from "./ComparisonTable"; import { CrossCourseBridge } from "./CrossCourseBridge"; interface ScenarioLayoutProps
, R> { scenario: ScenarioDef
; params: P; onParamChange: (key: keyof P, value: number) => void; onChangeParams?: (newParams: P) => void; onResetParams: () => void; onSelectPreset: (preset: PresetDef
) => void; onNavigateScenario?: (scenarioId: string) => void; } const BANNER_STYLES: Record< "info" | "success" | "warning" | "danger", string > = { info: "border-blue-200 bg-blue-50 text-blue-900", success: "border-emerald-200 bg-emerald-50 text-emerald-900", warning: "border-amber-200 bg-amber-50 text-amber-900", danger: "border-rose-200 bg-rose-50 text-rose-900", }; export function ScenarioLayout
, R>({ scenario, params, onParamChange, onChangeParams, onResetParams, onSelectPreset, onNavigateScenario, }: ScenarioLayoutProps
): React.JSX.Element { const result = useMemo(() => { return scenario.model(params); }, [scenario, params]); const view = useMemo(() => { return scenario.view(result, params); }, [scenario, result, params]); const ExplainComponent = scenario.explain; const bannerType = view.statusBanner?.type ?? "info"; const bannerStyle = BANNER_STYLES[bannerType]; const handleSelectSubCase = (subCase: SubCaseSpec
) => { const updated = { ...params, ...subCase.params }; if (onChangeParams) { onChangeParams(updated as P); } else { for (const [k, v] of Object.entries(subCase.params)) { if (v !== undefined) { onParamChange(k as keyof P, v as number); } } } }; return (