"use client"; import { useState } from "react"; import { useAuth } from "@/components/AuthProvider"; import { useAlerts } from "@/lib/useAlerts"; import { c2api } from "@/lib/c2api"; import type { AlertRule } from "@/lib/types"; function fmtTime(iso: string) { try { return new Date(iso).toLocaleString(); } catch { return iso; } } function RulesTab({ isAdmin }: { isAdmin: boolean }) { const [rules, setRules] = useState([]); const [loaded, setLoaded] = useState(false); const [name, setName] = useState(""); const [keywords, setKeywords] = useState(""); const [tgIds, setTgIds] = useState(""); const [webhook, setWebhook] = useState(""); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); async function load() { if (loaded) return; try { const data = await c2api.getAlertRules() as AlertRule[]; setRules(data); setLoaded(true); } catch (e) { console.error(e); } } // Load on first render of this tab if (!loaded) { load(); } async function handleCreate(e: React.FormEvent) { e.preventDefault(); setSaving(true); setError(null); try { const body = { name, keywords: keywords.split(",").map((k) => k.trim()).filter(Boolean), talkgroup_ids: tgIds.split(",").map((s) => parseInt(s.trim(), 10)).filter((n) => !isNaN(n)), enabled: true, discord_webhook: webhook || null, }; const created = await c2api.createAlertRule(body) as AlertRule; setRules((prev) => [created, ...prev]); setName(""); setKeywords(""); setTgIds(""); setWebhook(""); } catch { setError("Failed to create rule."); } finally { setSaving(false); } } async function handleToggle(rule: AlertRule) { try { await c2api.updateAlertRule(rule.rule_id, { enabled: !rule.enabled }); setRules((prev) => prev.map((r) => r.rule_id === rule.rule_id ? { ...r, enabled: !r.enabled } : r)); } catch (e) { console.error(e); } } async function handleDelete(id: string) { try { await c2api.deleteAlertRule(id); setRules((prev) => prev.filter((r) => r.rule_id !== id)); } catch (e) { console.error(e); } } return (
{isAdmin && (

New Alert Rule

setName(e.target.value)} placeholder="e.g. Structure Fire" className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500" />
setKeywords(e.target.value)} placeholder="fire, smoke, structure" className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500" />
setTgIds(e.target.value)} placeholder="9048, 9600" className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500" />
setWebhook(e.target.value)} placeholder="https://discord.com/api/webhooks/…" className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-indigo-500" />
{error &&

{error}

}
)}
{rules.length === 0 ? (

No alert rules configured.

) : ( {isAdmin && } {rules.map((rule) => ( {isAdmin && ( )} ))}
Name Keywords Talkgroups Webhook Enabled
{rule.name} {rule.keywords.join(", ") || "—"} {rule.talkgroup_ids.join(", ") || "—"} {rule.discord_webhook ? ( configured ) : "—"} {isAdmin ? ( ) : ( {rule.enabled ? "enabled" : "disabled"} )}
)}
); } export default function AlertsPage() { const { isAdmin } = useAuth(); const { alerts, loading } = useAlerts(); const [tab, setTab] = useState<"events" | "rules">("events"); async function handleAcknowledge(id: string) { try { await c2api.acknowledgeAlert(id); } catch (e) { console.error(e); } } const unacked = alerts.filter((a) => !a.acknowledged); return (

Alerts

{unacked.length > 0 && ( {unacked.length} unacknowledged )}
{/* Tabs */}
{(["events", ...(isAdmin ? ["rules"] : [])] as const).map((t) => ( ))}
{tab === "events" && ( loading ? (

Loading…

) : alerts.length === 0 ? (

No alerts triggered yet.

) : (
{alerts.map((alert) => ( ))}
Rule Talkgroup Matched Snippet Time Status
{alert.rule_name} {alert.talkgroup_name || alert.talkgroup_id || "—"}
{alert.matched_keywords.map((kw) => ( {kw} ))}
{alert.transcript_snippet || "—"} {fmtTime(alert.triggered_at)} {alert.acknowledged ? ( acked ) : ( )}
) )} {tab === "rules" && }
); }