"use client"; import { useEffect, useState } from "react"; import { useParams, useRouter } from "next/navigation"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { getScenario, getKpis, scenarioEventsUrl, scenarioExcelUrl, type ProgressEvent, } from "@/lib/api"; import { Button } from "@/components/ui/button"; import { InputsTab } from "@/components/InputsTab"; import { WorkbookView } from "@/components/WorkbookView"; // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- type ActiveSheet = | "inputs" | "summary" | "pnl" | "cfs" | "bs" | "debt" | "irr" | "generation" | "idc" | "opex"; const RESULT_SHEETS: { id: ActiveSheet; label: string }[] = [ { id: "summary", label: "Summary" }, { id: "pnl", label: "P&L" }, { id: "cfs", label: "Cash Flow" }, { id: "bs", label: "Bal. Sheet" }, { id: "debt", label: "Debt" }, { id: "irr", label: "IRR / Returns" }, { id: "generation", label: "Generation" }, { id: "idc", label: "IDC / Phasing" }, { id: "opex", label: "O&M" }, ]; // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- function ProgressBar({ pct }: { pct: number }) { return (
); } function StatusBadge({ status }: { status: string }) { const styles: Record = { success: "text-emerald-700 bg-emerald-50 border-emerald-200", failed: "text-red-600 bg-red-50 border-red-200", running: "text-primary bg-primary/10 border-primary/30", queued: "text-amber-700 bg-amber-50 border-amber-200", }; return ( {status} ); } // --------------------------------------------------------------------------- // Main page // --------------------------------------------------------------------------- export default function ScenarioPage() { const params = useParams<{ id: string }>(); const router = useRouter(); const queryClient = useQueryClient(); const id = params.id; const [activeSheet, setActiveSheet] = useState("inputs"); const [progress, setProgress] = useState(null); const [sseOpen, setSseOpen] = useState(true); const { data: scenario, refetch: refetchScenario } = useQuery({ queryKey: ["scenario", id], queryFn: () => getScenario(id), refetchInterval: sseOpen ? false : 3000, }); const { data: kpis, refetch: refetchKpis } = useQuery({ queryKey: ["kpis", id], queryFn: () => getKpis(id), enabled: scenario?.status === "success", }); useEffect(() => { if (scenario?.status === "success" && activeSheet === "inputs" && progress !== null) { setActiveSheet("summary"); } }, [scenario?.status]); // eslint-disable-line react-hooks/exhaustive-deps useEffect(() => { if (!id) return; setSseOpen(true); const es = new EventSource(scenarioEventsUrl(id)); es.onmessage = (event: MessageEvent) => { const data = JSON.parse(event.data) as ProgressEvent; setProgress(data); if (data.stage === "done" || data.stage === "error") { setSseOpen(false); es.close(); void refetchScenario(); void refetchKpis(); void queryClient.invalidateQueries({ queryKey: ["statements", id] }); } }; es.onerror = () => { setSseOpen(false); es.close(); }; return () => es.close(); }, [id, refetchScenario, refetchKpis, queryClient]); const isRunning = scenario?.status === "queued" || scenario?.status === "running"; const hasResults = scenario?.status === "success" && kpis != null; function handleInputsSaved() { setProgress(null); void refetchScenario(); setActiveSheet("summary"); } function nav(sheet: ActiveSheet) { if (sheet !== "inputs" && !hasResults) return; setActiveSheet(sheet); } return (
{/* ── Top header ─────────────────────────────────────────── */}

{scenario?.name ?? "Loading…"}

{scenario && } {isRunning && progress && ( {progress.stage} · {progress.pct}% )} {scenario?.runtime_s != null && ( {scenario.runtime_s.toFixed(1)}s )} {hasResults && ( ↓ Excel )}
{/* ── Progress bar ───────────────────────────────────────── */} {isRunning && } {/* ── Two-column layout ──────────────────────────────────── */}
{/* Sidebar */} {/* Content area */}
{activeSheet === "inputs" ? (
{scenario?.inputs_json != null ? ( ) : (
Loading inputs…
)}
) : (
{hasResults ? ( { try { const inputs = scenario?.inputs_json ? JSON.parse(scenario.inputs_json) : {}; return inputs?.project?.cod_year || inputs?.project?.cod_date ? new Date(inputs.project.cod_date || inputs.project.cod_year + '-04-01').getFullYear() : new Date().getFullYear(); } catch { return new Date().getFullYear(); } })()} /> ) : scenario?.status === "failed" ? (

Scenario failed

{scenario.error_message ?? "Check worker logs for details."}

) : (
{isRunning ? ( <>
Running… {progress?.stage} ({progress?.pct ?? 0}%) ) : ( <> No results yet. )}
)}
)}
); } function SidebarItem({ label, active, disabled, onClick, }: { label: string; active: boolean; disabled?: boolean; onClick: () => void; }) { return ( ); }