"use client"; import { useEffect, useState, useCallback } from "react"; import { toast } from "sonner"; import { fetchLeakStatus, type LeakSensorStatus } from "@/lib/api"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Skeleton } from "@/components/ui/skeleton"; import { Droplets, RefreshCw, CheckCircle2, AlertTriangle, MapPin, Wind, Clock } from "lucide-react"; import { cn } from "@/lib/utils"; const SITE_ID = "sg-01"; function SensorBadge({ state }: { state: string }) { const cfg = { detected: { cls: "bg-destructive/10 text-destructive border-destructive/30", label: "LEAK DETECTED" }, clear: { cls: "bg-green-500/10 text-green-400 border-green-500/20", label: "Clear" }, unknown: { cls: "bg-muted/30 text-muted-foreground border-border", label: "Unknown" }, }[state] ?? { cls: "bg-muted/30 text-muted-foreground border-border", label: state }; return ( {cfg.label} ); } function SensorCard({ sensor }: { sensor: LeakSensorStatus }) { const detected = sensor.state === "detected"; const sensorAny = sensor as LeakSensorStatus & { last_triggered_at?: string | null; trigger_count_30d?: number }; const triggerCount30d = sensorAny.trigger_count_30d ?? 0; const lastTriggeredAt = sensorAny.last_triggered_at ?? null; let lastTriggeredText: string; if (lastTriggeredAt) { const daysAgo = Math.floor((Date.now() - new Date(lastTriggeredAt).getTime()) / (1000 * 60 * 60 * 24)); lastTriggeredText = daysAgo === 0 ? "Today" : `${daysAgo}d ago`; } else if (detected) { lastTriggeredText = "Currently active"; } else { lastTriggeredText = "No recent events"; } return (
{sensor.sensor_id}
{sensor.floor_zone && (
Singapore DC01 — water sensor site map · refreshes every 15s
{label}
{value}
{sub}
{active.length} water leak{active.length > 1 ? "s" : ""} detected — immediate action required
• {s.sensor_id} {s.floor_zone ? ` — ${s.floor_zone}` : ""} {s.near_crac ? ` (near ${s.near_crac})` : ""} {s.under_floor ? " — under raised floor" : ""}
))}