- partner_schedules: work_date → start_date/end_date 기간 기반으로 변경 - project_id 컬럼 추가 (projects 테이블 연결, 선택사항) - 프로젝트 조회 API 추가 (GET /projects/active) - 일정 조회 시 기간 겹침 조건으로 필터링 - 체크인 시 기간 내 검증 추가 - 프론트엔드: 시작일/종료일 입력 + 프로젝트 선택 드롭다운 - 마이그레이션 SQL 포함 (scripts/migration-schedule-daterange.sql) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
83 lines
4.4 KiB
JavaScript
83 lines
4.4 KiB
JavaScript
/* tkpurchase-dashboard.js - Dashboard logic */
|
|
|
|
async function loadDashboardStats() {
|
|
try {
|
|
const [dlStats, schedules, reports] = await Promise.all([
|
|
api('/day-labor/stats'),
|
|
api('/schedules?date_from=' + todayStr() + '&date_to=' + todayStr()),
|
|
api('/work-reports?confirmed=false&page=1&limit=5')
|
|
]);
|
|
// Update stat cards
|
|
const pending = (dlStats.data || []).find(s => s.status === 'pending');
|
|
document.getElementById('statPending').textContent = pending ? pending.cnt : 0;
|
|
document.getElementById('statSchedules').textContent = (schedules.data || []).length;
|
|
document.getElementById('statUnconfirmed').textContent = (reports.data || []).length;
|
|
} catch(e) { console.warn('Dashboard stats error:', e); }
|
|
|
|
// Load active checkins count separately
|
|
try {
|
|
const checkins = await api('/checkins?status=checked_in&page=1&limit=1');
|
|
document.getElementById('statCheckins').textContent = checkins.total || 0;
|
|
} catch(e) { console.warn('Checkins stat error:', e); }
|
|
}
|
|
|
|
async function loadRecentDayLabor() {
|
|
try {
|
|
const r = await api('/day-labor?page=1&limit=5');
|
|
const list = r.data || [];
|
|
const c = document.getElementById('recentDayLabor');
|
|
if (!list.length) { c.innerHTML = '<p class="text-gray-400 text-center py-4 text-sm">신청 내역이 없습니다</p>'; return; }
|
|
const statusMap = { pending: ['bg-amber-50 text-amber-600', '대기'], approved: ['bg-emerald-50 text-emerald-600', '승인'], rejected: ['bg-red-50 text-red-600', '거절'], completed: ['bg-gray-100 text-gray-500', '완료'] };
|
|
c.innerHTML = list.map(d => {
|
|
const [cls, label] = statusMap[d.status] || ['bg-gray-100 text-gray-500', d.status];
|
|
return `<div class="p-3 bg-gray-50 rounded-lg">
|
|
<div class="flex items-center justify-between">
|
|
<span class="text-sm font-medium">${formatDate(d.work_date)}</span>
|
|
<span class="px-2 py-0.5 rounded text-xs ${cls}">${label}</span>
|
|
</div>
|
|
<div class="text-xs text-gray-500 mt-1">${escapeHtml(d.requester_name || '')} · ${d.worker_count}명 · ${escapeHtml(d.workplace_name || '')}</div>
|
|
${d.work_description ? `<div class="text-xs text-gray-400 mt-0.5 truncate">${escapeHtml(d.work_description)}</div>` : ''}
|
|
</div>`;
|
|
}).join('');
|
|
} catch(e) { console.warn(e); }
|
|
}
|
|
|
|
async function loadTodaySchedules() {
|
|
try {
|
|
const today = todayStr();
|
|
const r = await api('/schedules?date_from=' + today + '&date_to=' + today);
|
|
const list = r.data || [];
|
|
const c = document.getElementById('todaySchedules');
|
|
if (!list.length) { c.innerHTML = '<p class="text-gray-400 text-center py-4 text-sm">오늘 일정이 없습니다</p>'; return; }
|
|
const statusMap = { scheduled: ['badge-amber', '예정'], in_progress: ['badge-green', '진행중'], completed: ['badge-blue', '완료'], cancelled: ['badge-gray', '취소'] };
|
|
c.innerHTML = list.map(s => {
|
|
const [cls, label] = statusMap[s.status] || ['badge-gray', s.status];
|
|
const dateDisplay = formatDate(s.start_date) === formatDate(s.end_date) ? formatDate(s.start_date) : formatDate(s.start_date) + ' ~ ' + formatDate(s.end_date);
|
|
return `<div class="p-3 bg-gray-50 rounded-lg">
|
|
<div class="flex items-center justify-between">
|
|
<span class="text-sm font-medium">${escapeHtml(s.company_name || '')}</span>
|
|
<span class="badge ${cls}">${label}</span>
|
|
</div>
|
|
<div class="text-xs text-gray-500 mt-1">${dateDisplay} · ${escapeHtml(s.workplace_name || '')} · ${s.expected_workers || 0}명</div>
|
|
${s.work_description ? `<div class="text-xs text-gray-400 mt-0.5 truncate">${escapeHtml(s.work_description)}</div>` : ''}
|
|
</div>`;
|
|
}).join('');
|
|
} catch(e) { console.warn(e); }
|
|
}
|
|
|
|
function todayStr() { return new Date().toISOString().substring(0, 10); }
|
|
|
|
function initDashboard() {
|
|
if (!initAuth()) return;
|
|
// If partner account, redirect to portal
|
|
const token = getToken();
|
|
const decoded = decodeToken(token);
|
|
if (decoded && decoded.partner_company_id) {
|
|
location.href = '/partner-portal.html';
|
|
return;
|
|
}
|
|
loadDashboardStats();
|
|
loadRecentDayLabor();
|
|
loadTodaySchedules();
|
|
}
|