/** * Alertmanager Alerts tab (spec R2.4, R8.2). * * Lifts the Alertmanager alerts content from the old cross-service * ObservabilityPage into an instance-scoped tab. Renders the active-alert * summary (total + by severity) and the expandable alert list. * * The hooks (useAlertmanagerAlerts, useAlertmanagerStatus) are global / * first-configured for now — they don't accept a service_id yet. Wiring * `instance.id` into them is a documented follow-up once the hooks gain the * parameter. The `instance` prop is accepted for future scoping. */ import { AlertTriangle, Bell, ChevronDown, Inbox } from "lucide-react"; import { useAlertmanagerAlerts, useAlertmanagerStatus, } from "../../hooks/useObservability"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Skeleton } from "@/components/ui/skeleton"; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; import type { AlertmanagerAlert, ServiceInstance } from "../../types"; function severityVariant( severity: string, ): "default" | "secondary" | "destructive" | "outline" { switch (severity.toLowerCase()) { case "critical": return "destructive"; case "warning": return "default"; case "info": return "secondary"; default: return "outline"; } } function AlertItem({ alert }: { alert: AlertmanagerAlert }) { return (
{alert.name}
{alert.severity}
{alert.summary || alert.description}
{alert.active_since && (
Since {new Date(alert.active_since).toLocaleString()}
)}
{alert.description && (
Description:{" "} {alert.description}
)}
{alert.job_name && (
Job: {alert.job_name}
)} {alert.category && (
Category: {alert.category}
)}
State: {alert.state}
Since:{" "} {alert.active_since ? new Date(alert.active_since).toLocaleString() : "unknown"}
{alert.labels && Object.keys(alert.labels).length > 0 && (
{Object.entries(alert.labels).map(([key, value]) => ( {key}={value} ))}
)}
); } export function AlertsTab({ instance }: { instance: ServiceInstance }) { // Global / first-configured hooks for now; instance.id scoping is a // follow-up (see file docstring). void instance; const { data: alertsSummary, isLoading: alertsLoading, error: alertsError, } = useAlertmanagerAlerts(); const { data: status, isLoading: statusLoading } = useAlertmanagerStatus(); const statusDetail = status?.up ? status.version ? `version ${status.version}` : "reachable" : statusLoading ? "checking…" : "unreachable"; return (
Alertmanager {statusDetail}
{alertsError && ( Failed to load alerts {alertsError.message} )} Active Alerts ({alertsSummary?.total ?? 0}) {alertsLoading ? (
) : !alertsSummary || alertsSummary.total === 0 ? (
No active alerts
Everything looks quiet. Firing alerts will appear here.
) : ( <> {alertsSummary.alerts.map((alert, idx) => ( ))} {alertsSummary.total > alertsSummary.alerts.length && (
{alertsSummary.total - alertsSummary.alerts.length} more alert {alertsSummary.total - alertsSummary.alerts.length === 1 ? "" : "s"}{" "} in Alertmanager
)} )}
); }