refactor(design): adopt TanStack DataTable on /veeam-analysis, fill gaps

Follow-on polish for the nav-design overhaul (#9bfb575).

- /veeam-analysis migrates the bespoke TicketRow + custom pagination to
  the new DataTable using getRowCanExpand + renderSubRow. Drops ~85
  lines of fragment/colspan markup in favor of the standard pattern.

- PageHeader on the last common stragglers — /settings,
  /settings/security, /sentinelone/coverage, /sentinelone/mappings.
  Settings is reachable from the new top-bar UserMenu so it had to
  match the rest of the visual system.

- /dashboard and /status load with the new Skeleton helpers
  (SkeletonRows, SkeletonChart, SkeletonTable) so loading shells now
  approximate the post-load layout instead of a single h-NN bar.

- DESIGN.md: closed the straggler PageHeader item; deprioritized the
  hard-coded palette audit with a note that ~770 references are mostly
  semantic via the documented bg-{hue}-500/15 / text-{hue}-700 recipe.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
lorentz 2026-05-03 09:42:04 -04:00
parent 9bfb57553d
commit ab78e7bd4f
8 changed files with 242 additions and 250 deletions

View file

@ -5,6 +5,7 @@ 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 { PageHeader } from '@/components/navigation/page-header';
import {
Table, TableBody, TableCell, TableHead, TableHeader, TableRow,
} from '@/components/ui/table';
@ -64,19 +65,20 @@ export default function S1CoveragePage() {
});
return (
<div className="container mx-auto py-8 space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold flex items-center gap-3">
<Shield className="w-8 h-8 text-purple-600" />
SentinelOne Coverage
</h1>
<p className="text-muted-foreground mt-1">AV agent coverage and threat status per site</p>
</div>
<Button variant="outline" size="sm" onClick={fetchData} disabled={loading}>
<RefreshCw className={`w-4 h-4 mr-2 ${loading ? 'animate-spin' : ''}`} />Refresh
</Button>
</div>
<>
<PageHeader
title="SentinelOne coverage"
description="AV agent coverage and threat status per site"
breadcrumbs={[{ label: 'SentinelOne' }, { label: 'Coverage' }]}
accent
actions={
<Button variant="outline" size="sm" onClick={fetchData} disabled={loading}>
<RefreshCw className={`w-4 h-4 mr-2 ${loading ? 'animate-spin' : ''}`} />
Refresh
</Button>
}
/>
<div className="container mx-auto px-6 py-6 space-y-6">
{/* Summary cards */}
{summary && (
@ -182,6 +184,7 @@ export default function S1CoveragePage() {
</Table>
</CardContent>
</Card>
</div>
</div>
</>
);
}

View file

@ -3,6 +3,7 @@
import { useState, useEffect } from 'react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { PageHeader } from '@/components/navigation/page-header';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import {
@ -95,26 +96,26 @@ export default function S1MappingsPage() {
};
return (
<div className="container mx-auto py-8 space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold flex items-center gap-3">
<Shield className="w-8 h-8 text-purple-600" />
SentinelOne Site Mappings
</h1>
<p className="text-muted-foreground mt-2">Map SentinelOne sites to Autotask companies</p>
</div>
<div className="flex gap-2">
<Button onClick={handleSync} variant="outline" size="sm" disabled={syncing}>
{syncing
? <><RefreshCw className="w-4 h-4 mr-2 animate-spin" />Syncing...</>
: <><RefreshCw className="w-4 h-4 mr-2" />Sync S1</>}
</Button>
<Button onClick={fetchData} variant="outline" size="sm">
<RefreshCw className="w-4 h-4 mr-2" />Refresh
</Button>
</div>
</div>
<>
<PageHeader
title="SentinelOne site mappings"
description="Map SentinelOne sites to Autotask companies"
breadcrumbs={[{ label: 'SentinelOne' }, { label: 'Mappings' }]}
accent
actions={
<>
<Button onClick={handleSync} variant="outline" size="sm" disabled={syncing}>
<RefreshCw className={`w-4 h-4 mr-2 ${syncing ? 'animate-spin' : ''}`} />
{syncing ? 'Syncing…' : 'Sync S1'}
</Button>
<Button onClick={fetchData} variant="outline" size="sm">
<RefreshCw className="w-4 h-4 mr-2" />
Refresh
</Button>
</>
}
/>
<div className="container mx-auto px-6 py-6 space-y-6">
{/* Stats */}
<div className="grid grid-cols-3 gap-4">
@ -182,7 +183,8 @@ export default function S1MappingsPage() {
</div>
</CardContent>
</Card>
</div>
</div>
</>
);
}