/* ===== tkuser 협력업체 CRUD ===== */ let partnersLoaded = false; let partnersList = []; let partnerWorkersList = []; let selectedPartnerIdTkuser = null; let editingWorkerIdTkuser = null; async function loadPartnersTab() { if (partnersLoaded) return; partnersLoaded = true; if (currentUser && ['admin', 'system'].includes(currentUser.role)) { document.getElementById('btnAddPartnerTkuser')?.classList.remove('hidden'); } await loadPartnersList(); } async function loadPartnersList() { try { const isActive = document.getElementById('partnerFilterActiveTkuser')?.value; const search = document.getElementById('partnerSearchTkuser')?.value?.trim() || ''; const params = new URLSearchParams(); if (isActive !== '' && isActive !== undefined) params.set('is_active', isActive); if (search) params.set('search', search); const r = await api('/partners?' + params.toString()); partnersList = r.data || []; renderPartnersListTkuser(); } catch (e) { document.getElementById('partnersListTkuser').innerHTML = `

${e.message}

`; } } function renderPartnersListTkuser() { const c = document.getElementById('partnersListTkuser'); if (!partnersList.length) { c.innerHTML = '

등록된 협력업체가 없습니다.

'; return; } const isAdmin = currentUser && ['admin', 'system'].includes(currentUser.role); c.innerHTML = partnersList.map(p => { const types = tryParseJsonTkuser(p.business_type) || []; const typeStr = types.map(t => `${escHtml(t)}`).join(' '); const insuranceWarn = isInsuranceExpiringSoonTkuser(p.insurance_expiry); return `
${escHtml(p.company_name)} ${!p.is_active ? '비활성' : ''} ${insuranceWarn ? '보험만료' : ''}
${p.business_number ? `${p.business_number}` : ''} ${p.representative ? `${escHtml(p.representative)}` : ''} ${typeStr}
${isAdmin ? `
${p.is_active ? `` : ''}
` : ''}
`; }).join(''); } async function selectPartnerTkuser(id) { selectedPartnerIdTkuser = id; renderPartnersListTkuser(); try { const r = await api(`/partners/${id}`); const p = r.data; partnerWorkersList = p.workers || []; renderPartnerDetailTkuser(p); document.getElementById('partnerDetailTkuser').classList.remove('hidden'); document.getElementById('partnerEmptyTkuser').classList.add('hidden'); } catch (e) { showToast('상세 조회 실패: ' + e.message, 'error'); } } function renderPartnerDetailTkuser(p) { const types = tryParseJsonTkuser(p.business_type) || []; const workers = p.workers || []; const isAdmin = currentUser && ['admin', 'system'].includes(currentUser.role); document.getElementById('partnerDetailTkuser').innerHTML = `

${escHtml(p.company_name)}

사업자번호: ${escHtml(p.business_number) || '-'}
대표자: ${escHtml(p.representative) || '-'}
담당자: ${escHtml(p.contact_name) || '-'}
연락처: ${escHtml(p.contact_phone) || '-'}
주소: ${escHtml(p.address) || '-'}
업종: ${types.map(t => `${escHtml(t)}`).join(' ') || '-'}
산재보험: ${escHtml(p.insurance_number) || '-'} ${p.insurance_expiry ? `(만료: ${formatDate(p.insurance_expiry)})` : ''}
${p.notes ? `
비고: ${escHtml(p.notes)}
` : ''}

소속 작업자 (${workers.length}명)

${isAdmin ? `` : ''}
${workers.length ? workers.map(w => `
${escHtml(w.worker_name)} ${w.is_team_leader ? '팀장' : ''} ${!w.is_active ? '비활성' : ''}
${w.position ? `${escHtml(w.position)}` : ''} ${w.phone ? `${escHtml(w.phone)}` : ''} ${w.safety_training_date ? `안전교육: ${formatDate(w.safety_training_date)}` : ''}
${isAdmin ? `
${w.is_active ? `` : ''}
` : ''}
`).join('') : '

등록된 작업자가 없습니다

'}
`; } /* ===== 업체 등록 ===== */ function openAddPartnerTkuser() { document.getElementById('addPartnerModalTkuser').classList.remove('hidden'); } function closeAddPartnerTkuser() { document.getElementById('addPartnerModalTkuser').classList.add('hidden'); document.getElementById('addPartnerFormTkuser').reset(); } async function submitAddPartnerTkuser(e) { e.preventDefault(); const typesRaw = document.getElementById('newPartnerBusinessTypeTkuser').value.trim(); const data = { company_name: document.getElementById('newPartnerCompanyNameTkuser').value.trim(), business_number: document.getElementById('newPartnerBusinessNumberTkuser').value.trim() || null, representative: document.getElementById('newPartnerRepresentativeTkuser').value.trim() || null, contact_name: document.getElementById('newPartnerContactNameTkuser').value.trim() || null, contact_phone: document.getElementById('newPartnerContactPhoneTkuser').value.trim() || null, address: document.getElementById('newPartnerAddressTkuser').value.trim() || null, business_type: typesRaw ? typesRaw.split(',').map(s => s.trim()).filter(Boolean) : null, insurance_number: document.getElementById('newPartnerInsuranceNumberTkuser').value.trim() || null, insurance_expiry: document.getElementById('newPartnerInsuranceExpiryTkuser').value || null, notes: document.getElementById('newPartnerNotesTkuser').value.trim() || null, }; if (!data.company_name) { showToast('업체명은 필수입니다', 'error'); return; } try { await api('/partners', { method: 'POST', body: JSON.stringify(data) }); showToast('업체가 등록되었습니다'); closeAddPartnerTkuser(); await loadPartnersList(); } catch (e) { showToast(e.message, 'error'); } } /* ===== 업체 수정 ===== */ function openEditPartnerTkuser(id) { const p = partnersList.find(x => x.id === id); if (!p) return; const types = tryParseJsonTkuser(p.business_type) || []; document.getElementById('editPartnerIdTkuser').value = p.id; document.getElementById('editPartnerCompanyNameTkuser').value = p.company_name; document.getElementById('editPartnerBusinessNumberTkuser').value = p.business_number || ''; document.getElementById('editPartnerRepresentativeTkuser').value = p.representative || ''; document.getElementById('editPartnerContactNameTkuser').value = p.contact_name || ''; document.getElementById('editPartnerContactPhoneTkuser').value = p.contact_phone || ''; document.getElementById('editPartnerAddressTkuser').value = p.address || ''; document.getElementById('editPartnerBusinessTypeTkuser').value = types.join(', '); document.getElementById('editPartnerInsuranceNumberTkuser').value = p.insurance_number || ''; document.getElementById('editPartnerInsuranceExpiryTkuser').value = p.insurance_expiry ? formatDate(p.insurance_expiry) : ''; document.getElementById('editPartnerNotesTkuser').value = p.notes || ''; document.getElementById('editPartnerModalTkuser').classList.remove('hidden'); } function closeEditPartnerTkuser() { document.getElementById('editPartnerModalTkuser').classList.add('hidden'); } async function submitEditPartnerTkuser(e) { e.preventDefault(); const id = document.getElementById('editPartnerIdTkuser').value; const typesRaw = document.getElementById('editPartnerBusinessTypeTkuser').value.trim(); const data = { company_name: document.getElementById('editPartnerCompanyNameTkuser').value.trim(), business_number: document.getElementById('editPartnerBusinessNumberTkuser').value.trim() || null, representative: document.getElementById('editPartnerRepresentativeTkuser').value.trim() || null, contact_name: document.getElementById('editPartnerContactNameTkuser').value.trim() || null, contact_phone: document.getElementById('editPartnerContactPhoneTkuser').value.trim() || null, address: document.getElementById('editPartnerAddressTkuser').value.trim() || null, business_type: typesRaw ? typesRaw.split(',').map(s => s.trim()).filter(Boolean) : null, insurance_number: document.getElementById('editPartnerInsuranceNumberTkuser').value.trim() || null, insurance_expiry: document.getElementById('editPartnerInsuranceExpiryTkuser').value || null, notes: document.getElementById('editPartnerNotesTkuser').value.trim() || null, }; try { await api(`/partners/${id}`, { method: 'PUT', body: JSON.stringify(data) }); showToast('수정되었습니다'); closeEditPartnerTkuser(); await loadPartnersList(); if (selectedPartnerIdTkuser == id) selectPartnerTkuser(id); } catch (e) { showToast(e.message, 'error'); } } /* ===== 업체 비활성화 ===== */ async function deactivatePartnerTkuser(id, name) { if (!confirm(`"${name}" 업체를 비활성화하시겠습니까?`)) return; try { await api(`/partners/${id}`, { method: 'DELETE' }); showToast('비활성화 완료'); await loadPartnersList(); if (selectedPartnerIdTkuser === id) { document.getElementById('partnerDetailTkuser').classList.add('hidden'); document.getElementById('partnerEmptyTkuser').classList.remove('hidden'); selectedPartnerIdTkuser = null; } } catch (e) { showToast(e.message, 'error'); } } /* ===== 작업자 등록 ===== */ function openAddWorkerTkuser() { if (!selectedPartnerIdTkuser) { showToast('업체를 먼저 선택해주세요', 'error'); return; } document.getElementById('addWorkerModalTkuser').classList.remove('hidden'); } function closeAddWorkerTkuser() { document.getElementById('addWorkerModalTkuser').classList.add('hidden'); document.getElementById('addWorkerFormTkuser').reset(); } async function submitAddWorkerTkuser(e) { e.preventDefault(); const data = { worker_name: document.getElementById('newWorkerNameTkuser').value.trim(), position: document.getElementById('newWorkerPositionTkuser').value.trim() || null, is_team_leader: document.getElementById('newWorkerIsLeaderTkuser').checked, phone: document.getElementById('newWorkerPhoneTkuser').value.trim() || null, safety_training_date: document.getElementById('newWorkerSafetyDateTkuser').value || null, notes: document.getElementById('newWorkerNotesTkuser').value.trim() || null, }; if (!data.worker_name) { showToast('작업자명은 필수입니다', 'error'); return; } try { await api(`/partners/${selectedPartnerIdTkuser}/workers`, { method: 'POST', body: JSON.stringify(data) }); showToast('작업자가 등록되었습니다'); closeAddWorkerTkuser(); await selectPartnerTkuser(selectedPartnerIdTkuser); } catch (e) { showToast(e.message, 'error'); } } /* ===== 작업자 수정 ===== */ function openEditWorkerTkuser(id) { const w = partnerWorkersList.find(x => x.id === id); if (!w) return; editingWorkerIdTkuser = id; document.getElementById('editWorkerNameTkuser').value = w.worker_name; document.getElementById('editWorkerPositionTkuser').value = w.position || ''; document.getElementById('editWorkerIsLeaderTkuser').checked = w.is_team_leader; document.getElementById('editWorkerPhoneTkuser').value = w.phone || ''; document.getElementById('editWorkerSafetyDateTkuser').value = w.safety_training_date ? formatDate(w.safety_training_date) : ''; document.getElementById('editWorkerNotesTkuser').value = w.notes || ''; document.getElementById('editWorkerModalTkuser').classList.remove('hidden'); } function closeEditWorkerTkuser() { document.getElementById('editWorkerModalTkuser').classList.add('hidden'); editingWorkerIdTkuser = null; } async function submitEditWorkerTkuser(e) { e.preventDefault(); if (!editingWorkerIdTkuser) return; const data = { worker_name: document.getElementById('editWorkerNameTkuser').value.trim(), position: document.getElementById('editWorkerPositionTkuser').value.trim() || null, is_team_leader: document.getElementById('editWorkerIsLeaderTkuser').checked, phone: document.getElementById('editWorkerPhoneTkuser').value.trim() || null, safety_training_date: document.getElementById('editWorkerSafetyDateTkuser').value || null, notes: document.getElementById('editWorkerNotesTkuser').value.trim() || null, }; try { await api(`/partners/workers/${editingWorkerIdTkuser}`, { method: 'PUT', body: JSON.stringify(data) }); showToast('수정되었습니다'); closeEditWorkerTkuser(); await selectPartnerTkuser(selectedPartnerIdTkuser); } catch (e) { showToast(e.message, 'error'); } } async function deactivateWorkerTkuser(id) { if (!confirm('이 작업자를 비활성화하시겠습니까?')) return; try { await api(`/partners/workers/${id}`, { method: 'DELETE' }); showToast('비활성화 완료'); await selectPartnerTkuser(selectedPartnerIdTkuser); } catch (e) { showToast(e.message, 'error'); } } function tryParseJsonTkuser(val) { if (!val) return null; if (Array.isArray(val)) return val; try { return JSON.parse(val); } catch { return null; } } function isInsuranceExpiringSoonTkuser(expiry) { if (!expiry) return false; const exp = new Date(expiry); const now = new Date(); const diff = (exp - now) / (1000 * 60 * 60 * 24); return diff <= 30 && diff >= 0; } // 검색/필터 이벤트 + 모달 폼 이벤트 document.addEventListener('DOMContentLoaded', () => { let searchTimeout; const searchEl = document.getElementById('partnerSearchTkuser'); if (searchEl) searchEl.addEventListener('input', () => { clearTimeout(searchTimeout); searchTimeout = setTimeout(loadPartnersList, 300); }); const filterEl = document.getElementById('partnerFilterActiveTkuser'); if (filterEl) filterEl.addEventListener('change', loadPartnersList); document.getElementById('addPartnerFormTkuser')?.addEventListener('submit', submitAddPartnerTkuser); document.getElementById('editPartnerFormTkuser')?.addEventListener('submit', submitEditPartnerTkuser); document.getElementById('addWorkerFormTkuser')?.addEventListener('submit', submitAddWorkerTkuser); document.getElementById('editWorkerFormTkuser')?.addEventListener('submit', submitEditWorkerTkuser); });