'use client' import { useState } from 'react' import Link from 'next/link' import { trpc } from '@/lib/trpc/client' import { Button } from '@/components/ui/button' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog' import { toast } from 'sonner' import { MoreHorizontal, Mail, UserCog, Trash2, Loader2, Shield, Check, } from 'lucide-react' type Role = 'SUPER_ADMIN' | 'PROGRAM_ADMIN' | 'JURY_MEMBER' | 'MENTOR' | 'OBSERVER' const ROLE_LABELS: Record = { SUPER_ADMIN: 'Super Admin', PROGRAM_ADMIN: 'Program Admin', JURY_MEMBER: 'Jury Member', MENTOR: 'Mentor', OBSERVER: 'Observer', } interface UserActionsProps { userId: string userEmail: string userStatus: string userRole: Role currentUserRole?: Role } export function UserActions({ userId, userEmail, userStatus, userRole, currentUserRole }: UserActionsProps) { const [showDeleteDialog, setShowDeleteDialog] = useState(false) const [isSending, setIsSending] = useState(false) const utils = trpc.useUtils() const sendInvitation = trpc.user.sendInvitation.useMutation() const deleteUser = trpc.user.delete.useMutation({ onSuccess: () => { utils.user.list.invalidate() }, }) const updateUser = trpc.user.update.useMutation({ onSuccess: () => { utils.user.list.invalidate() toast.success('Role updated successfully') }, onError: (error) => { toast.error(error.message || 'Failed to update role') }, }) const isSuperAdmin = currentUserRole === 'SUPER_ADMIN' // Determine which roles can be assigned const getAvailableRoles = (): Role[] => { if (isSuperAdmin) { return ['SUPER_ADMIN', 'PROGRAM_ADMIN', 'JURY_MEMBER', 'MENTOR', 'OBSERVER'] } // Program admins can only assign lower roles return ['JURY_MEMBER', 'MENTOR', 'OBSERVER'] } // Can this user's role be changed by the current user? const canChangeRole = isSuperAdmin || (!['SUPER_ADMIN', 'PROGRAM_ADMIN'].includes(userRole)) const handleRoleChange = (newRole: Role) => { if (newRole === userRole) return updateUser.mutate({ id: userId, role: newRole }) } const handleSendInvitation = async () => { if (userStatus !== 'NONE' && userStatus !== 'INVITED') { toast.error('User has already accepted their invitation') return } setIsSending(true) try { await sendInvitation.mutateAsync({ userId }) toast.success(`Invitation sent to ${userEmail}`) // Invalidate in case status changed utils.user.list.invalidate() } catch (error) { toast.error(error instanceof Error ? error.message : 'Failed to send invitation') } finally { setIsSending(false) } } const handleDelete = async () => { try { await deleteUser.mutateAsync({ id: userId }) toast.success('User deleted successfully') setShowDeleteDialog(false) } catch (error) { toast.error(error instanceof Error ? error.message : 'Failed to delete user') } } return ( <> Edit {canChangeRole && ( {updateUser.isPending ? 'Updating...' : 'Change Role'} {getAvailableRoles().map((role) => ( handleRoleChange(role)} disabled={role === userRole} > {role === userRole && } {ROLE_LABELS[role]} ))} )} {isSending ? 'Sending...' : 'Send Invite'} setShowDeleteDialog(true)} > Remove Delete User Are you sure you want to delete {userEmail}? This action cannot be undone and will remove all their assignments and evaluations. Cancel {deleteUser.isPending ? ( ) : null} Delete ) } interface UserMobileActionsProps { userId: string userEmail: string userStatus: string userRole: Role currentUserRole?: Role } export function UserMobileActions({ userId, userEmail, userStatus, userRole, currentUserRole, }: UserMobileActionsProps) { const [isSending, setIsSending] = useState(false) const utils = trpc.useUtils() const sendInvitation = trpc.user.sendInvitation.useMutation() const updateUser = trpc.user.update.useMutation({ onSuccess: () => { utils.user.list.invalidate() toast.success('Role updated successfully') }, onError: (error) => { toast.error(error.message || 'Failed to update role') }, }) const isSuperAdmin = currentUserRole === 'SUPER_ADMIN' const canChangeRole = isSuperAdmin || (!['SUPER_ADMIN', 'PROGRAM_ADMIN'].includes(userRole)) const handleSendInvitation = async () => { if (userStatus !== 'NONE' && userStatus !== 'INVITED') { toast.error('User has already accepted their invitation') return } setIsSending(true) try { await sendInvitation.mutateAsync({ userId }) toast.success(`Invitation sent to ${userEmail}`) } catch (error) { toast.error(error instanceof Error ? error.message : 'Failed to send invitation') } finally { setIsSending(false) } } return (
{canChangeRole && ( )}
) }