Renamed the existing FP chart to "FP Finding Status" (counts findings per
workflow state) and added a new "FP Workflow Status" chart that counts
unique FP# ticket IDs per state — so 10 findings under one FP# ticket
counts as 1 ticket, not 10.
Backend: extractFPWorkflow now returns { id, state }; syncFPWorkflowCounts
builds both a finding-count map and a deduped FP# ID map, storing them in
separate columns (fp_workflow_counts_json, fp_id_counts_json). The endpoint
returns findingCounts/findingTotal and idCounts/idTotal.
Frontend: FPWorkflowDonut accepts a centerLabel prop; both donuts share the
same component fed with their respective data slices from the single fetch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
Adds a new SVG donut chart showing the distribution of FP workflow states
(Actionable, Requested, Reworked, Approved, Rejected, Expired, Unknown)
for all findings that have an associated FP# workflow ticket.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- New ivanti_finding_overrides table (finding_id, field, value) with
UNIQUE(finding_id, field) — same survival-across-sync pattern as notes
- PUT /api/ivanti/findings/:id/override (editor/admin only) — saves or
clears a field override; empty value = revert to Ivanti
- Overrides merged into findings at read time via readOverrides()
- Whitelisted fields: hostName, dns
Frontend:
- OverrideCell component — click to edit inline (editor/admin only),
Enter/blur to save, Escape to cancel
- Amber dot indicator on cells with an active local override
- Hover tooltip shows original Ivanti value when overridden
- RotateCcw button reverts cell back to Ivanti value in one click
- canWrite() gating via useAuth — viewers see the value, can't edit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace FP# Workflow chart with a 3-segment Action Coverage donut:
- FP Request — finding has an Ivanti FP# workflow
- Archer Exception — note matches EXC-\d+ pattern
- Pending — no action taken yet
Clicking a segment filters the findings table to that category with a
colored badge in the action bar (click again or × to clear).
Home page: each Archer ticket now has a filter icon button that navigates
directly to the Reporting page pre-filtered to findings whose notes
reference that EXC number. The EXC badge appears in the table action bar
with a one-click clear.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a second SVG donut chart showing the distribution of FP# workflow
states (Expired, Rejected, Reworked, Actionable, Requested, Approved,
No FP#) computed from the already-loaded findings array — no new API
calls or backend changes required.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend: adds ivanti_counts_cache table, fetches Closed count (page 0,
size 1) from Ivanti after each Open sync, and exposes GET /counts endpoint.
Frontend: replaces the Metrics placeholder with an SVG donut chart showing
Open vs Closed proportions with counts and percentages. Counts are fetched
on mount and refreshed after manual sync.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds an Export dropdown button to the Reporting page action bar.
Exports respect current filters, sort order, and column visibility.
CSV uses pure JS (UTF-8 BOM for Excel compatibility); XLSX uses SheetJS
with auto-fitted column widths.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Backend: only extract FP# workflows; SYS# auto-generated tickets
are no longer stored or shown (not actionable for triage purposes).
Findings with no FP# ticket show blank in the workflow column.
- Frontend: recolor workflow badges by action urgency —
Expired/Rejected = red (act now), Reworked/Actionable = amber
(resubmit), Requested = blue (waiting on approval).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Backend: extractFinding now flattens all workflowDistribution buckets
and prioritises FP# (False Positive) tickets over SYS# workflows.
Falls back to workflowGeneratedNames for FP# IDs not yet in distribution.
- Frontend: Add Workflow column (sortable, filterable) with state-coloured
badge (green=Approved, blue=Requested, amber=Reworked/Actionable,
red=Rejected, grey=Expired/unknown).
- Bump localStorage key to v2 so the new column appears on all clients
without needing a manual cache clear.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ID was already stored in the cache from f.id; exposed as a sortable
column (filterable: false — too many unique values to be useful as a filter).
Existing users get it appended to the end of their saved column order
via the loadColumnOrder merge logic; new users see it first.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CalendarWidget accepts onDateClick prop; due-date cells are clickable
with pointer cursor, red hover highlight, and updated tooltip
- App.js wires onDateClick: sets calendarFilter state and navigates to
the Reporting page
- NavDrawer navigation to Reporting clears calendarFilter so it only
applies on calendar-initiated navigation
- ReportingPage accepts filterDate prop; initializes columnFilters with
{ dueDate: Set([filterDate]) } so the view lands pre-filtered
- Existing Clear Filters button lets the user dismiss the filter normally
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Reporting page breaks out of max-w-7xl container to use full viewport width
- Table body scrolls within the panel (maxHeight: calc(100vh - 420px)) so you
no longer need to scroll the entire page to reach the horizontal scrollbar
- Column headers are sticky (position: sticky, top 0) with opaque background
so they remain visible while scrolling vertically through findings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Backend extracts cves[] array from f.vulnerabilities.vulnInfoList[].cve
- Frontend shows up to 2 CVE badges (purple) with "+N more" overflow tooltip
- Filter is multi-value aware: selecting a CVE matches any finding containing it
- FilterDropdown expands multi-value arrays into individual checkbox options
- Sort by CVE count (number of associated CVEs)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- buOwnership field extracted from assetCustomAttributes['1550_host_1'][0]
and stored in SQLite cache; badge-styled cell (sky=STEAM, amber=ACCESS-ENG)
- All columns except Notes get a funnel filter button in the header
- FilterDropdown uses ReactDOM.createPortal + fixed positioning to escape
overflowX:auto clipping; shows unique value checkboxes with search input,
Select All, Clear, and a selected/total count footer
- Severity filter groups by vrrGroup label (CRITICAL/HIGH) not numeric value
- columnFilters state gates a useMemo filtered array before sorting
- Active filter count shown in panel header with amber badge; Clear Filters
button appears in the toolbar when any filters are active
- Empty Set filter (Clear All) hides all rows, consistent with Excel
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- Extract dueDate from statusEmbedded.dueDate (strip time portion)
- Remove discoveredOn and source from extractFinding (not needed)
Frontend:
- Add Due Date column (color-coded: red=past due, amber=within 30d, gray=future)
- Remove Discovered and Source columns
- ColumnManager component: gear button opens popover with drag-to-reorder and
eye toggle per column; column state persisted to localStorage
- Column order/visibility survives page refresh and syncs
- SortIcon, TableCell, NoteCell all driven by current visible column list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- NavDrawer component: slide-in left drawer with backdrop, matches dark theme
- Nav items: Home, Reporting, Knowledge Base, Exports with color-coded icons
- Active page highlighted with colored background + indicator dot
- Placeholder pages for Reporting (amber), Knowledge Base (green), Exports (purple)
- Stats bar and three-column layout conditionally render on Home page only
- currentPage state drives all page switching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>