"use client"; import React, { useState, useCallback, useEffect } from "react"; import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from "@/components/ui/table"; import { Checkbox } from "@/components/ui/checkbox"; import { Input } from "@/components/ui/input"; import { Search, ChevronRight, ChevronDown } from "lucide-react"; import { RoleGroup, roleAPI } from "@/lib/api/role"; import { useAuth } from "@/hooks/useAuth"; interface MenuPermission { menuObjid: number; menuName: string; menuPath?: string; parentObjid?: number; createYn: string; readYn: string; updateYn: string; deleteYn: string; children?: MenuPermission[]; } interface MenuPermissionsTableProps { permissions: any[]; onPermissionsChange: (permissions: any[]) => void; roleGroup: RoleGroup; } /** * 메뉴 권한 설정 테이블 * * 기능: * - 메뉴 트리 구조 표시 * - CRUD 권한 체크박스 * - 전체 선택/해제 * - 검색 기능 */ export function MenuPermissionsTable({ permissions, onPermissionsChange, roleGroup }: MenuPermissionsTableProps) { const { user: currentUser } = useAuth(); const [searchText, setSearchText] = useState(""); const [expandedMenus, setExpandedMenus] = useState>(new Set()); const [allMenus, setAllMenus] = useState([]); const [isLoading, setIsLoading] = useState(true); // 최고 관리자 여부 확인 const isSuperAdmin = currentUser?.companyCode === "*" && currentUser?.userType === "SUPER_ADMIN"; // 전체 메뉴 목록 로드 useEffect(() => { // currentUser가 로드될 때까지 대기 if (!currentUser) { console.log("⏳ [MenuPermissionsTable] currentUser 로드 대기 중..."); return; } const loadAllMenus = async () => { // 최고 관리자: companyCode 없이 모든 메뉴 조회 // 회사 관리자: 자기 회사 메뉴만 조회 const targetCompanyCode = isSuperAdmin ? undefined : roleGroup.companyCode; console.log("🔍 [MenuPermissionsTable] 전체 메뉴 로드 시작", { currentUser: { userId: currentUser.userId, companyCode: currentUser.companyCode, userType: currentUser.userType, }, isSuperAdmin, roleGroupCompanyCode: roleGroup.companyCode, targetCompanyCode: targetCompanyCode || "전체", }); try { setIsLoading(true); const response = await roleAPI.getAllMenus(targetCompanyCode); console.log("✅ [MenuPermissionsTable] 전체 메뉴 로드 응답", { success: response.success, count: response.data?.length, data: response.data, }); if (response.success && response.data) { setAllMenus(response.data); console.log("✅ [MenuPermissionsTable] 메뉴 상태 업데이트 완료", { count: response.data.length, }); } } catch (error) { console.error("❌ [MenuPermissionsTable] 메뉴 로드 실패", error); } finally { setIsLoading(false); } }; loadAllMenus(); }, [currentUser, isSuperAdmin, roleGroup.companyCode]); // 메뉴 권한 상태 (로컬 상태 관리) const [menuPermissions, setMenuPermissions] = useState>(new Map()); const [isInitialized, setIsInitialized] = useState(false); // allMenus가 로드되면 초기 권한 상태 설정 (한 번만) useEffect(() => { if (allMenus.length > 0 && !isInitialized) { const permissionsMap = new Map(); allMenus.forEach((menu) => { // 기존 권한이 있으면 사용, 없으면 기본값 const existingPermission = permissions.find((p) => p.menuObjid === menu.objid); permissionsMap.set(menu.objid, { menuObjid: menu.objid, menuName: menu.menuName, menuPath: menu.menuUrl, parentObjid: menu.parentObjid, createYn: existingPermission?.createYn || "N", readYn: existingPermission?.readYn || "N", updateYn: existingPermission?.updateYn || "N", deleteYn: existingPermission?.deleteYn || "N", }); }); setMenuPermissions(permissionsMap); setIsInitialized(true); console.log("✅ [MenuPermissionsTable] 권한 상태 초기화", { count: permissionsMap.size, }); } }, [allMenus, permissions, isInitialized]); // menuPermissions가 변경되면 부모에 전달 (초기화 이후에만) useEffect(() => { if (isInitialized && menuPermissions.size > 0) { const updatedPermissions = Array.from(menuPermissions.values()); onPermissionsChange(updatedPermissions); } }, [menuPermissions, isInitialized, onPermissionsChange]); // 메뉴 트리 구조 생성 (menuPermissions에서) const menuTree: MenuPermission[] = Array.from(menuPermissions.values()); // 메뉴 펼치기/접기 토글 const toggleExpand = useCallback((menuObjid: number) => { setExpandedMenus((prev) => { const newSet = new Set(prev); if (newSet.has(menuObjid)) { newSet.delete(menuObjid); } else { newSet.add(menuObjid); } return newSet; }); }, []); // 권한 변경 핸들러 const handlePermissionChange = useCallback( (menuObjid: number, permission: "createYn" | "readYn" | "updateYn" | "deleteYn", checked: boolean) => { setMenuPermissions((prev) => { const newMap = new Map(prev); const menuPerm = newMap.get(menuObjid); if (menuPerm) { newMap.set(menuObjid, { ...menuPerm, [permission]: checked ? "Y" : "N", }); } return newMap; }); console.log("✅ 권한 변경:", { menuObjid, permission, checked }); }, [], ); // 전체 선택/해제 const handleSelectAll = useCallback( (permission: "createYn" | "readYn" | "updateYn" | "deleteYn", checked: boolean) => { setMenuPermissions((prev) => { const newMap = new Map(prev); newMap.forEach((menuPerm, menuObjid) => { newMap.set(menuObjid, { ...menuPerm, [permission]: checked ? "Y" : "N", }); }); return newMap; }); console.log("✅ 전체 선택:", { permission, checked }); }, [], ); // 메뉴 행 렌더링 const renderMenuRow = (menu: MenuPermission, level: number = 0) => { const hasChildren = menu.children && menu.children.length > 0; const isExpanded = expandedMenus.has(menu.menuObjid); const paddingLeft = level * 24; return ( {/* 메뉴명 */}
{hasChildren && ( )} {menu.menuName}
{/* 생성(Create) */}
handlePermissionChange(menu.menuObjid, "createYn", checked as boolean)} />
{/* 조회(Read) */}
handlePermissionChange(menu.menuObjid, "readYn", checked as boolean)} />
{/* 수정(Update) */}
handlePermissionChange(menu.menuObjid, "updateYn", checked as boolean)} />
{/* 삭제(Delete) */}
handlePermissionChange(menu.menuObjid, "deleteYn", checked as boolean)} />
{/* 자식 메뉴 렌더링 */} {hasChildren && isExpanded && menu.children!.map((child) => renderMenuRow(child, level + 1))}
); }; return (
{/* 검색 */}
setSearchText(e.target.value)} className="h-10 pl-10 text-sm" />
{/* 데스크톱 테이블 */}
메뉴
생성 (C) handleSelectAll("createYn", checked as boolean)} className="mt-1" />
조회 (R) handleSelectAll("readYn", checked as boolean)} className="mt-1" />
수정 (U) handleSelectAll("updateYn", checked as boolean)} className="mt-1" />
삭제 (D) handleSelectAll("deleteYn", checked as boolean)} className="mt-1" />
{menuTree.map((menu) => renderMenuRow(menu))}
{/* 모바일 카드 뷰 */}
{menuTree.map((menu) => (

{menu.menuName}

생성 (C) handlePermissionChange(menu.menuObjid, "createYn", checked as boolean)} />
조회 (R) handlePermissionChange(menu.menuObjid, "readYn", checked as boolean)} />
수정 (U) handlePermissionChange(menu.menuObjid, "updateYn", checked as boolean)} />
삭제 (D) handlePermissionChange(menu.menuObjid, "deleteYn", checked as boolean)} />
))}
); }