/* /status — System health dashboard. * * Pulls from: * GET /api/dashboard/integration-health (live API check + token expiry) * GET /api/dashboard/overview (syncHealth array) * * Surfaces what's wrong so the dashboard can stay focused on operational * KPIs. Polls every 60 s while the page is visible. */ 'use client'; import { useEffect, useState } from 'react'; import { PageHeader } from '@/components/navigation/page-header'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { Button } from '@/components/ui/button'; import { Skeleton } from '@/components/ui/skeleton'; import { EmptyState } from '@/components/ui/empty-state'; import { StatusLight, type StatusLightState } from '@/components/ui/status-light'; import { StatusBadge } from '@/components/ui/status-badge'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { WorkerPulse } from '@/components/status/worker-pulse'; import { AlertTriangle, KeyRound, RefreshCw, ShieldCheck, Plug, Clock, Activity, } from 'lucide-react'; // ── Types ──────────────────────────────────────────────────────────── type IntegrationCategory = | 'psa' | 'rmm' | 'docs' | 'security' | 'backup' | 'network' | 'identity' | 'mdm' | 'mail' | 'finance' | 'productivity' | 'llm'; interface IntegrationHealthItem { key: string; name: string; category: IntegrationCategory; status: 'ok' | 'auth_failed' | 'unreachable' | 'not_configured' | 'unknown' | 'disabled'; configured: boolean; latencyMs?: number; error?: string | null; tokenExpiry?: { envVar: string; expiresAt: string; daysRemaining: number; subject?: string | null; } | null; checkedAt: string; } interface IntegrationHealthResponse { items: IntegrationHealthItem[]; summary: { total: number; ok: number; failed: number; notConfigured: number; disabled: number; expiringWithin14Days: number; expired: number; hasIssues: boolean; }; } interface SyncHealthItem { id: string; name: string; syncType: string; isEnabled: boolean; lastRun: string | null; lastStatus: string | null; lastError: string | null; nextRun: string | null; } interface OverviewResponse { syncHealth: SyncHealthItem[]; } interface WorkerSnapshot { name: string; lastActivity: string | null; inFlight: number; oneHour: { success: number; failure: number }; } interface WorkersResponse { workers: WorkerSnapshot[]; } const WORKER_FRESHNESS: Record = { Analyzer: 5, 'RMM Overshell': 10, 'Sync scheduler': 60, }; // ── Helpers ────────────────────────────────────────────────────────── const STALE_HOURS = 24; const POLL_MS = 60_000; const CATEGORY_LABELS: Record = { psa: 'PSA', rmm: 'RMM', docs: 'Documentation', security: 'Security', backup: 'Backup', network: 'Network', identity: 'Identity', mdm: 'MDM', mail: 'Mail', finance: 'Finance', productivity: 'Productivity', llm: 'LLM', }; const CATEGORY_ORDER: IntegrationCategory[] = [ 'psa', 'rmm', 'docs', 'security', 'backup', 'network', 'identity', 'mdm', 'mail', 'finance', 'productivity', 'llm', ]; function relTime(iso: string | null): string { if (!iso) return 'never'; const ms = Date.now() - new Date(iso).getTime(); if (ms < 0) return 'in the future'; const min = Math.floor(ms / 60000); if (min < 1) return 'just now'; if (min < 60) return `${min} min ago`; const hr = Math.floor(min / 60); if (hr < 48) return `${hr} h ago`; const day = Math.floor(hr / 24); return `${day} d ago`; } function isStale(iso: string | null): boolean { if (!iso) return true; return Date.now() - new Date(iso).getTime() > STALE_HOURS * 3600_000; } function integrationLight(item: IntegrationHealthItem): StatusLightState { if (item.status === 'disabled') return 'idle'; const tokenExpired = item.tokenExpiry && item.tokenExpiry.daysRemaining <= 0; const tokenExpiring = item.tokenExpiry && item.tokenExpiry.daysRemaining > 0 && item.tokenExpiry.daysRemaining <= 14; if (item.status === 'auth_failed' || item.status === 'unreachable' || tokenExpired) { return 'error'; } if (tokenExpiring) return 'warn'; if (item.status === 'ok') return 'ok'; if (item.status === 'not_configured') return 'idle'; return 'idle'; } function syncLight(item: SyncHealthItem): StatusLightState { if (!item.isEnabled) return 'idle'; if (item.lastStatus === 'failed') return 'error'; if (isStale(item.lastRun)) return 'warn'; if (item.lastStatus === 'success') return 'ok'; return 'idle'; } // ── Page ───────────────────────────────────────────────────────────── export default function StatusPage() { const [health, setHealth] = useState(null); const [overview, setOverview] = useState(null); const [workers, setWorkers] = useState(null); const [error, setError] = useState(null); const [refreshing, setRefreshing] = useState(false); async function load(force = false) { setRefreshing(true); try { const [hRes, oRes, wRes] = await Promise.all([ fetch(`/api/dashboard/integration-health${force ? '?refresh=1' : ''}`, { cache: 'no-store' }), fetch('/api/dashboard/overview', { cache: 'no-store' }), fetch('/api/status/workers', { cache: 'no-store' }), ]); if (hRes.ok) setHealth((await hRes.json()) as IntegrationHealthResponse); if (oRes.ok) setOverview((await oRes.json()) as OverviewResponse); if (wRes.ok) { const j = (await wRes.json()) as WorkersResponse; setWorkers(j.workers); } setError(null); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to load'); } finally { setRefreshing(false); } } useEffect(() => { void load(); const id = setInterval(() => void load(), POLL_MS); return () => clearInterval(id); }, []); // Roll-up const overall: StatusLightState = !health ? 'idle' : health.summary.failed > 0 || health.summary.expired > 0 ? 'error' : health.summary.expiringWithin14Days > 0 || (overview?.syncHealth.some((s) => syncLight(s) === 'error') ?? false) ? 'warn' : 'ok'; const overallTitle = !health ? 'Loading…' : overall === 'error' ? `${health.summary.failed} integration${health.summary.failed === 1 ? '' : 's'} failing` : overall === 'warn' ? health.summary.expiringWithin14Days > 0 ? `${health.summary.expiringWithin14Days} token${health.summary.expiringWithin14Days === 1 ? '' : 's'} expiring soon` : 'Some sync tasks degraded' : 'All systems operational'; // Group integrations const grouped = (() => { if (!health) return null; const map: Record = {}; for (const item of health.items) { (map[item.category] ??= []).push(item); } return map; })(); const expiring = health?.items .filter((i) => i.tokenExpiry && i.tokenExpiry.daysRemaining <= 30) .sort((a, b) => (a.tokenExpiry!.daysRemaining ?? 999) - (b.tokenExpiry!.daysRemaining ?? 999)); const failingSyncs = overview?.syncHealth.filter((s) => syncLight(s) === 'error'); const failingIntegrations = health?.items.filter( (i) => i.status === 'auth_failed' || i.status === 'unreachable', ); return ( <> } />
{error && ( Failed to load status {error} )} {/* CONDITIONAL BANNER -------------------------------------------- */} {(failingIntegrations?.length || failingSyncs?.length) ? ( Action needed
    {failingIntegrations?.map((i) => (
  • {i.name} —{' '} {i.status === 'auth_failed' ? 'authentication failed' : 'unreachable'} {i.error && · {i.error.slice(0, 120)}}
  • ))} {failingSyncs?.map((s) => (
  • {s.name} sync failed {s.lastError && · {s.lastError.slice(0, 120)}}
  • ))}
) : null} {/* INTEGRATION TILES --------------------------------------------- */}

Integrations

{health && ( {health.summary.ok} of {health.summary.total - health.summary.notConfigured} healthy )}
{!grouped ? (
{[1, 2, 3, 4, 5, 6].map((i) => )}
) : (
{CATEGORY_ORDER.filter((c) => grouped[c]?.length).map((category) => (

{CATEGORY_LABELS[category]}

{grouped[category] .slice() .sort((a, b) => a.name.localeCompare(b.name)) .map((item) => )}
))}
)}
{/* WORKERS ------------------------------------------------------- */}

Workers

{!workers ? (
{[1, 2, 3].map((i) => )}
) : (
{workers.map((w) => ( ))}
)}
{/* TOKEN EXPIRY -------------------------------------------------- */} {expiring && expiring.length > 0 && ( Tokens expiring within 30 days
{expiring.map((item) => { const days = item.tokenExpiry!.daysRemaining; const tone = days <= 0 ? 'error' : days <= 14 ? 'warn' : 'pending'; return (
{item.name} · {item.tokenExpiry!.envVar}
{days <= 0 ? `expired ${Math.abs(days)} d ago` : `${days} d`}
); })}
)} {/* SYNC HEALTH --------------------------------------------------- */} Scheduled syncs {!overview ? (
) : overview.syncHealth.length === 0 ? (
) : ( Schedule Type Last run Next run Status {overview.syncHealth.map((s) => ( {s.name} {s.syncType} {relTime(s.lastRun)} {relTime(s.nextRun)} {s.isEnabled ? s.lastStatus === 'failed' ? failed : isStale(s.lastRun) ? stale : s.lastStatus === 'success' ? success : idle : off} ))}
)}
{/* COMPLIANCE FOOTER -------------------------------------------- */} {health && (

{health.summary.ok} healthy · {health.summary.failed} failing · {health.summary.notConfigured} unconfigured {health.summary.disabled > 0 && ( <> · {health.summary.disabled} disabled )} · {health.summary.expiringWithin14Days} expiring · last checked {relTime(health.items[0]?.checkedAt ?? null)}

)}
); } // ── Integration tile ──────────────────────────────────────────────── function IntegrationTile({ item }: { item: IntegrationHealthItem }) { const light = integrationLight(item); const expired = item.tokenExpiry && item.tokenExpiry.daysRemaining <= 0; const expiringSoon = item.tokenExpiry && item.tokenExpiry.daysRemaining > 0 && item.tokenExpiry.daysRemaining <= 14; let detail: string | null = null; if (item.status === 'disabled') detail = 'disabled by operator'; else if (item.status === 'auth_failed') detail = 'authentication failed'; else if (item.status === 'unreachable') detail = 'unreachable'; else if (expired) detail = `token expired ${Math.abs(item.tokenExpiry!.daysRemaining)} d ago`; else if (expiringSoon) detail = `token expires in ${item.tokenExpiry!.daysRemaining} d`; else if (item.status === 'not_configured') detail = 'not configured'; else if (item.status === 'ok' && item.latencyMs !== undefined) detail = `${item.latencyMs} ms`; else if (item.status === 'unknown' && item.configured) detail = 'configured'; return (

{item.name}

{(expired || expiringSoon) && ( )}
{detail && (

{detail}

)} {item.error && light === 'error' && (

{item.error.slice(0, 80)}

)}
); }