import { state } from '../core/state'; import { openHwModal } from '../components/Modal/HWModal'; import { ASSET_SCHEMA } from '../core/schema'; import { LOCATION_DATA, IMAGE_LOCATIONS } from '../components/Modal/SharedData'; /** * 위치 중심 자산 현황 뷰 (Vercel Integrated) */ export async function renderLocationView(container: HTMLElement) { if (!container) return; let currentLoc = '기술개발센터'; let currentDetail = '서버실'; let currentPage = 0; let mapConfig: any = {}; try { const res = await fetch('/api/maps'); mapConfig = await res.json(); } catch (err) { console.error('Failed to load map config', err); } const render = () => { const locImages = (IMAGE_LOCATIONS[currentLoc] && IMAGE_LOCATIONS[currentLoc][currentDetail]) ? IMAGE_LOCATIONS[currentLoc][currentDetail] : []; const mapPath = locImages[currentPage] || ''; // 모든 하드웨어 카테고리에서 자산 검색 const allHwAssets = [ ...state.masterData.pc, ...state.masterData.server, ...state.masterData.storage, ...state.masterData.network, ...state.masterData.equipment, ...state.masterData.survey, ...state.masterData.officeSupplies, ...state.masterData.pcParts ]; // map_config.json에 설정된 모든 박스를 복사해서 작업용으로 사용 const tempBoxes = (mapConfig[mapPath] || []).map((b: any) => ({ ...b })); // DB 데이터에서 현재 지도(mapPath) 및 위치와 좌표 정보(loc_x, loc_y)가 일치하는 자산 추출 allHwAssets.forEach((asset: any) => { const photoPath = asset.location_photo || asset.loc_img || ''; const hasCoords = asset.loc_x != null && asset.loc_y != null && asset.loc_x !== '' && asset.loc_y !== '' && asset.loc_x !== 'null' && asset.loc_y !== 'null'; if (hasCoords && photoPath.trim() === mapPath.trim()) { const ax = parseFloat(asset.loc_x); const ay = parseFloat(asset.loc_y); // map_config.json에서 읽어온 박스들 중 x, y 좌표가 일치하는 빈 박스가 있는지 찾음 (오차범위 0.1 고려) const matchedBox = tempBoxes.find((b: any) => { const bx = parseFloat(b.x); const by = parseFloat(b.y); return Math.abs(bx - ax) < 0.1 && Math.abs(by - ay) < 0.1; }); if (matchedBox) { // 이미 매칭된 박스가 존재하고 asset_id가 비어있다면 해당 박스에 asset_id를 주입 if (matchedBox.asset_id == null) { matchedBox.asset_id = asset.id; } } else { // 일치하는 기존 박스가 없을 때만 4x4 크기의 임시 박스로 동적 생성 const alreadyMatched = tempBoxes.some((b: any) => b.asset_id === asset.id); if (!alreadyMatched) { tempBoxes.push({ asset_id: asset.id, x: asset.loc_x, y: asset.loc_y, w: '4', h: '4', name: asset.asset_purpose || asset.asset_code || '미지정 자산' }); } } } }); // 최종적으로 asset_id가 null이 아닌(자산이 정상 매핑되거나 갱신된) 박스들만 남겨서 렌더링 const boxes = tempBoxes.filter((b: any) => b.asset_id != null); container.innerHTML = `
${mapPath ? `
${boxes.map((box: any, idx: number) => { const asset = allHwAssets.find(a => a.id === box.asset_id); const name = asset ? ((asset as any).asset_purpose || asset.asset_code) : (box.name || `#${idx+1}`); // w, h가 없거나 너무 작으면 최소 크기(3%) 보장하여 영역으로 표시 const width = Math.max(parseFloat(box.w || '3'), 3); const height = Math.max(parseFloat(box.h || '3'), 3); return `
`}).join('')}
` : '
해당 위치의 도면이 등록되지 않았습니다.
'}
지도에서 자산 위치를 클릭하세요.
`; const syncOverlaySize = () => { const img = container.querySelector('#main-map-img') as HTMLImageElement; const overlay = container.querySelector('#box-overlay') as HTMLElement; if (img && overlay && img.complete) { overlay.style.width = img.clientWidth + 'px'; overlay.style.height = img.clientHeight + 'px'; overlay.style.left = img.offsetLeft + 'px'; overlay.style.top = img.offsetTop + 'px'; } }; const img = container.querySelector('#main-map-img') as HTMLImageElement; if (img) { if (img.complete) { syncOverlaySize(); setTimeout(syncOverlaySize, 50); } else { img.onload = syncOverlaySize; } } window.removeEventListener('resize', syncOverlaySize); window.addEventListener('resize', syncOverlaySize); const selMain = container.querySelector('#sel-loc-main') as HTMLSelectElement; selMain?.addEventListener('change', () => { currentLoc = selMain.value; currentDetail = LOCATION_DATA[currentLoc][0]; currentPage = 0; render(); }); const selDetail = container.querySelector('#sel-loc-detail') as HTMLSelectElement; selDetail?.addEventListener('change', () => { currentDetail = selDetail.value; currentPage = 0; render(); }); container.querySelector('#btn-prev-page')?.addEventListener('click', () => { currentPage--; render(); }); container.querySelector('#btn-next-page')?.addEventListener('click', () => { currentPage++; render(); }); const chkBox = container.querySelector('#chk-list-view-loc') as HTMLInputElement; if (chkBox) { chkBox.checked = state.viewMode === 'list'; const handleToggle = () => { const isListMode = chkBox.checked; if (isListMode) { state.viewMode = 'list'; } else { state.viewMode = 'location'; } window.dispatchEvent(new Event('refresh-view')); }; chkBox.addEventListener('change', handleToggle); } container.querySelectorAll('.location-box-area').forEach(box => { box.addEventListener('click', () => { const assetId = box.getAttribute('data-asset-id'); if (!assetId) return; const targetAsset = allHwAssets.find(a => a.id === assetId); if (targetAsset) renderAssetDetail(targetAsset); container.querySelectorAll('.location-box-area').forEach(b => (b as HTMLElement).style.background = 'rgba(30, 81, 73, 0.1)'); (box as HTMLElement).style.background = 'rgba(30, 81, 73, 0.4)'; }); }); }; const renderAssetDetail = (asset: any) => { const title = container.querySelector('#loc-list-title')!; const tableContainer = container.querySelector('#loc-asset-table-container')!; title.innerHTML = `
${asset.asset_code || '미부여'} ${asset.service_type || '운영'} ${asset.asset_type || 'PC'}
${asset.is_audit_approved ? `승인완료` : ''}
`; const fields = [ { label: ASSET_SCHEMA.CURRENT_DEPT.ui, value: asset.current_dept }, { label: ASSET_SCHEMA.HW_STATUS.ui, value: asset.hw_status }, { label: ASSET_SCHEMA.MANAGER_MAIN.ui, value: asset.manager_primary }, { label: ASSET_SCHEMA.MANAGER_SUB.ui, value: asset.manager_secondary }, { label: ASSET_SCHEMA.ASSET_PURPOSE.ui, value: asset.asset_purpose, fullWidth: true }, { label: ASSET_SCHEMA.MODEL_NAME.ui, value: asset.model_name }, { label: ASSET_SCHEMA.OS.ui, value: asset.os }, { label: ASSET_SCHEMA.CPU.ui, value: asset.cpu }, { label: ASSET_SCHEMA.RAM.ui, value: asset.ram }, { label: ASSET_SCHEMA.GPU.ui, value: asset.gpu, fullWidth: true }, { label: ASSET_SCHEMA.IP_ADDR.ui, value: asset.ip_address }, { label: ASSET_SCHEMA.MAC_ADDR.ui, value: asset.mac_address }, { label: ASSET_SCHEMA.REMOTE_TOOL.ui, value: asset.remote_tool }, { label: ASSET_SCHEMA.MONITORING.ui, value: asset.monitoring }, { label: ASSET_SCHEMA.MEMO.ui, value: asset.memo, fullWidth: true } ]; const sectionsHTML = `
${fields.map(f => `
${f.label}
${f.value || '-'}
`).join('')}
`; tableContainer.innerHTML = `
${sectionsHTML}
`; container.querySelector('#btn-view-from-loc')?.addEventListener('click', () => { openHwModal(asset, 'view'); }); }; render(); }