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>
144 lines
6.9 KiB
JavaScript
144 lines
6.9 KiB
JavaScript
/* ===== Education Management ===== */
|
|
let educationList = [];
|
|
let editingEducationId = null;
|
|
|
|
async function loadEducation() {
|
|
try {
|
|
const dateFrom = document.getElementById('eduDateFrom')?.value || '';
|
|
const dateTo = document.getElementById('eduDateTo')?.value || '';
|
|
const targetType = document.getElementById('eduTargetType')?.value || '';
|
|
const params = new URLSearchParams();
|
|
if (dateFrom) params.set('date_from', dateFrom);
|
|
if (dateTo) params.set('date_to', dateTo);
|
|
if (targetType) params.set('target_type', targetType);
|
|
const r = await api('/education?' + params.toString());
|
|
educationList = r.data || [];
|
|
renderEducationList();
|
|
} catch (e) {
|
|
showToast('교육 목록 로드 실패: ' + e.message, 'error');
|
|
}
|
|
}
|
|
|
|
function renderEducationList() {
|
|
const tbody = document.getElementById('educationTableBody');
|
|
if (!educationList.length) {
|
|
tbody.innerHTML = '<tr><td colspan="7" class="text-center text-gray-400 py-8">등록된 안전교육이 없습니다</td></tr>';
|
|
return;
|
|
}
|
|
const typeLabels = { day_labor: '일용공', partner_schedule: '협력업체', manual: '수동등록' };
|
|
const statusLabels = { planned: '예정', completed: '완료', cancelled: '취소' };
|
|
const statusColors = { planned: 'badge-amber', completed: 'badge-green', cancelled: 'badge-gray' };
|
|
tbody.innerHTML = educationList.map(e => {
|
|
const attendeeCount = e.attendees ? (typeof e.attendees === 'string' ? JSON.parse(e.attendees) : e.attendees).length : 0;
|
|
return `<tr>
|
|
<td>${formatDate(e.education_date)}</td>
|
|
<td><span class="badge ${e.target_type === 'day_labor' ? 'badge-blue' : e.target_type === 'partner_schedule' ? 'badge-green' : 'badge-gray'}">${typeLabels[e.target_type] || e.target_type}</span></td>
|
|
<td>${escapeHtml(e.educator) || '-'}</td>
|
|
<td class="text-center">${attendeeCount}명</td>
|
|
<td><span class="badge ${statusColors[e.status] || 'badge-gray'}">${statusLabels[e.status] || e.status}</span></td>
|
|
<td class="hide-mobile">${escapeHtml(e.notes) || '-'}</td>
|
|
<td class="text-right">
|
|
<button onclick="openEditEducation(${e.id})" class="text-gray-400 hover:text-gray-600 text-xs" title="수정"><i class="fas fa-pen"></i></button>
|
|
<button onclick="doDeleteEducation(${e.id})" class="text-gray-400 hover:text-red-500 text-xs ml-1" title="삭제"><i class="fas fa-trash"></i></button>
|
|
</td>
|
|
</tr>`;
|
|
}).join('');
|
|
}
|
|
|
|
/* ===== Add education modal ===== */
|
|
function openAddEducation() {
|
|
document.getElementById('addEducationModal').classList.remove('hidden');
|
|
}
|
|
function closeAddEducation() {
|
|
document.getElementById('addEducationModal').classList.add('hidden');
|
|
document.getElementById('addEducationForm').reset();
|
|
}
|
|
|
|
async function submitAddEducation(e) {
|
|
e.preventDefault();
|
|
const attendeesRaw = document.getElementById('newAttendees').value.trim();
|
|
const attendees = attendeesRaw ? attendeesRaw.split('\n').map(line => {
|
|
const parts = line.split(',').map(s => s.trim());
|
|
return { name: parts[0] || '', company: parts[1] || '' };
|
|
}).filter(a => a.name) : [];
|
|
|
|
const data = {
|
|
target_type: document.getElementById('newTargetType').value,
|
|
education_date: document.getElementById('newEducationDate').value,
|
|
educator: document.getElementById('newEducator').value.trim() || null,
|
|
attendees: attendees,
|
|
status: document.getElementById('newEduStatus').value || 'planned',
|
|
notes: document.getElementById('newEduNotes').value.trim() || null,
|
|
};
|
|
if (!data.education_date) { showToast('교육일은 필수입니다', 'error'); return; }
|
|
try {
|
|
await api('/education', { method: 'POST', body: JSON.stringify(data) });
|
|
showToast('안전교육이 등록되었습니다');
|
|
closeAddEducation();
|
|
await loadEducation();
|
|
} catch (e) { showToast(e.message, 'error'); }
|
|
}
|
|
|
|
/* ===== Edit education ===== */
|
|
function openEditEducation(id) {
|
|
const edu = educationList.find(x => x.id === id);
|
|
if (!edu) return;
|
|
editingEducationId = id;
|
|
document.getElementById('editTargetType').value = edu.target_type;
|
|
document.getElementById('editEducationDate').value = formatDate(edu.education_date);
|
|
document.getElementById('editEducator').value = edu.educator || '';
|
|
document.getElementById('editEduStatus').value = edu.status;
|
|
document.getElementById('editEduNotes').value = edu.notes || '';
|
|
const attendees = edu.attendees ? (typeof edu.attendees === 'string' ? JSON.parse(edu.attendees) : edu.attendees) : [];
|
|
document.getElementById('editAttendees').value = attendees.map(a => `${a.name}${a.company ? ', ' + a.company : ''}`).join('\n');
|
|
document.getElementById('editEducationModal').classList.remove('hidden');
|
|
}
|
|
function closeEditEducation() {
|
|
document.getElementById('editEducationModal').classList.add('hidden');
|
|
editingEducationId = null;
|
|
}
|
|
|
|
async function submitEditEducation(e) {
|
|
e.preventDefault();
|
|
if (!editingEducationId) return;
|
|
const attendeesRaw = document.getElementById('editAttendees').value.trim();
|
|
const attendees = attendeesRaw ? attendeesRaw.split('\n').map(line => {
|
|
const parts = line.split(',').map(s => s.trim());
|
|
return { name: parts[0] || '', company: parts[1] || '' };
|
|
}).filter(a => a.name) : [];
|
|
|
|
const data = {
|
|
target_type: document.getElementById('editTargetType').value,
|
|
education_date: document.getElementById('editEducationDate').value,
|
|
educator: document.getElementById('editEducator').value.trim() || null,
|
|
attendees: attendees,
|
|
status: document.getElementById('editEduStatus').value,
|
|
notes: document.getElementById('editEduNotes').value.trim() || null,
|
|
};
|
|
try {
|
|
await api(`/education/${editingEducationId}`, { method: 'PUT', body: JSON.stringify(data) });
|
|
showToast('수정되었습니다');
|
|
closeEditEducation();
|
|
await loadEducation();
|
|
} catch (e) { showToast(e.message, 'error'); }
|
|
}
|
|
|
|
async function doDeleteEducation(id) {
|
|
if (!confirm('이 교육 기록을 삭제하시겠습니까?')) return;
|
|
try {
|
|
await api(`/education/${id}`, { method: 'DELETE' });
|
|
showToast('삭제되었습니다');
|
|
await loadEducation();
|
|
} catch (e) { showToast(e.message, 'error'); }
|
|
}
|
|
|
|
function initEducationPage() {
|
|
if (!initAuth()) return;
|
|
document.getElementById('addEducationForm').addEventListener('submit', submitAddEducation);
|
|
document.getElementById('editEducationForm').addEventListener('submit', submitEditEducation);
|
|
document.getElementById('eduDateFrom')?.addEventListener('change', loadEducation);
|
|
document.getElementById('eduDateTo')?.addEventListener('change', loadEducation);
|
|
document.getElementById('eduTargetType')?.addEventListener('change', loadEducation);
|
|
loadEducation();
|
|
}
|