feat(map): implement robust ID-based asset mapping and fix UI rendering inconsistencies
- Migrated map mapping from fuzzy coordinates to precise asset_id tracking - Updated MapEditor to allow explicit asset assignment via dropdown - Fixed LocationView rendering logic to search across all hardware categories - Standardized map indicators to always render as areas (boxes) with minimum size - Restored stable CSS max-height for detail modal photos to prevent clipping - Synced MapEditor saves directly to database via asset_id
This commit is contained in:
@@ -18,6 +18,7 @@ export class MapEditor {
|
||||
private startY: number = 0;
|
||||
private currentBox: HTMLElement | null = null;
|
||||
private currentPath: string = '';
|
||||
private assetOptions: {id: string, name: string}[] = [];
|
||||
|
||||
constructor() {
|
||||
this.container = document.getElementById('container')!;
|
||||
@@ -33,11 +34,35 @@ export class MapEditor {
|
||||
public async init() {
|
||||
this.renderFileSidebar();
|
||||
await this.loadConfig();
|
||||
await this.loadAssets();
|
||||
this.bindEvents();
|
||||
this.selectFirstFile();
|
||||
createIcons({ icons: { X, Save, Trash2, ChevronLeft, ChevronRight } });
|
||||
}
|
||||
|
||||
private async loadAssets() {
|
||||
try {
|
||||
const res = await fetch(`http://${location.hostname}:3000/api/assets/master`);
|
||||
const masterData = await res.json();
|
||||
const allHw = [
|
||||
...(masterData.pc || []),
|
||||
...(masterData.server || []),
|
||||
...(masterData.storage || []),
|
||||
...(masterData.network || []),
|
||||
...(masterData.equipment || []),
|
||||
...(masterData.survey || []),
|
||||
...(masterData.officeSupplies || []),
|
||||
...(masterData.pcParts || [])
|
||||
];
|
||||
this.assetOptions = allHw.map(a => ({
|
||||
id: a.id,
|
||||
name: `[${a.asset_code}] ${a.asset_purpose || a.model_name || a.category}`
|
||||
}));
|
||||
} catch (err) {
|
||||
console.error('Failed to load assets for mapping', err);
|
||||
}
|
||||
}
|
||||
|
||||
private renderFileSidebar() {
|
||||
let html = '';
|
||||
Object.entries(IMAGE_LOCATIONS).forEach(([bldg, details]) => {
|
||||
@@ -137,7 +162,8 @@ export class MapEditor {
|
||||
x: (parseFloat(this.currentBox.style.left) / rect.width * 100).toFixed(2),
|
||||
y: (parseFloat(this.currentBox.style.top) / rect.height * 100).toFixed(2),
|
||||
w: (width / rect.width * 100).toFixed(2),
|
||||
h: (height / rect.height * 100).toFixed(2)
|
||||
h: (height / rect.height * 100).toFixed(2),
|
||||
asset_id: null
|
||||
};
|
||||
this.boxes.push(boxData);
|
||||
this.render();
|
||||
@@ -210,6 +236,13 @@ export class MapEditor {
|
||||
|
||||
this.wrapper.appendChild(div);
|
||||
|
||||
// Create asset options dropdown
|
||||
let optionsHtml = '<option value="">-- 자산 매핑 안 됨 --</option>';
|
||||
this.assetOptions.forEach(opt => {
|
||||
const selected = box.asset_id === opt.id ? 'selected' : '';
|
||||
optionsHtml += `<option value="${opt.id}" ${selected}>${opt.name}</option>`;
|
||||
});
|
||||
|
||||
const item = document.createElement('div');
|
||||
item.className = 'box-item';
|
||||
item.innerHTML = `
|
||||
@@ -217,6 +250,11 @@ export class MapEditor {
|
||||
<span class="box-index">#${i+1}</span>
|
||||
<button class="btn-del" onclick="removeBox(${i})">×</button>
|
||||
</div>
|
||||
<div class="box-inputs" style="margin-bottom: 8px;">
|
||||
<select data-index="${i}" data-prop="asset_id" style="width: 100%; padding: 4px;">
|
||||
${optionsHtml}
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-inputs">
|
||||
<div class="input-group">
|
||||
<label>X</label>
|
||||
@@ -239,17 +277,20 @@ export class MapEditor {
|
||||
this.boxListEl.appendChild(item);
|
||||
});
|
||||
|
||||
// Add events to new inputs
|
||||
this.boxListEl.querySelectorAll('input').forEach(input => {
|
||||
// Add events to new inputs and selects
|
||||
this.boxListEl.querySelectorAll('input, select').forEach(input => {
|
||||
input.addEventListener('change', (e) => {
|
||||
const target = e.target as HTMLInputElement;
|
||||
const target = e.target as HTMLInputElement | HTMLSelectElement;
|
||||
const index = parseInt(target.dataset.index!);
|
||||
const prop = target.dataset.prop!;
|
||||
const val = parseFloat(target.value).toFixed(2);
|
||||
|
||||
if (this.boxes[index]) {
|
||||
this.boxes[index][prop] = val;
|
||||
this.render(); // Re-render to update the map and sync other inputs
|
||||
if (prop === 'asset_id') {
|
||||
this.boxes[index][prop] = target.value || null;
|
||||
} else {
|
||||
this.boxes[index][prop] = parseFloat(target.value).toFixed(2);
|
||||
this.render(); // Re-render to update the map visual size
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user