"use client"; import { useEffect, useState } from "react"; import { fetchCracStatus, fetchCracHistory, type CracStatus, type CracHistoryPoint } from "@/lib/api"; import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet"; import { Skeleton } from "@/components/ui/skeleton"; import { Badge } from "@/components/ui/badge"; import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; import { TimeRangePicker } from "@/components/ui/time-range-picker"; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, ReferenceLine, } from "recharts"; import { Thermometer, Wind, Zap, Gauge, Settings2, ArrowRight } from "lucide-react"; import { cn } from "@/lib/utils"; interface Props { siteId: string; cracId: string | null; onClose: () => void; } function fmt(v: number | null | undefined, dec = 1, unit = "") { if (v == null) return "—"; return `${v.toFixed(dec)}${unit}`; } function formatTime(iso: string) { try { return new Date(iso).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); } catch { return iso; } } function FillBar({ value, max, color, warn, crit, }: { value: number | null; max: number; color: string; warn?: number; crit?: number; }) { const pct = value != null ? Math.min(100, (value / max) * 100) : 0; const barColor = crit && value != null && value >= crit ? "#ef4444" : warn && value != null && value >= warn ? "#f59e0b" : color; return (
); } function SectionLabel({ icon: Icon, title }: { icon: React.ElementType; title: string }) { return (
{title}
); } function StatRow({ label, value, highlight }: { label: string; value: string; highlight?: boolean }) { return (
{label} {value}
); } function RefrigerantRow({ label, value, status }: { label: string; value: string; status: "ok" | "warn" | "crit" }) { return (
{label}
{value} {status === "ok" ? "normal" : status}
); } function MiniChart({ data, dataKey, color, label, unit, refLine, }: { data: CracHistoryPoint[]; dataKey: keyof CracHistoryPoint; color: string; label: string; unit: string; refLine?: number; }) { const vals = data.map(d => d[dataKey] as number | null).filter(v => v != null) as number[]; const last = vals[vals.length - 1]; return (
{label} {last != null ? `${last.toFixed(1)}${unit}` : "—"}
[`${(v as number).toFixed(1)}${unit}`, label]} labelFormatter={(l: unknown) => formatTime(String(l))} contentStyle={{ fontSize: 11, background: "#1e1e2e", border: "1px solid #333" }} /> {refLine != null && ( )}
); } // ── Overview tab ────────────────────────────────────────────────────────────── function OverviewTab({ status }: { status: CracStatus }) { const deltaWarn = (status.delta ?? 0) > 11; const deltaCrit = (status.delta ?? 0) > 14; const capWarn = (status.cooling_capacity_pct ?? 0) > 75; const capCrit = (status.cooling_capacity_pct ?? 0) > 90; const copWarn = (status.cop ?? 99) < 1.5; const filterWarn = (status.filter_dp_pa ?? 0) > 80; const filterCrit = (status.filter_dp_pa ?? 0) > 120; const compWarn = (status.compressor_load_pct ?? 0) > 95; return (
{/* ── Thermal hero ──────────────────────────────────────────── */}

Supply

{fmt(status.supply_temp, 1)}°C

ΔT {fmt(status.delta, 1)}°C

Return

{fmt(status.return_temp, 1)}°C

Filter ΔP {fmt(status.filter_dp_pa, 0)} Pa {!filterWarn && }
{/* ── Cooling capacity ──────────────────────────────────────── */}
{fmt(status.cooling_capacity_kw, 1)} kW of {status.rated_capacity_kw} kW rated

{fmt(status.cooling_capacity_pct, 1)}% utilised

{/* ── Compressor ────────────────────────────────────────────── */}
Load {status.compressor_state === 1 ? "● Running" : "○ Off"} {fmt(status.compressor_load_pct, 1)}%
{/* ── Fan ───────────────────────────────────────────────────── */}
Speed {fmt(status.fan_pct, 1)}% {status.fan_rpm != null ? ` · ${Math.round(status.fan_rpm).toLocaleString()} rpm` : ""}
{/* ── Electrical ────────────────────────────────────────────── */}
); } // ── Refrigerant tab ─────────────────────────────────────────────────────────── function RefrigerantTab({ status }: { status: CracStatus }) { const hiP = status.high_pressure_bar ?? 0; const loP = status.low_pressure_bar ?? 99; const sh = status.discharge_superheat_c ?? 0; const sc = status.liquid_subcooling_c ?? 0; const load = status.compressor_load_pct ?? 0; // Normal ranges for R410A-style DX unit const hiPStatus: "ok" | "warn" | "crit" = hiP > 22 ? "crit" : hiP > 20 ? "warn" : "ok"; const loPStatus: "ok" | "warn" | "crit" = loP < 3 ? "crit" : loP < 4 ? "warn" : "ok"; const shStatus: "ok" | "warn" | "crit" = sh > 16 ? "warn" : sh < 4 ? "warn" : "ok"; const scStatus: "ok" | "warn" | "crit" = sc < 2 ? "warn" : "ok"; const ldStatus: "ok" | "warn" | "crit" = load > 95 ? "warn" : "ok"; const compRunning = status.compressor_state === 1; return (

Refrigerant circuit data for the DX cooling system. Pressures assume an R410A charge. Values outside normal range are flagged automatically.

Compressor State {compRunning ? "● Running" : "○ Off"}
Compressor Power {fmt(status.compressor_power_kw, 2, " kW")}

Normal Ranges (R410A)

High side pressure: 15 – 20 bar

Low side pressure: 4 – 6 bar

Discharge superheat: 5 – 15°C

Liquid subcooling: 3 – 8°C

); } // ── Trends tab ──────────────────────────────────────────────────────────────── function TrendsTab({ history }: { history: CracHistoryPoint[] }) { if (history.length < 2) { return (

Not enough history yet — data accumulates every 5 minutes.

); } return (
); } // ── Sheet ───────────────────────────────────────────────────────────────────── export function CracDetailSheet({ siteId, cracId, onClose }: Props) { const [hours, setHours] = useState(6); const [status, setStatus] = useState(null); const [history, setHistory] = useState([]); const [loading, setLoading] = useState(false); useEffect(() => { if (!cracId) return; setLoading(true); Promise.all([ fetchCracStatus(siteId), fetchCracHistory(siteId, cracId, hours), ]).then(([statuses, hist]) => { setStatus(statuses.find(c => c.crac_id === cracId) ?? null); setHistory(hist); }).finally(() => setLoading(false)); }, [siteId, cracId, hours]); return ( { if (!open) onClose(); }}> {cracId?.toUpperCase() ?? "CRAC Unit"} {status && ( {status.state} )} {status && (

{status.room_id ? `Room: ${status.room_id}` : ""} {status.state === "online" ? " · Mode: Cooling · Setpoint 22°C" : ""}

)}
{loading && !status ? (
{Array.from({ length: 8 }).map((_, i) => ( ))}
) : status ? (
Overview Refrigerant Trends
) : (

No data available for {cracId}.

)}
); }