Phase 1: tkuser 협력업체 CRUD 이관 (읽기전용 → 전체 CRUD) Phase 2: tkpurchase 개편 — 일용공 신청/확정, 작업일정, 업무현황, 계정관리, 협력업체 포털 Phase 3: tksafety 신규 시스템 — 방문관리 + 안전교육 신고 Phase 4: SSO 인증 보강 (partner_company_id JWT, 만료일 체크), 권한 테이블 기반 접근 제어 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
174 lines
6.9 KiB
JavaScript
174 lines
6.9 KiB
JavaScript
/* tkpurchase-daylabor.js - Day labor management */
|
|
|
|
let dayLaborPage = 1;
|
|
const dayLaborLimit = 20;
|
|
|
|
async function loadDayLabor() {
|
|
const dateFrom = document.getElementById('filterDateFrom').value;
|
|
const dateTo = document.getElementById('filterDateTo').value;
|
|
const status = document.getElementById('filterStatus').value;
|
|
const department = document.getElementById('filterDepartment').value;
|
|
|
|
let query = `?page=${dayLaborPage}&limit=${dayLaborLimit}`;
|
|
if (dateFrom) query += '&date_from=' + dateFrom;
|
|
if (dateTo) query += '&date_to=' + dateTo;
|
|
if (status) query += '&status=' + status;
|
|
if (department) query += '&department=' + encodeURIComponent(department);
|
|
|
|
try {
|
|
const r = await api('/day-labor' + query);
|
|
renderDayLaborTable(r.data || [], r.total || 0);
|
|
} catch(e) {
|
|
console.warn('Day labor load error:', e);
|
|
document.getElementById('dayLaborTableBody').innerHTML = '<tr><td colspan="8" class="text-center text-red-400 py-8">로딩 실패</td></tr>';
|
|
}
|
|
}
|
|
|
|
function renderDayLaborTable(list, total) {
|
|
const tbody = document.getElementById('dayLaborTableBody');
|
|
if (!list.length) {
|
|
tbody.innerHTML = '<tr><td colspan="8" class="text-center text-gray-400 py-8">신청 내역이 없습니다</td></tr>';
|
|
document.getElementById('dayLaborPagination').innerHTML = '';
|
|
return;
|
|
}
|
|
|
|
const statusMap = {
|
|
pending: ['badge-amber', '대기'],
|
|
approved: ['badge-green', '승인'],
|
|
rejected: ['badge-red', '거절'],
|
|
completed: ['badge-gray', '완료']
|
|
};
|
|
|
|
tbody.innerHTML = list.map(d => {
|
|
const [cls, label] = statusMap[d.status] || ['badge-gray', d.status];
|
|
let actions = '';
|
|
if (d.status === 'pending') {
|
|
actions = `
|
|
<button onclick="approveDayLabor(${d.id})" class="text-emerald-600 hover:text-emerald-800 text-xs mr-1" title="승인"><i class="fas fa-check"></i></button>
|
|
<button onclick="rejectDayLabor(${d.id})" class="text-red-500 hover:text-red-700 text-xs" title="거절"><i class="fas fa-times"></i></button>`;
|
|
} else if (d.status === 'approved') {
|
|
actions = `<button onclick="completeDayLabor(${d.id})" class="text-blue-600 hover:text-blue-800 text-xs" title="완료"><i class="fas fa-check-double"></i></button>`;
|
|
}
|
|
return `<tr>
|
|
<td>${formatDate(d.created_at)}</td>
|
|
<td class="font-medium">${formatDate(d.work_date)}</td>
|
|
<td>${escapeHtml(d.requester_name || '')}</td>
|
|
<td class="hide-mobile">${escapeHtml(d.department || '')}</td>
|
|
<td class="text-center">${d.worker_count || 0}명</td>
|
|
<td>${escapeHtml(d.workplace_name || '')}</td>
|
|
<td><span class="badge ${cls}">${label}</span></td>
|
|
<td class="text-right">${actions}</td>
|
|
</tr>`;
|
|
}).join('');
|
|
|
|
// Pagination
|
|
const totalPages = Math.ceil(total / dayLaborLimit);
|
|
renderDayLaborPagination(totalPages);
|
|
}
|
|
|
|
function renderDayLaborPagination(totalPages) {
|
|
const container = document.getElementById('dayLaborPagination');
|
|
if (totalPages <= 1) { container.innerHTML = ''; return; }
|
|
|
|
let html = '';
|
|
if (dayLaborPage > 1) {
|
|
html += `<button onclick="goToDayLaborPage(${dayLaborPage - 1})" class="px-3 py-1 border rounded text-sm hover:bg-gray-50">«</button>`;
|
|
}
|
|
for (let i = 1; i <= totalPages; i++) {
|
|
if (i === dayLaborPage) {
|
|
html += `<button class="px-3 py-1 bg-emerald-600 text-white rounded text-sm">${i}</button>`;
|
|
} else if (Math.abs(i - dayLaborPage) <= 2 || i === 1 || i === totalPages) {
|
|
html += `<button onclick="goToDayLaborPage(${i})" class="px-3 py-1 border rounded text-sm hover:bg-gray-50">${i}</button>`;
|
|
} else if (Math.abs(i - dayLaborPage) === 3) {
|
|
html += '<span class="text-gray-400">...</span>';
|
|
}
|
|
}
|
|
if (dayLaborPage < totalPages) {
|
|
html += `<button onclick="goToDayLaborPage(${dayLaborPage + 1})" class="px-3 py-1 border rounded text-sm hover:bg-gray-50">»</button>`;
|
|
}
|
|
container.innerHTML = html;
|
|
}
|
|
|
|
function goToDayLaborPage(p) {
|
|
dayLaborPage = p;
|
|
loadDayLabor();
|
|
}
|
|
|
|
function openAddDayLabor() {
|
|
document.getElementById('addDayLaborForm').reset();
|
|
// Default to tomorrow
|
|
const tomorrow = new Date();
|
|
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
document.getElementById('addWorkDate').value = tomorrow.toISOString().substring(0, 10);
|
|
document.getElementById('addDayLaborModal').classList.remove('hidden');
|
|
}
|
|
|
|
function closeAddDayLabor() {
|
|
document.getElementById('addDayLaborModal').classList.add('hidden');
|
|
}
|
|
|
|
async function submitAddDayLabor(e) {
|
|
e.preventDefault();
|
|
const body = {
|
|
work_date: document.getElementById('addWorkDate').value,
|
|
worker_count: parseInt(document.getElementById('addWorkerCount').value) || 1,
|
|
work_description: document.getElementById('addWorkDescription').value.trim(),
|
|
workplace_name: document.getElementById('addWorkplaceName').value.trim(),
|
|
notes: document.getElementById('addNotes').value.trim()
|
|
};
|
|
if (!body.work_date) { showToast('작업일을 선택하세요', 'error'); return; }
|
|
|
|
try {
|
|
await api('/day-labor', { method: 'POST', body: JSON.stringify(body) });
|
|
showToast('일용공 신청이 등록되었습니다');
|
|
closeAddDayLabor();
|
|
loadDayLabor();
|
|
} catch(e) {
|
|
showToast(e.message || '등록 실패', 'error');
|
|
}
|
|
}
|
|
|
|
async function approveDayLabor(id) {
|
|
if (!confirm('이 신청을 승인하시겠습니까?')) return;
|
|
try {
|
|
await api('/day-labor/' + id + '/approve', { method: 'PUT' });
|
|
showToast('승인되었습니다');
|
|
loadDayLabor();
|
|
} catch(e) {
|
|
showToast(e.message || '승인 실패', 'error');
|
|
}
|
|
}
|
|
|
|
async function rejectDayLabor(id) {
|
|
const reason = prompt('거절 사유를 입력하세요:');
|
|
if (reason === null) return;
|
|
try {
|
|
await api('/day-labor/' + id + '/reject', { method: 'PUT', body: JSON.stringify({ reason }) });
|
|
showToast('거절되었습니다');
|
|
loadDayLabor();
|
|
} catch(e) {
|
|
showToast(e.message || '거절 실패', 'error');
|
|
}
|
|
}
|
|
|
|
async function completeDayLabor(id) {
|
|
if (!confirm('이 신청을 완료 처리하시겠습니까?')) return;
|
|
try {
|
|
await api('/day-labor/' + id + '/complete', { method: 'PUT' });
|
|
showToast('완료 처리되었습니다');
|
|
loadDayLabor();
|
|
} catch(e) {
|
|
showToast(e.message || '완료 처리 실패', 'error');
|
|
}
|
|
}
|
|
|
|
function initDayLaborPage() {
|
|
if (!initAuth()) return;
|
|
// Set default date range to this month
|
|
const now = new Date();
|
|
const firstDay = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
document.getElementById('filterDateFrom').value = firstDay.toISOString().substring(0, 10);
|
|
document.getElementById('filterDateTo').value = now.toISOString().substring(0, 10);
|
|
loadDayLabor();
|
|
}
|