New specs: archer-template-library, ccp-metrics-view-restructure, compliance-list-stale-after-sidebar-edit, compliance-metric-estimated-resolution-date, compliance-remediation-display-fix, flexible-jira-ticket-creation, forecast-burndown-chart, granite-loader-export, ivanti-queue-clear-completed-fix, multi-item-jira-ticket, queue-collapsible-sections, vendor-issue-type-dropdown New steering: archer-template-gen.md Updated: migration-registration-check hook, remediation-plan-history spec, gitlab-workflow, tech, versioning steering files
152 lines
8.7 KiB
Markdown
152 lines
8.7 KiB
Markdown
# Implementation Plan: Granite Loader Sheet Export
|
||
|
||
## Overview
|
||
|
||
Add a Granite Team_Device Loader xlsx generator accessible from the Ivanti Queue (for CARD/GRANITE items) and as a standalone tool. The system enriches device data from the CARD API, allows bulk defaults with per-row overrides in an editable preview table, and generates a properly formatted xlsx for upload to SNIP XperLoad. Implementation proceeds from column configuration utility → backend enrichment endpoint → frontend modal → queue integration → standalone access.
|
||
|
||
## Tasks
|
||
|
||
- [ ] 1. Create column configuration utility module
|
||
- [ ] 1.1 Create `frontend/src/utils/graniteLoaderConfig.js`
|
||
- Define `LOADER_COLUMNS` array with all 41 columns: id, label (exact Granite header name), group, requiredFor array
|
||
- Define `COLUMN_GROUPS` ordered array for UI grouping
|
||
- Define `OPERATION_TYPES` array: Change, Add, Delete, Move
|
||
- Export `getRequiredColumns(operationType)` helper that returns column IDs required for the given operation
|
||
- Export `getColumnsByGroup(group)` helper that returns columns in a group
|
||
- _Requirements: 2.1–2.6, 3.1–3.5_
|
||
|
||
- [ ] 1.2 Create `frontend/src/utils/graniteLoaderExport.js`
|
||
- Export `generateLoaderXlsx(config)` function that accepts `{ operationType, columnIds, rows }` and returns a Blob
|
||
- Use the `xlsx` library (already in frontend dependencies) to create a workbook with a single "Load_Sheet" worksheet
|
||
- First row contains exact canonical column headers from LOADER_COLUMNS (matched by columnIds, in canonical order)
|
||
- Subsequent rows contain device data; empty values become empty cells (not "null")
|
||
- DELETE column auto-filled with "X" when operationType is "Delete"
|
||
- EQUIPMENT CLASS defaults to "S" unless overridden
|
||
- Export `generateFilename(operationType, teamName)` helper returning `Loader_{op}_{team}_{YYYY-MM-DD}.xlsx`
|
||
- _Requirements: 6.1–6.8_
|
||
|
||
- [ ] 2. Backend CARD enrichment endpoint
|
||
- [ ] 2.1 Add `POST /api/card/enrich-batch` endpoint in `backend/routes/cardApi.js`
|
||
- Accept `{ ips: string[] }` in request body
|
||
- Validate: ips is a non-empty array, max 200 items, each item is a non-empty string
|
||
- Require Admin or Standard_User group
|
||
- Require CARD API to be configured (return 503 if not)
|
||
- For each IP, attempt owner lookup with known suffixes (CTEC, NATL, CHTR, etc.)
|
||
- Extract from asset record: equip_inst_id (from ncim_discovery, netops_granite_allips, or ise_granite_equipment), hostname, site_name, mgmt_ip_asn, responsible_team, equipment_class, equip_template, equip_status
|
||
- Return `{ results: [...], enriched_count, not_found_count, total }`
|
||
- Handle per-IP errors gracefully (mark as not-found, continue with others)
|
||
- Handle CARD API auth failures (return 502 with error message)
|
||
- _Requirements: 5.1–5.8_
|
||
|
||
- [ ] 2.2 Add `GET /api/card/configured` endpoint (or extend existing `/api/card/status`)
|
||
- Return `{ configured: boolean }` so the frontend knows whether to show the "Enrich from CARD" option
|
||
- This may already exist as `GET /api/card/status` — verify and reuse if so
|
||
- _Requirements: 5.8_
|
||
|
||
- [ ] 3. Frontend LoaderModal component
|
||
- [ ] 3.1 Create `frontend/src/components/LoaderModal.js`
|
||
- Accept props: `isOpen`, `onClose`, `initialDevices` (array of `{ ip_address, hostname }` or null)
|
||
- Render modal overlay with header "Generate Granite Loader Sheet"
|
||
- Include Operation Type selector (dropdown, defaults to "Change")
|
||
- Include Column Selection panel with collapsible groups and checkboxes
|
||
- Required columns for selected operation are pre-checked and disabled
|
||
- Include "Enrich from CARD" button (hidden if CARD not configured, checked via `/api/card/status` on mount)
|
||
- Include Bulk Defaults section: one input per selected column, setting value applies to all non-overridden rows
|
||
- Include editable Preview Table: rows = devices, columns = selected columns
|
||
- Cells are inline-editable on click; overridden cells show amber dot indicator
|
||
- Right-click or clear button on overridden cell reverts to bulk default
|
||
- Sticky column headers and bulk default row when scrolling
|
||
- Validation: highlight missing required fields in red, show warning count
|
||
- Download button: merges bulk defaults + overrides into final row data, calls `generateLoaderXlsx`, triggers browser download
|
||
- Cancel button closes modal
|
||
- _Requirements: 1.2, 2.1–2.6, 3.1–3.5, 4.1–4.7, 6.1–6.8, 8.1–8.5_
|
||
|
||
- [ ] 3.2 Implement CARD enrichment flow in LoaderModal
|
||
- On "Enrich from CARD" click, collect all device IPs, POST to `/api/card/enrich-batch`
|
||
- Show progress indicator during request
|
||
- On response, populate enriched fields into device rows (equip_inst_id, hostname, site_name, mgmt_ip_asn, etc.)
|
||
- Do NOT overwrite values the user has already manually entered
|
||
- Show warning indicators on rows where IP was not found
|
||
- Show error toast if CARD API auth fails
|
||
- _Requirements: 5.1–5.7_
|
||
|
||
- [ ] 3.3 Implement standalone mode (paste IPs)
|
||
- When `initialDevices` is null, show a textarea for pasting IPs (one per line or comma-separated)
|
||
- Parse input into device rows on "Load" button click
|
||
- Allow manually adding/removing rows via + and trash icons
|
||
- _Requirements: 7.1–7.4_
|
||
|
||
- [ ] 4. Integrate with Ivanti Queue page
|
||
- [ ] 4.1 Add "Generate Loader Sheet" button to IvantiTodoQueuePage floating action bar
|
||
- Show button when one or more selected items have workflow_type CARD or GRANITE
|
||
- Button label: "Generate Loader Sheet" with a FileSpreadsheet icon
|
||
- On click, open LoaderModal with `initialDevices` populated from selected items' ip_address and hostname
|
||
- _Requirements: 1.1–1.5_
|
||
|
||
- [ ] 4.2 Add standalone access point
|
||
- Add "Granite Loader" link in the navigation drawer under Tools section (or similar)
|
||
- Clicking opens LoaderModal in standalone mode (initialDevices = null)
|
||
- Alternatively, add a "Generate Loader Sheet" button on the CARD status section if one exists
|
||
- _Requirements: 7.1_
|
||
|
||
- [ ] 5. Checkpoint — Verify build and basic functionality
|
||
- Build frontend: `cd frontend && npm run build`
|
||
- Verify no lint errors or build failures
|
||
- Ensure all existing tests still pass
|
||
- Ask the user if questions arise
|
||
|
||
- [ ]* 6. Property-based tests for enrichment endpoint
|
||
- [ ]* 6.1 Write property test: Enrichment result count
|
||
- **Property 1: Result count equals input count** — For any array of N IPs (1 ≤ N ≤ 200), the response `results` array has exactly N elements
|
||
- File: `backend/__tests__/granite-loader-enrichment.property.test.js`
|
||
- **Validates: Requirements 5.1, 5.2**
|
||
|
||
- [ ]* 6.2 Write property test: Found results have equip_inst_id
|
||
- **Property 2: Found results have required fields** — For any result where `found === true`, `equip_inst_id` is a non-empty string
|
||
- **Validates: Requirements 5.2**
|
||
|
||
- [ ]* 6.3 Write property test: Not-found results have null fields
|
||
- **Property 3: Not-found results have null equip_inst_id** — For any result where `found === false`, `equip_inst_id` is null
|
||
- **Validates: Requirements 5.4**
|
||
|
||
- [ ]* 7. Unit tests for xlsx generation
|
||
- [ ]* 7.1 Write unit tests for `generateLoaderXlsx`
|
||
- Test correct column headers in canonical order
|
||
- Test DELETE column auto-fill for Delete operation
|
||
- Test EQUIPMENT CLASS defaults to "S"
|
||
- Test empty values produce empty cells (not "null" string)
|
||
- Test bulk default + override merge produces correct row values
|
||
- File: `backend/__tests__/granite-loader-xlsx-generation.test.js`
|
||
- **Validates: Requirements 6.1–6.8**
|
||
|
||
- [ ] 8. Final checkpoint
|
||
- Build frontend and verify no regressions
|
||
- Ensure all tests pass
|
||
- Ask the user if questions arise
|
||
|
||
## Notes
|
||
|
||
- Tasks marked with `*` are optional property-based and unit tests that can be skipped for faster MVP
|
||
- The `xlsx` library is already a frontend dependency — no new packages needed for xlsx generation
|
||
- The CARD API enrichment reuses the existing `cardApi.js` helper (token management, TLS skip, etc.)
|
||
- No database schema changes are required — this feature reads from queue items and CARD API only
|
||
- The LoaderModal follows the same pattern as ConsolidationModal (modal overlay, form state, action buttons)
|
||
- The preview table follows the same inline-edit pattern as the Reporting page (click to edit, amber dot for overrides)
|
||
- Maximum 200 devices per batch aligns with CARD API pagination limits and practical XperLoad batch sizes
|
||
|
||
## Task Dependency Graph
|
||
|
||
```json
|
||
{
|
||
"waves": [
|
||
{ "id": 0, "tasks": ["1.1", "1.2"] },
|
||
{ "id": 1, "tasks": ["2.1", "2.2"] },
|
||
{ "id": 2, "tasks": ["3.1"] },
|
||
{ "id": 3, "tasks": ["3.2", "3.3"] },
|
||
{ "id": 4, "tasks": ["4.1", "4.2"] },
|
||
{ "id": 5, "tasks": ["5"] },
|
||
{ "id": 6, "tasks": ["6.1", "6.2", "6.3", "7.1"] }
|
||
]
|
||
}
|
||
```
|