"use client" import React, { useEffect, useState } from "react" import { Sheet, SheetContent, SheetHeader, SheetTitle, } from "@/components/ui/sheet" import { fetchSensor, type SensorDevice } from "@/lib/api" import { cn } from "@/lib/utils" import { Cpu, Radio, WifiOff } from "lucide-react" import { Skeleton } from "@/components/ui/skeleton" // ── Props ───────────────────────────────────────────────────────────────────── interface Props { sensorId: number | null onClose: () => void } // ── Label maps ──────────────────────────────────────────────────────────────── const DEVICE_TYPE_LABELS: Record = { ups: "UPS", generator: "Generator", crac: "CRAC Unit", chiller: "Chiller", ats: "Transfer Switch (ATS)", rack: "Rack PDU", network_switch: "Network Switch", leak: "Leak Sensor", fire_zone: "Fire / VESDA Zone", custom: "Custom", } const PROTOCOL_LABELS: Record = { mqtt: "MQTT", modbus_tcp: "Modbus TCP", modbus_rtu: "Modbus RTU", snmp: "SNMP", bacnet: "BACnet", http: "HTTP Poll", } // ── Helpers ─────────────────────────────────────────────────────────────────── function formatTime(iso: string): string { try { return new Date(iso).toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit", second: "2-digit", }) } catch { return iso } } function formatDate(iso: string): string { try { return new Date(iso).toLocaleString("en-GB", { dateStyle: "medium", timeStyle: "short", }) } catch { return iso } } // ── Sub-components ──────────────────────────────────────────────────────────── function SectionHeading({ children }: { children: React.ReactNode }) { return (

{children}

) } function InfoRow({ label, value }: { label: string; value: React.ReactNode }) { return (
{label} {value ?? "—"}
) } // ── Loading skeleton ────────────────────────────────────────────────────────── function LoadingSkeleton() { return (
{Array.from({ length: 5 }).map((_, i) => ( ))}
{Array.from({ length: 3 }).map((_, i) => ( ))}
) } // ── Badge ───────────────────────────────────────────────────────────────────── function Badge({ children, variant = "default", }: { children: React.ReactNode variant?: "default" | "success" | "muted" }) { return ( {children} ) } // ── Main component ──────────────────────────────────────────────────────────── export function SensorDetailSheet({ sensorId, onClose }: Props) { const open = sensorId !== null const [sensor, setSensor] = useState(null) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) // ── Fetch when sensorId changes to a non-null value ─────────────────────── useEffect(() => { if (sensorId === null) { setSensor(null) setError(null) return } let cancelled = false setLoading(true) setError(null) fetchSensor(sensorId) .then(s => { if (!cancelled) setSensor(s) }) .catch(err => { if (!cancelled) setError(err instanceof Error ? err.message : "Failed to load sensor") }) .finally(() => { if (!cancelled) setLoading(false) }) return () => { cancelled = true } }, [sensorId]) // ── Protocol config rows ────────────────────────────────────────────────── function renderProtocolConfig(config: Record) { const entries = Object.entries(config) if (entries.length === 0) { return

No config stored.

} return (
{entries.map(([k, v]) => (
{k} {String(v)}
))}
) } // ── Recent readings table ───────────────────────────────────────────────── function renderRecentReadings(readings: NonNullable) { if (readings.length === 0) { return (
No readings in the last 10 minutes — check connection
) } return (
{readings.map((r, i) => ( ))}
Sensor Type Value Recorded At
{r.sensor_type} {r.value} {r.unit && ( {r.unit} )} {formatTime(r.recorded_at)}
) } // ── Render ──────────────────────────────────────────────────────────────── return ( { if (!v) onClose() }}> {loading ? : (sensor?.name ?? "Sensor Detail") } {/* Header badges */} {sensor && !loading && (
{DEVICE_TYPE_LABELS[sensor.device_type] ?? sensor.device_type} {sensor.enabled ? "Enabled" : "Disabled"} {PROTOCOL_LABELS[sensor.protocol] ?? sensor.protocol}
)}
{loading && } {!loading && error && (

{error}

)} {!loading && sensor && ( <> {/* ── Device Info ── */}
Device Info
{sensor.device_id}} />
{/* ── Protocol Config ── */}
Protocol Config {renderProtocolConfig(sensor.protocol_config ?? {})}
{/* ── Recent Readings ── */}
Recent Readings (last 10 mins) {renderRecentReadings(sensor.recent_readings ?? [])}
)}
) }