'use client' import { useState } from 'react' import { useSession } from 'next-auth/react' import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { z } from 'zod' import { trpc } from '@/lib/trpc/client' import { toast } from 'sonner' import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Badge } from '@/components/ui/badge' import { Skeleton } from '@/components/ui/skeleton' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog' import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from '@/components/ui/alert-dialog' import { Users, UserPlus, Crown, Mail, Trash2, Loader2, AlertCircle, CheckCircle, Clock, FileText, } from 'lucide-react' const inviteSchema = z.object({ name: z.string().min(1, 'Name is required'), email: z.string().email('Invalid email address'), role: z.enum(['MEMBER', 'ADVISOR']), title: z.string().optional(), }) type InviteFormData = z.infer const roleLabels: Record = { LEAD: 'Team Lead', MEMBER: 'Team Member', ADVISOR: 'Advisor', } const statusLabels: Record }> = { ACTIVE: { label: 'Active', icon: CheckCircle }, INVITED: { label: 'Pending', icon: Clock }, SUSPENDED: { label: 'Suspended', icon: AlertCircle }, } export default function ApplicantTeamPage() { const { data: session, status: sessionStatus } = useSession() const isAuthenticated = sessionStatus === 'authenticated' const [isInviteOpen, setIsInviteOpen] = useState(false) const { data: dashboardData, isLoading: dashLoading } = trpc.applicant.getMyDashboard.useQuery( undefined, { enabled: isAuthenticated } ) const projectId = dashboardData?.project?.id const { data: teamData, isLoading: teamLoading, refetch } = trpc.applicant.getTeamMembers.useQuery( { projectId: projectId! }, { enabled: !!projectId } ) const inviteMutation = trpc.applicant.inviteTeamMember.useMutation({ onSuccess: (result) => { if (result.requiresAccountSetup) { toast.success('Invitation email sent to team member') } else { toast.success('Team member added and notified by email') } setIsInviteOpen(false) refetch() }, onError: (error) => { toast.error(error.message) }, }) const removeMutation = trpc.applicant.removeTeamMember.useMutation({ onSuccess: () => { toast.success('Team member removed') refetch() }, onError: (error) => { toast.error(error.message) }, }) const form = useForm({ resolver: zodResolver(inviteSchema), defaultValues: { name: '', email: '', role: 'MEMBER', title: '', }, }) const onInvite = async (data: InviteFormData) => { if (!projectId) return await inviteMutation.mutateAsync({ projectId, ...data, }) form.reset() } const isLoading = dashLoading || teamLoading if (isLoading) { return (
{[1, 2, 3].map((i) => (
))}
) } if (!projectId) { return (

Team

No Project

Submit a project first to manage your team.

) } // Check if user is team lead const currentUserMember = teamData?.teamMembers.find( (tm) => tm.userId === session?.user?.id ) const isTeamLead = currentUserMember?.role === 'LEAD' || teamData?.submittedBy?.id === session?.user?.id return (
{/* Header */}

Team Members

Manage your project team

{isTeamLead && ( Invite Team Member Send an invitation to join your project team. They will receive an email with instructions to create their account.
{form.formState.errors.name && (

{form.formState.errors.name.message}

)}
{form.formState.errors.email && (

{form.formState.errors.email.message}

)}
)}
{/* Team Members List */} Team ({teamData?.teamMembers.length || 0} members) Everyone on this list can view and collaborate on this project. {teamData?.teamMembers.map((member) => { const StatusIcon = statusLabels[member.user.status]?.icon || AlertCircle return (
{member.role === 'LEAD' ? ( ) : ( {member.user.name?.charAt(0).toUpperCase() || '?'} )}
{member.user.name} {roleLabels[member.role] || member.role} {member.title && ( ({member.title}) )}
{member.user.email} {statusLabels[member.user.status]?.label || member.user.status}
{isTeamLead && member.role !== 'LEAD' && teamData.submittedBy?.id !== member.userId && ( Remove Team Member Are you sure you want to remove {member.user.name} from the team? They will no longer have access to this project. Cancel removeMutation.mutate({ projectId, userId: member.userId })} className="bg-destructive text-destructive-foreground hover:bg-destructive/90" > Remove )}
) })} {(!teamData?.teamMembers || teamData.teamMembers.length === 0) && (

No team members yet.

{isTeamLead && ( )}
)}
{/* Team Documents - visible via applicant documents page */} {/* Info Card */}

About Team Access

All team members can view project details and status updates. Only the team lead can invite or remove team members. Invited members will receive an email to set up their account.

) }