97 lines
4.2 KiB
TypeScript
97 lines
4.2 KiB
TypeScript
import { useRouter } from "next/navigation";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
import { Thermometer, Zap, Bell, ChevronRight } from "lucide-react";
|
|
import { cn } from "@/lib/utils";
|
|
import type { RoomStatus } from "@/lib/api";
|
|
|
|
interface Props {
|
|
rooms: RoomStatus[];
|
|
loading?: boolean;
|
|
}
|
|
|
|
const statusConfig: Record<string, { label: string; dot: string; bg: string }> = {
|
|
ok: { label: "Healthy", dot: "bg-green-500", bg: "bg-green-500/10 text-green-400" },
|
|
warning: { label: "Warning", dot: "bg-amber-500", bg: "bg-amber-500/10 text-amber-400" },
|
|
critical: { label: "Critical", dot: "bg-destructive", bg: "bg-destructive/10 text-destructive" },
|
|
};
|
|
|
|
const roomLabels: Record<string, string> = {
|
|
"hall-a": "Hall A",
|
|
"hall-b": "Hall B",
|
|
"hall-c": "Hall C",
|
|
};
|
|
|
|
export function RoomStatusGrid({ rooms, loading }: Props) {
|
|
const router = useRouter();
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader className="pb-2">
|
|
<CardTitle className="text-sm font-semibold">Room Status — Singapore DC01</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{loading ? (
|
|
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
|
|
{Array.from({ length: 2 }).map((_, i) => (
|
|
<Skeleton key={i} className="h-28 w-full rounded-lg" />
|
|
))}
|
|
</div>
|
|
) : rooms.length === 0 ? (
|
|
<div className="h-28 flex items-center justify-center text-sm text-muted-foreground">
|
|
Waiting for room data...
|
|
</div>
|
|
) : (
|
|
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
|
|
{rooms.map((room) => {
|
|
const s = statusConfig[room.status];
|
|
return (
|
|
<button
|
|
key={room.room_id}
|
|
onClick={() => router.push("/environmental")}
|
|
className="rounded-lg border border-border bg-muted/30 p-4 space-y-3 text-left w-full hover:bg-muted/50 hover:border-primary/30 transition-colors group"
|
|
>
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-sm font-medium">
|
|
{roomLabels[room.room_id] ?? room.room_id}
|
|
</span>
|
|
<div className="flex items-center gap-2">
|
|
<span className={cn("flex items-center gap-1.5 text-[10px] font-semibold px-2 py-0.5 rounded-full uppercase tracking-wide", s.bg)}>
|
|
<span className={cn("w-1.5 h-1.5 rounded-full", s.dot)} />
|
|
{s.label}
|
|
</span>
|
|
<ChevronRight className="w-3.5 h-3.5 text-muted-foreground/40 group-hover:text-primary transition-colors" />
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-3 gap-2 text-xs">
|
|
<div className="flex flex-col gap-0.5">
|
|
<span className="flex items-center gap-1 text-muted-foreground">
|
|
<Thermometer className="w-3 h-3" /> Temp
|
|
</span>
|
|
<span className="font-semibold">{room.avg_temp.toFixed(1)}°C</span>
|
|
</div>
|
|
<div className="flex flex-col gap-0.5">
|
|
<span className="flex items-center gap-1 text-muted-foreground">
|
|
<Zap className="w-3 h-3" /> Power
|
|
</span>
|
|
<span className="font-semibold">{room.total_kw.toFixed(1)} kW</span>
|
|
</div>
|
|
<div className="flex flex-col gap-0.5">
|
|
<span className="flex items-center gap-1 text-muted-foreground">
|
|
<Bell className="w-3 h-3" /> Alarms
|
|
</span>
|
|
<span className={cn("font-semibold", room.alarm_count > 0 ? "text-destructive" : "")}>
|
|
{room.alarm_count === 0 ? "None" : room.alarm_count}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|