diff --git a/web-ui/css/modern-dashboard.css b/web-ui/css/modern-dashboard.css index f7e6724..ff148b9 100644 --- a/web-ui/css/modern-dashboard.css +++ b/web-ui/css/modern-dashboard.css @@ -2940,6 +2940,23 @@ color: #6b7280; } +/* 임시 이동된 설비 */ +.equipment-marker.moved { + border-color: #f59e0b; + border-style: dashed; + border-width: 3px; + background: rgba(245, 158, 11, 0.15); + animation: movedPulse 2s infinite; +} +.equipment-marker.moved .marker-label { + color: #d97706; +} + +@keyframes movedPulse { + 0%, 100% { border-color: #f59e0b; } + 50% { border-color: #fbbf24; } +} + @keyframes pulse-border { 0%, 100% { border-color: #dc2626; } 50% { border-color: #fca5a5; } @@ -3825,3 +3842,168 @@ font-size: 9px; } } + +/* ==================== 임시 이동 설비 목록 ==================== */ +.moved-equipment-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 16px; +} + +.moved-equipment-card { + background: white; + border: 1px solid #e5e7eb; + border-radius: 12px; + padding: 16px; + cursor: pointer; + transition: all 0.15s ease; + border-left: 4px solid #f59e0b; +} + +.moved-equipment-card:hover { + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + transform: translateY(-2px); +} + +.moved-eq-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; +} + +.moved-eq-code { + font-weight: 700; + font-size: 0.9rem; + color: #374151; +} + +.moved-eq-badge { + background: #fef3c7; + color: #d97706; + font-size: 0.75rem; + font-weight: 600; + padding: 2px 8px; + border-radius: 12px; +} + +.moved-eq-name { + font-size: 1rem; + font-weight: 600; + color: #111827; + margin-bottom: 12px; +} + +.moved-eq-location { + background: #f9fafb; + border-radius: 8px; + padding: 12px; + margin-bottom: 12px; +} + +.location-row { + display: flex; + justify-content: space-between; + align-items: center; +} + +.location-label { + font-size: 0.8rem; + color: #6b7280; +} + +.location-value { + font-size: 0.85rem; + font-weight: 500; + color: #374151; +} + +.location-value.current { + color: #f59e0b; + font-weight: 600; +} + +.location-arrow { + text-align: center; + color: #9ca3af; + font-size: 1rem; + margin: 4px 0; +} + +.moved-eq-date { + font-size: 0.8rem; + color: #9ca3af; + margin-bottom: 12px; +} + +.moved-equipment-card .btn { + width: 100%; +} + +/* 모달 내 이동 설비 탭 스타일 */ +.moved-eq-tab-content { + padding: 16px 0; +} + +.moved-eq-list { + display: flex; + flex-direction: column; + gap: 10px; +} + +.moved-eq-item { + background: white; + border-radius: 8px; + padding: 12px; + border-left: 4px solid #9ca3af; +} + +.moved-eq-item.in { + border-left-color: #22c55e; + background: #f0fdf4; +} + +.moved-eq-item.out { + border-left-color: #f59e0b; + background: #fffbeb; +} + +.moved-eq-item-header { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 6px; +} + +.moved-eq-item-header .eq-code { + font-weight: 700; + font-size: 0.85rem; + color: #374151; +} + +.moved-eq-item-header .eq-name { + font-size: 0.9rem; + color: #111827; +} + +.moved-eq-item-info { + display: flex; + align-items: center; + gap: 8px; + font-size: 0.8rem; + color: #6b7280; +} + +.moved-eq-item-info .arrow { + color: #9ca3af; +} + +.moved-eq-item-info .to-location { + font-weight: 500; +} + +.moved-eq-item-date { + font-size: 0.75rem; + color: #9ca3af; + margin-top: 4px; +} diff --git a/web-ui/js/app-init.js b/web-ui/js/app-init.js index cdd9d70..b757e5d 100644 --- a/web-ui/js/app-init.js +++ b/web-ui/js/app-init.js @@ -201,8 +201,8 @@ } }); - // 저장된 상태 복원 - const isCollapsed = localStorage.getItem('sidebarCollapsed') === 'true'; + // 저장된 상태 복원 (기본값: 접힌 상태) + const isCollapsed = localStorage.getItem('sidebarCollapsed') !== 'false'; const sidebar = doc.querySelector('.sidebar-nav'); if (isCollapsed && sidebar) { sidebar.classList.add('collapsed'); diff --git a/web-ui/js/load-sidebar.js b/web-ui/js/load-sidebar.js index f8bbc12..037b7b8 100644 --- a/web-ui/js/load-sidebar.js +++ b/web-ui/js/load-sidebar.js @@ -116,10 +116,10 @@ function highlightCurrentPage(doc) { } /** - * 사이드바 상태 복원 + * 사이드바 상태 복원 (기본값: 접힌 상태) */ function restoreSidebarState(doc) { - const isCollapsed = localStorage.getItem('sidebarCollapsed') === 'true'; + const isCollapsed = localStorage.getItem('sidebarCollapsed') !== 'false'; const sidebar = doc.querySelector('.sidebar-nav'); if (isCollapsed && sidebar) { diff --git a/web-ui/js/workplace-status.js b/web-ui/js/workplace-status.js index 3061791..13a5eca 100644 --- a/web-ui/js/workplace-status.js +++ b/web-ui/js/workplace-status.js @@ -24,6 +24,9 @@ document.addEventListener('DOMContentLoaded', async () => { // 기본값으로 제1공장 선택 await selectFirstCategory(); + + // 임시 이동된 설비 로드 + await loadMovedEquipments(); }); // ==================== 카테고리 (공장) 로드 ==================== @@ -641,9 +644,15 @@ async function loadEquipmentMarkers(workplaceId) { equipments.forEach(eq => { // 위치 정보가 있는 설비만 마커 표시 if (eq.map_x_percent != null && eq.map_y_percent != null) { - const statusClass = eq.status === 'active' ? 'active' : - eq.status === 'maintenance' ? 'maintenance' : - eq.status === 'repair_needed' ? 'repair' : 'inactive'; + // 임시 이동된 설비는 별도 클래스 적용 + let statusClass = ''; + if (eq.is_temporarily_moved) { + statusClass = 'moved'; + } else { + statusClass = eq.status === 'active' ? 'active' : + eq.status === 'maintenance' ? 'maintenance' : + eq.status === 'repair_needed' ? 'repair' : 'inactive'; + } // 마커 크기 (기본값 또는 설정된 값) const width = eq.map_width_percent || 8; @@ -651,14 +660,15 @@ async function loadEquipmentMarkers(workplaceId) { // 표시 이름: [코드] 이름 const displayName = `[${eq.equipment_code}] ${eq.equipment_name}`; + const movedBadge = eq.is_temporarily_moved ? ' 🚚' : ''; markersHtml += `
- ${displayName} + ${displayName}${movedBadge}
`; } @@ -747,6 +757,11 @@ function switchWorkplaceTab(tabName) { // 선택한 탭 활성화 document.querySelector(`.workplace-tab[data-tab="${tabName}"]`).classList.add('active'); document.getElementById(`tab-${tabName}`).classList.add('active'); + + // 이동 설비 탭 선택 시 데이터 로드 + if (tabName === 'moved-eq' && currentModalWorkplace) { + loadWorkplaceMovedEquipments(currentModalWorkplace.workplace_id); + } } // 순회점검 페이지로 이동 @@ -1527,6 +1542,159 @@ async function submitPanelReturn() { } } +// ==================== 임시 이동 설비 목록 ==================== + +async function loadMovedEquipments() { + const listEl = document.getElementById('movedEquipmentList'); + const emptyEl = document.getElementById('noMovedEquipment'); + + if (!listEl) return; + + try { + const response = await window.apiCall('/equipments/moved/list', 'GET'); + + if (response && response.success && response.data && response.data.length > 0) { + const equipments = response.data; + emptyEl.style.display = 'none'; + listEl.style.display = 'grid'; + + listEl.innerHTML = equipments.map(eq => ` +
+
+ ${eq.equipment_code} + 임시이동 +
+
${eq.equipment_name}
+
+
+ 원래 위치 + ${eq.original_workplace_name || '-'} +
+
+
+ 현재 위치 + ${eq.current_workplace_name || '-'} +
+
+
+ 이동일: ${formatPanelDate(eq.moved_at)} +
+ +
+ `).join(''); + } else { + listEl.style.display = 'none'; + emptyEl.style.display = 'block'; + } + } catch (error) { + console.error('임시 이동 설비 로드 실패:', error); + listEl.innerHTML = '
로드 실패
'; + } +} + +// 작업장별 이동 설비 로드 +async function loadWorkplaceMovedEquipments(workplaceId) { + const movedInList = document.getElementById('movedInEquipmentList'); + const movedOutList = document.getElementById('movedOutEquipmentList'); + const badge = document.getElementById('movedEqCountBadge'); + + try { + const response = await window.apiCall('/equipments/moved/list', 'GET'); + + if (response && response.success && response.data) { + const allMoved = response.data; + + // 이 작업장으로 이동해 온 설비 (current_workplace_id = workplaceId) + const movedIn = allMoved.filter(eq => eq.current_workplace_id === workplaceId); + + // 이 작업장에서 다른 곳으로 이동한 설비 (workplace_id = workplaceId) + const movedOut = allMoved.filter(eq => eq.workplace_id === workplaceId); + + // 배지 업데이트 + const totalCount = movedIn.length + movedOut.length; + if (totalCount > 0) { + badge.textContent = totalCount; + badge.style.display = 'inline-flex'; + } else { + badge.style.display = 'none'; + } + + // 이동해 온 설비 렌더링 + if (movedIn.length > 0) { + movedInList.innerHTML = movedIn.map(eq => ` +
+
+ ${eq.equipment_code} + ${eq.equipment_name} +
+
+ 📤 ${eq.original_workplace_name || '알 수 없음'} + + 📥 여기 +
+
이동일: ${formatPanelDate(eq.moved_at)}
+
+ `).join(''); + } else { + movedInList.innerHTML = '

없음

'; + } + + // 이동해 간 설비 렌더링 + if (movedOut.length > 0) { + movedOutList.innerHTML = movedOut.map(eq => ` +
+
+ ${eq.equipment_code} + ${eq.equipment_name} +
+
+ 📤 여기 + + 📥 ${eq.current_workplace_name || '알 수 없음'} +
+
이동일: ${formatPanelDate(eq.moved_at)}
+
+ `).join(''); + } else { + movedOutList.innerHTML = '

없음

'; + } + } + } catch (error) { + console.error('작업장 이동 설비 로드 실패:', error); + movedInList.innerHTML = '

로드 실패

'; + movedOutList.innerHTML = '

로드 실패

'; + } +} + +// 원위치 복귀 +async function returnEquipmentToOriginal(equipmentId) { + if (!confirm('이 설비를 원래 위치로 복귀시키겠습니까?')) return; + + try { + const response = await window.apiCall(`/equipments/${equipmentId}/return`, 'POST'); + + if (response && response.success) { + alert('설비가 원래 위치로 복귀되었습니다.'); + loadMovedEquipments(); + // 지도 새로고침 + if (selectedCategory) { + renderMap(); + } + } + } catch (error) { + console.error('복귀 실패:', error); + alert('복귀에 실패했습니다.'); + } +} + +// 이동된 설비 상세 보기 +function showMovedEquipmentDetail(equipmentId) { + // TODO: 설비 상세 패널 열기 + console.log('설비 상세:', equipmentId); +} + // ==================== 유틸리티 ==================== function formatPanelDate(dateStr) { @@ -1566,3 +1734,7 @@ window.submitPanelExport = submitPanelExport; window.openPanelReturnModal = openPanelReturnModal; window.closePanelReturnModal = closePanelReturnModal; window.submitPanelReturn = submitPanelReturn; +window.loadMovedEquipments = loadMovedEquipments; +window.returnEquipmentToOriginal = returnEquipmentToOriginal; +window.showMovedEquipmentDetail = showMovedEquipmentDetail; +window.loadWorkplaceMovedEquipments = loadWorkplaceMovedEquipments; diff --git a/web-ui/pages/dashboard.html b/web-ui/pages/dashboard.html index c804b52..e78ab89 100644 --- a/web-ui/pages/dashboard.html +++ b/web-ui/pages/dashboard.html @@ -86,6 +86,26 @@ + +
+
+
+
+

🚚 임시 이동된 설비

+ +
+
+
+
+ +
+ +
+
+
@@ -142,6 +162,11 @@ 🗺️ 상세 지도 + @@ -216,6 +241,33 @@
+ + +
+
+ +
+

+ 📥 + 이 작업장으로 이동해 온 설비 +

+
+

없음

+
+
+ + +
+

+ 📤 + 다른 곳으로 이동한 설비 +

+
+

없음

+
+
+
+