2026-02-20 10:28:15 -05:00
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
import { useState, useEffect } from 'react';
|
|
|
|
|
import Link from 'next/link';
|
|
|
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
feat(design): nav/visual overhaul — brand layer, /status route, KPI dashboard, TanStack DataTable
Major UI refresh on the nav-design-improvements branch. Drops 2013-era
inline styles and consolidates patterns behind shared primitives.
Foundation
- New Wulf brand layer in app/styles/brand.css repointing --primary to
the standards-guide blue (#0075AD) with utility classes for numerics
(.num / .num-lg / .num-xl), metric labels, surface tints, and the
wolf-mark watermark
- Switch primary face to IBM Plex Sans + IBM Plex Mono via next/font;
Helvetica/Arial stays in the fallback chain for brand fidelity
- Wordmark subtitle changed from "PSA Management System" to
"Operations console" everywhere it appeared
- Tagline footer ("Don't be afraid to cry") on every non-mobile page
Status moved out of /dashboard
- New /status route with integration tiles grouped by category, sync
health table, worker pulse cards (analyzer / RMM / sync scheduler),
token-expiry section, conditional alert banner
- Top-bar StatusIndicator polls integration health every 60s and links
to /status
- INTEGRATIONS_DISABLED env var suppresses operator-disabled
integrations (e.g. SentinelOne) — no failure noise from broken-on-
purpose entries. Aliases supported (sentinelone → s1, etc.)
Dashboard rebuilt around KPIs
- /api/dashboard/overview adds today snapshot (opened, resolved, open
total, SLA breaches) with delta math
- /api/dashboard/trends backs queue × priority heatmap, 30-day volume
area chart, 30-day mean resolution time line chart, today's active
engineers leaderboard
Components
- StatusBadge driven by lib/status-registry.ts (priority, ticket
status, classification, source, company type, publish, active /
yes-no / billable / approved registries)
- StatusLight (8px geometric square, five states, three sizes)
- EmptyState (shared dashed panel with icon + headline + optional CTA)
- KpiCard with delta indicator and tonal left border
- WulfMark (mark / wordmark variants from /public/branding)
- Skeleton helpers (SkeletonRow / Rows / Card / Chart / Header / Table)
Navigation
- Admin flat link → dropdown with seven shortcuts
- New UserMenu (initials avatar, role badge, settings + sign-out)
- Active-route highlight is now a 2px Wulf-blue underline echoing the
PageHeader rule (consistent across flat links and submenu triggers);
active children inside dropdowns use bg-primary/10
- Submenu width is content-driven (min-w 320 / max-w 440, single col)
- Mobile hamburger via Sheet, reuses the same nav config
Pages migrated
- 16 admin sub-pages adopt PageHeader (with accent prop)
- /addigy-devices: shadcn Table + Checkbox; PageHeader; status badges
- 10 raw <table> blocks across admin/sync/* migrated to shadcn Table
- /veeam-analysis migrated to shadcn Table (kept its expansion logic)
- Detail routes (analyzer ticket, analyzer analysis) get breadcrumbs
DataTable
- Rewritten on @tanstack/react-table v8 in manual mode; external API
unchanged so all 10+ data-browser pages keep working
- New optional props for drill-down rows: getRowCanExpand + renderSubRow
Mobile
- Multi-select Popover gets max-w-[calc(100vw-1rem)] and
collisionPadding so dropdowns can't overflow narrow viewports
- CI filter bar wraps and shrinks; stat pill flows below
Docs
- New ARCHITECTURE.md (load-bearing reference for runtime, data flow,
workers, analyzer pipeline, auth, deployment, gotchas)
- New DESIGN.md (tokens, layout, navigation IA, component vocabulary,
rolling backlog of remaining cleanup)
- CLAUDE.md refreshed with pointers to the two new docs and the
INTEGRATIONS_DISABLED operator config note
- shadcn registry registered as project-level MCP server (.mcp.json)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 09:33:13 -04:00
|
|
|
import {
|
|
|
|
|
Table,
|
|
|
|
|
TableBody,
|
|
|
|
|
TableCell,
|
|
|
|
|
TableHead,
|
|
|
|
|
TableHeader,
|
|
|
|
|
TableRow,
|
|
|
|
|
} from '@/components/ui/table';
|
|
|
|
|
import { StatusBadge } from '@/components/ui/status-badge';
|
2026-02-20 10:28:15 -05:00
|
|
|
import {
|
|
|
|
|
ArrowLeft, Activity, History, Monitor, Loader2, RefreshCw,
|
|
|
|
|
ExternalLink, Server, Wifi, WifiOff, AlertTriangle, Bell, XCircle, CheckCircle2, Clock,
|
|
|
|
|
} from 'lucide-react';
|
|
|
|
|
|
|
|
|
|
function fmtDate(d: string | null) {
|
|
|
|
|
if (!d) return 'Never';
|
|
|
|
|
return new Date(d).toLocaleString(undefined, { month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit' });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function StatCard({ label, value, sub, icon: Icon, cls }: { label: string; value: string | number; sub?: string; icon?: React.ElementType; cls?: string }) {
|
|
|
|
|
return (
|
|
|
|
|
<div className={`rounded-lg border p-4 flex flex-col gap-1 ${cls ?? ''}`}>
|
|
|
|
|
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
|
|
|
|
{Icon && <Icon className="w-3.5 h-3.5" />}{label}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="text-2xl font-bold tabular-nums">{value}</div>
|
|
|
|
|
{sub && <div className="text-xs text-muted-foreground">{sub}</div>}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function StatusTab({ data, onSync, syncing }: { data: any; onSync: () => void; syncing: boolean }) {
|
|
|
|
|
if (!data) return <div className="flex items-center justify-center py-12"><Loader2 className="w-5 h-5 animate-spin text-muted-foreground" /></div>;
|
|
|
|
|
|
|
|
|
|
const devs = data.devices ?? {};
|
|
|
|
|
const alerts = data.openAlerts ?? {};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
<div className="flex items-center justify-between rounded-lg border p-4 bg-muted/30">
|
|
|
|
|
<div className="space-y-0.5">
|
|
|
|
|
<p className="text-sm font-medium">{data.configured ? 'Connected' : 'Not configured'}</p>
|
|
|
|
|
<p className="text-xs text-muted-foreground">Last sync: {fmtDate(data.lastSync)}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex gap-2">
|
|
|
|
|
<a href="https://concord.rmm.datto.com" target="_blank" rel="noopener noreferrer">
|
|
|
|
|
<Button variant="outline" size="sm" className="gap-2">
|
|
|
|
|
<ExternalLink className="w-4 h-4" />Portal
|
|
|
|
|
</Button>
|
|
|
|
|
</a>
|
|
|
|
|
<Button size="sm" onClick={onSync} disabled={syncing || !data.configured}>
|
|
|
|
|
{syncing ? <Loader2 className="w-4 h-4 animate-spin mr-2" /> : <RefreshCw className="w-4 h-4 mr-2" />}
|
|
|
|
|
Full Sync
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
|
|
|
|
<StatCard label="Sites" value={data.sites ?? 0} icon={Server} />
|
|
|
|
|
<StatCard label="Devices" value={devs.total ?? 0} icon={Monitor} />
|
|
|
|
|
<StatCard label="Online" value={devs.online ?? 0} icon={Wifi}
|
|
|
|
|
cls="border-green-500/30 bg-green-500/5" />
|
|
|
|
|
<StatCard label="Offline" value={devs.offline ?? 0} icon={WifiOff}
|
|
|
|
|
cls={(devs.offline ?? 0) > 0 ? 'border-yellow-500/30 bg-yellow-500/5' : ''} />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
|
|
|
|
<StatCard label="Open Alerts" value={alerts.total ?? 0} icon={Bell}
|
|
|
|
|
cls={(alerts.total ?? 0) > 0 ? 'border-red-500/30 bg-red-500/5' : ''} />
|
|
|
|
|
<StatCard label="Critical / High" value={`${alerts.critical ?? 0} / ${alerts.high ?? 0}`} icon={XCircle}
|
|
|
|
|
cls={(alerts.critical ?? 0) > 0 ? 'border-red-500/30 bg-red-500/5' : ''} />
|
|
|
|
|
<StatCard label="Moderate" value={alerts.moderate ?? 0} icon={AlertTriangle} />
|
|
|
|
|
<StatCard label="With Ticket" value={alerts.withTicket ?? 0} icon={CheckCircle2}
|
|
|
|
|
sub="linked to Autotask" />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{(alerts.critical > 0 || alerts.high > 0) && (
|
|
|
|
|
<div className="rounded-lg border p-4 space-y-2">
|
|
|
|
|
<p className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">Attention Required</p>
|
|
|
|
|
{alerts.critical > 0 && (
|
|
|
|
|
<div className="flex items-center gap-2 text-sm text-red-600">
|
|
|
|
|
<XCircle className="w-4 h-4" />{alerts.critical} critical alert{alerts.critical !== 1 ? 's' : ''}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
{alerts.high > 0 && (
|
|
|
|
|
<div className="flex items-center gap-2 text-sm text-orange-600">
|
|
|
|
|
<AlertTriangle className="w-4 h-4" />{alerts.high} high priority alert{alerts.high !== 1 ? 's' : ''}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function HistoryTab({ refreshKey }: { refreshKey: number }) {
|
|
|
|
|
const [rows, setRows] = useState<any[]>([]);
|
|
|
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
setLoading(true);
|
|
|
|
|
fetch('/api/sync/history?entityType=datto_rmm&limit=50')
|
|
|
|
|
.then(r => r.json())
|
|
|
|
|
.then(d => setRows(d.history ?? []))
|
|
|
|
|
.catch(() => setRows([]))
|
|
|
|
|
.finally(() => setLoading(false));
|
|
|
|
|
}, [refreshKey]);
|
|
|
|
|
|
|
|
|
|
if (loading) return <div className="flex items-center justify-center py-12"><Loader2 className="w-5 h-5 animate-spin text-muted-foreground" /></div>;
|
|
|
|
|
if (!rows.length) return <div className="text-center py-12 text-muted-foreground text-sm">No sync history yet — run a sync to populate</div>;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="rounded-lg border overflow-hidden">
|
feat(design): nav/visual overhaul — brand layer, /status route, KPI dashboard, TanStack DataTable
Major UI refresh on the nav-design-improvements branch. Drops 2013-era
inline styles and consolidates patterns behind shared primitives.
Foundation
- New Wulf brand layer in app/styles/brand.css repointing --primary to
the standards-guide blue (#0075AD) with utility classes for numerics
(.num / .num-lg / .num-xl), metric labels, surface tints, and the
wolf-mark watermark
- Switch primary face to IBM Plex Sans + IBM Plex Mono via next/font;
Helvetica/Arial stays in the fallback chain for brand fidelity
- Wordmark subtitle changed from "PSA Management System" to
"Operations console" everywhere it appeared
- Tagline footer ("Don't be afraid to cry") on every non-mobile page
Status moved out of /dashboard
- New /status route with integration tiles grouped by category, sync
health table, worker pulse cards (analyzer / RMM / sync scheduler),
token-expiry section, conditional alert banner
- Top-bar StatusIndicator polls integration health every 60s and links
to /status
- INTEGRATIONS_DISABLED env var suppresses operator-disabled
integrations (e.g. SentinelOne) — no failure noise from broken-on-
purpose entries. Aliases supported (sentinelone → s1, etc.)
Dashboard rebuilt around KPIs
- /api/dashboard/overview adds today snapshot (opened, resolved, open
total, SLA breaches) with delta math
- /api/dashboard/trends backs queue × priority heatmap, 30-day volume
area chart, 30-day mean resolution time line chart, today's active
engineers leaderboard
Components
- StatusBadge driven by lib/status-registry.ts (priority, ticket
status, classification, source, company type, publish, active /
yes-no / billable / approved registries)
- StatusLight (8px geometric square, five states, three sizes)
- EmptyState (shared dashed panel with icon + headline + optional CTA)
- KpiCard with delta indicator and tonal left border
- WulfMark (mark / wordmark variants from /public/branding)
- Skeleton helpers (SkeletonRow / Rows / Card / Chart / Header / Table)
Navigation
- Admin flat link → dropdown with seven shortcuts
- New UserMenu (initials avatar, role badge, settings + sign-out)
- Active-route highlight is now a 2px Wulf-blue underline echoing the
PageHeader rule (consistent across flat links and submenu triggers);
active children inside dropdowns use bg-primary/10
- Submenu width is content-driven (min-w 320 / max-w 440, single col)
- Mobile hamburger via Sheet, reuses the same nav config
Pages migrated
- 16 admin sub-pages adopt PageHeader (with accent prop)
- /addigy-devices: shadcn Table + Checkbox; PageHeader; status badges
- 10 raw <table> blocks across admin/sync/* migrated to shadcn Table
- /veeam-analysis migrated to shadcn Table (kept its expansion logic)
- Detail routes (analyzer ticket, analyzer analysis) get breadcrumbs
DataTable
- Rewritten on @tanstack/react-table v8 in manual mode; external API
unchanged so all 10+ data-browser pages keep working
- New optional props for drill-down rows: getRowCanExpand + renderSubRow
Mobile
- Multi-select Popover gets max-w-[calc(100vw-1rem)] and
collisionPadding so dropdowns can't overflow narrow viewports
- CI filter bar wraps and shrinks; stat pill flows below
Docs
- New ARCHITECTURE.md (load-bearing reference for runtime, data flow,
workers, analyzer pipeline, auth, deployment, gotchas)
- New DESIGN.md (tokens, layout, navigation IA, component vocabulary,
rolling backlog of remaining cleanup)
- CLAUDE.md refreshed with pointers to the two new docs and the
INTEGRATIONS_DISABLED operator config note
- shadcn registry registered as project-level MCP server (.mcp.json)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 09:33:13 -04:00
|
|
|
<Table>
|
|
|
|
|
<TableHeader className="bg-muted/50">
|
|
|
|
|
<TableRow>
|
|
|
|
|
<TableHead>Type</TableHead>
|
|
|
|
|
<TableHead>Status</TableHead>
|
|
|
|
|
<TableHead className="text-right">Records</TableHead>
|
|
|
|
|
<TableHead>Started</TableHead>
|
|
|
|
|
<TableHead className="text-right">Duration</TableHead>
|
|
|
|
|
</TableRow>
|
|
|
|
|
</TableHeader>
|
|
|
|
|
<TableBody>
|
2026-02-20 10:28:15 -05:00
|
|
|
{rows.map((row: any, i: number) => {
|
|
|
|
|
const dur = row.completed_at && row.started_at
|
|
|
|
|
? Math.round((new Date(row.completed_at).getTime() - new Date(row.started_at).getTime()) / 1000)
|
|
|
|
|
: null;
|
feat(design): nav/visual overhaul — brand layer, /status route, KPI dashboard, TanStack DataTable
Major UI refresh on the nav-design-improvements branch. Drops 2013-era
inline styles and consolidates patterns behind shared primitives.
Foundation
- New Wulf brand layer in app/styles/brand.css repointing --primary to
the standards-guide blue (#0075AD) with utility classes for numerics
(.num / .num-lg / .num-xl), metric labels, surface tints, and the
wolf-mark watermark
- Switch primary face to IBM Plex Sans + IBM Plex Mono via next/font;
Helvetica/Arial stays in the fallback chain for brand fidelity
- Wordmark subtitle changed from "PSA Management System" to
"Operations console" everywhere it appeared
- Tagline footer ("Don't be afraid to cry") on every non-mobile page
Status moved out of /dashboard
- New /status route with integration tiles grouped by category, sync
health table, worker pulse cards (analyzer / RMM / sync scheduler),
token-expiry section, conditional alert banner
- Top-bar StatusIndicator polls integration health every 60s and links
to /status
- INTEGRATIONS_DISABLED env var suppresses operator-disabled
integrations (e.g. SentinelOne) — no failure noise from broken-on-
purpose entries. Aliases supported (sentinelone → s1, etc.)
Dashboard rebuilt around KPIs
- /api/dashboard/overview adds today snapshot (opened, resolved, open
total, SLA breaches) with delta math
- /api/dashboard/trends backs queue × priority heatmap, 30-day volume
area chart, 30-day mean resolution time line chart, today's active
engineers leaderboard
Components
- StatusBadge driven by lib/status-registry.ts (priority, ticket
status, classification, source, company type, publish, active /
yes-no / billable / approved registries)
- StatusLight (8px geometric square, five states, three sizes)
- EmptyState (shared dashed panel with icon + headline + optional CTA)
- KpiCard with delta indicator and tonal left border
- WulfMark (mark / wordmark variants from /public/branding)
- Skeleton helpers (SkeletonRow / Rows / Card / Chart / Header / Table)
Navigation
- Admin flat link → dropdown with seven shortcuts
- New UserMenu (initials avatar, role badge, settings + sign-out)
- Active-route highlight is now a 2px Wulf-blue underline echoing the
PageHeader rule (consistent across flat links and submenu triggers);
active children inside dropdowns use bg-primary/10
- Submenu width is content-driven (min-w 320 / max-w 440, single col)
- Mobile hamburger via Sheet, reuses the same nav config
Pages migrated
- 16 admin sub-pages adopt PageHeader (with accent prop)
- /addigy-devices: shadcn Table + Checkbox; PageHeader; status badges
- 10 raw <table> blocks across admin/sync/* migrated to shadcn Table
- /veeam-analysis migrated to shadcn Table (kept its expansion logic)
- Detail routes (analyzer ticket, analyzer analysis) get breadcrumbs
DataTable
- Rewritten on @tanstack/react-table v8 in manual mode; external API
unchanged so all 10+ data-browser pages keep working
- New optional props for drill-down rows: getRowCanExpand + renderSubRow
Mobile
- Multi-select Popover gets max-w-[calc(100vw-1rem)] and
collisionPadding so dropdowns can't overflow narrow viewports
- CI filter bar wraps and shrinks; stat pill flows below
Docs
- New ARCHITECTURE.md (load-bearing reference for runtime, data flow,
workers, analyzer pipeline, auth, deployment, gotchas)
- New DESIGN.md (tokens, layout, navigation IA, component vocabulary,
rolling backlog of remaining cleanup)
- CLAUDE.md refreshed with pointers to the two new docs and the
INTEGRATIONS_DISABLED operator config note
- shadcn registry registered as project-level MCP server (.mcp.json)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 09:33:13 -04:00
|
|
|
const tone = row.status === 'completed' ? 'ok' : row.status === 'failed' ? 'error' : 'warn';
|
2026-02-20 10:28:15 -05:00
|
|
|
return (
|
feat(design): nav/visual overhaul — brand layer, /status route, KPI dashboard, TanStack DataTable
Major UI refresh on the nav-design-improvements branch. Drops 2013-era
inline styles and consolidates patterns behind shared primitives.
Foundation
- New Wulf brand layer in app/styles/brand.css repointing --primary to
the standards-guide blue (#0075AD) with utility classes for numerics
(.num / .num-lg / .num-xl), metric labels, surface tints, and the
wolf-mark watermark
- Switch primary face to IBM Plex Sans + IBM Plex Mono via next/font;
Helvetica/Arial stays in the fallback chain for brand fidelity
- Wordmark subtitle changed from "PSA Management System" to
"Operations console" everywhere it appeared
- Tagline footer ("Don't be afraid to cry") on every non-mobile page
Status moved out of /dashboard
- New /status route with integration tiles grouped by category, sync
health table, worker pulse cards (analyzer / RMM / sync scheduler),
token-expiry section, conditional alert banner
- Top-bar StatusIndicator polls integration health every 60s and links
to /status
- INTEGRATIONS_DISABLED env var suppresses operator-disabled
integrations (e.g. SentinelOne) — no failure noise from broken-on-
purpose entries. Aliases supported (sentinelone → s1, etc.)
Dashboard rebuilt around KPIs
- /api/dashboard/overview adds today snapshot (opened, resolved, open
total, SLA breaches) with delta math
- /api/dashboard/trends backs queue × priority heatmap, 30-day volume
area chart, 30-day mean resolution time line chart, today's active
engineers leaderboard
Components
- StatusBadge driven by lib/status-registry.ts (priority, ticket
status, classification, source, company type, publish, active /
yes-no / billable / approved registries)
- StatusLight (8px geometric square, five states, three sizes)
- EmptyState (shared dashed panel with icon + headline + optional CTA)
- KpiCard with delta indicator and tonal left border
- WulfMark (mark / wordmark variants from /public/branding)
- Skeleton helpers (SkeletonRow / Rows / Card / Chart / Header / Table)
Navigation
- Admin flat link → dropdown with seven shortcuts
- New UserMenu (initials avatar, role badge, settings + sign-out)
- Active-route highlight is now a 2px Wulf-blue underline echoing the
PageHeader rule (consistent across flat links and submenu triggers);
active children inside dropdowns use bg-primary/10
- Submenu width is content-driven (min-w 320 / max-w 440, single col)
- Mobile hamburger via Sheet, reuses the same nav config
Pages migrated
- 16 admin sub-pages adopt PageHeader (with accent prop)
- /addigy-devices: shadcn Table + Checkbox; PageHeader; status badges
- 10 raw <table> blocks across admin/sync/* migrated to shadcn Table
- /veeam-analysis migrated to shadcn Table (kept its expansion logic)
- Detail routes (analyzer ticket, analyzer analysis) get breadcrumbs
DataTable
- Rewritten on @tanstack/react-table v8 in manual mode; external API
unchanged so all 10+ data-browser pages keep working
- New optional props for drill-down rows: getRowCanExpand + renderSubRow
Mobile
- Multi-select Popover gets max-w-[calc(100vw-1rem)] and
collisionPadding so dropdowns can't overflow narrow viewports
- CI filter bar wraps and shrinks; stat pill flows below
Docs
- New ARCHITECTURE.md (load-bearing reference for runtime, data flow,
workers, analyzer pipeline, auth, deployment, gotchas)
- New DESIGN.md (tokens, layout, navigation IA, component vocabulary,
rolling backlog of remaining cleanup)
- CLAUDE.md refreshed with pointers to the two new docs and the
INTEGRATIONS_DISABLED operator config note
- shadcn registry registered as project-level MCP server (.mcp.json)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 09:33:13 -04:00
|
|
|
<TableRow key={i}>
|
|
|
|
|
<TableCell className="capitalize">{row.sync_type}</TableCell>
|
|
|
|
|
<TableCell>
|
|
|
|
|
<StatusBadge tone={tone}>{row.status}</StatusBadge>
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell className="text-right num">{row.records_added ?? 0}</TableCell>
|
|
|
|
|
<TableCell className="text-muted-foreground num">{fmtDate(row.started_at)}</TableCell>
|
|
|
|
|
<TableCell className="text-right text-muted-foreground num">
|
|
|
|
|
{dur != null ? `${dur}s` : '—'}
|
|
|
|
|
</TableCell>
|
|
|
|
|
</TableRow>
|
2026-02-20 10:28:15 -05:00
|
|
|
);
|
|
|
|
|
})}
|
feat(design): nav/visual overhaul — brand layer, /status route, KPI dashboard, TanStack DataTable
Major UI refresh on the nav-design-improvements branch. Drops 2013-era
inline styles and consolidates patterns behind shared primitives.
Foundation
- New Wulf brand layer in app/styles/brand.css repointing --primary to
the standards-guide blue (#0075AD) with utility classes for numerics
(.num / .num-lg / .num-xl), metric labels, surface tints, and the
wolf-mark watermark
- Switch primary face to IBM Plex Sans + IBM Plex Mono via next/font;
Helvetica/Arial stays in the fallback chain for brand fidelity
- Wordmark subtitle changed from "PSA Management System" to
"Operations console" everywhere it appeared
- Tagline footer ("Don't be afraid to cry") on every non-mobile page
Status moved out of /dashboard
- New /status route with integration tiles grouped by category, sync
health table, worker pulse cards (analyzer / RMM / sync scheduler),
token-expiry section, conditional alert banner
- Top-bar StatusIndicator polls integration health every 60s and links
to /status
- INTEGRATIONS_DISABLED env var suppresses operator-disabled
integrations (e.g. SentinelOne) — no failure noise from broken-on-
purpose entries. Aliases supported (sentinelone → s1, etc.)
Dashboard rebuilt around KPIs
- /api/dashboard/overview adds today snapshot (opened, resolved, open
total, SLA breaches) with delta math
- /api/dashboard/trends backs queue × priority heatmap, 30-day volume
area chart, 30-day mean resolution time line chart, today's active
engineers leaderboard
Components
- StatusBadge driven by lib/status-registry.ts (priority, ticket
status, classification, source, company type, publish, active /
yes-no / billable / approved registries)
- StatusLight (8px geometric square, five states, three sizes)
- EmptyState (shared dashed panel with icon + headline + optional CTA)
- KpiCard with delta indicator and tonal left border
- WulfMark (mark / wordmark variants from /public/branding)
- Skeleton helpers (SkeletonRow / Rows / Card / Chart / Header / Table)
Navigation
- Admin flat link → dropdown with seven shortcuts
- New UserMenu (initials avatar, role badge, settings + sign-out)
- Active-route highlight is now a 2px Wulf-blue underline echoing the
PageHeader rule (consistent across flat links and submenu triggers);
active children inside dropdowns use bg-primary/10
- Submenu width is content-driven (min-w 320 / max-w 440, single col)
- Mobile hamburger via Sheet, reuses the same nav config
Pages migrated
- 16 admin sub-pages adopt PageHeader (with accent prop)
- /addigy-devices: shadcn Table + Checkbox; PageHeader; status badges
- 10 raw <table> blocks across admin/sync/* migrated to shadcn Table
- /veeam-analysis migrated to shadcn Table (kept its expansion logic)
- Detail routes (analyzer ticket, analyzer analysis) get breadcrumbs
DataTable
- Rewritten on @tanstack/react-table v8 in manual mode; external API
unchanged so all 10+ data-browser pages keep working
- New optional props for drill-down rows: getRowCanExpand + renderSubRow
Mobile
- Multi-select Popover gets max-w-[calc(100vw-1rem)] and
collisionPadding so dropdowns can't overflow narrow viewports
- CI filter bar wraps and shrinks; stat pill flows below
Docs
- New ARCHITECTURE.md (load-bearing reference for runtime, data flow,
workers, analyzer pipeline, auth, deployment, gotchas)
- New DESIGN.md (tokens, layout, navigation IA, component vocabulary,
rolling backlog of remaining cleanup)
- CLAUDE.md refreshed with pointers to the two new docs and the
INTEGRATIONS_DISABLED operator config note
- shadcn registry registered as project-level MCP server (.mcp.json)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 09:33:13 -04:00
|
|
|
</TableBody>
|
|
|
|
|
</Table>
|
2026-02-20 10:28:15 -05:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function DattoRmmPage() {
|
|
|
|
|
const [status, setStatus] = useState<any>(null);
|
|
|
|
|
const [syncing, setSyncing] = useState(false);
|
|
|
|
|
const [refreshKey, setRefreshKey] = useState(0);
|
|
|
|
|
|
|
|
|
|
const fetchStatus = async () => {
|
|
|
|
|
const res = await fetch('/api/integrations/status');
|
|
|
|
|
if (res.ok) { const d = await res.json(); setStatus(d.dattoRmm); }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
useEffect(() => { fetchStatus(); }, [refreshKey]);
|
|
|
|
|
|
|
|
|
|
const handleSync = async () => {
|
|
|
|
|
setSyncing(true);
|
|
|
|
|
try {
|
|
|
|
|
await fetch('/api/datto-rmm/sync', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
body: JSON.stringify({ syncType: 'full' }),
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
});
|
|
|
|
|
const poll = setInterval(async () => {
|
|
|
|
|
const r = await fetch('/api/datto-rmm/sync');
|
|
|
|
|
if (r.ok) {
|
|
|
|
|
const d = await r.json();
|
|
|
|
|
if (!d.isSyncing) {
|
|
|
|
|
clearInterval(poll);
|
|
|
|
|
setSyncing(false);
|
|
|
|
|
setRefreshKey(k => k + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}, 5000);
|
|
|
|
|
} catch {
|
|
|
|
|
setSyncing(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="container mx-auto py-4 md:py-8 px-4 space-y-6">
|
|
|
|
|
<div className="flex items-center gap-4">
|
|
|
|
|
<Link href="/admin/sync">
|
|
|
|
|
<Button variant="outline" size="sm" className="gap-2">
|
|
|
|
|
<ArrowLeft className="w-4 h-4" />
|
|
|
|
|
Integrations
|
|
|
|
|
</Button>
|
|
|
|
|
</Link>
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<div className="p-2 rounded-lg border border-orange-500/30 bg-orange-500/5">
|
|
|
|
|
<Monitor className="w-5 h-5 text-orange-500" />
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<h1 className="text-2xl font-bold">RMM — Datto RMM</h1>
|
|
|
|
|
<p className="text-sm text-muted-foreground">Sites, devices, alerts, patch management</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<Tabs defaultValue="status" className="w-full">
|
|
|
|
|
<TabsList className="grid w-full max-w-md grid-cols-3">
|
|
|
|
|
<TabsTrigger value="status" className="gap-2">
|
|
|
|
|
<Activity className="h-4 w-4" />Status
|
|
|
|
|
</TabsTrigger>
|
|
|
|
|
<TabsTrigger value="history" className="gap-2">
|
|
|
|
|
<History className="h-4 w-4" />History
|
|
|
|
|
</TabsTrigger>
|
|
|
|
|
<TabsTrigger value="about" className="gap-2">
|
|
|
|
|
<Monitor className="h-4 w-4" />About
|
|
|
|
|
</TabsTrigger>
|
|
|
|
|
</TabsList>
|
|
|
|
|
<TabsContent value="status" className="mt-6">
|
|
|
|
|
<StatusTab data={status} onSync={handleSync} syncing={syncing} />
|
|
|
|
|
</TabsContent>
|
|
|
|
|
<TabsContent value="history" className="mt-6">
|
|
|
|
|
<HistoryTab refreshKey={refreshKey} />
|
|
|
|
|
</TabsContent>
|
|
|
|
|
<TabsContent value="about" className="mt-6">
|
|
|
|
|
<div className="space-y-4 text-sm text-muted-foreground">
|
|
|
|
|
<div className="rounded-lg border p-4 space-y-2">
|
|
|
|
|
<p className="font-medium text-foreground">Synced Entities</p>
|
|
|
|
|
<ul className="space-y-1 list-disc list-inside">
|
|
|
|
|
<li><strong>Sites</strong> — RMM sites with device counts, mapped to Autotask companies</li>
|
|
|
|
|
<li><strong>Devices</strong> — all managed devices with OS, IP, AV, patch status, UDFs</li>
|
|
|
|
|
<li><strong>Open Alerts</strong> — active alerts with priority, device context, ticket linkage</li>
|
|
|
|
|
<li><strong>Resolved Alerts</strong> — recent resolved alerts with response action history</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="rounded-lg border p-4 space-y-2">
|
|
|
|
|
<p className="font-medium text-foreground">Authentication</p>
|
|
|
|
|
<p>OAuth2 password grant — API key + secret → Bearer token (100h TTL, refreshed at 50min)</p>
|
|
|
|
|
<p>Rate limit: 600 requests / 60 seconds across the account</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</TabsContent>
|
|
|
|
|
</Tabs>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|