Fix Action Coverage donut not updating when notes change
NoteCell now propagates saved notes back to the findings state via onNoteSaved callback. This allows classifyFinding() to immediately reclassify items from 'pending' to 'archer' when an EXC- note is added, updating the Action Coverage donut without a page refresh.
This commit is contained in:
@@ -860,7 +860,7 @@ function OverrideCell({ findingId, field, originalValue, initialOverride, canWri
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// NoteCell — inline editable, saves on blur
|
// NoteCell — inline editable, saves on blur
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
function NoteCell({ findingId, initialNote }) {
|
function NoteCell({ findingId, initialNote, onNoteSaved }) {
|
||||||
const [value, setValue] = useState(initialNote || '');
|
const [value, setValue] = useState(initialNote || '');
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const lastSaved = useRef(initialNote || '');
|
const lastSaved = useRef(initialNote || '');
|
||||||
@@ -881,12 +881,13 @@ function NoteCell({ findingId, initialNote }) {
|
|||||||
body: JSON.stringify({ note: value })
|
body: JSON.stringify({ note: value })
|
||||||
});
|
});
|
||||||
lastSaved.current = value;
|
lastSaved.current = value;
|
||||||
|
if (onNoteSaved) onNoteSaved(findingId, value);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to save note:', e);
|
console.error('Failed to save note:', e);
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
}
|
}
|
||||||
}, [findingId, value]);
|
}, [findingId, value, onNoteSaved]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ position: 'relative' }}>
|
<div style={{ position: 'relative' }}>
|
||||||
@@ -1188,7 +1189,7 @@ function FilterDropdown({ anchorEl, colKey, findings, activeFilter, onFilterChan
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Render a single table cell by column key
|
// Render a single table cell by column key
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
function TableCell({ colKey, finding, canWrite, onCveMouseEnter, onCveMouseLeave, onIpMouseEnter, onIpMouseLeave, fpSubmissions, onEditSubmission, atlasStatusMap, onAtlasBadgeClick }) {
|
function TableCell({ colKey, finding, canWrite, onCveMouseEnter, onCveMouseLeave, onIpMouseEnter, onIpMouseLeave, fpSubmissions, onEditSubmission, atlasStatusMap, onAtlasBadgeClick, onNoteSaved }) {
|
||||||
switch (colKey) {
|
switch (colKey) {
|
||||||
case 'findingId':
|
case 'findingId':
|
||||||
return (
|
return (
|
||||||
@@ -1351,7 +1352,7 @@ function TableCell({ colKey, finding, canWrite, onCveMouseEnter, onCveMouseLeave
|
|||||||
case 'note':
|
case 'note':
|
||||||
return (
|
return (
|
||||||
<td style={{ padding: '0.45rem 0.75rem' }}>
|
<td style={{ padding: '0.45rem 0.75rem' }}>
|
||||||
<NoteCell findingId={finding.id} initialNote={finding.note} />
|
<NoteCell findingId={finding.id} initialNote={finding.note} onNoteSaved={onNoteSaved} />
|
||||||
</td>
|
</td>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
@@ -6413,6 +6414,10 @@ export default function VulnerabilityTriagePage({ filterDate, filterEXC }) {
|
|||||||
setEditSubmission(submission);
|
setEditSubmission(submission);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleNoteSaved = useCallback((findingId, note) => {
|
||||||
|
setFindings(prev => prev.map(f => f.id === findingId ? { ...f, note } : f));
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleEditSuccess = useCallback(() => {
|
const handleEditSuccess = useCallback(() => {
|
||||||
fetchFpSubmissions();
|
fetchFpSubmissions();
|
||||||
fetchQueue();
|
fetchQueue();
|
||||||
@@ -7331,7 +7336,7 @@ export default function VulnerabilityTriagePage({ filterDate, filterEXC }) {
|
|||||||
<input type="checkbox" readOnly checked={queued || isSelected} style={{ accentColor: queued ? '#10B981' : '#0EA5E9', width: '13px', height: '13px', cursor: queued ? 'default' : 'pointer', pointerEvents: 'none' }} />
|
<input type="checkbox" readOnly checked={queued || isSelected} style={{ accentColor: queued ? '#10B981' : '#0EA5E9', width: '13px', height: '13px', cursor: queued ? 'default' : 'pointer', pointerEvents: 'none' }} />
|
||||||
</td>
|
</td>
|
||||||
{visibleCols.map((col) => (
|
{visibleCols.map((col) => (
|
||||||
<TableCell key={col.key} colKey={col.key} finding={finding} canWrite={canWrite()} onCveMouseEnter={handleCveMouseEnter} onCveMouseLeave={handleCveMouseLeave} onIpMouseEnter={handleIpMouseEnter} onIpMouseLeave={handleIpMouseLeave} fpSubmissions={fpSubmissions} onEditSubmission={handleEditSubmission} atlasStatusMap={atlasStatusMap} onAtlasBadgeClick={(hostId) => { setAtlasSelectedHostId(hostId); setAtlasSelectedHostName(finding.hostName || finding.ipAddress || ''); setAtlasSelectedFindingId(finding.id || null); setAtlasPanelOpen(true); }} />
|
<TableCell key={col.key} colKey={col.key} finding={finding} canWrite={canWrite()} onCveMouseEnter={handleCveMouseEnter} onCveMouseLeave={handleCveMouseLeave} onIpMouseEnter={handleIpMouseEnter} onIpMouseLeave={handleIpMouseLeave} fpSubmissions={fpSubmissions} onEditSubmission={handleEditSubmission} atlasStatusMap={atlasStatusMap} onAtlasBadgeClick={(hostId) => { setAtlasSelectedHostId(hostId); setAtlasSelectedHostName(finding.hostName || finding.ipAddress || ''); setAtlasSelectedFindingId(finding.id || null); setAtlasPanelOpen(true); }} onNoteSaved={handleNoteSaved} />
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
@@ -7439,7 +7444,7 @@ export default function VulnerabilityTriagePage({ filterDate, filterEXC }) {
|
|||||||
<input type="checkbox" readOnly checked={queued || isSelected} style={{ accentColor: queued ? '#10B981' : '#0EA5E9', width: '13px', height: '13px', cursor: queued ? 'default' : 'pointer', pointerEvents: 'none' }} />
|
<input type="checkbox" readOnly checked={queued || isSelected} style={{ accentColor: queued ? '#10B981' : '#0EA5E9', width: '13px', height: '13px', cursor: queued ? 'default' : 'pointer', pointerEvents: 'none' }} />
|
||||||
</td>
|
</td>
|
||||||
{visibleCols.map((col) => (
|
{visibleCols.map((col) => (
|
||||||
<TableCell key={col.key} colKey={col.key} finding={finding} canWrite={canWrite()} onCveMouseEnter={handleCveMouseEnter} onCveMouseLeave={handleCveMouseLeave} onIpMouseEnter={handleIpMouseEnter} onIpMouseLeave={handleIpMouseLeave} fpSubmissions={fpSubmissions} onEditSubmission={handleEditSubmission} atlasStatusMap={atlasStatusMap} onAtlasBadgeClick={(hostId) => { setAtlasSelectedHostId(hostId); setAtlasSelectedHostName(finding.hostName || finding.ipAddress || ''); setAtlasSelectedFindingId(finding.id || null); setAtlasPanelOpen(true); }} />
|
<TableCell key={col.key} colKey={col.key} finding={finding} canWrite={canWrite()} onCveMouseEnter={handleCveMouseEnter} onCveMouseLeave={handleCveMouseLeave} onIpMouseEnter={handleIpMouseEnter} onIpMouseLeave={handleIpMouseLeave} fpSubmissions={fpSubmissions} onEditSubmission={handleEditSubmission} atlasStatusMap={atlasStatusMap} onAtlasBadgeClick={(hostId) => { setAtlasSelectedHostId(hostId); setAtlasSelectedHostName(finding.hostName || finding.ipAddress || ''); setAtlasSelectedFindingId(finding.id || null); setAtlasPanelOpen(true); }} onNoteSaved={handleNoteSaved} />
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
@@ -7540,7 +7545,7 @@ export default function VulnerabilityTriagePage({ filterDate, filterEXC }) {
|
|||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
{visibleCols.map((col) => (
|
{visibleCols.map((col) => (
|
||||||
<TableCell key={col.key} colKey={col.key} finding={finding} canWrite={canWrite()} onCveMouseEnter={handleCveMouseEnter} onCveMouseLeave={handleCveMouseLeave} onIpMouseEnter={handleIpMouseEnter} onIpMouseLeave={handleIpMouseLeave} fpSubmissions={fpSubmissions} onEditSubmission={handleEditSubmission} atlasStatusMap={atlasStatusMap} onAtlasBadgeClick={(hostId) => { setAtlasSelectedHostId(hostId); setAtlasSelectedHostName(finding.hostName || finding.ipAddress || ''); setAtlasSelectedFindingId(finding.id || null); setAtlasPanelOpen(true); }} />
|
<TableCell key={col.key} colKey={col.key} finding={finding} canWrite={canWrite()} onCveMouseEnter={handleCveMouseEnter} onCveMouseLeave={handleCveMouseLeave} onIpMouseEnter={handleIpMouseEnter} onIpMouseLeave={handleIpMouseLeave} fpSubmissions={fpSubmissions} onEditSubmission={handleEditSubmission} atlasStatusMap={atlasStatusMap} onAtlasBadgeClick={(hostId) => { setAtlasSelectedHostId(hostId); setAtlasSelectedHostName(finding.hostName || finding.ipAddress || ''); setAtlasSelectedFindingId(finding.id || null); setAtlasPanelOpen(true); }} onNoteSaved={handleNoteSaved} />
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user