Files
tk-factory-services/tkpurchase/web/static/js/tkpurchase-partner-history.js
Hyungi Ahn 509691eebb feat(tkpurchase): 협력업체 포털/이력에 프로젝트 정보 배지 추가
- 포털 스케줄 카드에 프로젝트명·job_no 초록 배지 표시
- 이력 카드에 프로젝트명·job_no 초록 배지 표시
- checkinModel.findHistoryByCompany에 LEFT JOIN projects 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 08:34:56 +09:00

140 lines
6.2 KiB
JavaScript

/* tkpurchase-partner-history.js - Partner work history */
let historyPage = 1;
const historyLimit = 20;
function initPartnerHistory() {
if (!initAuth()) return;
const token = getToken();
const decoded = decodeToken(token);
if (!decoded || !decoded.partner_company_id) {
location.href = '/';
return;
}
// 기본 날짜: 최근 30일
const today = new Date();
const thirtyDaysAgo = new Date(today);
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
document.getElementById('filterDateTo').value = toLocalDate(today);
document.getElementById('filterDateFrom').value = toLocalDate(thirtyDaysAgo);
loadHistory();
}
async function loadHistory(page) {
historyPage = page || 1;
const dateFrom = document.getElementById('filterDateFrom').value;
const dateTo = document.getElementById('filterDateTo').value;
const params = new URLSearchParams();
if (dateFrom) params.set('date_from', dateFrom);
if (dateTo) params.set('date_to', dateTo);
params.set('page', historyPage);
params.set('limit', historyLimit);
const container = document.getElementById('historyList');
container.innerHTML = '<p class="text-gray-400 text-center py-8 text-sm">로딩 중...</p>';
try {
const r = await api('/checkins/my-history?' + params.toString());
const checkins = r.data || [];
const total = r.total || 0;
renderHistoryList(checkins);
renderPagination(total);
} catch(e) {
container.innerHTML = '<p class="text-red-400 text-center py-8 text-sm">데이터를 불러올 수 없습니다.</p>';
}
}
function renderHistoryList(checkins) {
const container = document.getElementById('historyList');
if (!checkins.length) {
container.innerHTML = `<div class="bg-white rounded-xl shadow-sm p-8 text-center">
<i class="fas fa-inbox text-gray-300 text-3xl mb-3"></i>
<p class="text-gray-500 text-sm">조회 기간에 작업 이력이 없습니다.</p>
</div>`;
return;
}
container.innerHTML = checkins.map(c => {
const checkinDate = formatDate(c.check_in_time);
const checkinTime = formatTime(c.check_in_time);
const checkoutTime = c.check_out_time ? formatTime(c.check_out_time) : null;
const reports = c.reports || [];
// 상태 배지
let statusHtml = '';
if (!c.check_out_time) {
statusHtml = '<span class="text-xs px-2 py-0.5 rounded-full bg-amber-100 text-amber-700">진행중</span>';
} else {
statusHtml = '<span class="text-xs px-2 py-0.5 rounded-full bg-blue-100 text-blue-700">완료</span>';
}
// 보고 정보
let reportHtml = '';
if (reports.length > 0) {
reportHtml = reports.map(r => {
const rWorkers = r.workers || [];
const totalHours = rWorkers.reduce((sum, w) => sum + Number(w.hours_worked || 0), 0);
const isConfirmed = !!r.confirmed_by;
const isRejected = !!r.rejected_by;
const rStatus = isConfirmed
? '<span class="text-xs text-emerald-600"><i class="fas fa-check-circle"></i> 확인완료</span>'
: isRejected
? '<span class="text-xs text-red-600"><i class="fas fa-times-circle"></i> 반려</span>'
: '<span class="text-xs text-amber-500">미확인</span>';
const workersDetail = rWorkers.length > 0
? `<div class="mt-1 text-xs text-gray-500">${rWorkers.map(w => escapeHtml(w.worker_name) + ' ' + w.hours_worked + 'h').join(', ')}</div>`
: '';
return `<div class="p-2 bg-gray-50 rounded text-sm">
<div class="flex items-center justify-between">
<span class="text-gray-700">${escapeHtml((r.work_content || '').substring(0, 60))}${(r.work_content || '').length > 60 ? '...' : ''}</span>
${rStatus}
</div>
<div class="text-xs text-gray-400 mt-1">${rWorkers.length}명 · ${totalHours}h</div>
${workersDetail}
</div>`;
}).join('');
}
return `<div class="bg-white rounded-xl shadow-sm overflow-hidden">
<div class="p-4">
<div class="flex items-center justify-between mb-2">
<div class="flex items-center gap-2">
<span class="text-sm font-semibold text-gray-800">${checkinDate}</span>
${statusHtml}
</div>
<div class="flex items-center gap-2">
${c.project_name ? `<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-emerald-100 text-emerald-700 text-xs font-medium"><i class="fas fa-project-diagram text-[10px]"></i>${escapeHtml(c.project_name)}${c.job_no ? ' · ' + escapeHtml(c.job_no) : ''}</span>` : ''}
<span class="text-xs text-gray-500">${escapeHtml(c.workplace_name || '')}</span>
</div>
</div>
${c.work_description ? `<p class="text-sm text-gray-600 mb-2">${escapeHtml(c.work_description)}</p>` : ''}
<div class="flex gap-4 text-xs text-gray-500 mb-2">
<span><i class="fas fa-clock mr-1"></i>${checkinTime}${checkoutTime ? ' ~ ' + checkoutTime : ' ~'}</span>
<span><i class="fas fa-users mr-1"></i>${c.actual_worker_count || 0}명</span>
</div>
${reportHtml ? `<div class="space-y-2 mt-3 border-t pt-3">${reportHtml}</div>` : ''}
</div>
</div>`;
}).join('');
}
function renderPagination(total) {
const container = document.getElementById('historyPagination');
const totalPages = Math.ceil(total / historyLimit);
if (totalPages <= 1) { container.innerHTML = ''; return; }
let html = '';
for (let i = 1; i <= totalPages; i++) {
const active = i === historyPage;
html += `<button onclick="loadHistory(${i})" class="px-3 py-1 rounded text-sm ${active ? 'bg-emerald-600 text-white' : 'bg-white text-gray-600 border hover:bg-gray-50'}">${i}</button>`;
}
container.innerHTML = html;
}