/* ===== Permission Page Definitions ===== */ const SYSTEM1_PAGES = { '작업 관리': [ { key: 's1.dashboard', title: '대시보드', icon: 'fa-chart-line', def: true }, { key: 's1.work.tbm', title: 'TBM 관리', icon: 'fa-hard-hat', def: true }, { key: 's1.work.report_create', title: '작업보고서 작성', icon: 'fa-file-pen', def: true }, { key: 's1.work.analysis', title: '작업 분석', icon: 'fa-magnifying-glass-chart', def: false }, { key: 's1.work.nonconformity', title: '부적합 현황', icon: 'fa-triangle-exclamation', def: true }, ], '공장 관리': [ { key: 's1.factory.repair_management', title: '시설설비 관리', icon: 'fa-wrench', def: false }, { key: 's1.inspection.daily_patrol', title: '일일순회점검', icon: 'fa-clipboard-check', def: false }, { key: 's1.inspection.checkin', title: '출근 체크', icon: 'fa-fingerprint', def: true }, { key: 's1.inspection.work_status', title: '근무 현황', icon: 'fa-user-clock', def: false }, ], '안전 관리': [ { key: 's1.safety.visit_request', title: '출입 신청', icon: 'fa-id-badge', def: true }, { key: 's1.safety.management', title: '안전 관리', icon: 'fa-fire-extinguisher', def: false }, { key: 's1.safety.checklist_manage', title: '체크리스트 관리', icon: 'fa-list-check', def: false }, ], '근태 관리': [ { key: 's1.attendance.my_vacation_info', title: '내 연차 정보', icon: 'fa-umbrella-beach', def: true }, { key: 's1.attendance.monthly', title: '월간 근태', icon: 'fa-calendar-days', def: true }, { key: 's1.attendance.vacation_request', title: '휴가 신청', icon: 'fa-paper-plane', def: true }, { key: 's1.attendance.vacation_management', title: '휴가 관리', icon: 'fa-calendar-check', def: false }, { key: 's1.attendance.vacation_allocation', title: '휴가 발생 입력', icon: 'fa-calendar-plus', def: false }, { key: 's1.attendance.annual_overview', title: '연간 휴가 현황', icon: 'fa-chart-pie', def: false }, ], '시스템 관리': [ { key: 's1.admin.workers', title: '작업자 관리', icon: 'fa-people-group', def: false }, { key: 's1.admin.projects', title: '프로젝트 관리', icon: 'fa-folder-open', def: false }, { key: 's1.admin.tasks', title: '작업 관리', icon: 'fa-list-check', def: false }, { key: 's1.admin.workplaces', title: '작업장 관리', icon: 'fa-warehouse', def: false }, { key: 's1.admin.equipments', title: '설비 관리', icon: 'fa-gears', def: false }, { key: 's1.admin.issue_categories', title: '신고 카테고리', icon: 'fa-tags', def: false }, { key: 's1.admin.attendance_report', title: '출퇴근-보고서 대조', icon: 'fa-scale-balanced', def: false }, ] }; const SYSTEM3_PAGES = { '메인': [ { key: 'issues_dashboard', title: '현황판', icon: 'fa-chart-line', def: true }, { key: 'issues_inbox', title: '수신함', icon: 'fa-inbox', def: true }, { key: 'issues_management', title: '관리함', icon: 'fa-cog', def: false }, { key: 'issues_archive', title: '폐기함', icon: 'fa-archive', def: false }, ], '업무': [ { key: 'daily_work', title: '일일 공수', icon: 'fa-calendar-check', def: false }, { key: 'projects_manage', title: '프로젝트 관리', icon: 'fa-folder-open', def: false }, ], '보고서': [ { key: 'reports', title: '보고서', icon: 'fa-chart-bar', def: false }, { key: 'reports_daily', title: '일일보고서', icon: 'fa-file-excel', def: false }, { key: 'reports_weekly', title: '주간보고서', icon: 'fa-calendar-week', def: false }, { key: 'reports_monthly', title: '월간보고서', icon: 'fa-calendar-alt', def: false }, ], 'AI': [ { key: 'ai_assistant', title: 'AI 어시스턴트', icon: 'fa-robot', def: false }, ] }; const TKPURCHASE_PAGES = { '구매 관리': [ { key: 'purchasing_daylabor', title: '일용공 관리', icon: 'fa-hard-hat', def: false }, { key: 'purchasing_schedule', title: '작업일정 관리', icon: 'fa-calendar-alt', def: false }, { key: 'purchasing_workreport', title: '업무현황 관리', icon: 'fa-clipboard-list', def: false }, { key: 'purchasing_accounts', title: '협력업체 계정', icon: 'fa-user-shield', def: false }, ], '협력업체': [ { key: 'purchasing_partner_portal', title: '협력업체 포털', icon: 'fa-building', def: false }, { key: 'purchasing_partner_checkin', title: '협력업체 체크인', icon: 'fa-check-circle', def: false }, ] }; const TKSAFETY_PAGES = { '안전 관리': [ { key: 'safety_visit', title: '방문 관리', icon: 'fa-door-open', def: false }, { key: 'safety_education', title: '안전교육 관리', icon: 'fa-graduation-cap', def: false }, ] }; const TKUSER_PAGES = { '통합 관리': [ { key: 'tkuser.users', title: '사용자 관리', icon: 'fa-users', def: false, tab: 'users' }, { key: 'tkuser.projects', title: '프로젝트 관리', icon: 'fa-folder-open', def: false, tab: 'projects' }, { key: 'tkuser.workplaces', title: '작업장 관리', icon: 'fa-building', def: false, tab: 'workplaces' }, { key: 'tkuser.workers', title: '작업자 관리', icon: 'fa-hard-hat', def: false, tab: 'workers' }, { key: 'tkuser.departments', title: '부서 관리', icon: 'fa-sitemap', def: false, tab: 'departments' }, { key: 'tkuser.issue_types', title: '이슈 유형 관리', icon: 'fa-exclamation-triangle', def: false, tab: 'issueTypes' }, { key: 'tkuser.tasks', title: '작업 관리', icon: 'fa-tasks', def: false, tab: 'tasks' }, { key: 'tkuser.vacations', title: '휴가 관리', icon: 'fa-umbrella-beach', def: false, tab: 'vacations' }, { key: 'tkuser.partners', title: '협력업체 관리', icon: 'fa-truck', def: false, tab: 'partners' }, { key: 'tkuser.notification_recipients', title: '알림 수신자 관리', icon: 'fa-bell', def: false, tab: 'notificationRecipients' }, ] }; /* ===== Permissions Tab State ===== */ let permissionsTabLoaded = false; function loadPermissionsTab() { populateDeptPermSelect(); updatePermissionUserSelect(); permissionsTabLoaded = true; } /* ===== Users State ===== */ let users = [], selectedUserId = null, currentPermissions = {}, currentPermSources = {}, currentDeptGranted = {}; /* ===== Users CRUD ===== */ async function loadUsers() { try { const r = await api('/users'); users = r.data || r; displayUsers(); updatePermissionUserSelect(); populateUserDeptSelects(); } catch (err) { document.getElementById('userList').innerHTML = `

${err.message}

`; } } function populateUserDeptSelects() { ['newDepartmentId','editDepartmentId'].forEach(id => { const sel = document.getElementById(id); if (!sel) return; const val = sel.value; sel.innerHTML = ''; departmentsCache.forEach(d => { const o = document.createElement('option'); o.value = d.department_id; o.textContent = d.department_name; sel.appendChild(o); }); sel.value = val; }); } function displayUsers() { const c = document.getElementById('userList'); if (!users.length) { c.innerHTML = '

등록된 사용자가 없습니다.

'; return; } c.innerHTML = users.map(u => `
${u.name||u.username}
${u.username} ${u.department||u.department_id?`${deptLabel(u.department, u.department_id)}`:''} ${u.role==='admin'?'관리자':'사용자'} ${u.is_active===0||u.is_active===false?'비활성':''}
${u.username!=='hyungi'?``:''}
`).join(''); } document.getElementById('addUserForm').addEventListener('submit', async e => { e.preventDefault(); const deptIdVal = document.getElementById('newDepartmentId').value; try { await api('/users', { method:'POST', body: JSON.stringify({ username: document.getElementById('newUsername').value.trim(), name: document.getElementById('newFullName').value.trim(), password: document.getElementById('newPassword').value, department_id: deptIdVal ? parseInt(deptIdVal) : null, role: document.getElementById('newRole').value }) }); showToast('사용자가 추가되었습니다.'); document.getElementById('addUserForm').reset(); await loadUsers(); } catch(e) { showToast(e.message,'error'); } }); function editUser(id) { const u = users.find(x=>x.user_id===id); if(!u) return; document.getElementById('editUserId').value=u.user_id; document.getElementById('editUsername').value=u.username; document.getElementById('editFullName').value=u.name||''; populateUserDeptSelects(); document.getElementById('editDepartmentId').value=u.department_id||''; document.getElementById('editRole').value=u.role; document.getElementById('editUserModal').classList.remove('hidden'); } function closeEditModal() { document.getElementById('editUserModal').classList.add('hidden'); } document.getElementById('editUserForm').addEventListener('submit', async e => { e.preventDefault(); const deptIdVal = document.getElementById('editDepartmentId').value; try { await api(`/users/${document.getElementById('editUserId').value}`, { method:'PUT', body: JSON.stringify({ name: document.getElementById('editFullName').value.trim()||null, department_id: deptIdVal ? parseInt(deptIdVal) : null, role: document.getElementById('editRole').value }) }); showToast('수정되었습니다.'); closeEditModal(); await loadUsers(); } catch(e) { showToast(e.message,'error'); } }); async function resetPassword(id, name) { if (!confirm(`${name}의 비밀번호를 "000000"으로 초기화?`)) return; try { await api(`/users/${id}/reset-password`,{method:'POST',body:JSON.stringify({new_password:'000000'})}); showToast(`${name} 비밀번호 초기화 완료`); } catch(e) { showToast(e.message,'error'); } } async function deleteUser(id, name) { if (!confirm(`${name}을(를) 비활성화?`)) return; try { await api(`/users/${id}`,{method:'DELETE'}); showToast('비활성화 완료'); await loadUsers(); } catch(e) { showToast(e.message,'error'); } } document.getElementById('changePasswordForm').addEventListener('submit', async e => { e.preventDefault(); const np = document.getElementById('newPasswordChange').value; if (np !== document.getElementById('confirmPassword').value) { showToast('비밀번호 불일치','error'); return; } try { await api('/users/change-password',{method:'POST',body:JSON.stringify({current_password:document.getElementById('currentPassword').value,new_password:np})}); showToast('비밀번호 변경 완료'); document.getElementById('changePasswordForm').reset(); } catch(e) { showToast(e.message,'error'); } }); /* ===== Permissions ===== */ function updatePermissionUserSelect() { const sel = document.getElementById('permissionUserSelect'); sel.innerHTML = ''; users.filter(u=>u.role==='user').forEach(u => { const o=document.createElement('option'); o.value=u.user_id; o.textContent=`${u.name||u.username} (${u.username})`; sel.appendChild(o); }); } document.getElementById('permissionUserSelect').addEventListener('change', async e => { selectedUserId = e.target.value; if (selectedUserId) { await loadUserPermissions(selectedUserId); renderPermissionGrid(); document.getElementById('permissionPanel').classList.remove('hidden'); document.getElementById('permissionEmpty').classList.add('hidden'); } else { document.getElementById('permissionPanel').classList.add('hidden'); document.getElementById('permissionEmpty').classList.remove('hidden'); } }); async function loadUserPermissions(userId) { currentPermissions = {}; currentPermSources = {}; currentDeptGranted = {}; const allDefs = { ...SYSTEM1_PAGES, ...SYSTEM3_PAGES, ...TKPURCHASE_PAGES, ...TKSAFETY_PAGES, ...TKUSER_PAGES }; Object.values(allDefs).flat().forEach(p => { currentPermissions[p.key] = p.def; currentPermSources[p.key] = 'default'; currentDeptGranted[p.key] = false; }); try { const result = await api(`/permissions/users/${userId}/effective-permissions`); if (result.permissions) { for (const [pageName, info] of Object.entries(result.permissions)) { currentPermissions[pageName] = info.can_access; currentPermSources[pageName] = info.source; currentDeptGranted[pageName] = !!info.dept_granted; } } } catch(e) { console.warn('권한 로드 실패:', e); } } function renderPermissionGrid() { renderSystemPerms('s1-perms', SYSTEM1_PAGES, 'blue'); renderSystemPerms('s3-perms', SYSTEM3_PAGES, 'purple'); renderSystemPerms('tkpurchase-perms', TKPURCHASE_PAGES, 'green'); renderSystemPerms('tksafety-perms', TKSAFETY_PAGES, 'orange'); renderSystemPerms('tkuser-perms', TKUSER_PAGES, 'slate'); } function sourceLabel(src) { if (src === 'explicit') return '개인'; if (src === 'department') return '부서'; return ''; } function renderSystemPerms(containerId, pageDef, color) { const container = document.getElementById(containerId); let html = ''; Object.entries(pageDef).forEach(([groupName, pages]) => { const groupId = containerId + '-' + groupName.replace(/\s/g,''); const allChecked = pages.every(p => currentPermissions[p.key]); html += `
${groupName} ${pages.length}
${pages.map(p => { const checked = currentPermissions[p.key] || false; const src = currentPermSources[p.key] || 'default'; const deptLocked = currentDeptGranted[p.key] || false; if (deptLocked) { return ` `; } return ` `; }).join('')}
`; }); container.innerHTML = html; } function onPermChange(cb) { const item = cb.closest('.perm-item'); const icon = item.querySelector('i[data-color]'); const color = icon.dataset.color; item.classList.toggle('checked', cb.checked); icon.classList.toggle(`text-${color}-500`, cb.checked); icon.classList.toggle('text-gray-400', !cb.checked); // 그룹 전체 체크박스 동기화 const group = item.dataset.group; const groupCbs = document.querySelectorAll(`[data-group="${group}"] input[type="checkbox"]`); const allChecked = [...groupCbs].every(c => c.checked); const groupHeader = document.getElementById(group)?.previousElementSibling; if (groupHeader) { const gc = groupHeader.querySelector('input[type="checkbox"]'); if(gc) gc.checked = allChecked; } } function toggleGroup(groupId) { const el = document.getElementById(groupId); const arrow = document.getElementById('arrow-' + groupId); el.classList.toggle('hidden'); arrow?.classList.toggle('-rotate-90'); } function toggleGroupAll(groupId, checked) { document.querySelectorAll(`#${groupId} input[type="checkbox"]:not(:disabled)`).forEach(cb => { cb.checked = checked; onPermChange(cb); }); } function toggleSystemAll(prefix, checked) { const containerMap = { s1: 's1-perms', s3: 's3-perms', tkpurchase: 'tkpurchase-perms' }; const containerId = containerMap[prefix] || prefix + '-perms'; document.querySelectorAll(`#${containerId} input[type="checkbox"]:not(:disabled)`).forEach(cb => { cb.checked = checked; onPermChange(cb); }); // 그룹 전체 체크박스도 동기화 document.querySelectorAll(`#${containerId} .group-header input[type="checkbox"]`).forEach(cb => cb.checked = checked); } // 저장 document.getElementById('savePermissionsBtn').addEventListener('click', async () => { if (!selectedUserId) return; const btn = document.getElementById('savePermissionsBtn'); const st = document.getElementById('permissionSaveStatus'); btn.disabled = true; btn.innerHTML = '저장 중...'; try { const allPages = [...Object.values(SYSTEM1_PAGES).flat(), ...Object.values(SYSTEM3_PAGES).flat(), ...Object.values(TKPURCHASE_PAGES).flat(), ...Object.values(TKSAFETY_PAGES).flat(), ...Object.values(TKUSER_PAGES).flat()]; const permissions = allPages .filter(p => !currentDeptGranted[p.key]) .map(p => { const cb = document.getElementById('perm_' + p.key); return { page_name: p.key, can_access: cb ? cb.checked : false }; }); await api('/permissions/bulk-grant', { method:'POST', body: JSON.stringify({ user_id: parseInt(selectedUserId), permissions }) }); st.textContent = '저장 완료'; st.className = 'text-sm text-emerald-600'; showToast('권한이 저장되었습니다.'); setTimeout(() => { st.textContent = ''; }, 3000); } catch(e) { st.textContent = e.message; st.className = 'text-sm text-red-500'; showToast('저장 실패: ' + e.message, 'error'); } finally { btn.disabled = false; btn.innerHTML = '권한 저장'; } }); /* ===== Department Permissions ===== */ let selectedDeptId = null, deptPermissions = {}; function populateDeptPermSelect() { const sel = document.getElementById('deptPermSelect'); if (!sel) return; sel.innerHTML = ''; departmentsCache.forEach(d => { const o = document.createElement('option'); o.value = d.department_id; o.textContent = d.department_name; sel.appendChild(o); }); } document.addEventListener('DOMContentLoaded', () => { const sel = document.getElementById('deptPermSelect'); if (sel) sel.addEventListener('change', async e => { selectedDeptId = e.target.value; if (selectedDeptId) { await loadDeptPermissions(selectedDeptId); renderDeptPermissionGrid(); document.getElementById('deptPermPanel').classList.remove('hidden'); document.getElementById('deptPermEmpty').classList.add('hidden'); } else { document.getElementById('deptPermPanel').classList.add('hidden'); document.getElementById('deptPermEmpty').classList.remove('hidden'); } }); const saveBtn = document.getElementById('saveDeptPermBtn'); if (saveBtn) saveBtn.addEventListener('click', saveDeptPermissions); const resetBtn = document.getElementById('resetToDefaultBtn'); if (resetBtn) resetBtn.addEventListener('click', resetUserPermToDefault); }); async function loadDeptPermissions(deptId) { deptPermissions = {}; const allDefs = { ...SYSTEM1_PAGES, ...SYSTEM3_PAGES, ...TKPURCHASE_PAGES, ...TKSAFETY_PAGES, ...TKUSER_PAGES }; Object.values(allDefs).flat().forEach(p => { deptPermissions[p.key] = p.def; }); try { const result = await api(`/permissions/departments/${deptId}/permissions`); (result.data || []).forEach(p => { deptPermissions[p.page_name] = !!p.can_access; }); } catch(e) { console.warn('부서 권한 로드 실패:', e); } } function renderDeptPermissionGrid() { renderDeptSystemPerms('dept-s1-perms', SYSTEM1_PAGES, 'blue'); renderDeptSystemPerms('dept-s3-perms', SYSTEM3_PAGES, 'purple'); renderDeptSystemPerms('dept-tkpurchase-perms', TKPURCHASE_PAGES, 'green'); renderDeptSystemPerms('dept-tksafety-perms', TKSAFETY_PAGES, 'orange'); renderDeptSystemPerms('dept-tkuser-perms', TKUSER_PAGES, 'slate'); } function renderDeptSystemPerms(containerId, pageDef, color) { const container = document.getElementById(containerId); if (!container) return; let html = ''; Object.entries(pageDef).forEach(([groupName, pages]) => { const groupId = containerId + '-' + groupName.replace(/\s/g,''); const allChecked = pages.every(p => deptPermissions[p.key]); html += `
${groupName} ${pages.length}
${pages.map(p => { const checked = deptPermissions[p.key] || false; return ` `; }).join('')}
`; }); container.innerHTML = html; } function onDeptPermChange(cb) { const item = cb.closest('.perm-item'); const icon = item.querySelector('i[data-color]'); const color = icon.dataset.color; item.classList.toggle('checked', cb.checked); icon.classList.toggle(`text-${color}-500`, cb.checked); icon.classList.toggle('text-gray-400', !cb.checked); const group = item.dataset.group; const groupCbs = document.querySelectorAll(`[data-group="${group}"] input[type="checkbox"]`); const allChecked = [...groupCbs].every(c => c.checked); const groupHeader = document.getElementById(group)?.previousElementSibling; if (groupHeader) { const gc = groupHeader.querySelector('input[type="checkbox"]'); if(gc) gc.checked = allChecked; } } function toggleDeptGroupAll(groupId, checked) { document.querySelectorAll(`#${groupId} input[type="checkbox"]`).forEach(cb => { cb.checked = checked; onDeptPermChange(cb); }); } function toggleDeptSystemAll(prefix, checked) { const containerMap = { s1: 'dept-s1-perms', s3: 'dept-s3-perms', tkpurchase: 'dept-tkpurchase-perms' }; const containerId = containerMap[prefix] || 'dept-' + prefix + '-perms'; document.querySelectorAll(`#${containerId} input[type="checkbox"]`).forEach(cb => { cb.checked = checked; onDeptPermChange(cb); }); document.querySelectorAll(`#${containerId} .group-header input[type="checkbox"]`).forEach(cb => cb.checked = checked); } async function saveDeptPermissions() { if (!selectedDeptId) return; const btn = document.getElementById('saveDeptPermBtn'); const st = document.getElementById('deptPermSaveStatus'); btn.disabled = true; btn.innerHTML = '저장 중...'; try { const allPages = [...Object.values(SYSTEM1_PAGES).flat(), ...Object.values(SYSTEM3_PAGES).flat(), ...Object.values(TKPURCHASE_PAGES).flat(), ...Object.values(TKSAFETY_PAGES).flat(), ...Object.values(TKUSER_PAGES).flat()]; const permissions = allPages.map(p => { const cb = document.getElementById('dperm_' + p.key); return { page_name: p.key, can_access: cb ? cb.checked : false }; }); await api(`/permissions/departments/${selectedDeptId}/bulk-set`, { method:'POST', body: JSON.stringify({ permissions }) }); st.textContent = '저장 완료'; st.className = 'text-sm text-emerald-600'; showToast('부서 권한이 저장되었습니다.'); setTimeout(() => { st.textContent = ''; }, 3000); } catch(e) { st.textContent = e.message; st.className = 'text-sm text-red-500'; showToast('저장 실패: ' + e.message, 'error'); } finally { btn.disabled = false; btn.innerHTML = '부서 권한 저장'; } } async function resetUserPermToDefault() { if (!selectedUserId) return; if (!confirm('이 사용자의 개인 권한을 모두 삭제하고 부서 기본 권한으로 되돌리시겠습니까?')) return; try { const perms = await api(`/users/${selectedUserId}/page-permissions`); const permArr = Array.isArray(perms) ? perms : []; for (const p of permArr) { await api(`/permissions/${p.id}`, { method: 'DELETE' }); } showToast('부서 기본 권한으로 초기화되었습니다.'); await loadUserPermissions(selectedUserId); renderPermissionGrid(); } catch(e) { showToast('초기화 실패: ' + e.message, 'error'); } } /* ===== Workers CRUD ===== */ let workers = [], workersLoaded = false, departmentsForSelect = []; const JOB_TYPE = { leader: '반장', worker: '작업자' }; function jobTypeBadge(t) { if (t === 'leader') return '반장'; if (t === 'worker') return '작업자'; return t ? `${t}` : ''; } function workerStatusBadge(s) { if (s === 'inactive') return '비활성'; return '재직'; } async function loadDepartmentsForSelect() { try { const r = await api('/departments'); departmentsForSelect = (r.data || r).filter(d => d.is_active !== 0 && d.is_active !== false); populateDeptSelects(); } catch(e) { console.warn('부서 로드 실패:', e); } } function populateDeptSelects() { ['newWorkerDept','editWorkerDept'].forEach(id => { const sel = document.getElementById(id); if (!sel) return; const val = sel.value; sel.innerHTML = ''; departmentsForSelect.forEach(d => { const o = document.createElement('option'); o.value = d.department_id; o.textContent = d.department_name; sel.appendChild(o); }); sel.value = val; }); } async function loadWorkers() { await loadDepartmentsForSelect(); try { const r = await api('/workers'); workers = r.data || r; workersLoaded = true; displayWorkers(); } catch (err) { document.getElementById('workerList').innerHTML = `

${err.message}

`; } } function displayWorkers() { const c = document.getElementById('workerList'); if (!workers.length) { c.innerHTML = '

등록된 작업자가 없습니다.

'; return; } c.innerHTML = workers.map(w => `
${w.worker_name}
${jobTypeBadge(w.job_type)} ${w.department_name ? `${w.department_name}` : ''} ${workerStatusBadge(w.status)} ${w.phone_number ? `${w.phone_number}` : ''}
${w.status !== 'inactive' ? `` : ''}
`).join(''); } document.getElementById('addWorkerForm').addEventListener('submit', async e => { e.preventDefault(); try { await api('/workers', { method: 'POST', body: JSON.stringify({ worker_name: document.getElementById('newWorkerName').value.trim(), job_type: document.getElementById('newJobType').value || null, department_id: document.getElementById('newWorkerDept').value ? parseInt(document.getElementById('newWorkerDept').value) : null, phone_number: document.getElementById('newWorkerPhone').value.trim() || null, hire_date: document.getElementById('newWorkerHireDate').value || null, notes: document.getElementById('newWorkerNotes').value.trim() || null })}); showToast('작업자가 추가되었습니다.'); document.getElementById('addWorkerForm').reset(); await loadWorkers(); } catch(e) { showToast(e.message, 'error'); } }); function editWorker(id) { const w = workers.find(x => x.worker_id === id); if (!w) return; document.getElementById('editWorkerId').value = w.worker_id; document.getElementById('editWorkerName').value = w.worker_name; document.getElementById('editJobType').value = w.job_type || ''; document.getElementById('editWorkerDept').value = w.department_id || ''; document.getElementById('editWorkerPhone').value = w.phone_number || ''; document.getElementById('editWorkerHireDate').value = formatDate(w.hire_date); document.getElementById('editWorkerNotes').value = w.notes || ''; document.getElementById('editWorkerStatus').value = w.status || 'active'; document.getElementById('editEmploymentStatus').value = w.employment_status || 'employed'; populateDeptSelects(); document.getElementById('editWorkerDept').value = w.department_id || ''; document.getElementById('editWorkerModal').classList.remove('hidden'); } function closeWorkerModal() { document.getElementById('editWorkerModal').classList.add('hidden'); } document.getElementById('editWorkerForm').addEventListener('submit', async e => { e.preventDefault(); try { await api(`/workers/${document.getElementById('editWorkerId').value}`, { method: 'PUT', body: JSON.stringify({ worker_name: document.getElementById('editWorkerName').value.trim(), job_type: document.getElementById('editJobType').value || null, department_id: document.getElementById('editWorkerDept').value ? parseInt(document.getElementById('editWorkerDept').value) : null, phone_number: document.getElementById('editWorkerPhone').value.trim() || null, hire_date: document.getElementById('editWorkerHireDate').value || null, notes: document.getElementById('editWorkerNotes').value.trim() || null, status: document.getElementById('editWorkerStatus').value, employment_status: document.getElementById('editEmploymentStatus').value })}); showToast('수정되었습니다.'); closeWorkerModal(); await loadWorkers(); } catch(e) { showToast(e.message, 'error'); } }); async function deactivateWorker(id, name) { if (!confirm(`"${name}" 작업자를 비활성화?`)) return; try { await api(`/workers/${id}`, { method: 'DELETE' }); showToast('작업자 비활성화 완료'); await loadWorkers(); } catch(e) { showToast(e.message, 'error'); } }