/** * User Management Page * * Manage organization users, roles, and invitations */ 'use client'; import { useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { listUsers, updateUser, deleteUser, canInviteUser } from '@/lib/api'; import { createInvitation } from '@/lib/api/invitations'; import { useAuth } from '@/lib/context/auth-context'; import Link from 'next/link'; export default function UsersManagementPage() { const router = useRouter(); const queryClient = useQueryClient(); const { user: currentUser } = useAuth(); const [showInviteModal, setShowInviteModal] = useState(false); const [openMenuId, setOpenMenuId] = useState(null); const [menuPosition, setMenuPosition] = useState<{ top: number; left: number } | null>(null); const [inviteForm, setInviteForm] = useState({ email: '', firstName: '', lastName: '', role: 'USER' as 'MANAGER' | 'USER' | 'VIEWER', }); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); const { data: users, isLoading } = useQuery({ queryKey: ['users'], queryFn: () => listUsers(), }); // Check license availability const { data: licenseStatus } = useQuery({ queryKey: ['canInvite'], queryFn: () => canInviteUser(), }); const inviteMutation = useMutation({ mutationFn: (data: typeof inviteForm) => { return createInvitation({ email: data.email, firstName: data.firstName, lastName: data.lastName, role: data.role, }); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }); queryClient.invalidateQueries({ queryKey: ['canInvite'] }); setSuccess('Invitation sent successfully! The user will receive an email with a registration link.'); setShowInviteModal(false); setInviteForm({ email: '', firstName: '', lastName: '', role: 'USER', }); setTimeout(() => setSuccess(''), 5000); }, onError: (err: any) => { setError(err.response?.data?.message || 'Failed to send invitation'); setTimeout(() => setError(''), 5000); }, }); const changeRoleMutation = useMutation({ mutationFn: ({ id, role }: { id: string; role: 'ADMIN' | 'MANAGER' | 'USER' | 'VIEWER' }) => { return updateUser(id, { role }); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }); setSuccess('Role updated successfully'); setTimeout(() => setSuccess(''), 3000); }, onError: (err: any) => { setError(err.response?.data?.message || 'Failed to update role'); setTimeout(() => setError(''), 5000); }, }); const toggleActiveMutation = useMutation({ mutationFn: ({ id, isActive }: { id: string; isActive: boolean }) => { return updateUser(id, { isActive: !isActive }); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }); queryClient.invalidateQueries({ queryKey: ['canInvite'] }); setSuccess('User status updated successfully'); setTimeout(() => setSuccess(''), 3000); }, onError: (err: any) => { setError(err.response?.data?.message || 'Failed to update user status'); setTimeout(() => setError(''), 5000); }, }); const deleteMutation = useMutation({ mutationFn: (id: string) => deleteUser(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }); queryClient.invalidateQueries({ queryKey: ['canInvite'] }); setSuccess('User deleted successfully'); setTimeout(() => setSuccess(''), 3000); }, onError: (err: any) => { setError(err.response?.data?.message || 'Failed to delete user'); setTimeout(() => setError(''), 5000); }, }); // Restrict access to ADMIN and MANAGER only useEffect(() => { if (currentUser && currentUser.role !== 'ADMIN' && currentUser.role !== 'MANAGER') { router.push('/dashboard'); } }, [currentUser, router]); // Don't render until we've checked permissions if (!currentUser || (currentUser.role !== 'ADMIN' && currentUser.role !== 'MANAGER')) { return (
); } const handleInvite = (e: React.FormEvent) => { e.preventDefault(); setError(''); inviteMutation.mutate(inviteForm); }; const handleRoleChange = (userId: string, newRole: string) => { changeRoleMutation.mutate({ id: userId, role: newRole as 'ADMIN' | 'MANAGER' | 'USER' | 'VIEWER' }); }; 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 (
{/* License Warning */} {licenseStatus && !licenseStatus.canInvite && (

License limit reached

Your organization has used all available licenses ({licenseStatus.usedLicenses}/{licenseStatus.maxLicenses}). Upgrade your subscription to invite more users.

Upgrade Subscription
)} {/* License Usage Info */} {licenseStatus && licenseStatus.canInvite && licenseStatus.availableLicenses <= 2 && licenseStatus.maxLicenses !== -1 && (
{licenseStatus.availableLicenses} license{licenseStatus.availableLicenses !== 1 ? 's' : ''} remaining ({licenseStatus.usedLicenses}/{licenseStatus.maxLicenses} used)
Manage Subscription
)} {/* Header */}

User Management

Manage team members and their permissions

{licenseStatus?.canInvite ? ( ) : ( + Upgrade to Invite )}
{success && (
{success}
)} {error && (
{error}
)} {/* Users Table */}
{isLoading ? (
Loading users...
) : users?.users && users.users.length > 0 ? (
{users.users.map(user => ( ))}
User Email Role Status Last Login Actions
{user.firstName[0]} {user.lastName[0]}
{user.firstName} {user.lastName}
{user.email}
{user.email}
{user.isActive ? 'Active' : 'Inactive'} {new Date(user.createdAt).toLocaleDateString()}
) : (

No users

Get started by inviting a team member

{licenseStatus?.canInvite ? ( ) : ( + Upgrade to Invite )}
)}
{/* Actions Menu Modal */} {openMenuId && menuPosition && ( <>
{ setOpenMenuId(null); setMenuPosition(null); }} />
)} {/* 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" />
{currentUser?.role !== 'ADMIN' && (

Only platform administrators can assign the ADMIN role

)}
)}
); }