/**
* 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
)}
>
)}
);
}