From 8b23b471638a155fd1bfa3a8c853b26d9315b272 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Fri, 26 Sep 2025 09:57:24 +0000 Subject: (대표님) 권한관리, 문서업로드, rfq첨부, SWP문서룰 등 (최겸) 입찰 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../permissions/permission-assignment-manager.tsx | 319 +++++++++++++++++++++ 1 file changed, 319 insertions(+) create mode 100644 components/permissions/permission-assignment-manager.tsx (limited to 'components/permissions/permission-assignment-manager.tsx') diff --git a/components/permissions/permission-assignment-manager.tsx b/components/permissions/permission-assignment-manager.tsx new file mode 100644 index 00000000..3649631f --- /dev/null +++ b/components/permissions/permission-assignment-manager.tsx @@ -0,0 +1,319 @@ +// components/permissions/permission-assignment-manager.tsx (업데이트) + +"use client"; + +import { useState, useEffect } from "react"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { + Users, + User, + Plus, + X, + Search, + Shield, + Loader2 +} from "lucide-react"; +import { toast } from "sonner"; +import { + getPermissionAssignments, + assignPermissionToRoles, + assignPermissionToUsers, + removePermissionFromRole, + removePermissionFromUser, +} from "@/lib/permissions/permission-assignment-actions"; +import { cn } from "@/lib/utils"; + +interface Permission { + id: number; + permissionKey: string; + name: string; + description?: string; + permissionType: string; + resource: string; + action: string; + scope: string; + menuPath?: string; +} + +interface AssignedRole { + id: number; + name: string; + domain: string; + userCount: number; +} + +interface AssignedUser { + id: number; + name: string; + email: string; + imageUrl?: string; + domain: string; + isGrant: boolean; + reason?: string; +} + +export function PermissionAssignmentManager() { + const [permissions, setPermissions] = useState([]); + const [selectedPermission, setSelectedPermission] = useState(null); + const [assignedRoles, setAssignedRoles] = useState([]); + const [assignedUsers, setAssignedUsers] = useState([]); + const [searchQuery, setSearchQuery] = useState(""); + const [loading, setLoading] = useState(false); + + useEffect(() => { + loadPermissions(); + }, []); + + useEffect(() => { + if (selectedPermission) { + loadAssignments(selectedPermission.id); + } + }, [selectedPermission]); + + const loadPermissions = async () => { + setLoading(true); + try { + const data = await getPermissionAssignments(); + setPermissions(data.permissions); + } catch (error) { + toast.error("권한 목록을 불러오는데 실패했습니다."); + } finally { + setLoading(false); + } + }; + + const loadAssignments = async (permissionId: number) => { + try { + const data = await getPermissionAssignments(permissionId); + setAssignedRoles(data.roles); + setAssignedUsers(data.users); + } catch (error) { + toast.error("할당 정보를 불러오는데 실패했습니다."); + } + }; + + const handleRemoveRole = async (roleId: number) => { + if (!selectedPermission) return; + + try { + await removePermissionFromRole(selectedPermission.id, roleId); + toast.success("역할에서 권한이 제거되었습니다."); + loadAssignments(selectedPermission.id); + } catch (error) { + toast.error("권한 제거에 실패했습니다."); + } + }; + + const handleRemoveUser = async (userId: number) => { + if (!selectedPermission) return; + + try { + await removePermissionFromUser(selectedPermission.id, userId); + toast.success("사용자에서 권한이 제거되었습니다."); + loadAssignments(selectedPermission.id); + } catch (error) { + toast.error("권한 제거에 실패했습니다."); + } + }; + + // 권한 필터링 + const filteredPermissions = permissions.filter(p => + p.name.toLowerCase().includes(searchQuery.toLowerCase()) || + p.permissionKey.toLowerCase().includes(searchQuery.toLowerCase()) || + p.resource.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + // 리소스별 권한 그룹화 + const groupedPermissions = filteredPermissions.reduce((acc, perm) => { + const group = perm.resource; + if (!acc[group]) acc[group] = []; + acc[group].push(perm); + return acc; + }, {} as Record); + + return ( +
+ {/* 권한 목록 */} + + + 권한 목록 + 권한을 선택하여 할당을 관리하세요. + + +
+ {/* 검색 */} +
+ + setSearchQuery(e.target.value)} + className="pl-8" + /> +
+ + {/* 권한 목록 */} + {loading ? ( +
+ +
+ ) : ( + +
+ {Object.entries(groupedPermissions).map(([resource, perms]) => ( +
+

+ {resource} +

+
+ {perms.map(permission => ( + + ))} +
+
+ ))} +
+
+ )} +
+
+
+ + {/* 할당 관리 */} + {selectedPermission ? ( + + + {selectedPermission.name} + +
+ {selectedPermission.permissionKey} + {selectedPermission.permissionType} + {selectedPermission.scope} +
+
+
+ + + + + + 역할 ({assignedRoles.length}) + + + + 사용자 ({assignedUsers.length}) + + + + +
+ +
+ {assignedRoles.map((role) => ( +
+
+
{role.name}
+
+ {role.domain} • {role.userCount}명 사용자 +
+
+ +
+ ))} +
+
+
+ + +
+ +
+ {assignedUsers.map((user) => ( +
+
+ + + {user.name[0]} + +
+
{user.name}
+
{user.email}
+
+
+
+ {user.isGrant ? ( + 부여 + ) : ( + 제한 + )} + +
+
+ ))} +
+
+
+
+
+
+ ) : ( + +
+ +

권한을 선택하면 할당 정보가 표시됩니다.

+
+
+ )} +
+ ); +} \ No newline at end of file -- cgit v1.2.3