Add Granite Loader Sheet generator with CARD enrichment
Implement the Granite Team_Device Loader xlsx export feature: - Add graniteLoaderConfig.js with all 41 columns, groupings, and operation-type requirements (Change/Add/Delete/Move) - Add graniteLoaderExport.js for client-side xlsx generation using the xlsx library - Add LoaderModal component with operation type selection, column checkboxes, bulk defaults with per-row overrides, editable preview table, CARD enrichment integration, and standalone paste-IPs mode - Add POST /api/card/enrich-batch endpoint for batch IP lookup in CARD returning EQUIP_INST_ID, hostname, site, ASN, team - Integrate 'Generate Loader Sheet' button in Ivanti Queue floating action bar (visible when CARD/GRANITE/DECOM items selected) - Add card-connectivity-test.js script for verifying CARD API access
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import { ListTodo, RefreshCw, CheckSquare, Square, Loader, AlertCircle, X, Plus, CheckCircle, ChevronDown, ChevronRight } from 'lucide-react';
|
||||
import { ListTodo, RefreshCw, CheckSquare, Square, Loader, AlertCircle, X, Plus, CheckCircle, ChevronDown, ChevronRight, FileSpreadsheet } from 'lucide-react';
|
||||
import { useAuth } from '../../contexts/AuthContext';
|
||||
import ConsolidationModal from '../ConsolidationModal';
|
||||
import LoaderModal from '../LoaderModal';
|
||||
import { generateConsolidatedSummary, generateConsolidatedDescription, extractFirstCve, extractCommonVendor } from '../../utils/jiraConsolidation';
|
||||
import { groupQueueItems } from '../../utils/queueGrouping';
|
||||
import { VENDOR_PROJECT_KEYS, VENDOR_ISSUE_TYPES, STEAM_ISSUE_TYPES, isVendorProject, getIssueTypesForProject } from './JiraPage';
|
||||
@@ -300,6 +301,7 @@ export default function IvantiTodoQueuePage() {
|
||||
|
||||
// Single-item Jira creation modal state (Requirement 2.4)
|
||||
const [showSingleJiraModal, setShowSingleJiraModal] = useState(false);
|
||||
const [showLoaderModal, setShowLoaderModal] = useState(false);
|
||||
const [singleJiraItem, setSingleJiraItem] = useState(null);
|
||||
const [singleJiraForm, setSingleJiraForm] = useState({ cve_id: '', vendor: '', summary: '', description: '', source_context: 'ivanti_queue', project_key: '', issue_type: '' });
|
||||
const [singleJiraError, setSingleJiraError] = useState(null);
|
||||
@@ -908,6 +910,20 @@ export default function IvantiTodoQueuePage() {
|
||||
<Plus style={{ width: '14px', height: '14px' }} />
|
||||
Create Jira Ticket
|
||||
</button>
|
||||
{(() => {
|
||||
const selectedItems = queueItems.filter(i => selectedIds.has(i.id));
|
||||
const hasCardGranite = selectedItems.some(i => ['CARD', 'GRANITE', 'DECOM'].includes(i.workflow_type));
|
||||
return hasCardGranite ? (
|
||||
<button
|
||||
onClick={() => setShowLoaderModal(true)}
|
||||
style={STYLES.btnSuccess}
|
||||
title="Generate Granite Team_Device Loader Sheet from selected items"
|
||||
>
|
||||
<FileSpreadsheet style={{ width: '14px', height: '14px' }} />
|
||||
Generate Loader Sheet
|
||||
</button>
|
||||
) : null;
|
||||
})()}
|
||||
<button
|
||||
onClick={cancelSelection}
|
||||
style={STYLES.btnCancel}
|
||||
@@ -1014,6 +1030,13 @@ export default function IvantiTodoQueuePage() {
|
||||
onSuccess={handleConsolidationSuccess}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Granite Loader Sheet Modal */}
|
||||
<LoaderModal
|
||||
isOpen={showLoaderModal}
|
||||
onClose={() => setShowLoaderModal(false)}
|
||||
initialDevices={showLoaderModal ? queueItems.filter(i => selectedIds.has(i.id) && ['CARD', 'GRANITE', 'DECOM'].includes(i.workflow_type)).map(i => ({ ip_address: i.ip_address || '', hostname: i.hostname || '' })) : null}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user