// components/permissions/menu-permission-manager.tsx "use client"; import { useState, useEffect } 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 { Input } from "@/components/ui/input"; import { Checkbox } from "@/components/ui/checkbox"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Separator } from "@/components/ui/separator"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Menu, Search, Shield, Lock, Unlock, Users, User, Settings, FileText, Eye, Edit, Trash, Plus, ChevronRight, AlertCircle, CheckCircle, ExternalLink } from "lucide-react"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; import { getMenuPermissions, updateMenuPermissions, getMenuManagers, updateMenuManagers, } from "@/lib/permissions/service"; // 메뉴 구조 타입 interface MenuItem { menuPath: string; menuTitle: string; menuDescription?: string; sectionTitle?: string; menuGroup?: string; domain: string; isActive: boolean; manager1?: { id: number; name: string; email: string; imageUrl?: string }; manager2?: { id: number; name: string; email: string; imageUrl?: string }; requiredPermissions: Array<{ id: number; permissionKey: string; name: string; description?: string; isRequired: boolean; }>; accessCount?: number; // 접근 통계 lastAccessed?: Date; } // 메뉴 섹션별 그룹화 interface MenuSection { title: string; items: MenuItem[]; } export function MenuPermissionManager() { const [searchQuery, setSearchQuery] = useState(""); const [menus, setMenus] = useState([]); const [selectedMenu, setSelectedMenu] = useState(null); const [availablePermissions, setAvailablePermissions] = useState([]); const [loading, setLoading] = useState(false); const [editDialogOpen, setEditDialogOpen] = useState(false); const [selectedDomain, setSelectedDomain] = useState("all"); useEffect(() => { loadMenus(); }, [selectedDomain]); const loadMenus = async () => { setLoading(true); try { const data = await getMenuPermissions(selectedDomain); setMenus(data.menus); setAvailablePermissions(data.availablePermissions); } catch (error) { toast.error("메뉴 정보를 불러오는데 실패했습니다."); } finally { setLoading(false); } }; // 메뉴 검색 필터링 const filteredMenus = menus.filter(menu => menu.menuTitle.toLowerCase().includes(searchQuery.toLowerCase()) || menu.menuPath.toLowerCase().includes(searchQuery.toLowerCase()) || menu.menuDescription?.toLowerCase().includes(searchQuery.toLowerCase()) ); // 섹션별로 메뉴 그룹화 const groupedMenus = filteredMenus.reduce((acc, menu) => { const section = menu.sectionTitle || "기타"; if (!acc[section]) { acc[section] = []; } acc[section].push(menu); return acc; }, {} as Record); return (
{/* 메뉴 목록 */} 메뉴 목록 권한을 설정할 메뉴를 선택하세요.
{/* 도메인 필터 */}
setSelectedDomain("all")} > 전체 setSelectedDomain("evcp")} > EVCP setSelectedDomain("partners")} > Partners
{/* 검색 */}
setSearchQuery(e.target.value)} className="pl-8" />
{/* 메뉴 트리 */} {Object.entries(groupedMenus).map(([section, items]) => (
{section} {items.length}
{items.map((menu) => ( ))}
))}
{/* 메뉴 상세 및 권한 설정 */} {selectedMenu ? (
{selectedMenu.menuTitle} {!selectedMenu.isActive && ( 비활성 )} {selectedMenu.menuPath} {selectedMenu.menuDescription && (

{selectedMenu.menuDescription}

)}
{/* 담당자 정보 */}

담당자

{/* 담당자 변경 다이얼로그 */}} /> {/* 담당자 변경 다이얼로그 */}} />
{/* 필수 권한 */}

필수 권한

{selectedMenu.requiredPermissions.length > 0 ? (
{selectedMenu.requiredPermissions.map((perm) => (
{perm.name}
{perm.permissionKey}
{perm.description && (
{perm.description}
)}
{perm.isRequired ? ( 필수 ) : ( 선택 )}
))}
) : (

설정된 권한이 없습니다.

모든 사용자가 접근 가능합니다.

)}
{/* 접근 통계 */}

접근 통계

{selectedMenu.accessCount || 0}
총 접근 횟수
{selectedMenu.lastAccessed ? new Date(selectedMenu.lastAccessed).toLocaleDateString() : "-"}
최근 접근일
) : (

메뉴를 선택하면 권한 설정이 표시됩니다.

)} {/* 메뉴 권한 편집 다이얼로그 */} {selectedMenu && ( { loadMenus(); setEditDialogOpen(false); }} /> )}
); } // 담당자 정보 컴포넌트 function ManagerInfo({ label, manager, onEdit }: { label: string; manager?: { id: number; name: string; email: string; imageUrl?: string }; onEdit: () => void; }) { if (!manager) { return (
{label}: 미지정
); } return (
{manager.name[0]}
{manager.name}
{manager.email}
); } // 메뉴 권한 편집 다이얼로그 function MenuPermissionEditDialog({ open, onOpenChange, menu, availablePermissions, onSuccess, }: { open: boolean; onOpenChange: (open: boolean) => void; menu: MenuItem; availablePermissions: any[]; onSuccess: () => void; }) { const [selectedPermissions, setSelectedPermissions] = useState< Array<{ id: number; isRequired: boolean }> >(() => menu.requiredPermissions.map(p => ({ id: p.id, isRequired: p.isRequired }))) const [saving, setSaving] = useState(false); const handleSave = async () => { setSaving(true); try { await updateMenuPermissions(menu.menuPath, selectedPermissions); toast.success("메뉴 권한이 업데이트되었습니다."); onSuccess(); } catch (error) { toast.error("권한 업데이트에 실패했습니다."); } finally { setSaving(false); } }; const togglePermission = (permissionId: number) => { const existing = selectedPermissions.find(p => p.id === permissionId); if (existing) { setSelectedPermissions(selectedPermissions.filter(p => p.id !== permissionId)); } else { setSelectedPermissions([...selectedPermissions, { id: permissionId, isRequired: true }]); } }; const toggleRequired = (permissionId: number) => { setSelectedPermissions( selectedPermissions.map(p => p.id === permissionId ? { ...p, isRequired: !p.isRequired } : p ) ); }; // 권한을 카테고리별로 그룹화 const groupedPermissions = availablePermissions.reduce((acc, perm) => { const category = perm.resource || "기타"; if (!acc[category]) acc[category] = []; acc[category].push(perm); return acc; }, {} as Record); return ( {menu.menuTitle} 권한 설정 이 메뉴에 접근하기 위한 필수/선택 권한을 설정합니다.
{Object.entries(groupedPermissions).map(([category, perms]) => (

{category}

{perms.map((permission) => { const selected = selectedPermissions.find(p => p.id === permission.id); return (
togglePermission(permission.id)} />
{permission.name}
{permission.permissionKey}
{permission.description && (
{permission.description}
)}
{selected && (
)}
); })}
))}
); }