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

@@ -312,6 +312,23 @@ async function searchByIvantiHostId(ivantiHostId, options) {
return { status: res.status, body: res.body, ok: res.status >= 200 && res.status < 300 };
}
/**
* GET /api/v2/asset-search/{assetId}?search_param=deep_search
* Search CARD by asset ID (e.g., "24.24.100.20-CTEC"). Returns the full
* enriched asset record including ncim_discovery, netops_granite_allips, etc.
*
* @param {string} assetId - CARD asset identifier (IP-SUFFIX format)
* @param {object} [options] - { timeout }
*/
async function searchByAssetId(assetId, options) {
const id = (assetId || '').trim();
if (!id) {
return { status: 400, body: '{"error":"Asset ID is required."}', ok: false };
}
const res = await cardGet(`/api/v2/asset-search/${encodeURIComponent(id)}?search_param=deep_search`, options);
return { status: res.status, body: res.body, ok: res.status >= 200 && res.status < 300 };
}
/**
* Resolve a bare IP address to a full CARD asset ID by trying known suffixes.
* Returns the first asset ID that returns a valid owner record, or null if none found.
@@ -322,7 +339,7 @@ async function searchByIvantiHostId(ivantiHostId, options) {
async function resolveAssetId(ip, options) {
const quick = options && options.quick;
const SUFFIXES = quick ? ['CTEC'] : ['CTEC', 'NATL', 'CHTR', 'COML', 'RESI', 'WIFI', 'VOIP'];
const timeout = quick ? 15000 : undefined; // 15s timeout for quick mode
const timeout = quick ? 30000 : undefined; // 30s timeout for quick mode
const trimmedIp = (ip || '').trim();
if (!trimmedIp) return null;
@@ -384,4 +401,5 @@ module.exports = {
invalidateToken,
resolveAssetId,
searchByIvantiHostId,
searchByAssetId,
};