fix(reporting): source FP workflow status chart from DB instead of open-findings cache
The FP Workflow Status donut was reading from the in-memory open findings array, so Approved FPs (which close the finding and remove it from the open cache) were invisible. Backend: during each sync, compute FP workflow state counts from open findings then sweep all pages of closed findings to capture Approved (and any other closed-state) FP workflows. Counts are stored in a new fp_workflow_counts_json column on ivanti_counts_cache and exposed via GET /api/ivanti/findings/fp-workflow-counts. Frontend: FPWorkflowDonut now receives counts/total props from the new endpoint (fetched on load and refreshed after manual sync) instead of deriving them from the findings prop. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -398,28 +398,13 @@ const FP_WORKFLOW_DEFS = [
|
||||
{ key: 'Unknown', label: 'Unknown', color: '#334155' },
|
||||
];
|
||||
|
||||
function FPWorkflowDonut({ findings }) {
|
||||
function FPWorkflowDonut({ counts, total }) {
|
||||
const SIZE = 180;
|
||||
const CX = SIZE / 2;
|
||||
const CY = SIZE / 2;
|
||||
const OUTER = 72;
|
||||
const INNER = 48;
|
||||
|
||||
const counts = useMemo(() => {
|
||||
const map = {};
|
||||
FP_WORKFLOW_DEFS.forEach(d => { map[d.key] = 0; });
|
||||
findings.forEach((f) => {
|
||||
if (!f.workflow) return;
|
||||
const state = f.workflow.state || '';
|
||||
const def = FP_WORKFLOW_DEFS.find(d => d.key.toLowerCase() === state.toLowerCase());
|
||||
const key = def ? def.key : 'Unknown';
|
||||
map[key] = (map[key] || 0) + 1;
|
||||
});
|
||||
return map;
|
||||
}, [findings]);
|
||||
|
||||
const total = Object.values(counts).reduce((a, b) => a + b, 0);
|
||||
|
||||
if (total === 0) {
|
||||
return (
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: `${SIZE}px` }}>
|
||||
@@ -1091,6 +1076,7 @@ export default function ReportingPage({ filterDate, filterEXC }) {
|
||||
const [syncing, setSyncing] = useState(false);
|
||||
const [statusCounts, setStatusCounts] = useState({ open: 0, closed: 0 });
|
||||
const [countsLoading, setCountsLoading] = useState(true);
|
||||
const [fpWorkflowCounts, setFPWorkflowCounts] = useState({ counts: {}, total: 0 });
|
||||
const [sort, setSort] = useState({ field: 'severity', dir: 'desc' });
|
||||
const [columnOrder, setColumnOrder] = useState(loadColumnOrder);
|
||||
const [columnFilters, setColumnFilters] = useState(() =>
|
||||
@@ -1127,6 +1113,16 @@ export default function ReportingPage({ filterDate, filterEXC }) {
|
||||
}
|
||||
};
|
||||
|
||||
const fetchFPWorkflowCounts = async () => {
|
||||
try {
|
||||
const res = await fetch(`${API_BASE}/ivanti/findings/fp-workflow-counts`, { credentials: 'include' });
|
||||
const data = await res.json();
|
||||
if (res.ok) setFPWorkflowCounts({ counts: data.counts || {}, total: data.total || 0 });
|
||||
} catch (e) {
|
||||
console.error('Error loading FP workflow counts:', e);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchFindings = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
@@ -1147,7 +1143,8 @@ export default function ReportingPage({ filterDate, filterEXC }) {
|
||||
const data = await res.json();
|
||||
if (res.ok) {
|
||||
applyState(data);
|
||||
fetchCounts(); // refresh counts after sync
|
||||
fetchCounts(); // refresh counts after sync
|
||||
fetchFPWorkflowCounts(); // refresh FP workflow counts after sync
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error syncing findings:', e);
|
||||
@@ -1159,6 +1156,7 @@ export default function ReportingPage({ filterDate, filterEXC }) {
|
||||
useEffect(() => {
|
||||
fetchFindings();
|
||||
fetchCounts();
|
||||
fetchFPWorkflowCounts();
|
||||
}, []); // eslint-disable-line
|
||||
|
||||
// Set/clear a single column filter
|
||||
@@ -1361,7 +1359,7 @@ export default function ReportingPage({ filterDate, filterEXC }) {
|
||||
<div style={{ fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: '0.75rem' }}>
|
||||
FP Workflow Status
|
||||
</div>
|
||||
<FPWorkflowDonut findings={findings} />
|
||||
<FPWorkflowDonut counts={fpWorkflowCounts.counts} total={fpWorkflowCounts.total} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user