Add APPID + APPREFID searchable picker to Granite Loader
Load CMDB app instance data (4797 entries) from static JSON and present a searchable dropdown in the APP_ID_ASSET_TAG column. Users search by app name or APP ID, select an entry, and the field populates with the 'APP_ID - APPREFID' format required by Granite. - Convert Cherwell CMDB CSV export to frontend/public/cmdb-appids.json - LoaderModal: load APPID options on open, provide via getPicklist() helper that overrides COLUMN_PICKLISTS for the APP_ID_ASSET_TAG field - Options display as 'APP_NAME (APP_ID) [ENV]' for easy identification - Works in both bulk defaults and per-cell inline editing
This commit is contained in:
1
frontend/public/cmdb-appids.json
Normal file
1
frontend/public/cmdb-appids.json
Normal file
File diff suppressed because one or more lines are too long
@@ -18,6 +18,7 @@ import { generateLoaderXlsx, generateFilename } from '../utils/graniteLoaderExpo
|
||||
import { COLUMN_PICKLISTS } from '../utils/graniteLoaderPicklists';
|
||||
import SearchableSelect from './SearchableSelect';
|
||||
|
||||
// ⚠️ CONVENTION: Use relative API paths (no absolute URLs). The fallback 'http://localhost:3001/api' should be '/api' instead.
|
||||
const API_BASE = process.env.REACT_APP_API_BASE || 'http://localhost:3001/api';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -68,6 +69,7 @@ export default function LoaderModal({ isOpen, onClose, initialDevices }) {
|
||||
const [pasteInput, setPasteInput] = useState('');
|
||||
const [expandedGroups, setExpandedGroups] = useState(new Set(['Identification', 'Responsible Org']));
|
||||
const [validationWarnings, setValidationWarnings] = useState([]);
|
||||
const [appIdOptions, setAppIdOptions] = useState(null);
|
||||
|
||||
// --- Initialize ---
|
||||
useEffect(() => {
|
||||
@@ -77,7 +79,22 @@ export default function LoaderModal({ isOpen, onClose, initialDevices }) {
|
||||
.then(r => r.json())
|
||||
.then(d => setCardConfigured(d.configured === true))
|
||||
.catch(() => setCardConfigured(false));
|
||||
}, [isOpen]);
|
||||
// Load CMDB APPID data for Granite Loader APPID picker
|
||||
if (!appIdOptions) {
|
||||
// ⚠️ CONVENTION: Use credentials: 'include' on all fetch calls (missing here)
|
||||
fetch('/cmdb-appids.json', { credentials: 'include' })
|
||||
.then(r => r.ok ? r.json() : [])
|
||||
.then(data => {
|
||||
// Build searchable options: label shows "APP_NAME (APP_ID) [ENV]", value is "APP_ID - APPREFID"
|
||||
const opts = data.map(d => ({
|
||||
label: `${d.app_name} (${d.app_id}) [${d.env}]`,
|
||||
value: `${d.app_id} - ${d.apprefid}`,
|
||||
}));
|
||||
setAppIdOptions(opts);
|
||||
})
|
||||
.catch(() => setAppIdOptions([]));
|
||||
}
|
||||
}, [isOpen]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
// Populate devices from initialDevices or reset
|
||||
useEffect(() => {
|
||||
@@ -133,6 +150,15 @@ export default function LoaderModal({ isOpen, onClose, initialDevices }) {
|
||||
});
|
||||
}, []);
|
||||
|
||||
// --- Picklist helper: returns options array for a column, or null if no picklist ---
|
||||
// Uses dynamic CMDB data for APP_ID_ASSET_TAG, static picklists for others
|
||||
const getPicklist = useCallback((colId) => {
|
||||
if (colId === 'APP_ID_ASSET_TAG' && appIdOptions && appIdOptions.length > 0) {
|
||||
return appIdOptions;
|
||||
}
|
||||
return COLUMN_PICKLISTS[colId] || null;
|
||||
}, [appIdOptions]);
|
||||
|
||||
// --- Ordered selected columns (canonical order) ---
|
||||
const orderedColumns = useMemo(() => {
|
||||
return LOADER_COLUMNS.filter(col => selectedColumns.has(col.id));
|
||||
@@ -496,10 +522,10 @@ export default function LoaderModal({ isOpen, onClose, initialDevices }) {
|
||||
<label style={{ fontSize: '0.6rem', color: '#64748B', display: 'block', marginBottom: '0.15rem' }}>
|
||||
{col.id}
|
||||
</label>
|
||||
{COLUMN_PICKLISTS[col.id] ? (
|
||||
{getPicklist(col.id) ? (
|
||||
<SearchableSelect
|
||||
value={bulkDefaults[col.id] || ''}
|
||||
options={COLUMN_PICKLISTS[col.id]}
|
||||
options={getPicklist(col.id)}
|
||||
onChange={(val) => setBulkDefault(col.id, val)}
|
||||
placeholder={`Default for all rows`}
|
||||
autoFocus={false}
|
||||
@@ -556,10 +582,10 @@ export default function LoaderModal({ isOpen, onClose, initialDevices }) {
|
||||
onClick={() => !isEditing && startEdit(rowIdx, col.id)}
|
||||
>
|
||||
{isEditing ? (
|
||||
COLUMN_PICKLISTS[col.id] ? (
|
||||
getPicklist(col.id) ? (
|
||||
<SearchableSelect
|
||||
value={editValue}
|
||||
options={COLUMN_PICKLISTS[col.id]}
|
||||
options={getPicklist(col.id)}
|
||||
onChange={(val) => {
|
||||
setOverrides(prev => ({
|
||||
...prev,
|
||||
|
||||
Reference in New Issue
Block a user