import { useState } from "react"; import { useParams, Link } from "react-router-dom"; import { Shield, ArrowLeft, Trash2, Plus, Tag, Snowflake, Sun, ShieldCheck, User, FileJson, Search, } from "lucide-react"; import { useIdentity, usePermissionSets, useCreateIdentityRoleAssignment, useDeleteIdentityRoleAssignment, useCreatePermissionAssignment, useDeletePermissionAssignment, useFreezeIdentity, useUnfreezeIdentity, } from "@/hooks/usePermissions"; interface RoleAssignment { id: number; identity_id: number; role: string; source: string; managed: boolean; created: string; updated: string; } interface DirectPermission { id: number; identity_id: number; permission_set_id: number; permission_set_ref: string; created: string; } interface IdentityDetail { id: number; login: string; display_name: string | null; frozen: boolean; attributes: Record; roles: RoleAssignment[]; direct_permissions: DirectPermission[]; } export default function IdentityDetailPage() { const { id: idParam } = useParams<{ id: string }>(); const id = Number(idParam) || 0; const { data: rawData, isLoading, error } = useIdentity(id); const { data: permissionSets } = usePermissionSets(); const createRoleMutation = useCreateIdentityRoleAssignment(); const deleteRoleMutation = useDeleteIdentityRoleAssignment(); const createPermMutation = useCreatePermissionAssignment(); const deletePermMutation = useDeletePermissionAssignment(); const freezeMutation = useFreezeIdentity(); const unfreezeMutation = useUnfreezeIdentity(); const [showAddRole, setShowAddRole] = useState(false); const [newRole, setNewRole] = useState(""); const [showAssignPerm, setShowAssignPerm] = useState(false); const [selectedPermSetRef, setSelectedPermSetRef] = useState(""); const [permSetSearch, setPermSetSearch] = useState(""); const identity = (rawData as unknown as { data: IdentityDetail } | undefined)?.data; const handleAddRole = async (e: React.FormEvent) => { e.preventDefault(); if (!newRole.trim()) return; try { await createRoleMutation.mutateAsync({ identityId: id, role: newRole.trim() }); setNewRole(""); setShowAddRole(false); } catch (err) { console.error("Failed to add role:", err); } }; const handleDeleteRole = async (assignmentId: number, role: string) => { if (window.confirm("Remove role \"" + role + "\" from this identity?")) { try { await deleteRoleMutation.mutateAsync(assignmentId); } catch (err) { console.error("Failed to delete role assignment:", err); } } }; const handleAssignPermission = async (e: React.FormEvent) => { e.preventDefault(); if (!selectedPermSetRef) return; try { await createPermMutation.mutateAsync({ identity_id: id, permission_set_ref: selectedPermSetRef }); setSelectedPermSetRef(""); setPermSetSearch(""); setShowAssignPerm(false); } catch (err) { console.error("Failed to assign permission set:", err); } }; const handleDeletePermission = async (assignmentId: number, ref: string) => { if (window.confirm("Remove permission set \"" + ref + "\" from this identity?")) { try { await deletePermMutation.mutateAsync(assignmentId); } catch (err) { console.error("Failed to remove permission assignment:", err); } } }; const handleToggleFreeze = async () => { if (!identity) return; const action = identity.frozen ? "unfreeze" : "freeze"; if (!window.confirm("Are you sure you want to " + action + " identity \"" + identity.login + "\"?")) return; try { if (identity.frozen) { await unfreezeMutation.mutateAsync(id); } else { await freezeMutation.mutateAsync(id); } } catch (err) { console.error("Failed to " + action + " identity:", err); } }; const formatDate = (dateString: string) => new Date(dateString).toLocaleString(); const assignedPermSetRefs = new Set(identity?.direct_permissions?.map((p) => p.permission_set_ref) ?? []); const availablePermSets = (permissionSets ?? []).filter((ps) => !assignedPermSetRefs.has(ps.ref)); const filteredAvailablePermSets = permSetSearch.trim() ? availablePermSets.filter((ps) => ps.ref.toLowerCase().includes(permSetSearch.toLowerCase()) || (ps.label ?? "").toLowerCase().includes(permSetSearch.toLowerCase()) ) : availablePermSets; if (isLoading) { return (

Loading identity...

); } if (error || !identity) { return (
Back to Access Control

Failed to load identity

{error instanceof Error ? error.message : "Identity not found"}

); } return (
Back to Access Control {/* Header */}

{identity.login}

{identity.frozen && ( Frozen )}
{identity.display_name &&

{identity.display_name}

}

ID: {identity.id}

{/* Roles Section */}

Role Assignments

({identity.roles?.length || 0})
{showAddRole && (
setNewRole(e.target.value)} placeholder="Role name (e.g. admin, operator, viewer)" className="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-violet-500 text-sm" autoFocus />
)} {identity.roles && identity.roles.length > 0 ? (
{identity.roles.map((ra) => (
{ra.role} Source: {ra.source} {ra.managed && ( Managed )} {formatDate(ra.created)}
{!ra.managed && ( )}
))}
) : (

No roles assigned

)}
{/* Direct Permission Sets Section */}

Direct Permission Sets

({identity.direct_permissions?.length || 0})
{showAssignPerm && (
{selectedPermSetRef ? (
{selectedPermSetRef}
) : (
setPermSetSearch(e.target.value)} placeholder="Search permission sets by ref or label..." className="w-full pl-9 pr-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 text-sm" autoFocus />
{permSetSearch.trim() && (
{filteredAvailablePermSets.length === 0 ? (
No matching permission sets
) : ( filteredAvailablePermSets.map((ps) => ( )) )}
)}
)}
)} {identity.direct_permissions && identity.direct_permissions.length > 0 ? (
{identity.direct_permissions.map((dp) => (
{dp.permission_set_ref} Assigned {formatDate(dp.created)}
))}
) : (

No direct permission sets assigned

Permission sets can also be inherited through roles

)}
{/* Attributes Section */}

Attributes

          {JSON.stringify(identity.attributes, null, 2)}
        
); }