'use client' import { useEffect, useState } from 'react' import { useParams, useRouter } from 'next/navigation' import Link from 'next/link' import { trpc } from '@/lib/trpc/client' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Skeleton } from '@/components/ui/skeleton' import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { toast } from 'sonner' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { TagInput } from '@/components/shared/tag-input' import { UserActivityLog } from '@/components/shared/user-activity-log' import { EvaluationEditSheet } from '@/components/admin/evaluation-edit-sheet' import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog' import { UserAvatar } from '@/components/shared/user-avatar' import { ArrowLeft, Save, Mail, User, Shield, Loader2, AlertCircle, ClipboardList, Eye, ThumbsUp, ThumbsDown, Globe, Building2, FileText, FolderOpen, } from 'lucide-react' import { getCountryName, getCountryFlag } from '@/lib/countries' export default function MemberDetailPage() { const params = useParams() const router = useRouter() const utils = trpc.useUtils() const userId = params.id as string const { data: user, isLoading, error, refetch } = trpc.user.get.useQuery({ id: userId }) const { data: currentUser } = trpc.user.me.useQuery() const isSuperAdmin = currentUser?.role === 'SUPER_ADMIN' const updateUser = trpc.user.update.useMutation() const sendInvitation = trpc.user.sendInvitation.useMutation() // Mentor assignments (only fetched for mentors) const { data: mentorAssignments } = trpc.mentor.listAssignments.useQuery( { mentorId: userId, page: 1, perPage: 50 }, { enabled: user?.role === 'MENTOR' } ) // Juror evaluations (only fetched for jury members) const isJuror = user?.role === 'JURY_MEMBER' || user?.roles?.includes('JURY_MEMBER') const { data: jurorEvaluations } = trpc.evaluation.getJurorEvaluations.useQuery( { userId }, { enabled: !!user && !!isJuror } ) // eslint-disable-next-line @typescript-eslint/no-explicit-any const [selectedEvaluation, setSelectedEvaluation] = useState(null) const [name, setName] = useState('') const [email, setEmail] = useState('') const [role, setRole] = useState('JURY_MEMBER') const [status, setStatus] = useState('NONE') const [expertiseTags, setExpertiseTags] = useState([]) const [maxAssignments, setMaxAssignments] = useState('') const [showSuperAdminConfirm, setShowSuperAdminConfirm] = useState(false) const [pendingSuperAdminRole, setPendingSuperAdminRole] = useState(false) useEffect(() => { if (user) { setName(user.name || '') setEmail(user.email || '') setRole(user.role) setStatus(user.status) setExpertiseTags(user.expertiseTags || []) setMaxAssignments(user.maxAssignments?.toString() || '') } }, [user]) const handleSave = async () => { try { await updateUser.mutateAsync({ id: userId, email: email || undefined, name: name || null, role: role as 'SUPER_ADMIN' | 'PROGRAM_ADMIN' | 'AWARD_MASTER' | 'JURY_MEMBER' | 'MENTOR' | 'OBSERVER' | 'APPLICANT' | 'AUDIENCE', status: status as 'NONE' | 'INVITED' | 'ACTIVE' | 'SUSPENDED', expertiseTags, maxAssignments: maxAssignments ? parseInt(maxAssignments) : null, }) utils.user.get.invalidate({ id: userId }) utils.user.list.invalidate() toast.success('Member updated successfully') router.push('/admin/members') } catch (error) { toast.error(error instanceof Error ? error.message : 'Failed to update member') } } const handleSendInvitation = async () => { try { await sendInvitation.mutateAsync({ userId }) toast.success('Invitation email sent successfully') refetch() utils.user.list.invalidate() } catch (error) { toast.error(error instanceof Error ? error.message : 'Failed to send invitation') } } if (isLoading) { return (
) } if (error || !user) { return (
Error Loading Member {error?.message || 'The member you\'re looking for does not exist.'} {process.env.NODE_ENV === 'development' && (
User ID: {userId}
)}
) } return (
{/* Header */}

{user.name || 'Unnamed Member'}

{user.email}

{user.status === 'NONE' ? 'Not Invited' : user.status}
{(user.status === 'NONE' || user.status === 'INVITED') && ( )}
Profile {isJuror && ( Evaluations {jurorEvaluations && jurorEvaluations.length > 0 && ( {jurorEvaluations.length} )} )} {/* Profile Details (read-only) */} {(user.nationality || user.country || user.institution || user.bio) && ( Profile Details Information provided during onboarding
{user.nationality && (
{getCountryFlag(user.nationality)}

Nationality

{getCountryName(user.nationality)}

)} {user.country && (
{getCountryFlag(user.country)}

Country of Residence

{getCountryName(user.country)}

)} {user.institution && (

Institution / Organization

{user.institution}

)} {user.bio && (

Bio

{user.bio}

)}
)} {/* Team Memberships / Projects */} {user.teamMemberships && user.teamMemberships.length > 0 && ( Projects ({user.teamMemberships.length})
{user.teamMemberships.map((tm) => (

{tm.project.title}

{tm.project.teamName && (

{tm.project.teamName}

)}
{tm.project.competitionCategory && ( {tm.project.competitionCategory.replace('_', ' ')} )} {tm.role === 'LEAD' ? 'Lead' : tm.role === 'ADVISOR' ? 'Advisor' : 'Member'}
))}
)} {/* Jury Groups (for jury members) */} {user.juryGroupMemberships && user.juryGroupMemberships.length > 0 && ( Jury Groups ({user.juryGroupMemberships.length})
{user.juryGroupMemberships.map((m: { id: string; role: string; juryGroup: { id: string; name: string } }) => ( {m.juryGroup.name} ({m.role === 'CHAIR' ? 'Chair' : m.role === 'OBSERVER' ? 'Observer' : 'Member'}) ))}
)}
{/* Basic Info */} Basic Information
setEmail(e.target.value)} />
setName(e.target.value)} placeholder="Enter name" />
{/* Expertise & Capacity — only for jury/mentor/observer/admin roles */} {!['APPLICANT', 'AUDIENCE'].includes(user.role) && ( Expertise & Capacity
setMaxAssignments(e.target.value)} placeholder="Unlimited" />
{user._count && (

Statistics

Jury Assignments

{user._count.assignments}

Mentor Assignments

{user._count.mentorAssignments}

)}
)}
{/* Mentor Assignments Section */} {user.role === 'MENTOR' && mentorAssignments && mentorAssignments.assignments.length > 0 && ( Mentored Projects Projects this mentor is assigned to Project Category Status Assigned {mentorAssignments.assignments.map((assignment) => ( {assignment.project.title} {assignment.project.teamName && (

{assignment.project.teamName}

)}
{assignment.project.competitionCategory ? ( {assignment.project.competitionCategory.replace('_', ' ')} ) : ( '-' )} {assignment.project.status ?? 'SUBMITTED'} {new Date(assignment.assignedAt).toLocaleDateString()}
))}
)} {/* Activity Log */} {/* Status Alert */} {user.status === 'NONE' && ( Not Yet Invited This member was added to the platform via project import but hasn't been invited yet. Send them an invitation using the button above. )} {user.status === 'INVITED' && ( Invitation Pending This member hasn't accepted their invitation yet. You can resend the invitation email using the button above. )} {/* Save Button */}
{/* Evaluations Tab */} {isJuror && ( {!jurorEvaluations || jurorEvaluations.length === 0 ? (

No evaluations submitted yet

) : ( (() => { // Group evaluations by round const byRound = new Map() for (const ev of jurorEvaluations) { const key = ev.roundName if (!byRound.has(key)) byRound.set(key, []) byRound.get(key)!.push(ev) } return Array.from(byRound.entries()).map(([roundName, evals]) => ( {roundName} {evals.length} evaluation{evals.length !== 1 ? 's' : ''} Project Score Decision Status Submitted {evals.map((ev) => ( {ev.projectTitle} {ev.evaluation.globalScore !== null && ev.evaluation.globalScore !== undefined ? {ev.evaluation.globalScore}/10 : -} {ev.evaluation.binaryDecision !== null && ev.evaluation.binaryDecision !== undefined ? ( ev.evaluation.binaryDecision ? (
Yes
) : (
No
) ) : ( - )}
{ev.evaluation.status.replace('_', ' ')} {ev.evaluation.submittedAt ? new Date(ev.evaluation.submittedAt).toLocaleDateString() : '-'}
))}
)) })() )} { if (!open) setSelectedEvaluation(null) }} onSaved={() => utils.evaluation.getJurorEvaluations.invalidate({ userId })} />
)}
{/* Super Admin Confirmation Dialog */} Grant Super Admin Access? This will grant {name || user?.name || 'this user'} full Super Admin access, including user management, system settings, and all administrative capabilities. This action should only be performed for trusted administrators. { setPendingSuperAdminRole(false) }} > Cancel { setRole('SUPER_ADMIN') setPendingSuperAdminRole(false) setShowSuperAdminConfirm(false) }} className="bg-red-600 hover:bg-red-700" > Confirm Super Admin
) }