From 4e8f4cbb10ce858fa5df46529c35aab0398310d1 Mon Sep 17 00:00:00 2001 From: Jordan Ramos Date: Wed, 3 Jun 2026 13:55:10 -0600 Subject: [PATCH] Allow redirecting pending queue items in place without duplicating Previously, redirecting a queue item required completing it first, which created a duplicate entry. Now: - Pending items: redirect updates workflow_type in place (no new row) - Completed items: still creates a new pending item (legacy behavior) - Redirect arrow now visible on all items, not just completed ones - Frontend handles in-place updates by replacing the item in state --- backend/routes/ivantiTodoQueue.js | 44 ++++++++++++++++--- .../src/components/pages/ReportingPage.js | 24 ++++++---- 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/backend/routes/ivantiTodoQueue.js b/backend/routes/ivantiTodoQueue.js index 59e1eec..534d554 100644 --- a/backend/routes/ivantiTodoQueue.js +++ b/backend/routes/ivantiTodoQueue.js @@ -318,16 +318,17 @@ function createIvantiTodoQueueRouter() { /** * POST /api/ivanti/todo-queue/:id/redirect * - * Redirects a completed queue item to a different workflow by creating a new - * pending queue item with the same finding data but a new workflow type/vendor. + * Redirects a queue item to a different workflow type. If the item is pending, + * updates workflow_type in place. If the item is complete, creates a new pending + * queue item with the same finding data but a new workflow type/vendor. * Requires Admin or Standard_User group. * - * @param {string} id — Queue item ID of the completed item (URL parameter) + * @param {string} id — Queue item ID (URL parameter) * @body {Object} * - workflow_type {string} Required. One of: FP, Archer, CARD, GRANITE, DECOM * - vendor {string} Required for FP, Archer, and DECOM workflows; max 200 chars - * @returns {Object} The newly created queue item with parsed `cves` array - * @error 400 Invalid input or item not in complete status + * @returns {Object} The updated or newly created queue item with parsed `cves` array + * @error 400 Invalid input * @error 404 Queue item not found * @error 500 Internal server error */ @@ -358,10 +359,38 @@ function createIvantiTodoQueueRouter() { if (!original) { return res.status(404).json({ error: 'Queue item not found.' }); } - if (original.status !== 'complete') { - return res.status(400).json({ error: 'Only completed queue items can be redirected.' }); + + // If the item is still pending, update workflow_type in place (no duplication) + if (original.status === 'pending') { + const { rows } = await pool.query( + `UPDATE ivanti_todo_queue SET workflow_type = $1, vendor = $2, updated_at = NOW() + WHERE id = $3 AND user_id = $4 RETURNING *`, + [workflow_type, vendorVal, id, req.user.id] + ); + + logAudit({ + userId: req.user.id, + username: req.user.username, + action: 'queue_item_redirected', + entityType: 'ivanti_todo_queue', + entityId: String(original.id), + details: { + original_workflow_type: original.workflow_type, + target_workflow_type: workflow_type, + method: 'in_place_update', + vendor: vendorVal, + }, + ipAddress: req.ip, + }); + + const result = { + ...rows[0], + cves: rows[0].cves_json ? JSON.parse(rows[0].cves_json) : [], + }; + return res.json(result); } + // If the item is complete, create a new pending item (legacy behavior) const { rows } = await pool.query( `INSERT INTO ivanti_todo_queue (user_id, finding_id, finding_title, cves_json, ip_address, hostname, vendor, workflow_type) @@ -379,6 +408,7 @@ function createIvantiTodoQueueRouter() { details: { original_workflow_type: original.workflow_type, target_workflow_type: workflow_type, + method: 'new_item_from_complete', new_item_id: rows[0].id, vendor: vendorVal, }, diff --git a/frontend/src/components/pages/ReportingPage.js b/frontend/src/components/pages/ReportingPage.js index dfa1d10..1cd080a 100644 --- a/frontend/src/components/pages/ReportingPage.js +++ b/frontend/src/components/pages/ReportingPage.js @@ -2002,13 +2002,13 @@ function QueuePanel({ open, items, onClose, onUpdate, onDelete, onDeleteMany, on )} - {/* Redirect button — completed items only */} - {canWrite && done && ( + {/* Redirect button — available on all items */} + {canWrite && (