/** * User Management Page * * Manage organization users, roles, and invitations */ 'use client'; import { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { usersApi } from '@/lib/api'; export default function UsersManagementPage() { const queryClient = useQueryClient(); const [showInviteModal, setShowInviteModal] = useState(false); const [inviteForm, setInviteForm] = useState({ email: '', firstName: '', lastName: '', role: 'user' as 'admin' | 'manager' | 'user' | 'viewer', phoneNumber: '', }); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); const { data: users, isLoading } = useQuery({ queryKey: ['users'], queryFn: () => usersApi.list(), }); const inviteMutation = useMutation({ mutationFn: (data: typeof inviteForm & { organizationId: string }) => usersApi.create(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }); setSuccess('User invited successfully'); setShowInviteModal(false); setInviteForm({ email: '', firstName: '', lastName: '', role: 'user', phoneNumber: '', }); setTimeout(() => setSuccess(''), 3000); }, onError: (err: any) => { setError(err.response?.data?.message || 'Failed to invite user'); }, }); const changeRoleMutation = useMutation({ mutationFn: ({ id, role }: { id: string; role: 'admin' | 'manager' | 'user' | 'viewer' }) => usersApi.changeRole(id, role), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }); }, }); const toggleActiveMutation = useMutation({ mutationFn: ({ id, isActive }: { id: string; isActive: boolean }) => isActive ? usersApi.deactivate(id) : usersApi.activate(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }); }, }); const deleteMutation = useMutation({ mutationFn: (id: string) => usersApi.delete(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }); }, }); const handleInvite = (e: React.FormEvent) => { e.preventDefault(); setError(''); // TODO: Get actual organizationId from auth context inviteMutation.mutate({ ...inviteForm, organizationId: 'default-org-id' }); }; const handleRoleChange = (userId: string, newRole: string) => { changeRoleMutation.mutate({ id: userId, role: newRole as any }); }; const handleToggleActive = (userId: string, isActive: boolean) => { if ( window.confirm(`Are you sure you want to ${isActive ? 'deactivate' : 'activate'} this user?`) ) { toggleActiveMutation.mutate({ id: userId, isActive }); } }; const handleDelete = (userId: string) => { if ( window.confirm('Are you sure you want to delete this user? This action cannot be undone.') ) { deleteMutation.mutate(userId); } }; const getRoleBadgeColor = (role: string) => { const colors: Record = { admin: 'bg-red-100 text-red-800', manager: 'bg-blue-100 text-blue-800', user: 'bg-green-100 text-green-800', viewer: 'bg-gray-100 text-gray-800', }; return colors[role] || 'bg-gray-100 text-gray-800'; }; return (
{/* Header */}

User Management

Manage team members and their permissions

{success && (
{success}
)} {error && (
{error}
)} {/* Users Table */}
{isLoading ? (
Loading users...
) : users && users.length > 0 ? (
{users.map(user => ( ))}
User Email Role Status Last Login Actions
{user.firstName[0]} {user.lastName[0]}
{user.firstName} {user.lastName}
{user.phoneNumber || 'No phone'}
{user.email}
{user.isEmailVerified ? ( ✓ Verified ) : ( ⚠ Not verified )}
{user.lastLoginAt ? new Date(user.lastLoginAt).toLocaleDateString() : 'Never'}
) : (

No users

Get started by inviting a team member

)}
{/* Invite Modal */} {showInviteModal && (
setShowInviteModal(false)} />

Invite User

setInviteForm({ ...inviteForm, firstName: e.target.value })} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm px-3 py-2 border" />
setInviteForm({ ...inviteForm, lastName: e.target.value })} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm px-3 py-2 border" />
setInviteForm({ ...inviteForm, email: e.target.value })} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm px-3 py-2 border" />
setInviteForm({ ...inviteForm, phoneNumber: e.target.value })} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm px-3 py-2 border" />

A temporary password will be sent to the user's email

)}
); }