feat: 대시보드 작업장 현황 지도 구현

- 실시간 작업장 현황을 지도로 시각화
- 작업장 관리 페이지에서 정의한 구역 정보 활용
- TBM 작업자 및 방문자 현황 표시

주요 변경사항:
- dashboard.html: 작업장 현황 섹션 추가 (기존 작업 현황 테이블 제거)
- workplace-status.js: 지도 렌더링 및 데이터 통합 로직 구현
- modern-dashboard.js: 삭제된 DOM 요소 조건부 체크 추가

시각화 방식:
- 인원 없음: 회색 테두리 + 작업장 이름
- 내부 작업자: 파란색 영역 + 인원 수
- 외부 방문자: 보라색 영역 + 인원 수
- 둘 다: 초록색 영역 + 총 인원 수

기술 구현:
- Canvas API 기반 사각형 영역 렌더링
- map-regions API를 통한 데이터 일관성 보장
- 클릭 이벤트로 상세 정보 모달 표시

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-01-29 15:46:47 +09:00
parent e1227a69fe
commit b6485e3140
87 changed files with 17509 additions and 698 deletions

View File

@@ -34,18 +34,18 @@ const elements = {
userName: document.getElementById('userName'),
userRole: document.getElementById('userRole'),
userInitial: document.getElementById('userInitial'),
selectedDate: document.getElementById('selectedDate'),
refreshBtn: document.getElementById('refreshBtn'),
selectedDate: document.getElementById('selectedDate'), // 작업장 현황으로 교체되어 없을 수 있음
refreshBtn: document.getElementById('refreshBtn'), // 작업장 현황으로 교체되어 없을 수 있음
logoutBtn: document.getElementById('logoutBtn'),
// 요약 카드
todayWorkers: document.getElementById('todayWorkers'),
totalHours: document.getElementById('totalHours'),
activeProjects: document.getElementById('activeProjects'),
errorCount: document.getElementById('errorCount'),
// 컨테이너
workStatusContainer: document.getElementById('workStatusContainer'),
workStatusContainer: document.getElementById('workStatusContainer'), // 작업장 현황으로 교체되어 없을 수 있음
workersContainer: document.getElementById('workersContainer'),
toastContainer: document.getElementById('toastContainer')
};
@@ -84,15 +84,19 @@ async function initializeDashboard() {
// 시간 업데이트 시작
updateCurrentTime();
setInterval(updateCurrentTime, 1000);
// 날짜 설정
elements.selectedDate.value = selectedDate;
// 날짜 설정 (요소가 있을 때만)
if (elements.selectedDate) {
elements.selectedDate.value = selectedDate;
}
// 이벤트 리스너 설정
setupEventListeners();
// 데이터 로드
await loadDashboardData();
// 데이터 로드 (작업 현황 컨테이너가 있을 때만)
if (elements.workStatusContainer) {
await loadDashboardData();
}
// 관리자 권한 확인
checkAdminAccess();
@@ -154,18 +158,22 @@ function updateCurrentTime() {
// ========== 이벤트 리스너 ========== //
function setupEventListeners() {
// 날짜 변경
elements.selectedDate.addEventListener('change', (e) => {
selectedDate = e.target.value;
loadDashboardData();
});
// 새로고침 버튼
elements.refreshBtn.addEventListener('click', () => {
loadDashboardData();
showToast('데이터를 새로고침했습니다.', 'success');
});
// 날짜 변경 (요소가 있을 때만)
if (elements.selectedDate) {
elements.selectedDate.addEventListener('change', (e) => {
selectedDate = e.target.value;
loadDashboardData();
});
}
// 새로고침 버튼 (요소가 있을 때만)
if (elements.refreshBtn) {
elements.refreshBtn.addEventListener('click', () => {
loadDashboardData();
showToast('데이터를 새로고침했습니다.', 'success');
});
}
// 로그아웃 버튼 (navbar 컴포넌트가 이미 처리하므로 버튼이 있을 때만)
if (elements.logoutBtn) {
elements.logoutBtn.addEventListener('click', () => {
@@ -747,19 +755,31 @@ async function checkTbmPageAccess() {
return;
}
console.log('🛠️ TBM 페이지 권한 확인 중...');
const tbmQuickAction = document.getElementById('tbmQuickAction');
if (!tbmQuickAction) {
console.log('⚠️ TBM 빠른 작업 버튼 요소를 찾을 수 없습니다');
return;
}
// 사용자의 페이지 접근 권한 조회
console.log('🛠️ TBM 페이지 권한 확인 중...', { role: currentUser.role, access_level: currentUser.access_level });
// Admin은 모든 페이지 접근 가능
if (currentUser.role === 'Admin' || currentUser.role === 'System Admin' || currentUser.access_level === 'admin' || currentUser.access_level === 'system') {
tbmQuickAction.style.display = 'block';
console.log('✅ Admin 사용자 - TBM 빠른 작업 버튼 표시');
return;
}
// 일반 사용자는 페이지 접근 권한 조회
const response = await window.apiCall(`/users/${currentUser.user_id}/page-access`);
if (response && response.success) {
const pageAccess = response.data?.pageAccess || [];
// 'tbm' 페이지 접근 권한 확인
const tbmPage = pageAccess.find(p => p.page_key === 'tbm');
const tbmQuickAction = document.getElementById('tbmQuickAction');
// 'work.tbm' 페이지 접근 권한 확인 (마이그레이션에서 work.tbm으로 등록함)
const tbmPage = pageAccess.find(p => p.page_key === 'work.tbm');
if (tbmPage && tbmPage.can_access && tbmQuickAction) {
if (tbmPage && tbmPage.can_access) {
tbmQuickAction.style.display = 'block';
console.log('✅ TBM 페이지 접근 권한 있음 - 빠른 작업 버튼 표시');
} else {