import { DashboardLayout } from '@/frontend/components/DashboardLayout' import { StatsCard } from '@/frontend/components/StatsCard' import { ActionIcon, Avatar, Badge, Box, Button, Card, Container, Divider, Group, List, Loader, Modal, Pagination, Paper, PasswordInput, Select, SimpleGrid, Stack, Table, Tabs, Text, TextInput, ThemeIcon, Title, Tooltip, } from '@mantine/core' import { useDisclosure } from '@mantine/hooks' import { notifications } from '@mantine/notifications' import { createFileRoute } from '@tanstack/react-router' import { useEffect, useState } from 'react' import { TbAccessPoint, TbCircleCheck, TbCircleX, TbPencil, TbPlus, TbSearch, TbShieldCheck, TbTrash, TbUserCheck, TbUserPlus, TbUsers, } from 'react-icons/tb' import useSWR from 'swr' import { API_URLS } from '../config/api' import { useSession } from '../hooks/useAuth' export const Route = createFileRoute('/users')({ component: UsersPage, }) const fetcher = (url: string) => fetch(url).then((res) => res.json()) const ROLE_COLOR: Record = { DEVELOPER: 'violet', ADMIN: 'brand-blue', USER: 'gray', } const ROLE_LABEL: Record = { DEVELOPER: 'Developer', ADMIN: 'Admin', USER: 'User', } const roles = [ { name: 'DEVELOPER', color: 'violet', description: 'Super admin with full system access, including the Dev Console.', permissions: [ 'Access Dev Console (/dev)', 'User & role management', 'Manage bug reports & feedback', 'View all apps & activity logs', 'Manage app versions & status', 'Delete system logs', ], }, { name: 'ADMIN', color: 'blue', description: 'Operator who can manage applications, bugs, and view activity logs.', permissions: [ 'View & manage all applications', 'Manage bug reports', 'View activity logs', 'View user, village, and order data', 'Update village & product status', ], }, { name: 'USER', color: 'gray', description: 'New account pending approval. Awaiting review by an Admin or Developer.', permissions: [ 'Access profile page', 'View account approval status', ], }, ] function UsersPage() { const [search, setSearch] = useState('') const [debouncedSearch, setDebouncedSearch] = useState('') const [page, setPage] = useState(1) const { data: session } = useSession() const isDeveloper = session?.user?.role === 'DEVELOPER' useEffect(() => { const timer = setTimeout(() => setDebouncedSearch(search), 300) return () => clearTimeout(timer) }, [search]) const { data: stats, mutate: mutateStats } = useSWR(API_URLS.getOperatorStats(), fetcher) const { data: response, isLoading, mutate: mutateOperators } = useSWR( API_URLS.getOperators(page, debouncedSearch), fetcher, ) const operators = response?.data || [] // ── Create User Modal ── const [createOpened, { open: openCreate, close: closeCreate }] = useDisclosure(false) const [isCreating, setIsCreating] = useState(false) const [createForm, setCreateForm] = useState({ name: '', email: '', password: '', role: 'ADMIN' }) const handleCreateUser = async () => { if (!createForm.name || !createForm.email || !createForm.password) { notifications.show({ title: 'Validation Error', message: 'Please fill in all required fields.', color: 'red' }) return } setIsCreating(true) try { const res = await fetch(API_URLS.createOperator(), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(createForm), }) if (res.ok) { notifications.show({ title: 'Success', message: 'User has been created.', color: 'teal', icon: }) mutateOperators() mutateStats() closeCreate() setCreateForm({ name: '', email: '', password: '', role: 'ADMIN' }) } else { const err = await res.json() throw new Error(err.error || 'Failed to create user') } } catch (e: any) { notifications.show({ title: 'Error', message: e.message || 'Something went wrong.', color: 'red', icon: }) } finally { setIsCreating(false) } } // ── Edit User Modal ── const [editOpened, { open: openEdit, close: closeEdit }] = useDisclosure(false) const [isEditing, setIsEditing] = useState(false) const [editingUserId, setEditingUserId] = useState(null) const [editForm, setEditForm] = useState({ name: '', email: '', role: '' }) const handleOpenEdit = (user: any) => { setEditingUserId(user.id) setEditForm({ name: user.name, email: user.email, role: user.role }) openEdit() } const handleEditUser = async () => { if (!editingUserId || !editForm.name || !editForm.email) return setIsEditing(true) try { const res = await fetch(API_URLS.editOperator(editingUserId), { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(editForm), }) if (res.ok) { notifications.show({ title: 'Success', message: 'User has been updated.', color: 'teal', icon: }) mutateOperators() closeEdit() } else { throw new Error('Failed to update user') } } catch { notifications.show({ title: 'Error', message: 'Something went wrong.', color: 'red', icon: }) } finally { setIsEditing(false) } } // ── Delete User Modal ── const [deleteOpened, { open: openDelete, close: closeDelete }] = useDisclosure(false) const [isDeleting, setIsDeleting] = useState(false) const [deletingUser, setDeletingUser] = useState(null) const handleOpenDelete = (user: any) => { setDeletingUser(user) openDelete() } const handleDeleteUser = async () => { if (!deletingUser) return setIsDeleting(true) try { const res = await fetch(API_URLS.deleteOperator(deletingUser.id), { method: 'DELETE' }) if (res.ok) { notifications.show({ title: 'Success', message: 'User has been deleted.', color: 'teal', icon: }) mutateOperators() mutateStats() closeDelete() } else { const err = await res.json() throw new Error(err.error || 'Failed to delete user') } } catch (e: any) { notifications.show({ title: 'Error', message: e.message || 'Something went wrong.', color: 'red', icon: }) } finally { setIsDeleting(false) } } // ── Activate User ── const handleActivateUser = async (user: any) => { try { const res = await fetch(`/api/operators/${user.id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ active: true }), }) if (res.ok) { notifications.show({ title: 'Success', message: `${user.name} has been reactivated.`, color: 'teal', icon: }) mutateOperators() mutateStats() } else { const err = await res.json() throw new Error(err.error || 'Failed to activate user') } } catch (e: any) { notifications.show({ title: 'Error', message: e.message || 'Something went wrong.', color: 'red', icon: }) } } return ( User Management Manage platform users, security roles, and access control. }>User Management }>Role Reference } radius="md" w={320} variant="filled" value={search} onChange={(e) => { setSearch(e.currentTarget.value); setPage(1) }} /> {isDeveloper && ( )} Name & Contact Role Joined Actions {isLoading ? ( ) : operators.length === 0 ? ( No users found. ) : ( operators.map((user: any) => ( {user.name.charAt(0)} {user.active === false && ( )} {user.name} {user.active === false && ( Inactive )} {user.email} {ROLE_LABEL[user.role] ?? user.role} {new Date(user.createdAt).toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric', })} {user.active === false ? ( handleActivateUser(user)} > ) : ( <> handleOpenEdit(user)} > handleOpenDelete(user)} > )} )) )}
{response?.totalPages > 1 && ( )}
{roles.map((role) => ( {ROLE_LABEL[role.name] ?? role.name} {role.description} Key Permissions } > {role.permissions.map((p) => ( {p} ))} ))}
{/* Create User Modal */} Add New User} radius="md" overlayProps={{ backgroundOpacity: 0.55, blur: 3 }} > setCreateForm({ ...createForm, name: e.target.value })} /> setCreateForm({ ...createForm, email: e.target.value })} /> setCreateForm({ ...createForm, password: e.target.value })} /> setEditForm({ ...editForm, role: val || 'ADMIN' })} /> {/* Delete Confirmation Modal */} Delete User} radius="md" size="sm" overlayProps={{ backgroundOpacity: 0.55, blur: 3 }} > Are you sure you want to delete{' '} {deletingUser?.name}? This action cannot be undone.
) }