"use client"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { useNodes, useUnconfiguredNodes } from "@/lib/useNodes"; import { useCalls, useActiveCalls } from "@/lib/useCalls"; import { useSystems } from "@/lib/useSystems"; import { useActiveIncidents } from "@/lib/useIncidents"; import { NodeCard } from "@/components/NodeCard"; import { CallRow } from "@/components/CallRow"; import { NodeConfigModal } from "@/components/NodeConfigModal"; import { TypeBadge } from "@/components/IncidentBadges"; import { severityBadge, severityRank } from "@/lib/severity"; import { useState } from "react"; import type { NodeRecord, IncidentRecord } from "@/lib/types"; import { useAuth } from "@/components/AuthProvider"; import { PageHeader } from "@/components/ui/PageHeader"; import { Card } from "@/components/ui/Card"; import { Badge } from "@/components/ui/Badge"; import { Button } from "@/components/ui/Button"; import { EmptyState, ErrorBanner } from "@/components/ui/EmptyState"; function StatCard({ label, value, accent }: { label: string; value: string | number; accent?: string }) { return (

{label}

{value}

); } function fmtTime(iso: string) { try { return new Date(iso).toLocaleString([], { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" }); } catch { return iso; } } function IncidentSummaryCard({ incident }: { incident: IncidentRecord }) { const router = useRouter(); return ( router.push(`/incidents/${incident.incident_id}`)}>
{severityBadge(incident.severity)}

{incident.title ?? "Untitled incident"}

{fmtTime(incident.started_at)} · {incident.call_ids.length} call{incident.call_ids.length !== 1 ? "s" : ""}

); } export default function DashboardPage() { const { nodes, error: nodesError } = useNodes(); const { nodes: pending } = useUnconfiguredNodes(); const { calls, error: callsError } = useCalls(20); const activeCalls = useActiveCalls(); const { systems, error: systemsError } = useSystems(); const activeIncidents = useActiveIncidents(); const [configNode, setConfigNode] = useState(null); const { isAdmin } = useAuth(); const systemMap = Object.fromEntries(systems.map((s) => [s.system_id, s])); const onlineCount = nodes.filter((n) => n.status !== "offline").length; const fsError = nodesError ?? callsError ?? systemsError; // Worst-first: the incident that most needs a human's attention leads the panel. const sortedIncidents = [...activeIncidents].sort( (a, b) => severityRank(b.severity) - severityRank(a.severity) || b.started_at.localeCompare(a.started_at) ); const notableIncidentCount = activeIncidents.filter((i) => severityRank(i.severity) >= 2).length; return (
0 && {notableIncidentCount} moderate+ active} /> {fsError && } {/* Pending config banner */} {pending.length > 0 && (

{pending.length} new node{pending.length > 1 ? "s" : ""} connected and need{pending.length === 1 ? "s" : ""} configuration.

)} {/* Stats */}
0 ? "text-orange-400" : undefined} /> 0 ? "text-orange-400" : undefined} />
{/* Active incidents — the primary "what's happening" view */}

Active Incidents

View all →
{sortedIncidents.length === 0 ? ( ) : (
{sortedIncidents.slice(0, 6).map((inc) => ( ))}
)}
{/* Nodes */}

Nodes

{nodes.length === 0 ? ( ) : (
{nodes.map((n) => ( ))}
)}
{/* Recent calls */}

Recent Calls

{calls.length === 0 ? ( ) : ( {calls.map((c) => ( ))}
Time Talkgroup System Node Duration Audio
)}
{configNode && ( setConfigNode(null)} /> )}
); }