- notifications/unread 호출 제거 → tkuser 링크로 대체 - attendance/today-summary → daily-status 엔드포인트로 변경 - GET /equipments/repair-requests 엔드포인트 신규 구현 - 캐시 버스팅 tkfb-dashboard.js?v=2026031701 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
118 lines
5.0 KiB
JavaScript
118 lines
5.0 KiB
JavaScript
/* ===== Dashboard (대시보드) ===== */
|
|
|
|
const today = new Date().toISOString().substring(0, 10);
|
|
|
|
function updateDateTime() {
|
|
const now = new Date();
|
|
const days = ['일', '월', '화', '수', '목', '금', '토'];
|
|
const h = String(now.getHours()).padStart(2, '0');
|
|
const m = String(now.getMinutes()).padStart(2, '0');
|
|
const el = document.getElementById('dateTimeDisplay');
|
|
if (el) el.textContent = `${now.getFullYear()}년 ${now.getMonth()+1}월 ${now.getDate()}일 (${days[now.getDay()]}) ${h}:${m}`;
|
|
}
|
|
|
|
async function loadDashboard() {
|
|
updateDateTime();
|
|
|
|
const results = await Promise.allSettled([
|
|
api('/tbm/sessions/date/' + today).catch(() => ({ data: [] })),
|
|
api('/equipments/repair-requests?status=pending').catch(() => ({ data: [] })),
|
|
api('/attendance/daily-status?date=' + today).catch(() => ({ data: [] })),
|
|
]);
|
|
|
|
const tbmData = results[0].status === 'fulfilled' ? results[0].value : { data: [] };
|
|
const repairData = results[1].status === 'fulfilled' ? results[1].value : { data: [] };
|
|
const attendData = results[2].status === 'fulfilled' ? results[2].value : { data: [] };
|
|
|
|
const tbmSessions = tbmData.data || [];
|
|
const repairs = repairData.data || [];
|
|
const attendList = Array.isArray(attendData.data) ? attendData.data : [];
|
|
const checkedInCount = attendList.filter(d => d.status !== 'incomplete').length;
|
|
|
|
// Stats
|
|
document.getElementById('statTbm').textContent = tbmSessions.length;
|
|
document.getElementById('statWorkers').textContent = checkedInCount;
|
|
document.getElementById('statRepairs').textContent = repairs.length;
|
|
document.getElementById('statNotifications').textContent = '-';
|
|
|
|
// TBM list
|
|
renderTbmList(tbmSessions);
|
|
renderNotificationPanel();
|
|
renderRepairList(repairs);
|
|
}
|
|
|
|
function renderTbmList(sessions) {
|
|
const el = document.getElementById('tbmList');
|
|
if (!sessions.length) {
|
|
el.innerHTML = '<p class="text-gray-400 text-sm text-center py-4">금일 TBM이 없습니다</p>';
|
|
return;
|
|
}
|
|
el.innerHTML = sessions.slice(0, 5).map(s => {
|
|
const workers = s.team_member_count || 0;
|
|
return `<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
|
<div>
|
|
<div class="text-sm font-medium text-gray-800">${escapeHtml(s.workplace_name || s.session_title || 'TBM')}</div>
|
|
<div class="text-xs text-gray-500">${escapeHtml(s.leader_name || '-')} · ${workers}명</div>
|
|
</div>
|
|
<span class="badge ${s.status === 'completed' ? 'badge-green' : 'badge-amber'}">${s.status === 'completed' ? '완료' : '진행중'}</span>
|
|
</div>`;
|
|
}).join('');
|
|
if (sessions.length > 5) {
|
|
el.innerHTML += `<a href="/pages/work/tbm.html" class="block text-center text-xs text-orange-600 hover:text-orange-700 mt-2">전체 보기 (${sessions.length}건)</a>`;
|
|
}
|
|
}
|
|
|
|
function renderNotificationPanel() {
|
|
const el = document.getElementById('notificationList');
|
|
el.innerHTML = `<div class="flex flex-col items-center gap-3 py-6">
|
|
<i class="fas fa-bell text-gray-300 text-3xl"></i>
|
|
<p class="text-gray-400 text-sm">알림은 사용자관리에서 확인하세요</p>
|
|
<a href="${_tkuserBase}/?tab=notificationRecipients" class="text-sm text-orange-600 hover:text-orange-700 font-medium">
|
|
<i class="fas fa-external-link-alt mr-1"></i>알림 관리 바로가기
|
|
</a>
|
|
</div>`;
|
|
}
|
|
|
|
function renderRepairList(repairs) {
|
|
const el = document.getElementById('repairList');
|
|
if (!repairs.length) {
|
|
el.innerHTML = '<p class="text-gray-400 text-sm text-center py-4">대기 중인 수리 요청이 없습니다</p>';
|
|
return;
|
|
}
|
|
el.innerHTML = repairs.slice(0, 5).map(r => {
|
|
return `<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
|
<div>
|
|
<div class="text-sm font-medium text-gray-800">${escapeHtml(r.equipment_name || r.title || '수리 요청')}</div>
|
|
<div class="text-xs text-gray-500">${formatDate(r.created_at)}</div>
|
|
</div>
|
|
<span class="badge badge-red">대기</span>
|
|
</div>`;
|
|
}).join('');
|
|
if (repairs.length > 5) {
|
|
el.innerHTML += `<a href="/pages/admin/repair-management.html" class="block text-center text-xs text-orange-600 hover:text-orange-700 mt-2">전체 보기 (${repairs.length}건)</a>`;
|
|
}
|
|
}
|
|
|
|
function formatTimeAgo(dateStr) {
|
|
if (!dateStr) return '';
|
|
const d = new Date(dateStr);
|
|
const now = new Date();
|
|
const diff = now - d;
|
|
const mins = Math.floor(diff / 60000);
|
|
const hours = Math.floor(diff / 3600000);
|
|
const days = Math.floor(diff / 86400000);
|
|
if (mins < 1) return '방금 전';
|
|
if (mins < 60) return `${mins}분 전`;
|
|
if (hours < 24) return `${hours}시간 전`;
|
|
if (days < 7) return `${days}일 전`;
|
|
return formatDate(dateStr);
|
|
}
|
|
|
|
/* ===== Init ===== */
|
|
(async function() {
|
|
if (!await initAuth()) return;
|
|
updateDateTime();
|
|
setInterval(updateDateTime, 60000);
|
|
loadDashboard();
|
|
})();
|