import type { Metadata } from 'next'; import Link from 'next/link'; import { IncidentsList } from '@/components/home/IncidentsList'; import { IncidentRow } from '@/components/incidents/IncidentRow'; import { Empty, Section } from '@/components/ui/primitives'; import { apiGet } from '@/lib/api'; import { fmtInt } from '@/lib/format'; import type { IncidentList } from '@/lib/types'; export const dynamic = 'force-dynamic'; export const metadata: Metadata = { title: 'Incidents', description: 'Detected, developing, active, recovering and resolved Internet incidents with evidence, hypotheses and confidence.' }; export default async function IncidentsPage({ searchParams }: { searchParams: Promise<{ page?: string }> }) { const sp = await searchParams; const page = Math.max(1, Number(sp.page ?? 1) || 1); const limit = 25; const [active, resolved] = await Promise.all([apiGet('/api/v1/incidents?status=active&limit=50'), apiGet(`/api/v1/incidents?status=resolved&limit=${limit}&offset=${(page - 1) * limit}`)]); const pages = Math.max(1, Math.ceil(resolved.total / limit)); return (

Event engine

Incidents

An incident opens when a component or regional score stays above the detection threshold for consecutive cycles, then moves detected → developing → active → recovering → resolved. Confidence reflects probe count, geographic diversity, signal agreement and BGP corroboration. Pages are kept forever.

{fmtInt(active.total)} open · live}>
{fmtInt(resolved.total)} total}> {resolved.incidents.length ? (
    {resolved.incidents.map((i) => ( ))}
) : ( No resolved incident yet. )} {pages > 1 && ( )}
); }