// mobile-dashboard.js - 모바일 대시보드 v2 // 공장별 카테고리 탭 → 작업장 리스트 → 작업장별 상태 요약 (function() { 'use strict'; if (window.innerWidth > 768) return; var today = new Date().toISOString().slice(0, 10); // ==================== 캐시 변수 ==================== var categories = []; var allWorkplaces = []; var tbmByWorkplace = {}; var visitorsByWorkplace = {}; var movedByWorkplace = {}; var issuesByWorkplace = {}; var workplacesByCategory = {}; // ==================== 유틸리티 ==================== // escapeHtml, waitForApi → api-base.js 전역 사용 // ==================== 데이터 그룹핑 ==================== function groupTbmByWorkplace(sessions) { tbmByWorkplace = {}; if (!Array.isArray(sessions)) return; sessions.forEach(function(s) { var wpId = s.workplace_id; if (!wpId) return; if (!tbmByWorkplace[wpId]) { tbmByWorkplace[wpId] = { taskCount: 0, totalWorkers: 0, sessions: [] }; } tbmByWorkplace[wpId].taskCount++; tbmByWorkplace[wpId].totalWorkers += (parseInt(s.team_member_count) || 0); tbmByWorkplace[wpId].sessions.push(s); }); } function groupVisitorsByWorkplace(requests) { visitorsByWorkplace = {}; if (!Array.isArray(requests)) return; requests.forEach(function(r) { // 오늘 날짜 + 승인된 건만 if (r.visit_date !== today) return; if (r.status !== 'approved') return; var wpId = r.workplace_id; if (!wpId) return; if (!visitorsByWorkplace[wpId]) { visitorsByWorkplace[wpId] = { visitCount: 0, totalVisitors: 0, requests: [] }; } visitorsByWorkplace[wpId].visitCount++; visitorsByWorkplace[wpId].totalVisitors += parseInt(r.visitor_count) || 0; visitorsByWorkplace[wpId].requests.push(r); }); } function groupMovedByWorkplace(items) { movedByWorkplace = {}; if (!Array.isArray(items)) return; items.forEach(function(eq) { var wpId = eq.current_workplace_id; if (!wpId) return; if (!movedByWorkplace[wpId]) { movedByWorkplace[wpId] = { movedCount: 0, items: [] }; } movedByWorkplace[wpId].movedCount++; movedByWorkplace[wpId].items.push(eq); }); } function groupIssuesByWorkplace(issues) { issuesByWorkplace = {}; if (!Array.isArray(issues)) return; var activeStatuses = ['reported', 'received', 'in_progress']; issues.forEach(function(issue) { var wpId = issue.workplace_id; if (!wpId) return; if (activeStatuses.indexOf(issue.status) === -1) return; if (!issuesByWorkplace[wpId]) { issuesByWorkplace[wpId] = { activeCount: 0, items: [] }; } issuesByWorkplace[wpId].activeCount++; issuesByWorkplace[wpId].items.push(issue); }); } function groupWorkplacesByCategory(workplaces) { workplacesByCategory = {}; if (!Array.isArray(workplaces)) return; workplaces.forEach(function(wp) { var catId = wp.category_id; if (!catId) return; if (!workplacesByCategory[catId]) { workplacesByCategory[catId] = []; } workplacesByCategory[catId].push(wp); }); } // ==================== 렌더링 ==================== function renderCategoryTabs() { var container = document.getElementById('mCategoryTabs'); if (!container || !categories.length) return; var html = ''; categories.forEach(function(cat, idx) { html += ''; }); // 전체 탭 html += ''; container.innerHTML = html; // 이벤트 바인딩 var tabs = container.querySelectorAll('.md-cat-tab'); tabs.forEach(function(tab) { tab.addEventListener('click', function() { tabs.forEach(function(t) { t.classList.remove('active'); }); tab.classList.add('active'); var catId = tab.getAttribute('data-id'); selectCategory(catId); }); }); // 첫 번째 카테고리 자동 선택 if (categories.length > 0) { selectCategory(String(categories[0].category_id)); } } function selectCategory(categoryId) { var workplaces; if (categoryId === 'all') { workplaces = allWorkplaces.filter(function(wp) { return wp.is_active !== false; }); } else { workplaces = (workplacesByCategory[categoryId] || []).filter(function(wp) { return wp.is_active !== false; }); } renderWorkplaceList(workplaces); } function renderWorkplaceList(workplaces) { var container = document.getElementById('mWorkplaceList'); if (!container) return; if (!workplaces || workplaces.length === 0) { container.innerHTML = '
등록된 작업장이 없습니다.
'; return; } var html = ''; workplaces.forEach(function(wp) { var wpId = wp.workplace_id; var tbm = tbmByWorkplace[wpId]; var visitors = visitorsByWorkplace[wpId]; var moved = movedByWorkplace[wpId]; var issues = issuesByWorkplace[wpId]; var hasAny = tbm || visitors || moved || issues; html += '
'; // 헤더 (클릭 영역) html += '
'; html += '

' + escapeHtml(wp.workplace_name); if (hasAny) { html += ''; } html += '

'; if (!hasAny) { html += '

오늘 활동이 없습니다

'; } else { html += '
'; // TBM 작업 if (tbm) { html += '
' + '🛠' + '작업 ' + tbm.taskCount + '건 · ' + tbm.totalWorkers + '명' + '
'; } // 방문 if (visitors) { html += '
' + '🚪' + '방문 ' + visitors.visitCount + '건 · ' + visitors.totalVisitors + '명' + '
'; } // 신고 (미완료만) if (issues && issues.activeCount > 0) { html += '
' + '' + '신고 ' + issues.activeCount + '건' + '
'; } // 이동설비 if (moved && moved.movedCount > 0) { html += '
' + '' + '이동설비 ' + moved.movedCount + '건' + '
'; } html += '
'; } html += '
'; // .md-wp-header // 상세 영역 (활동 있는 카드만) if (hasAny) { html += '
' + renderCardDetail(wpId) + '
'; } html += '
'; // .md-wp-card }); container.innerHTML = html; // 클릭 이벤트 바인딩 var cards = container.querySelectorAll('.md-wp-card[data-wp-id]'); cards.forEach(function(card) { var wpId = card.getAttribute('data-wp-id'); var hasActivity = tbmByWorkplace[wpId] || visitorsByWorkplace[wpId] || movedByWorkplace[wpId] || issuesByWorkplace[wpId]; if (!hasActivity) return; card.querySelector('.md-wp-header').addEventListener('click', function() { toggleCard(wpId); }); }); } // ==================== 카드 확장/접기 ==================== function toggleCard(wpId) { var allCards = document.querySelectorAll('.md-wp-card.expanded'); var targetCard = document.querySelector('.md-wp-card[data-wp-id="' + wpId + '"]'); if (!targetCard) return; var isExpanded = targetCard.classList.contains('expanded'); // 다른 카드 모두 접기 (아코디언) allCards.forEach(function(card) { card.classList.remove('expanded'); }); // 토글 if (!isExpanded) { targetCard.classList.add('expanded'); } } function renderCardDetail(wpId) { var html = ''; var tbm = tbmByWorkplace[wpId]; var visitors = visitorsByWorkplace[wpId]; var issues = issuesByWorkplace[wpId]; var moved = movedByWorkplace[wpId]; // TBM 작업 if (tbm && tbm.sessions.length > 0) { html += '
'; html += '
▶ 작업
'; tbm.sessions.forEach(function(s) { var taskName = s.task_name || '작업명 미지정'; var leaderName = s.leader_name || '미지정'; var memberCount = (parseInt(s.team_member_count) || 0); html += '
'; html += '
' + escapeHtml(taskName) + '
'; html += '
' + escapeHtml(leaderName) + ' · ' + memberCount + '명
'; html += '
'; }); html += '
'; } // 방문 if (visitors && visitors.requests.length > 0) { html += '
'; html += '
▶ 방문
'; visitors.requests.forEach(function(r) { var company = r.visitor_company || '업체 미지정'; var count = parseInt(r.visitor_count) || 0; var purpose = r.purpose_name || ''; html += '
'; html += '
' + escapeHtml(company) + ' · ' + count + '명'; if (purpose) html += ' · ' + escapeHtml(purpose); html += '
'; html += '
'; }); html += '
'; } // 신고 if (issues && issues.items.length > 0) { var statusMap = { reported: '신고', received: '접수', in_progress: '처리중' }; html += '
'; html += '
▶ 신고
'; issues.items.forEach(function(issue) { var category = issue.issue_category_name || '미분류'; var desc = issue.additional_description || ''; if (desc.length > 30) desc = desc.substring(0, 30) + '...'; var statusText = statusMap[issue.status] || issue.status; var statusClass = 'md-wp-issue-status--' + (issue.status || 'reported'); var reporter = issue.reporter_name || ''; var icon = issue.status === 'in_progress' ? '🔴' : '⚠'; html += '
'; html += '
' + icon + ' ' + escapeHtml(category); if (desc) html += ' · ' + escapeHtml(desc); html += '
'; html += '
' + statusText + ''; if (reporter) html += ' → ' + escapeHtml(reporter); html += '
'; html += '
'; }); html += '
'; } // 이동설비 if (moved && moved.items.length > 0) { html += '
'; html += '
▶ 이동설비
'; moved.items.forEach(function(eq) { var eqName = eq.equipment_name || '설비명 미지정'; var fromWp = eq.original_workplace_name || '?'; var toWp = eq.current_workplace_name || '?'; html += '
'; html += '
' + escapeHtml(eqName) + '
'; html += '
' + escapeHtml(fromWp) + ' → ' + escapeHtml(toWp) + '
'; html += '
'; }); html += '
'; } return html; } // ==================== 초기화 ==================== document.addEventListener('DOMContentLoaded', async function() { try { await waitForApi(); } catch (e) { console.error('mobile-dashboard: apiCall not available'); return; } var view = document.getElementById('mobileDashboardView'); if (!view) return; view.style.display = 'block'; // 날짜 표시 var now = new Date(); var days = ['일', '월', '화', '수', '목', '금', '토']; var dateEl = document.getElementById('mDateValue'); if (dateEl) { dateEl.textContent = now.getFullYear() + '.' + String(now.getMonth() + 1).padStart(2, '0') + '.' + String(now.getDate()).padStart(2, '0') + ' (' + days[now.getDay()] + ')'; } // 로딩 표시 var listContainer = document.getElementById('mWorkplaceList'); if (listContainer) { listContainer.innerHTML = '
' + '
' + '
'; } // 데이터 병렬 로딩 var results = await Promise.allSettled([ window.apiCall('/workplaces/categories'), window.apiCall('/tbm/sessions/date/' + today), window.apiCall('/workplace-visits/requests?visit_date=' + today + '&status=approved'), window.apiCall('/equipments/moved/list'), window.apiCall('/work-issues?start_date=' + today + '&end_date=' + today), window.apiCall('/workplaces') ]); // 카테고리 if (results[0].status === 'fulfilled' && results[0].value && results[0].value.success) { categories = results[0].value.data || []; } // TBM if (results[1].status === 'fulfilled' && results[1].value && results[1].value.success) { groupTbmByWorkplace(results[1].value.data || []); } // 방문 if (results[2].status === 'fulfilled' && results[2].value && results[2].value.success) { groupVisitorsByWorkplace(results[2].value.data || []); } // 이동설비 if (results[3].status === 'fulfilled' && results[3].value && results[3].value.success) { groupMovedByWorkplace(results[3].value.data || []); } // 신고 if (results[4].status === 'fulfilled' && results[4].value && results[4].value.success) { groupIssuesByWorkplace(results[4].value.data || []); } // 작업장 전체 (카테고리별 그룹핑) if (results[5].status === 'fulfilled' && results[5].value && results[5].value.success) { allWorkplaces = results[5].value.data || []; groupWorkplacesByCategory(allWorkplaces); } // 렌더링 renderCategoryTabs(); }); })();