Append remediation notes to Jira ticket description in QueuePanel

- Single-item: openCreateJiraFromQueue fetches notes for Remediate items
  and pre-fills the description with a Remediation Notes section
- Multi-item: ConsolidationModal fetches notes for all Remediate items
  and appends them via appendRemediationNotes utility

Previously notes were only integrated in IvantiTodoQueuePage.js but
the actual Jira creation flow users interact with is in ReportingPage.js
QueuePanel and ConsolidationModal.
This commit is contained in:
Jordan Ramos
2026-06-08 14:59:48 -06:00
parent 889d4658e5
commit 3b5dfee235
2 changed files with 46 additions and 3 deletions

View File

@@ -5,6 +5,7 @@ import {
generateConsolidatedDescription,
extractFirstCve,
extractCommonVendor,
appendRemediationNotes,
} from '../utils/jiraConsolidation';
const API_BASE = process.env.REACT_APP_API_BASE || 'http://localhost:3001/api';
@@ -285,9 +286,31 @@ export default function ConsolidationModal({ items, onClose, onSuccess }) {
useEffect(() => {
if (items.length >= 2) {
setSummary(generateConsolidatedSummary(items));
setDescription(generateConsolidatedDescription(items));
setCveId(extractFirstCve(items));
setVendor(extractCommonVendor(items));
// Build description, appending remediation notes for Remediate items
const baseDescription = generateConsolidatedDescription(items);
const remediateItems = items.filter(i => i.workflow_type === 'Remediate');
if (remediateItems.length > 0) {
Promise.all(
remediateItems.map(item =>
fetch(`${API_BASE}/ivanti/todo-queue/${item.id}/notes`, { credentials: 'include' })
.then(r => r.ok ? r.json() : [])
.catch(() => [])
)
).then(results => {
const notesMap = {};
remediateItems.forEach((item, idx) => {
if (results[idx] && results[idx].length > 0) {
notesMap[item.id] = results[idx];
}
});
setDescription(appendRemediationNotes(baseDescription, notesMap));
});
} else {
setDescription(baseDescription);
}
}
}, [items]);

View File

@@ -1759,7 +1759,7 @@ function QueuePanel({ open, items, onClose, onUpdate, onDelete, onDeleteMany, on
};
// Open Create Jira modal pre-populated from a queue item
const openCreateJiraFromQueue = (item) => {
const openCreateJiraFromQueue = async (item) => {
// Parse cves_json — it may be a JSON string or already an array
let cves = [];
if (item.cves_json) {
@@ -1769,12 +1769,32 @@ function QueuePanel({ open, items, onClose, onUpdate, onDelete, onDeleteMany, on
}
const firstCve = (Array.isArray(cves) && cves.length > 0) ? cves[0] : '';
const summary = (item.finding_title || '').slice(0, 255);
// Build description — include remediation notes for Remediate items
let description = '';
if (item.workflow_type === 'Remediate') {
try {
const notesRes = await fetch(`${API_BASE}/ivanti/todo-queue/${item.id}/notes`, { credentials: 'include' });
if (notesRes.ok) {
const notes = await notesRes.json();
if (notes.length > 0) {
const sorted = [...notes].sort((a, b) => new Date(a.created_at) - new Date(b.created_at));
description = '== Remediation Notes ==\n';
for (const note of sorted) {
const date = note.created_at ? note.created_at.slice(0, 10) : 'Unknown';
description += `[${date}] ${note.username}: ${note.note_text}\n`;
}
}
}
} catch (_) { /* best-effort */ }
}
setCreateJiraForm({
summary,
cve_id: firstCve,
vendor: item.vendor || '',
source_context: 'ivanti_queue',
description: '',
description,
project_key: '',
issue_type: '',
});