/* ===== Departments CRUD ===== */ let departments = [], departmentsLoaded = false; const APPROVAL_TYPE_LABELS = { VACATION: '휴가', PURCHASE: '구매', DOCUMENT: '문서' }; async function loadDepartments() { try { const r = await api('/departments'); departments = r.data || r; departmentsLoaded = true; displayDepartments(); } catch (err) { document.getElementById('departmentList').innerHTML = `

${err.message}

`; } } let selectedDeptForMembers = null; function displayDepartments() { const c = document.getElementById('departmentList'); if (!departments.length) { c.innerHTML = '

등록된 부서가 없습니다.

'; return; } c.innerHTML = departments.map(d => `
${escHtml(d.department_name)}
순서: ${d.display_order || 0} | 팀장: ${d.leader_name ? escHtml(d.leader_name) : '미지정'}
`).join(''); } async function showDeptMembers(deptId) { selectedDeptForMembers = deptId; displayDepartments(); const panel = document.getElementById('deptMembersPanel'); const list = document.getElementById('deptMembersList'); panel.classList.remove('hidden'); list.innerHTML = '
'; let deptUsers = users; if (!deptUsers || !deptUsers.length) { try { const r = await api('/users'); deptUsers = r.data || r; } catch (e) { list.innerHTML = `

${e.message}

`; return; } } const members = deptUsers.filter(u => u.department_id === deptId); const dept = departments.find(d => d.department_id === deptId); const title = panel.querySelector('h3'); if (title) title.innerHTML = `소속 인원 — ${dept ? escHtml(dept.department_name) : ''}`; if (!members.length) { list.innerHTML = '

소속 인원이 없습니다

'; } else { list.innerHTML = members.map(u => `
${(u.name || u.username).charAt(0)}
${u.name || u.username}
${u.username}
${u.role === 'admin' ? '관리자' : '사용자'} ${u.is_active === 0 || u.is_active === false ? '비활성' : '활성'}
`).join(''); } loadApprovalAuthorities(deptId); } document.getElementById('addDepartmentForm').addEventListener('submit', async e => { e.preventDefault(); try { await api('/departments', { method: 'POST', body: JSON.stringify({ department_name: document.getElementById('newDeptName').value.trim(), description: document.getElementById('newDeptDescription').value.trim() || null, display_order: parseInt(document.getElementById('newDeptOrder').value) || 0 })}); showToast('부서가 추가되었습니다.'); document.getElementById('addDepartmentForm').reset(); await loadDepartments(); } catch(e) { showToast(e.message, 'error'); } }); async function editDepartment(id) { const d = departments.find(x => x.department_id === id); if (!d) return; document.getElementById('editDeptId').value = d.department_id; document.getElementById('editDeptName').value = d.department_name; document.getElementById('editDeptDescription').value = d.description || ''; document.getElementById('editDeptOrder').value = d.display_order || 0; // Populate leader dropdown const leaderSel = document.getElementById('editDeptLeader'); leaderSel.innerHTML = ''; let deptUsers = users; if (!deptUsers || !deptUsers.length) { try { const r = await api('/users'); deptUsers = r.data || r; } catch(e) { /* ignore */ } } const activeUsers = (deptUsers || []).filter(u => u.is_active !== 0 && u.is_active !== false); const byDept = {}; activeUsers.forEach(u => { const dName = deptLabel(u.department, u.department_id); if (!byDept[dName]) byDept[dName] = []; byDept[dName].push(u); }); const currentDeptName = d.department_name; const sortedKeys = Object.keys(byDept).sort((a, b) => { if (a === currentDeptName) return -1; if (b === currentDeptName) return 1; return a.localeCompare(b, 'ko'); }); sortedKeys.forEach(dName => { const grp = document.createElement('optgroup'); grp.label = dName; byDept[dName].forEach(u => { const o = document.createElement('option'); o.value = u.user_id; o.textContent = u.name || u.username; if (d.leader_user_id && d.leader_user_id === u.user_id) o.selected = true; grp.appendChild(o); }); leaderSel.appendChild(grp); }); document.getElementById('editDepartmentModal').classList.remove('hidden'); } function closeDepartmentModal() { document.getElementById('editDepartmentModal').classList.add('hidden'); } document.getElementById('editDepartmentForm').addEventListener('submit', async e => { e.preventDefault(); try { const leaderId = document.getElementById('editDeptLeader').value; await api(`/departments/${document.getElementById('editDeptId').value}`, { method: 'PUT', body: JSON.stringify({ department_name: document.getElementById('editDeptName').value.trim(), description: document.getElementById('editDeptDescription').value.trim() || null, display_order: parseInt(document.getElementById('editDeptOrder').value) || 0, leader_user_id: leaderId ? parseInt(leaderId) : null })}); showToast('수정되었습니다.'); closeDepartmentModal(); await loadDepartments(); await loadDepartmentsForSelect(); } catch(e) { showToast(e.message, 'error'); } }); async function deleteDepartment(id, name) { if (!confirm(`"${name}" 부서를 삭제하시겠습니까? 소속 인원은 부서 미지정으로 변경됩니다.`)) return; try { await api(`/departments/${id}`, { method: 'DELETE' }); showToast('부서가 삭제되었습니다'); await loadDepartments(); } catch(e) { showToast(e.message, 'error'); } } /* ===== Approval Authorities ===== */ async function loadApprovalAuthorities(deptId) { const section = document.getElementById('deptApprovalSection'); if (!section) return; try { const r = await api(`/departments/${deptId}/approval-authorities`); const data = r.data || r; section.classList.remove('hidden'); renderApprovalAuthorities(deptId, data); } catch (e) { section.classList.add('hidden'); } } function renderApprovalAuthorities(deptId, data) { const list = document.getElementById('deptApprovalList'); if (!data || !data.length) { list.innerHTML = '

등록된 승인권한이 없습니다

'; return; } list.innerHTML = `${data.map(a => ``).join('')}
유형 승인자 순서
${APPROVAL_TYPE_LABELS[a.approval_type] || a.approval_type} ${escHtml(a.approver_name || '')} ${a.approval_order || 1}
`; } function openApprovalModal() { if (!selectedDeptForMembers) return; document.getElementById('approvalDeptId').value = selectedDeptForMembers; document.getElementById('approvalAuthorityForm').reset(); document.getElementById('approvalOrder').value = '1'; // Populate approver dropdown const sel = document.getElementById('approvalUserId'); sel.innerHTML = ''; let availUsers = users; if (availUsers && availUsers.length) { availUsers.filter(u => u.is_active !== 0 && u.is_active !== false).forEach(u => { sel.innerHTML += ``; }); } document.getElementById('approvalAuthorityModal').classList.remove('hidden'); } function closeApprovalModal() { document.getElementById('approvalAuthorityModal').classList.add('hidden'); } document.getElementById('approvalAuthorityForm').addEventListener('submit', async e => { e.preventDefault(); const deptId = document.getElementById('approvalDeptId').value; try { await api(`/departments/${deptId}/approval-authorities`, { method: 'POST', body: JSON.stringify({ approval_type: document.getElementById('approvalType').value, approver_user_id: parseInt(document.getElementById('approvalUserId').value), approval_order: parseInt(document.getElementById('approvalOrder').value) || 1 })}); showToast('승인권한이 추가되었습니다.'); closeApprovalModal(); await loadApprovalAuthorities(parseInt(deptId)); } catch (e) { showToast(e.message, 'error'); } }); async function deleteApprovalAuthority(deptId, authId) { if (!confirm('이 승인권한을 삭제하시겠습니까?')) return; try { await api(`/departments/${deptId}/approval-authorities/${authId}`, { method: 'DELETE' }); showToast('삭제되었습니다.'); await loadApprovalAuthorities(deptId); } catch (e) { showToast(e.message, 'error'); } }