/** * Queue grouping utility — extracts the hybrid Inventory + vendor grouping logic * from IvantiTodoQueuePage into a testable pure function. * * Spec: .kiro/specs/queue-collapsible-sections * Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7 */ const INVENTORY_TYPES = new Set(['CARD', 'GRANITE', 'DECOM']); /** * Groups visible queue items into the hybrid section layout. * * @param {Array} visibleItems - Queue items with status 'pending' * @returns {Array<{key: string, label: string, type: string, items: Array}>} * * Rules: * - Items with workflow_type CARD, GRANITE, or DECOM → Inventory section * - Items with workflow_type FP or Archer → grouped by vendor field * - Items with null/undefined/empty vendor → placed in "Unknown" vendor section * - Inventory section appears first (if non-empty) * - Vendor sections sorted alphabetically by label * - Sections with zero items are omitted from output */ export function groupQueueItems(visibleItems) { const inventoryItems = []; const vendorMap = new Map(); for (const item of visibleItems) { if (INVENTORY_TYPES.has(item.workflow_type)) { inventoryItems.push(item); } else { const vendor = item.vendor?.trim() || 'Unknown'; if (!vendorMap.has(vendor)) vendorMap.set(vendor, []); vendorMap.get(vendor).push(item); } } const sections = []; if (inventoryItems.length > 0) { sections.push({ key: 'inventory', label: 'Inventory', type: 'inventory', items: inventoryItems, }); } const sortedVendors = [...vendorMap.entries()] .sort((a, b) => a[0].localeCompare(b[0])); for (const [vendor, items] of sortedVendors) { sections.push({ key: `vendor:${vendor}`, label: vendor, type: 'vendor', items, }); } return sections; }