// components/permissions/menu-permission-generator-optimized.tsx "use client"; import { useState, useEffect, useMemo, useCallback } from "react"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Checkbox } from "@/components/ui/checkbox"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { RefreshCw, AlertCircle, CheckCircle, Plus, Search, ChevronDown, ChevronUp } from "lucide-react"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; import { analyzeMenuPermissions, generateMenuPermissions } from "@/lib/permissions/permission-settings-actions"; export function MenuBasedPermissionGenerator() { const [analysis, setAnalysis] = useState([]); const [selectedMenus, setSelectedMenus] = useState>(new Set()); const [selectedPermissions, setSelectedPermissions] = useState>>(new Map()); const [loading, setLoading] = useState(false); const [generating, setGenerating] = useState(false); // 필터링과 페이지네이션 const [searchQuery, setSearchQuery] = useState(""); const [filterType, setFilterType] = useState<"all" | "configured" | "unconfigured">("unconfigured"); const [currentPage, setCurrentPage] = useState(1); const [expandedRow, setExpandedRow] = useState(null); // 한 번에 하나만 확장 const itemsPerPage = 20; // 필터링된 데이터 const filteredAnalysis = useMemo(() => { let filtered = analysis; if (searchQuery) { filtered = filtered.filter(m => m.menuTitle.toLowerCase().includes(searchQuery.toLowerCase()) || m.menuPath.toLowerCase().includes(searchQuery.toLowerCase()) ); } if (filterType === "configured") { filtered = filtered.filter(m => m.existingPermissions.length > 0); } else if (filterType === "unconfigured") { filtered = filtered.filter(m => m.existingPermissions.length === 0); } return filtered; }, [analysis, searchQuery, filterType]); // 페이지네이션 적용 const paginatedData = useMemo(() => { const start = (currentPage - 1) * itemsPerPage; return filteredAnalysis.slice(start, start + itemsPerPage); }, [filteredAnalysis, currentPage, itemsPerPage]); const totalPages = Math.ceil(filteredAnalysis.length / itemsPerPage); // 필터 변경 시 첫 페이지로 useEffect(() => { setCurrentPage(1); }, [searchQuery, filterType]); const loadAnalysis = async () => { setLoading(true); try { const data = await analyzeMenuPermissions(); setAnalysis(data); // 초기에는 선택하지 않음 (사용자가 필요한 것만 선택) setSelectedMenus(new Set()); setSelectedPermissions(new Map()); } catch (error) { toast.error("메뉴 분석에 실패했습니다."); } finally { setLoading(false); } }; useEffect(() => { loadAnalysis(); }, []); const toggleMenu = useCallback((menuPath: string) => { setSelectedMenus(prev => { const newSelected = new Set(prev); if (newSelected.has(menuPath)) { newSelected.delete(menuPath); setSelectedPermissions(perms => { const newPerms = new Map(perms); newPerms.delete(menuPath); return newPerms; }); } else { newSelected.add(menuPath); const menu = analysis.find(m => m.menuPath === menuPath); if (menu) { setSelectedPermissions(perms => { const newPerms = new Map(perms); newPerms.set( menuPath, new Set(menu.suggestedPermissions.map(p => p.permissionKey)) ); return newPerms; }); } } return newSelected; }); }, [analysis]); const togglePermission = useCallback((menuPath: string, permissionKey: string) => { setSelectedPermissions(prev => { const newPerms = new Map(prev); const menuPerms = newPerms.get(menuPath) || new Set(); if (menuPerms.has(permissionKey)) { menuPerms.delete(permissionKey); } else { menuPerms.add(permissionKey); } newPerms.set(menuPath, menuPerms); return newPerms; }); }, []); const handleGenerate = async () => { const permissionsToGenerate = []; for (const [menuPath, permKeys] of selectedPermissions.entries()) { const menu = analysis.find(m => m.menuPath === menuPath); if (menu) { for (const permKey of permKeys) { const perm = menu.suggestedPermissions.find(p => p.permissionKey === permKey); if (perm) { permissionsToGenerate.push({ ...perm, menuPath, }); } } } } if (permissionsToGenerate.length === 0) { toast.error("생성할 권한을 선택해주세요."); return; } setGenerating(true); try { const result = await generateMenuPermissions(permissionsToGenerate); toast.success(`${result.created}개의 권한이 생성되었습니다.`); loadAnalysis(); } catch (error) { toast.error("권한 생성에 실패했습니다."); } finally { setGenerating(false); } }; const selectAllInPage = () => { paginatedData.forEach(menu => { if (!selectedMenus.has(menu.menuPath)) { toggleMenu(menu.menuPath); } }); }; const deselectAllInPage = () => { paginatedData.forEach(menu => { if (selectedMenus.has(menu.menuPath)) { toggleMenu(menu.menuPath); } }); }; const totalSelected = Array.from(selectedPermissions.values()) .reduce((sum, set) => sum + set.size, 0); return (
메뉴 기반 권한 자동 생성 등록된 메뉴를 분석하여 필요한 권한을 자동으로 생성합니다.
{/* 요약 정보 */}
setFilterType("all")}>
{analysis.length}

전체 메뉴

setFilterType("configured")}>
{analysis.filter(m => m.existingPermissions.length > 0).length}

권한 설정됨

setFilterType("unconfigured")}>
{analysis.filter(m => m.existingPermissions.length === 0).length}

권한 미설정

{/* 필터 섹션 */}
setSearchQuery(e.target.value)} className="pl-8" />
{/* 일괄 선택 버튼 */}
{filteredAnalysis.length}개 중 {paginatedData.length}개 표시
{/* 메뉴 리스트 */}
{paginatedData.map(menu => { const isSelected = selectedMenus.has(menu.menuPath); const isExpanded = expandedRow === menu.menuPath; const menuPermissions = selectedPermissions.get(menu.menuPath); return (
toggleMenu(menu.menuPath)} />
{menu.menuTitle}
{menu.menuPath}
{menu.domain} {menu.existingPermissions.length > 0 ? (
{menu.existingPermissions.length}개
) : (
미설정
)} {isSelected && menu.suggestedPermissions.length > 0 && ( )}
{/* 권한 상세 (확장 시에만 표시) */} {isExpanded && isSelected && (
{menu.suggestedPermissions.map(perm => ( ))}
)}
); })}
{/* 페이지네이션 */} {totalPages > 1 && (
{currentPage} / {totalPages}
)}
); }