"use client";
import { useState, useEffect, useCallback } from "react";
import { FlaskConical, Play, RotateCcw, ChevronDown, Clock, Zap } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ui/sheet";
import { Badge } from "@/components/ui/badge";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { fetchScenarios, triggerScenario, type ScenarioInfo } from "@/lib/api";
// ── Small select for target override ─────────────────────────────────────────
function TargetSelect({
targets,
value,
onChange,
}: {
targets: string[];
value: string;
onChange: (v: string) => void;
}) {
if (targets.length <= 1) return null;
return (
);
}
// ── Single scenario card ──────────────────────────────────────────────────────
function ScenarioCard({
scenario,
active,
onRun,
}: {
scenario: ScenarioInfo;
active: boolean;
onRun: (name: string, target?: string) => void;
}) {
const [target, setTarget] = useState(scenario.default_target ?? "");
return (
{/* Header row */}
{scenario.label}
{scenario.compound && (
compound
)}
{active && (
running
)}
{!scenario.compound && (
)}
{/* Description */}
{scenario.description}
{/* Footer */}
{scenario.duration}
);
}
// ── Main panel ────────────────────────────────────────────────────────────────
export function SimulatorPanel() {
const [open, setOpen] = useState(false);
const [scenarios, setScenarios] = useState([]);
const [loading, setLoading] = useState(false);
const [activeScenario, setActiveScenario] = useState(null);
const [status, setStatus] = useState<{ message: string; ok: boolean } | null>(null);
// Load scenario list once when panel first opens
useEffect(() => {
if (!open || scenarios.length > 0) return;
setLoading(true);
fetchScenarios()
.then(setScenarios)
.catch(() => setStatus({ message: "Failed to load scenarios", ok: false }))
.finally(() => setLoading(false));
}, [open, scenarios.length]);
const showStatus = useCallback((message: string, ok: boolean) => {
setStatus({ message, ok });
setTimeout(() => setStatus(null), 3000);
}, []);
const handleRun = useCallback(
async (name: string, target?: string) => {
try {
await triggerScenario(name, target);
setActiveScenario(name);
showStatus(
`▶ ${name}${target ? ` → ${target}` : ""} triggered`,
true
);
} catch {
showStatus("Failed to trigger scenario", false);
}
},
[showStatus]
);
const handleReset = useCallback(async () => {
try {
await triggerScenario("RESET");
setActiveScenario(null);
showStatus("All scenarios reset", true);
} catch {
showStatus("Failed to reset", false);
}
}, [showStatus]);
const compound = scenarios.filter((s) => s.compound);
const single = scenarios.filter((s) => !s.compound);
return (
Scenario Simulator
{/* Header */}
Scenario Simulator
demo only
Inject realistic fault scenarios into the live simulator. Changes are reflected immediately across all dashboard pages.
{/* Reset bar */}
{status && (
{status.message}
)}
{/* Scrollable scenario list */}
{loading && (
Loading scenarios…
)}
{!loading && compound.length > 0 && (
Compound Scenarios
Multi-device, time-sequenced chains — fires automatically across the site.
{compound.map((s) => (
))}
)}
{!loading && single.length > 0 && (
)}
);
}