"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 ? (
) : 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 (
);
}