import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import type { AxiosError } from "axios"; import { useState } from "react"; import { fetchAllTenants, fetchMe, fetchUsers, fetchSystemRelations, addSystemRelation, removeSystemRelation, type TenantRelation, } from "../../../lib/adminApi"; import { t } from "../../../lib/i18n"; import { TenantFineGrainedPermissionsTab } from "./TenantFineGrainedPermissionsTab"; import { ShieldCheck, Search, Plus, UserPlus, Trash2, Settings, Shield } from "lucide-react"; import { Badge } from "../../../components/ui/badge"; import { Button } from "../../../components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "../../../components/ui/card"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "../../../components/ui/dialog"; import { Input } from "../../../components/ui/input"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "../../../components/ui/table"; import { toast } from "../../../components/ui/use-toast"; export function TenantFineGrainedPermissionsPage() { const queryClient = useQueryClient(); const [activeTab, setActiveTab] = useState<"tenant" | "system">("tenant"); const [selectedTenantId, setSelectedTenantId] = useState(""); const [searchTerm, setSearchTerm] = useState(""); const [isDialogOpen, setIsDialogOpen] = useState(false); const { data: profile } = useQuery({ queryKey: ["me"], queryFn: fetchMe, }); const isSuperAdmin = profile?.role === "super_admin"; const tenantsQuery = useQuery({ queryKey: ["tenants", "list-all"], queryFn: () => fetchAllTenants(), enabled: isSuperAdmin, }); const tenants = isSuperAdmin ? (tenantsQuery.data?.items ?? []) : (profile?.manageableTenants ?? []); // System Relations (Admin Control) Queries & Mutations const systemRelationsQuery = useQuery({ queryKey: ["system-relations"], queryFn: fetchSystemRelations, enabled: isSuperAdmin && activeTab === "system", }); const systemRelations = systemRelationsQuery.data ?? []; const addSystemRelationMutation = useMutation({ mutationFn: (payload: { userId: string; relation: string }) => addSystemRelation(payload.userId, payload.relation), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["system-relations"] }); toast.success(t("msg.admin.system.relations.add_success", "시스템 메뉴 권한이 추가되었습니다.")); }, onError: (err: AxiosError<{ error?: string }>) => { toast.error(err.response?.data?.error || t("msg.common.error", "오류가 발생했습니다.")); }, }); const removeSystemRelationMutation = useMutation({ mutationFn: (payload: { userId: string; relation: string }) => removeSystemRelation(payload.userId, payload.relation), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["system-relations"] }); toast.success(t("msg.admin.system.relations.remove_success", "시스템 메뉴 권한이 회수되었습니다.")); }, onError: (err: AxiosError<{ error?: string }>) => { toast.error(err.response?.data?.error || t("msg.common.error", "오류가 발생했습니다.")); }, }); const handleSystemRelationChange = async ( userId: string, relation: string, hasAccess: boolean, ) => { if (hasAccess) { await addSystemRelationMutation.mutateAsync({ userId, relation }); } else { await removeSystemRelationMutation.mutateAsync({ userId, relation }); } }; const handleRemoveAllSystemRelations = async (userId: string, userRelations: string[]) => { if (!window.confirm(t("msg.admin.system.relations.remove_all_confirm", "이 사용자의 모든 시스템 메뉴 권한을 삭제하시겠습니까?"))) { return; } for (const rel of userRelations) { await removeSystemRelationMutation.mutateAsync({ userId, relation: rel }); } }; const usersQuery = useQuery({ queryKey: ["admin-users-search", searchTerm], queryFn: () => fetchUsers(20, 0, searchTerm), enabled: isDialogOpen && searchTerm.length >= 2, }); const handleAddSystemUser = (userId: string) => { addSystemRelationMutation.mutate({ userId, relation: "overview_viewers" }); setIsDialogOpen(false); setSearchTerm(""); }; const searchResults = usersQuery.data?.items || []; const systemMenus = [ { label: t("ui.admin.nav.overview", "개요"), relation: "overview_viewers" }, { label: t("ui.admin.nav.tenants", "테넌트"), relation: "tenants_viewers" }, { label: t("ui.admin.nav.org_chart", "조직도"), relation: "org_chart_viewers" }, { label: t("ui.admin.nav.worksmobile", "Worksmobile"), relation: "worksmobile_viewers" }, { label: t("ui.admin.nav.ory_ssot", "Ory SSOT"), relation: "ory_ssot_viewers" }, { label: t("ui.admin.nav.data_integrity", "정합성"), relation: "data_integrity_viewers" }, { label: t("ui.admin.nav.users", "사용자"), relation: "users_viewers" }, { label: t("ui.admin.nav.permissions_direct", "권한 부여"), relation: "permissions_direct_viewers" }, { label: t("ui.admin.nav.auth_guard", "인증 가드"), relation: "auth_guard_viewers" }, { label: t("ui.admin.nav.api_keys", "API 키"), relation: "api_keys_viewers" }, { label: t("ui.admin.nav.audit_logs", "감사 로그"), relation: "audit_logs_viewers" }, ]; return (

{t("ui.admin.nav.permissions_direct", "권한 부여")}

{t( "msg.admin.permissions_direct.description", "테넌트의 세부 기능 권한 및 글로벌 사이드바 메뉴 탭 접근 권한을 지정하고 부여합니다.", )}

{/* Tab Selectors */} {isSuperAdmin && (
)} {activeTab === "tenant" ? ( <> {t("ui.admin.permissions_direct.select_tenant", "대상 테넌트 선택")} {t( "msg.admin.permissions_direct.select_tenant_desc", "세부 기능 권한을 부여할 대상 테넌트를 리스트에서 선택해 주세요.", )} {selectedTenantId ? ( ) : (
{t("msg.admin.permissions_direct.select_prompt", "상단에서 테넌트를 선택하면 세부 권한 격리 설정 격자가 노출됩니다.")}
)} ) : ( /* 시스템 메뉴 권한 (Admin Control) Tab Panel */
{t("ui.admin.permissions_direct.tab_system_title", "글로벌 메뉴 접근 제어 (Admin Control)")} {t( "msg.admin.permissions_direct.tab_system_desc", "사이드바 각 메뉴별 접근 권한을 사용자에게 직접 부여합니다. 최고 관리자(super_admin)는 기본적으로 언제나 모든 권한을 우회 통과합니다.", )}
{t("ui.common.name", "이름")} {systemMenus.map((menu) => ( {menu.label} ))} {t("ui.common.action", "작업")} {systemRelations.length === 0 ? ( {t("msg.admin.permissions_direct.system_empty", "지정된 글로벌 메뉴 세부 권한자가 없습니다. 사용자를 추가해 관리해 주세요.")} ) : ( systemRelations.map((user) => { return (
{user.name} {user.email}
{systemMenus.map((menu) => { const hasAccess = user.relations.includes(menu.relation); return ( ); })}
); }) )}
)} {/* User Search Dialog for System relations */} { if (!open) { setIsDialogOpen(false); setSearchTerm(""); } }} > {t("ui.admin.permissions_direct.dialog_title_system", "시스템 권한 관리 유저 추가")} {t( "ui.admin.tenants.admins.dialog_description", "이름 또는 이메일로 사용자를 검색하세요.", )}
setSearchTerm(e.target.value)} />
{searchTerm.length < 2 ? (

{t( "ui.admin.tenants.admins.dialog_search_hint", "검색어를 입력해 주세요.", )}

) : usersQuery.isLoading ? (
) : searchResults.length === 0 ? (
{t( "ui.admin.tenants.admins.dialog_no_results", "검색 결과가 없습니다.", )}
) : (
{searchResults.map((user) => { const isAlreadyInMatrix = systemRelations.some( (r) => r.userId === user.id, ); return (
{user.name.charAt(0)}
{user.name} {user.email}
); })}
)}
); }