/* ===== Workplaces CRUD ===== */ let workplaces = [], workplacesLoaded = false, workplaceCategories = []; let selectedWorkplaceId = null, selectedWorkplaceName = ''; let equipments = [], equipmentTypes = []; let wpNavLevel = 'categories'; // 'categories' | 'workplaces' let wpNavCategoryId = null; let wpNavCategoryName = ''; let previewMapRegions = []; function purposeBadge(p) { const colors = { '작업구역': 'bg-blue-50 text-blue-600', '창고': 'bg-amber-50 text-amber-600', '설비': 'bg-purple-50 text-purple-600', '휴게시설': 'bg-green-50 text-green-600' }; return p ? `${p}` : ''; } async function loadWorkplaceCategories() { try { const r = await api('/workplaces/categories'); workplaceCategories = r.data || r; populateCategorySelects(); renderSidebar(); } catch(e) { console.warn('카테고리 로드 실패:', e); } } function populateCategorySelects() { ['newWorkplaceCategory','editWorkplaceCategory'].forEach(id => { const sel = document.getElementById(id); if (!sel) return; const val = sel.value; sel.innerHTML = ''; workplaceCategories.forEach(c => { const o = document.createElement('option'); o.value = c.category_id; o.textContent = c.category_name; sel.appendChild(o); }); sel.value = val; }); } async function loadWorkplaces() { await loadWorkplaceCategories(); try { const r = await api('/workplaces'); workplaces = r.data || r; workplacesLoaded = true; renderSidebar(); } catch (err) { document.getElementById('wpSidebarContent').innerHTML = `
${err.message}
등록된 공장이 없습니다.
'; } else { html += '등록된 작업장이 없습니다.
'; } else { html += '미분류 작업장에는 구역지도가 없습니다.
'; return; } loadLayoutPreview(categoryId); } function backToCategory() { if (!wpNavCategoryId && wpNavCategoryId !== 0) { backToCategories(); return; } selectedWorkplaceId = null; renderSidebar(); showZoneMapForCategory(wpNavCategoryId); } function openAddWorkplaceModal() { populateCategorySelects(); document.getElementById('addWorkplaceForm').reset(); // 공장 드릴다운 상태이면 카테고리 자동 선택 if (wpNavLevel === 'workplaces' && wpNavCategoryId && wpNavCategoryId !== 0) { document.getElementById('newWorkplaceCategory').value = wpNavCategoryId; } document.getElementById('addWorkplaceModal').classList.remove('hidden'); } function closeAddWorkplaceModal() { document.getElementById('addWorkplaceModal').classList.add('hidden'); } document.getElementById('addWorkplaceForm').addEventListener('submit', async e => { e.preventDefault(); try { await api('/workplaces', { method: 'POST', body: JSON.stringify({ workplace_name: document.getElementById('newWorkplaceName').value.trim(), category_id: document.getElementById('newWorkplaceCategory').value ? parseInt(document.getElementById('newWorkplaceCategory').value) : null, workplace_purpose: document.getElementById('newWorkplacePurpose').value || null, description: document.getElementById('newWorkplaceDesc').value.trim() || null, display_priority: parseInt(document.getElementById('newWorkplacePriority').value) || 0 })}); showToast('작업장이 추가되었습니다.'); document.getElementById('addWorkplaceForm').reset(); closeAddWorkplaceModal(); await loadWorkplaces(); } catch(e) { showToast(e.message, 'error'); } }); function editWorkplace(id) { const w = workplaces.find(x => x.workplace_id === id); if (!w) return; document.getElementById('editWorkplaceId').value = w.workplace_id; document.getElementById('editWorkplaceName').value = w.workplace_name; document.getElementById('editWorkplaceDesc').value = w.description || ''; document.getElementById('editWorkplacePriority').value = w.display_priority || 0; document.getElementById('editWorkplaceActive').value = (w.is_active === 0 || w.is_active === false) ? '0' : '1'; document.getElementById('editWorkplacePurpose').value = w.workplace_purpose || ''; populateCategorySelects(); document.getElementById('editWorkplaceCategory').value = w.category_id || ''; document.getElementById('editWorkplaceModal').classList.remove('hidden'); } function closeWorkplaceModal() { document.getElementById('editWorkplaceModal').classList.add('hidden'); } document.getElementById('editWorkplaceForm').addEventListener('submit', async e => { e.preventDefault(); try { await api(`/workplaces/${document.getElementById('editWorkplaceId').value}`, { method: 'PUT', body: JSON.stringify({ workplace_name: document.getElementById('editWorkplaceName').value.trim(), category_id: document.getElementById('editWorkplaceCategory').value ? parseInt(document.getElementById('editWorkplaceCategory').value) : null, workplace_purpose: document.getElementById('editWorkplacePurpose').value || null, description: document.getElementById('editWorkplaceDesc').value.trim() || null, display_priority: parseInt(document.getElementById('editWorkplacePriority').value) || 0, is_active: document.getElementById('editWorkplaceActive').value === '1' })}); showToast('수정되었습니다.'); closeWorkplaceModal(); await loadWorkplaces(); } catch(e) { showToast(e.message, 'error'); } }); async function deactivateWorkplace(id, name) { if (!confirm(`"${name}" 작업장을 비활성화?`)) return; try { await api(`/workplaces/${id}`, { method: 'DELETE' }); showToast('작업장 비활성화 완료'); await loadWorkplaces(); } catch(e) { showToast(e.message, 'error'); } } /* ===== Equipment CRUD ===== */ let eqMapImg = null, eqMapCanvas = null, eqMapCtx = null, eqDetailEqId = null; function eqStatusBadge(status) { const map = { active:'bg-emerald-50 text-emerald-600', maintenance:'bg-amber-50 text-amber-600', inactive:'bg-gray-100 text-gray-500', external:'bg-blue-50 text-blue-600', repair_external:'bg-blue-50 text-blue-600', repair_needed:'bg-red-50 text-red-600' }; const labels = { active:'가동중', maintenance:'점검중', inactive:'비활성', external:'외부반출', repair_external:'수리외주', repair_needed:'수리필요' }; return `${labels[status] || status || ''}`; } function selectWorkplaceForEquipments(id, name) { selectedWorkplaceId = id; selectedWorkplaceName = name; // 카테고리 레벨에서 직접 호출된 경우, 해당 카테고리로 드릴인 if (wpNavLevel === 'categories') { const wp = workplaces.find(w => w.workplace_id === id); if (wp && wp.category_id) { wpNavLevel = 'workplaces'; wpNavCategoryId = wp.category_id; wpNavCategoryName = wp.category_name || ''; } } renderSidebar(); document.getElementById('workplaceEmptyState')?.classList.add('hidden'); document.getElementById('zoneMapSection')?.classList.add('hidden'); document.getElementById('equipmentSection').classList.remove('hidden'); document.getElementById('eqWorkplaceName').textContent = name; // 뒤로가기 버튼 표시 (공장 구역지도로 돌아가기) const backBtn = document.getElementById('eqBackToCategory'); if (backBtn && wpNavCategoryId !== null) { document.getElementById('eqBackLabel').textContent = `${wpNavCategoryName} 구역지도`; backBtn.classList.remove('hidden'); } else if (backBtn) { backBtn.classList.add('hidden'); } loadEquipments(); loadEquipmentTypes(); loadEqMap(); } async function loadEquipments() { try { const r = await api(`/equipments/workplace/${selectedWorkplaceId}`); equipments = r.data || []; displayEquipments(); drawEqMapEquipments(); } catch(e) { document.getElementById('equipmentList').innerHTML = `${e.message}
설비가 없습니다.
'; return; } c.innerHTML = filtered.map(e => { const placed = e.map_x_percent != null && e.map_y_percent != null; return `사진 없음
'; return; } c.innerHTML = photos.map(p => { const fname = (p.photo_path||'').replace(/^\/uploads\//, ''); return `로드 실패
'; } } async function uploadEqPhoto() { const file = document.getElementById('eqPhotoFile').files[0]; if (!file || !eqDetailEqId) return; try { const fd = new FormData(); fd.append('photo', file); const token = getToken(); const res = await fetch(`${API_BASE}/equipments/${eqDetailEqId}/photos`, { method: 'POST', headers: { 'Authorization': token ? `Bearer ${token}` : '' }, body: fd }); const result = await res.json(); if (!res.ok) throw new Error(result.error || '업로드 실패'); showToast('사진이 추가되었습니다.'); loadEqPhotos(eqDetailEqId); } catch(e) { showToast(e.message, 'error'); } document.getElementById('eqPhotoFile').value = ''; } async function deleteEqPhoto(photoId) { if (!confirm('사진을 삭제하시겠습니까?')) return; try { await api(`/equipments/photos/${photoId}`, { method: 'DELETE' }); showToast('삭제됨'); loadEqPhotos(eqDetailEqId); } catch(e) { showToast(e.message, 'error'); } }