"use client"; import { useState, useEffect } from "react"; import { Plus, ChevronDown, ChevronRight, Users, Trash2 } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import type { Department, DepartmentFormData } from "@/types/department"; import * as departmentAPI from "@/lib/api/department"; interface DepartmentStructureProps { companyCode: string; selectedDepartment: Department | null; onSelectDepartment: (department: Department | null) => void; } /** * 부서 구조 컴포넌트 (트리 형태) */ export function DepartmentStructure({ companyCode, selectedDepartment, onSelectDepartment, }: DepartmentStructureProps) { const [departments, setDepartments] = useState([]); const [expandedDepts, setExpandedDepts] = useState>(new Set()); const [isLoading, setIsLoading] = useState(false); // 부서 추가 모달 const [isAddModalOpen, setIsAddModalOpen] = useState(false); const [parentDeptForAdd, setParentDeptForAdd] = useState(null); const [newDeptName, setNewDeptName] = useState(""); // 부서 목록 로드 useEffect(() => { loadDepartments(); }, [companyCode]); const loadDepartments = async () => { setIsLoading(true); try { const response = await departmentAPI.getDepartments(companyCode); if (response.success && response.data) { setDepartments(response.data); } else { console.error("부서 목록 로드 실패:", response.error); setDepartments([]); } } catch (error) { console.error("부서 목록 로드 실패:", error); setDepartments([]); } finally { setIsLoading(false); } }; // 부서 트리 구조 생성 const buildTree = (parentCode: string | null): Department[] => { return departments .filter((dept) => dept.parent_dept_code === parentCode) .sort((a, b) => (a.sort_order || 0) - (b.sort_order || 0)); }; // 부서 추가 핸들러 const handleAddDepartment = (parentDeptCode: string | null = null) => { setParentDeptForAdd(parentDeptCode); setNewDeptName(""); setIsAddModalOpen(true); }; // 부서 저장 const handleSaveDepartment = async () => { if (!newDeptName.trim()) return; try { const response = await departmentAPI.createDepartment(companyCode, { dept_name: newDeptName, parent_dept_code: parentDeptForAdd, }); if (response.success) { setIsAddModalOpen(false); loadDepartments(); } else { alert(response.error || "부서 추가에 실패했습니다."); } } catch (error) { console.error("부서 추가 실패:", error); alert("부서 추가 중 오류가 발생했습니다."); } }; // 부서 삭제 const handleDeleteDepartment = async (deptCode: string) => { if (!confirm("이 부서를 삭제하시겠습니까?")) return; try { const response = await departmentAPI.deleteDepartment(deptCode); if (response.success) { loadDepartments(); } else { alert(response.error || "부서 삭제에 실패했습니다."); } } catch (error) { console.error("부서 삭제 실패:", error); alert("부서 삭제 중 오류가 발생했습니다."); } }; // 확장/축소 토글 const toggleExpand = (deptCode: string) => { const newExpanded = new Set(expandedDepts); if (newExpanded.has(deptCode)) { newExpanded.delete(deptCode); } else { newExpanded.add(deptCode); } setExpandedDepts(newExpanded); }; // 부서 트리 렌더링 (재귀) const renderDepartmentTree = (parentCode: string | null, level: number = 0) => { const children = buildTree(parentCode); return children.map((dept) => { const hasChildren = departments.some((d) => d.parent_dept_code === dept.dept_code); const isExpanded = expandedDepts.has(dept.dept_code); const isSelected = selectedDepartment?.dept_code === dept.dept_code; return (
{/* 부서 항목 */}
onSelectDepartment(dept)} > {/* 확장/축소 아이콘 */} {hasChildren ? ( ) : (
)} {/* 부서명 */} {dept.dept_name} {/* 인원수 */}
{dept.memberCount || 0}
{/* 액션 버튼 */}
{/* 하위 부서 (재귀) */} {hasChildren && isExpanded && renderDepartmentTree(dept.dept_code, level + 1)}
); }); }; return (
{/* 헤더 */}

부서 구조

{/* 부서 트리 */}
{isLoading ? (
로딩 중...
) : departments.length === 0 ? (
부서가 없습니다. 최상위 부서를 추가해주세요.
) : ( renderDepartmentTree(null) )}
{/* 부서 추가 모달 */} {parentDeptForAdd ? "하위 부서 추가" : "최상위 부서 추가"}
setNewDeptName(e.target.value)} placeholder="부서명을 입력하세요" autoFocus />
); }