import React, { useState, useEffect } from 'react'; import { Card } from '../ui/card'; import { Button } from '../ui/button'; import { Input } from '../ui/input'; import { UserTableRow } from './UserTableRow'; import { BanUserModal } from './modals/BanUserModal'; import { User } from '../../types'; import { Search, Shield, Activity, Users, Download, UserPlus, Loader2 } from 'lucide-react'; import { useToast } from '../../components/feedback/ToastProvider'; import { useGetUsers } from '@/services/generated/user/user'; export const AdminUsersView: React.FC = () => { const { addToast } = useToast(); const [search, setSearch] = useState(''); const [selectedUser, setSelectedUser] = useState(null); const [users, setUsers] = useState([]); // Use generated hook. The orval-generated response type wraps in a // {data, status, headers} discriminated union, but the apiClient response // interceptor (services/api/interceptors/response.ts) unwraps the // {success, data} envelope before the mutator returns. So at runtime // `usersData` IS the payload — cast accordingly. const { data: usersData, isLoading: loading } = useGetUsers(); useEffect(() => { const payload = usersData as unknown as { users?: User[] } | undefined; if (payload?.users) { setUsers(payload.users); } }, [usersData]); const handleBan = (duration: string) => { if (!selectedUser) return; addToast(`Node ${selectedUser.username} quarantined for ${duration}.`, 'success'); setUsers(users.filter((u) => u.id !== selectedUser.id)); setSelectedUser(null); }; const handleDelete = (user: User) => { if (confirm(`INITIATE PERMANENT DELETION: ${user.username}?`)) { setUsers(users.filter((u) => u.id !== user.id)); addToast(`Identity purged: ${user.username}`, 'info'); } }; const filteredUsers = users.filter( (u) => u.username.toLowerCase().includes(search.toLowerCase()) || u.email.toLowerCase().includes(search.toLowerCase()), ); return (

IDENTITY GRID

USER DIRECTORY • ACCESS PERMISSIONS

} placeholder="Search by ID or frequency..." value={search} onChange={(e: React.ChangeEvent) => setSearch(e.target.value)} className="bg-muted/30 border-border" />
{loading ? (
) : (
{filteredUsers.map((user) => ( setSelectedUser(user)} onDelete={() => handleDelete(user)} onEditRole={() => addToast(`Modifying access roles for ${user.username}`, 'info')} /> ))} {filteredUsers.length === 0 && ( )}
Identity Access Origin Assigned Roles Network Plan Registered Last Sync Management
No entities found.
)}
Displaying {filteredUsers.length} / {users.length} active nodes
{selectedUser && ( setSelectedUser(null)} onConfirm={(_reason, _details, duration) => handleBan(duration)} /> )}
); };