From 6ed6c668106610ea76e96d942b0a58faed608f61 Mon Sep 17 00:00:00 2001 From: lorentz Date: Sun, 12 Jul 2026 07:52:21 -0400 Subject: [PATCH] feat(260712-ash): add PAX8 card to sync overview page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Append PAX8 entry to INTEGRATIONS with logo, description, and detail link - Fetch /api/pax8/sync in fetchAll, wire pax8 branches in getSummary/getStatusIcon - Add PAX8 stats block (last sync, companies, subscriptions) to card render - Add public/logos/pax8.ico as placeholder (copied from itglue.ico — no network access available to fetch the real PAX8 favicon; replace with the real logo when convenient) --- app/admin/sync/page.tsx | 37 ++++++++++++++++++++++++++++++++++++- public/logos/pax8.ico | Bin 0 -> 621 bytes 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 public/logos/pax8.ico diff --git a/app/admin/sync/page.tsx b/app/admin/sync/page.tsx index 6cef917..d41545a 100644 --- a/app/admin/sync/page.tsx +++ b/app/admin/sync/page.tsx @@ -26,6 +26,7 @@ const INTEGRATIONS: IntegrationCard[] = [ { id: 'sentinelone', category: 'EDR/AV', product: 'SentinelOne', description: 'Endpoint agents, threat detections, site coverage, AV health', href: '/admin/sync/sentinelone', logo: '/logos/sentinelone.ico', color: 'purple' }, { id: 'mimecast', category: 'Email Security', product: 'Mimecast', description: 'Message tracking logs, threat events, SIEM data, 120-day retention', href: '/admin/sync/mimecast', logo: '/logos/mimecast.ico', color: 'blue' }, { id: 'duo', category: '2FA / MFA', product: 'Duo Security', description: 'Users, phones, auth logs, groups, integrations across all child accounts', href: '/admin/sync/duo', logo: '/logos/duo.ico', color: 'green' }, + { id: 'pax8', category: 'Licensing', product: 'PAX8', description: 'Companies, subscriptions, products, and license billing', href: '/admin/sync/pax8', logo: '/logos/pax8.ico', color: 'blue' }, ]; const COLOR_MAP: Record = { @@ -59,16 +60,18 @@ export default function SyncOverviewPage() { const [mimecastData, setMimecastData] = useState(null); const [duoData, setDuoData] = useState(null); + const [pax8Data, setPax8Data] = useState(null); const fetchAll = async () => { try { - const [intRes, atRes, itgRes, s1Res, mcRes, duoRes] = await Promise.all([ + const [intRes, atRes, itgRes, s1Res, mcRes, duoRes, pax8Res] = await Promise.all([ fetch('/api/integrations/status'), fetch('/api/sync/last-sync'), fetch('/api/itglue/sync'), fetch('/api/sentinelone/sync'), fetch('/api/mimecast/status'), fetch('/api/duo/status'), + fetch('/api/pax8/sync'), ]); if (intRes.ok) setStatus(await intRes.json()); if (atRes.ok) { @@ -79,6 +82,7 @@ export default function SyncOverviewPage() { if (s1Res.ok) setS1SyncData(await s1Res.json()); if (mcRes.ok) setMimecastData(await mcRes.json()); if (duoRes.ok) setDuoData(await duoRes.json()); + if (pax8Res.ok) setPax8Data(await pax8Res.json()); } catch (e) { console.error(e); } finally { @@ -180,6 +184,16 @@ export default function SyncOverviewPage() { bypass: c.bypass ?? 0, }; } + if (id === 'pax8') { + if (!pax8Data) return null; + const h = pax8Data.history?.[0]; + return { + lastSync: h?.completed_at ?? null, + status: h?.status ?? null, + companies: Number(pax8Data.counts?.companies ?? 0), + subscriptions: Number(pax8Data.counts?.subscriptions ?? 0), + }; + } return null; }; @@ -219,6 +233,11 @@ export default function SyncOverviewPage() { if ((summary as any).bypass > 0) return ; return ; } + if (id === 'pax8') { + if (!summary.lastSync) return ; + if (summary.status === 'failed') return ; + return ; + } return ; }; @@ -411,6 +430,22 @@ export default function SyncOverviewPage() { )} )} + {intg.id === 'pax8' && summary && ( + <> +
+ Last sync + {fmtDate(summary.lastSync)} +
+
+ Companies + {(summary as any).companies?.toLocaleString()} +
+
+ Subscriptions + {(summary as any).subscriptions?.toLocaleString()} +
+ + )} {(intg.id === 'auvik' || intg.id === 'addigy') && (
Status diff --git a/public/logos/pax8.ico b/public/logos/pax8.ico new file mode 100644 index 0000000000000000000000000000000000000000..70f4d8c803db532829a7f83254520d3d77ad040e GIT binary patch literal 621 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!bOU@sT-^(NfJ`F6+HG;mflB#G zg8YIRzC6v}*)sKlppvgy(u&}pQ-c{87$17NIEF;DE}edRTeAX>i(tT>1^@r=bYE!e z=fM&7=~>jvW1dC}rIKSL;@V1|@zj^N7^J=Z`L}S#KJguK2fs1030E;D>nF%rG0x4k zU`%U&E^v4A2L=TtvCleg3^O{`e_(TH)9l@T^N*|_d(U(ZozsVA%;u7~_W7dYE`LsM z=WDzhzEvHo?9pVN*~Gyh6I}SfVD_Yw)y)k{1l6nR6`FuXOnq`^Q&z*{S4Y@hnyEKl zwg+;S2=dPf5;EG$$8ofA{x$znK~@FRSL%YQ3Y`a-k56d}yI;y6JTbaIEI!oXQV099 zYm2Y4%Nk5&fA-ULwSO+dP2HVYqS0XtE-c%Z<}#mX+?kbedL{3HyGl2TvY9-t7QHS! z;3Tck;V``?w;{mPEq4b^->$3 z)=Xc@Brr{qZ^r&k(}oRaPVJR1Ww?Lh$){iX-l7gJ1<`SOGfNNjC{^U|E3cAO$Vu7u zy_rkl^J)EA=jX0t-0-zYce{!cvw+~8X^|~l0%k_1cExOZ!60%_-Rk~JtphGUwr@9z zY`o&iVv@4coI&jo_Xqy}Y3?