import { useCoverageGaps, useSymbolCoverage } from '../api/hooks'; import { LoadingSpinner, StatusBadge, Card } from '../components/ui'; export function OpsCoveragePage() { const { data: gaps, isLoading: gapsLoading } = useCoverageGaps(); const { data: coverage, isLoading: covLoading } = useSymbolCoverage(); if (gapsLoading || covLoading) return ; const missing = (gaps?.missing_source_types ?? []) as Array>; const stale = (gaps?.stale_sources ?? []) as Array>; const matrix = (coverage ?? []) as Array>; return ( Source Coverage {/* Coverage Matrix */} Company × Source Type Matrix Ticker Name Market News Filings Web Broker Total {matrix.map((row, i) => ( {String(row.ticker)} {String(row.legal_name)} {String(row.active_sources)} ))} {/* Missing Source Types */} {missing.length > 0 && ( Missing Source Types ({missing.length}) {missing.map((m, i) => { const activeTypes = (m.active_types as string[]) ?? []; const expected = (m.expected_types as string[]) ?? []; const missingTypes = expected.filter((t) => !activeTypes.includes(t)); return ( {String(m.ticker)} missing: {missingTypes.map((t) => ( ))} ); })} )} {/* Stale Sources */} {stale.length > 0 && ( Stale Sources ({stale.length}) {stale.map((s, i) => ( {String(s.ticker)} {String(s.source_name)} Last success: {s.last_success ? new Date(String(s.last_success)).toLocaleString() : 'never'} {s.recent_failures ? ` | ${s.recent_failures} failures (24h)` : ''} ))} )} ); } function CoverageCell({ count }: { count: number }) { const color = count > 0 ? 'text-green-400' : 'text-red-400'; return {count}; }