import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { ArrowLeft, Plus, Shield, Trash2, UserPlus, Users } from "lucide-react"; import { useState } from "react"; import { Link, useParams } from "react-router-dom"; 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, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "../../../components/ui/dialog"; import { Input } from "../../../components/ui/input"; import { Label } from "../../../components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "../../../components/ui/select"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "../../../components/ui/table"; import { addGroupMember, assignGroupRole, fetchGroup, fetchGroupRoles, fetchTenants, fetchUsers, removeGroupMember, removeGroupRole, } from "../../../lib/adminApi"; function getErrorMessage(error: unknown, fallback: string): string { if (typeof error === "object" && error !== null) { const response = (error as { response?: { data?: { error?: unknown } } }) .response; const responseError = response?.data?.error; if (typeof responseError === "string" && responseError.length > 0) { return responseError; } const message = (error as { message?: unknown }).message; if (typeof message === "string" && message.length > 0) { return message; } } if (error instanceof Error && error.message) { return error.message; } return fallback; } export function UserGroupDetailPage() { const { tenantId, id } = useParams<{ tenantId: string; id: string }>(); const queryClient = useQueryClient(); const [isAddMemberOpen, setIsAddMemberOpen] = useState(false); const [selectedUserId, setSelectedUserId] = useState(""); const [searchUser, setSearchUser] = useState(""); const [isAddRoleOpen, setIsAddRoleOpen] = useState(false); const [selectedTargetTenantId, setSelectedTargetTenantId] = useState(""); const [selectedRelation, setSelectedRelation] = useState("view"); // Fetch specific group details const { data: currentGroup, isLoading: isGroupLoading, error, } = useQuery({ queryKey: ["user-group-detail", id], queryFn: () => { if (!tenantId || !id) { throw new Error("tenantId and id are required"); } return fetchGroup(tenantId, id); }, enabled: !!id && !!tenantId, retry: false, }); // Fetch assigned roles const { data: groupRoles, isLoading: isRolesLoading } = useQuery({ queryKey: ["user-group-roles", id], queryFn: () => { if (!tenantId || !id) { throw new Error("tenantId and id are required"); } return fetchGroupRoles(tenantId, id); }, enabled: !!id && !!tenantId, }); // Fetch all users for selection const { data: userList } = useQuery({ queryKey: ["admin-users", searchUser], queryFn: () => fetchUsers(20, 0, searchUser), enabled: isAddMemberOpen, }); // Fetch all tenants for role assignment const { data: tenantList } = useQuery({ queryKey: ["admin-tenants"], queryFn: () => fetchTenants(100, 0), enabled: isAddRoleOpen, }); const addMemberMutation = useMutation({ mutationFn: (userId: string) => { if (!tenantId || !id) { throw new Error("tenantId and id are required"); } return addGroupMember(tenantId, id, userId); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["user-group-detail", id] }); setIsAddMemberOpen(false); setSelectedUserId(""); alert("Member added successfully"); }, onError: (error: unknown) => { alert(getErrorMessage(error, "Failed to add member")); }, }); const removeMemberMutation = useMutation({ mutationFn: (userId: string) => { if (!tenantId || !id) { throw new Error("tenantId and id are required"); } return removeGroupMember(tenantId, id, userId); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["user-group-detail", id] }); alert("Member removed successfully"); }, }); const assignRoleMutation = useMutation({ mutationFn: () => { if (!tenantId || !id) { throw new Error("tenantId and id are required"); } return assignGroupRole( tenantId, id, selectedTargetTenantId, selectedRelation, ); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["user-group-roles", id] }); setIsAddRoleOpen(false); alert(`Role '${selectedRelation}' assigned successfully`); }, onError: (error: unknown) => { alert(getErrorMessage(error, "Failed to assign role")); }, }); const removeRoleMutation = useMutation({ mutationFn: (role: { targetTenantId: string; relation: string }) => { if (!tenantId || !id) { throw new Error("tenantId and id are required"); } return removeGroupRole(tenantId, id, role.targetTenantId, role.relation); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["user-group-roles", id] }); alert("Role removed successfully"); }, }); if (isGroupLoading) return (
Loading group details...
); if (error || !currentGroup) return (

Could not load group

Error: {getErrorMessage(error, "Not found")}

Path: /admin/tenants/{tenantId}/user-groups/{id}

The group ID might be invalid or you don't have sufficient permissions.

Return to Group List
); return (
Tenant Detail / User Group

{currentGroup.name}

{currentGroup.description || "No description provided."}

User Group Tenant: {tenantId?.split("-")[0]}...
{/* Members Management */}
Members Manage users in this group.
Add Member Select a user to add to this group.
setSearchUser(e.target.value)} />
User Action {!currentGroup.members || currentGroup.members.length === 0 ? ( No members in this group. ) : ( currentGroup.members.map((member) => (

{member.name}

{member.email}

)) )}
{/* Roles/Permissions Management (Keto Based) */}
Permissions Tenant roles assigned to this group.
Assign Tenant Role Members of this group will inherit this role on the target tenant.
Target Tenant Role Action {isRolesLoading ? ( Loading... ) : !groupRoles || groupRoles.length === 0 ? ( No roles assigned. ) : ( groupRoles.map((role, idx) => (
{role.tenantName || role.tenantId}
{role.relation}
)) )}
); }