feat: per-BU trend lines in counts history chart

- Create ivanti_counts_history_by_bu table (bu_ownership, state, count per sync)
- Sync writes per-BU snapshot alongside global history on each sync
- Seed table with current counts for immediate first data point
- GET /counts/history accepts ?teams param — queries per-BU table when filtered
- IvantiCountsChart accepts teamsParam prop, re-fetches on scope change
- ReportingPage passes getActiveTeamsParam() to the chart
- Historical per-BU data accumulates from this point forward
- Global history (no filter) still uses the original aggregate table
This commit is contained in:
Jordan Ramos
2026-05-06 13:38:38 -06:00
parent 77f113e9ae
commit 573903a885
5 changed files with 87 additions and 5 deletions

View File

@@ -5187,8 +5187,16 @@ export default function VulnerabilityTriagePage({ filterDate, filterEXC }) {
}, []); // eslint-disable-line
// Re-fetch counts when admin scope changes (per-BU counts from Postgres)
// Silent fetch — no loading spinner, just update the numbers
useEffect(() => {
fetchCounts();
const teamsParam = getActiveTeamsParam();
const url = teamsParam
? `${API_BASE}/ivanti/findings/counts?teams=${encodeURIComponent(teamsParam)}`
: `${API_BASE}/ivanti/findings/counts`;
fetch(url, { credentials: 'include' })
.then(r => r.ok ? r.json() : null)
.then(data => { if (data) setStatusCounts({ open: data.open ?? 0, closed: data.closed ?? 0 }); })
.catch(() => {});
}, [adminScope]); // eslint-disable-line
// Set/clear a single column filter
@@ -5786,7 +5794,7 @@ export default function VulnerabilityTriagePage({ filterDate, filterEXC }) {
Panel 1.5 — Open vs Closed trend over time
---------------------------------------------------------------- */}
{metricsTab === 'ivanti' && <AnomalyBanner />}
{metricsTab === 'ivanti' && <IvantiCountsChart />}
{metricsTab === 'ivanti' && <IvantiCountsChart teamsParam={getActiveTeamsParam()} />}
{/* ----------------------------------------------------------------
Panel 2 — Findings table