Add Granite Loader to AEO Compliance page with CARD enrichment and pagination

- Add checkbox selection + Granite Loader button to compliance device table
- Integrate LoaderModal for generating loader sheets from compliance devices
- Add direct IP resolve path (resolveAssetId + searchByAssetId) for CARD
  enrichment on compliance devices without Ivanti host IDs
- Add searchByAssetId helper for full enriched record via asset-search endpoint
- Include NTS-AEO-ACCESS-OPS in default enrich-batch team search
- Increase CARD quick-mode timeout from 15s to 30s
- Add timeout vs not-found distinction in enrichment error reporting
- Fix LoaderModal enriching state not resetting on modal reopen
- Add pagination to compliance device table (25/50/100/200 per page)
- Page resets on team, tab, filter, or search change
This commit is contained in:
Jordan Ramos
2026-06-19 13:49:26 -06:00
parent c7274be66d
commit e9d6038636
4 changed files with 269 additions and 20 deletions

View File

@@ -95,6 +95,7 @@ export default function LoaderModal({ isOpen, onClose, initialDevices }) {
setBulkDefaults({});
setEnrichErrors([]);
setValidationWarnings([]);
setEnriching(false);
}, [isOpen, initialDevices]);
// Auto-select required columns + useful defaults when operation type changes
@@ -417,11 +418,27 @@ export default function LoaderModal({ isOpen, onClose, initialDevices }) {
{/* Enrich errors */}
{enrichErrors.length > 0 && (
<div style={{ marginBottom: '0.75rem', padding: '0.5rem 0.75rem', background: 'rgba(239, 68, 68, 0.1)', border: '1px solid rgba(239, 68, 68, 0.3)', borderRadius: '0.375rem' }}>
<div style={{ fontSize: '0.7rem', color: '#EF4444', display: 'flex', alignItems: 'center', gap: '0.3rem' }}>
<AlertCircle style={{ width: '12px', height: '12px' }} />
{enrichErrors.length === 1 && enrichErrors[0].ip === 'all'
? enrichErrors[0].error
: `${enrichErrors.length} device(s) not found in CARD`}
<div style={{ fontSize: '0.7rem', color: '#EF4444', display: 'flex', alignItems: 'center', gap: '0.3rem', justifyContent: 'space-between' }}>
<span style={{ display: 'flex', alignItems: 'center', gap: '0.3rem' }}>
<AlertCircle style={{ width: '12px', height: '12px' }} />
{enrichErrors.length === 1 && enrichErrors[0].ip === 'all'
? enrichErrors[0].error
: enrichErrors.some(e => e.error && e.error.includes('timed out'))
? `${enrichErrors.length} device(s) timed out — CARD may be slow`
: `${enrichErrors.length} device(s) not found in CARD`}
</span>
<button
onClick={enrichFromCard}
disabled={enriching}
style={{
background: 'rgba(239, 68, 68, 0.15)', border: '1px solid rgba(239, 68, 68, 0.4)',
borderRadius: '0.25rem', padding: '0.2rem 0.5rem',
color: '#F87171', cursor: 'pointer',
fontSize: '0.65rem', fontFamily: 'monospace', fontWeight: '600',
}}
>
Retry
</button>
</div>
</div>
)}