feat: 임시 이동 설비 현황 표시 기능 추가
- 대시보드 하단에 임시 이동된 설비 카드 섹션 추가 - 작업장 모달에 '이동 설비' 탭 추가 - 이 작업장으로 이동해 온 설비 표시 - 다른 곳으로 이동한 설비 표시 - 설비 마커에 이동 상태 색상 구분 (주황색 점선 + 깜빡임) - 원위치 복귀 기능 - 사이드바 기본값을 접힌 상태로 변경 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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');
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 += `
|
||||
<div class="equipment-marker ${statusClass}"
|
||||
style="left: ${eq.map_x_percent}%; top: ${eq.map_y_percent}%;
|
||||
width: ${width}%; height: ${height}%;"
|
||||
title="${displayName}"
|
||||
title="${displayName}${eq.is_temporarily_moved ? ' (임시이동)' : ''}"
|
||||
onclick="openEquipmentPanel(${JSON.stringify(eq).replace(/"/g, '"')})">
|
||||
<span class="marker-label">${displayName}</span>
|
||||
<span class="marker-label">${displayName}${movedBadge}</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -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 => `
|
||||
<div class="moved-equipment-card" onclick="showMovedEquipmentDetail(${eq.equipment_id})">
|
||||
<div class="moved-eq-header">
|
||||
<span class="moved-eq-code">${eq.equipment_code}</span>
|
||||
<span class="moved-eq-badge">임시이동</span>
|
||||
</div>
|
||||
<div class="moved-eq-name">${eq.equipment_name}</div>
|
||||
<div class="moved-eq-location">
|
||||
<div class="location-row">
|
||||
<span class="location-label">원래 위치</span>
|
||||
<span class="location-value">${eq.original_workplace_name || '-'}</span>
|
||||
</div>
|
||||
<div class="location-arrow">↓</div>
|
||||
<div class="location-row">
|
||||
<span class="location-label">현재 위치</span>
|
||||
<span class="location-value current">${eq.current_workplace_name || '-'}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="moved-eq-date">
|
||||
이동일: ${formatPanelDate(eq.moved_at)}
|
||||
</div>
|
||||
<button class="btn btn-sm btn-outline" onclick="event.stopPropagation(); returnEquipmentToOriginal(${eq.equipment_id})">
|
||||
원위치 복귀
|
||||
</button>
|
||||
</div>
|
||||
`).join('');
|
||||
} else {
|
||||
listEl.style.display = 'none';
|
||||
emptyEl.style.display = 'block';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('임시 이동 설비 로드 실패:', error);
|
||||
listEl.innerHTML = '<div style="text-align: center; padding: 20px; color: var(--gray-500);">로드 실패</div>';
|
||||
}
|
||||
}
|
||||
|
||||
// 작업장별 이동 설비 로드
|
||||
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 => `
|
||||
<div class="moved-eq-item in">
|
||||
<div class="moved-eq-item-header">
|
||||
<span class="eq-code">${eq.equipment_code}</span>
|
||||
<span class="eq-name">${eq.equipment_name}</span>
|
||||
</div>
|
||||
<div class="moved-eq-item-info">
|
||||
<span class="from-location">📤 ${eq.original_workplace_name || '알 수 없음'}</span>
|
||||
<span class="arrow">→</span>
|
||||
<span class="to-location">📥 여기</span>
|
||||
</div>
|
||||
<div class="moved-eq-item-date">이동일: ${formatPanelDate(eq.moved_at)}</div>
|
||||
</div>
|
||||
`).join('');
|
||||
} else {
|
||||
movedInList.innerHTML = '<p class="empty-message">없음</p>';
|
||||
}
|
||||
|
||||
// 이동해 간 설비 렌더링
|
||||
if (movedOut.length > 0) {
|
||||
movedOutList.innerHTML = movedOut.map(eq => `
|
||||
<div class="moved-eq-item out">
|
||||
<div class="moved-eq-item-header">
|
||||
<span class="eq-code">${eq.equipment_code}</span>
|
||||
<span class="eq-name">${eq.equipment_name}</span>
|
||||
</div>
|
||||
<div class="moved-eq-item-info">
|
||||
<span class="from-location">📤 여기</span>
|
||||
<span class="arrow">→</span>
|
||||
<span class="to-location">📥 ${eq.current_workplace_name || '알 수 없음'}</span>
|
||||
</div>
|
||||
<div class="moved-eq-item-date">이동일: ${formatPanelDate(eq.moved_at)}</div>
|
||||
</div>
|
||||
`).join('');
|
||||
} else {
|
||||
movedOutList.innerHTML = '<p class="empty-message">없음</p>';
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('작업장 이동 설비 로드 실패:', error);
|
||||
movedInList.innerHTML = '<p class="empty-message">로드 실패</p>';
|
||||
movedOutList.innerHTML = '<p class="empty-message">로드 실패</p>';
|
||||
}
|
||||
}
|
||||
|
||||
// 원위치 복귀
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user