'use client'; import { useState, useEffect } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { Shield, Monitor, AlertTriangle, RefreshCw, Search, CheckCircle2, XCircle, Link2Off } from 'lucide-react'; interface SiteCoverage { s1_site_id: string; s1_site_name: string; state: string; sku: string; health_status: boolean; active_licenses: number; company_id: number | null; company_name: string | null; total_agents: number; active_agents: number; infected_agents: number; outdated_agents: number; decommissioned_agents: number; total_threats: number; active_threats: number; last_seen: string | null; } export default function S1CoveragePage() { const [sites, setSites] = useState([]); const [summary, setSummary] = useState(null); const [loading, setLoading] = useState(true); const [search, setSearch] = useState(''); const [filter, setFilter] = useState<'all' | 'issues' | 'unmapped'>('all'); const fetchData = async () => { setLoading(true); try { const res = await fetch('/api/sentinelone/coverage'); if (res.ok) { const data = await res.json(); setSites(data.sites ?? []); setSummary(data.summary ?? null); } } finally { setLoading(false); } }; useEffect(() => { fetchData(); }, []); const filtered = sites.filter(s => { const matchesSearch = !search || s.s1_site_name.toLowerCase().includes(search.toLowerCase()) || (s.company_name || '').toLowerCase().includes(search.toLowerCase()); const matchesFilter = filter === 'all' || (filter === 'issues' && (s.infected_agents > 0 || s.active_threats > 0 || s.outdated_agents > 0)) || (filter === 'unmapped' && !s.company_id); return matchesSearch && matchesFilter; }); return (

SentinelOne Coverage

AV agent coverage and threat status per site

{/* Summary cards */} {summary && (
{[ { label: 'Total Sites', value: summary.totalSites, color: 'text-foreground' }, { label: 'Mapped Sites', value: summary.mappedSites, color: 'text-blue-500' }, { label: 'Total Agents', value: summary.totalAgents, color: 'text-foreground' }, { label: 'Infected', value: summary.infectedAgents, color: summary.infectedAgents > 0 ? 'text-red-500' : 'text-green-500' }, { label: 'Active Threats', value: summary.activeThreats, color: summary.activeThreats > 0 ? 'text-orange-500' : 'text-green-500' }, { label: 'Outdated', value: summary.outdatedAgents, color: summary.outdatedAgents > 0 ? 'text-yellow-500' : 'text-green-500' }, ].map(({ label, value, color }) => ( {label}
{Number(value).toLocaleString()}
))}
)} {/* Filters */}
setSearch(e.target.value)} />
{(['all', 'issues', 'unmapped'] as const).map(f => ( ))}
{/* Table */} Site Company SKU Agents Active Infected Outdated Threats Status {loading ? ( Loading... ) : filtered.length === 0 ? ( No sites found ) : filtered.map(s => ( 0 || s.active_threats > 0 ? 'bg-red-500/5' : ''}>
{s.s1_site_name}
{s.state}
{s.company_name ? {s.company_name} : Unmapped} {s.sku || '—'} {s.total_agents} {s.active_agents} {s.infected_agents > 0 ? {s.infected_agents} : 0} {s.outdated_agents > 0 ? {s.outdated_agents} : 0} {s.active_threats > 0 ? {s.active_threats} : 0} {s.infected_agents > 0 || s.active_threats > 0 ? Action Needed : s.outdated_agents > 0 ? Outdated : s.total_agents === 0 ? No Agents : OK }
))}
); }