Fix Remediate integration in QueuePanel on Reporting Page

- Add remediationModalItem state to QueuePanel
- Render RemediationModal from QueuePanel for notes access
- Add Remediate color to wfColor mapping in renderQueueItem
- Add Remediate option to SelectionToolbar workflow type buttons

The Notes button and Remediate workflow option were only added to the
standalone IvantiTodoQueuePage but not the QueuePanel slide-out on the
Reporting Page, which is the primary interface for queue interaction.
This commit is contained in:
Jordan Ramos
2026-06-08 14:36:50 -06:00
parent 6c7b8cb2fa
commit 889d4658e5

View File

@@ -9,6 +9,7 @@ import CveTooltip from '../CveTooltip';
import CardOwnerTooltip from '../CardOwnerTooltip';
import CardDetailModal from '../CardDetailModal';
import RedirectModal from '../RedirectModal';
import RemediationModal from '../RemediationModal';
import AtlasBadge from '../AtlasBadge';
import LoaderModal from '../LoaderModal';
import CardActionModal from '../CardActionModal';
@@ -1558,6 +1559,9 @@ function QueuePanel({ open, items, onClose, onUpdate, onDelete, onDeleteMany, on
const [createJiraOpen, setCreateJiraOpen] = useState(false);
const [createJiraForm, setCreateJiraForm] = useState({ summary: '', cve_id: '', vendor: '', source_context: 'ivanti_queue', description: '', project_key: '', issue_type: '' });
const [createJiraError, setCreateJiraError] = useState(null);
// Remediation Modal state
const [remediationModalItem, setRemediationModalItem] = useState(null);
const [createJiraSaving, setCreateJiraSaving] = useState(false);
const [createJiraSummaryError, setCreateJiraSummaryError] = useState(null);
@@ -1819,6 +1823,7 @@ function QueuePanel({ open, items, onClose, onUpdate, onDelete, onDeleteMany, on
: item.workflow_type === 'Archer' ? { col: '#0EA5E9', rgb: '14,165,233' }
: item.workflow_type === 'GRANITE' ? { col: '#A1887F', rgb: '161,136,127' }
: item.workflow_type === 'DECOM' ? { col: '#EF4444', rgb: '239,68,68' }
: item.workflow_type === 'Remediate' ? { col: '#A855F7', rgb: '168,85,247' }
: { col: '#10B981', rgb: '16,185,129' };
const cves = item.cves || [];
const cveDisplay = cves.length > 0
@@ -2010,6 +2015,53 @@ function QueuePanel({ open, items, onClose, onUpdate, onDelete, onDeleteMany, on
</div>
)}
{/* Remediation Notes button — Remediate items only */}
{item.workflow_type === 'Remediate' && (
<button
onClick={() => setRemediationModalItem(item)}
style={{
background: 'rgba(168, 85, 247, 0.08)',
border: '1px solid rgba(168, 85, 247, 0.25)',
borderRadius: '0.2rem',
padding: '0.15rem 0.35rem',
cursor: 'pointer',
display: 'inline-flex',
alignItems: 'center',
gap: '0.2rem',
color: '#C084FC',
fontFamily: 'monospace',
fontSize: '0.55rem',
fontWeight: '700',
textTransform: 'uppercase',
letterSpacing: '0.04em',
flexShrink: 0,
transition: 'all 0.12s',
position: 'relative',
}}
onMouseEnter={(e) => { e.currentTarget.style.background = 'rgba(168, 85, 247, 0.18)'; e.currentTarget.style.borderColor = 'rgba(168, 85, 247, 0.45)'; }}
onMouseLeave={(e) => { e.currentTarget.style.background = 'rgba(168, 85, 247, 0.08)'; e.currentTarget.style.borderColor = 'rgba(168, 85, 247, 0.25)'; }}
title="View remediation notes"
>
<FileText style={{ width: '10px', height: '10px' }} />
Notes
{(item.remediation_notes_count || 0) > 0 && (
<span style={{
fontFamily: 'monospace',
fontSize: '0.5rem',
fontWeight: 700,
color: '#A855F7',
background: 'rgba(168, 85, 247, 0.15)',
border: '1px solid rgba(168, 85, 247, 0.3)',
borderRadius: '999px',
padding: '0 0.25rem',
marginLeft: '0.1rem',
}}>
{item.remediation_notes_count > 99 ? '99+' : item.remediation_notes_count}
</span>
)}
</button>
)}
{/* Redirect button — available on all items */}
{canWrite && (
<button
@@ -2959,6 +3011,17 @@ function QueuePanel({ open, items, onClose, onUpdate, onDelete, onDeleteMany, on
/>
)}
{/* Remediation Notes modal */}
{remediationModalItem && (
<RemediationModal
item={remediationModalItem}
onClose={() => setRemediationModalItem(null)}
onNoteAdded={() => {
if (onQueueRefresh) onQueueRefresh();
}}
/>
)}
{/* Create Jira Ticket modal */}
{createJiraOpen && (
<div style={{ position: 'fixed', inset: 0, zIndex: 10100, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
@@ -3236,8 +3299,11 @@ function AttachmentSourcePicker({ files, onFilesChange, libraryDocs, onLibraryDo
const debounceRef = useRef(null);
// Format file size helper
const formatSize = (bytes) => {
const n = Number(bytes);
const formatSize = (val) => {
if (!val && val !== 0) return '0 B';
// If already a formatted string (e.g. "12.34 KB"), return as-is
if (typeof val === 'string' && /[A-Za-z]/.test(val)) return val;
const n = Number(val);
if (isNaN(n) || n < 0) return '0 B';
if (n < 1024) return n + ' B';
if (n < 1024 * 1024) return (n / 1024).toFixed(1) + ' KB';
@@ -5009,6 +5075,7 @@ function SelectionToolbar({ count, workflowType, vendor, submitting, error, onWo
{ type: 'CARD', color: '#10B981', rgb: '16,185,129' },
{ type: 'GRANITE', color: '#A1887F', rgb: '161,136,127' },
{ type: 'DECOM', color: '#EF4444', rgb: '239,68,68' },
{ type: 'Remediate', color: '#A855F7', rgb: '168,85,247' },
].map(({ type, color, rgb }) => {
const active = workflowType === type;
return (