'use client'; import { useState } from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { type ColumnDef } from '@tanstack/react-table'; import { Pencil, Trash2, Plus, ShieldCheck, ShieldOff, Power, PowerOff } from 'lucide-react'; import { DataTable } from '@/components/shared/data-table'; import { PageHeader } from '@/components/shared/page-header'; import { ConfirmationDialog } from '@/components/shared/confirmation-dialog'; import { PermissionGate } from '@/components/shared/permission-gate'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { apiFetch } from '@/lib/api/client'; import { formatRole } from '@/lib/constants'; import { UserCard } from './user-card'; import { UserForm } from './user-form'; interface UserRow { userId: string; displayName: string; email: string; phone: string | null; isActive: boolean; isSuperAdmin: boolean; lastLoginAt: string | null; role: { id: string; name: string }; assignedAt: string; } const USERS_QUERY_KEY = ['admin', 'users'] as const; export function UserList() { const queryClient = useQueryClient(); const [formOpen, setFormOpen] = useState(false); const [editingUser, setEditingUser] = useState(null); const { data: users = [], isLoading: loading } = useQuery({ queryKey: USERS_QUERY_KEY, queryFn: () => apiFetch<{ data: UserRow[] }>('/api/v1/admin/users').then((r) => r.data), }); const fetchUsers = () => queryClient.invalidateQueries({ queryKey: USERS_QUERY_KEY }); const removeMutation = useMutation({ mutationFn: (userId: string) => apiFetch(`/api/v1/admin/users/${userId}`, { method: 'DELETE' }), onSuccess: () => fetchUsers(), }); const toggleMutation = useMutation({ mutationFn: (user: UserRow) => apiFetch(`/api/v1/admin/users/${user.userId}`, { method: 'PATCH', body: { isActive: !user.isActive }, }), onSuccess: () => fetchUsers(), }); const deletingId = removeMutation.isPending ? removeMutation.variables : null; const togglingId = toggleMutation.isPending ? (toggleMutation.variables?.userId ?? null) : null; function handleNewUser() { setEditingUser(null); setFormOpen(true); } function handleEditUser(user: UserRow) { setEditingUser(user); setFormOpen(true); } function handleRemoveUser(userId: string) { removeMutation.mutate(userId); } function handleToggleActive(user: UserRow) { toggleMutation.mutate(user); } const columns: ColumnDef[] = [ { accessorKey: 'displayName', header: 'Name', cell: ({ row }) => (
{row.original.displayName} {row.original.email}
), }, { accessorKey: 'role', header: 'Role', cell: ({ row }) => {formatRole(row.original.role.name)}, }, { accessorKey: 'isActive', header: 'Status', cell: ({ row }) => row.original.isActive ? ( Active ) : ( Disabled ), }, { accessorKey: 'lastLoginAt', header: 'Last Login', cell: ({ row }) => row.original.lastLoginAt ? new Date(row.original.lastLoginAt).toLocaleDateString() : 'Never', }, { id: 'actions', header: '', cell: ({ row }) => (
{row.original.isActive ? ( ) : ( )} {row.original.isActive ? 'Disable' : 'Enable'} } title={row.original.isActive ? 'Disable user' : 'Enable user'} description={ row.original.isActive ? `Disable sign-in for "${row.original.displayName}"? Their account stays intact; they just can't log in until you re-enable.` : `Re-enable sign-in for "${row.original.displayName}"?` } confirmLabel={row.original.isActive ? 'Disable' : 'Enable'} onConfirm={() => handleToggleActive(row.original)} loading={togglingId === row.original.userId} /> Remove } title="Remove User" description={`Remove "${row.original.displayName}" from this port? They will lose access but their account remains.`} confirmLabel="Remove" onConfirm={() => handleRemoveUser(row.original.userId)} loading={deletingId === row.original.userId} />
), enableSorting: false, size: 120, }, ]; return (
New User } /> row.userId} cardRender={(row) => ( )} emptyState={

No users assigned to this port.

} />
); }