'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 { CountrySelect } from '@/components/ui/country-select' import { Checkbox as CheckboxPrimitive } from '@/components/ui/checkbox' import { ProjectLogoUpload } from '@/components/shared/project-logo-upload' import { UserAvatar } from '@/components/shared/user-avatar' import { FolderOpen, Users, UserPlus, Crown, Mail, Trash2, Loader2, AlertCircle, CheckCircle, Clock, FileText, ImageIcon, MapPin, Waves, GraduationCap, Heart, Calendar, } from 'lucide-react' import { formatDateOnly } from '@/lib/utils' 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(), nationality: z.string().optional(), country: z.string().optional(), institution: z.string().optional(), sendInvite: z.boolean().default(true), }) 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 }, } const OCEAN_ISSUE_LABELS: Record = { POLLUTION_REDUCTION: 'Pollution Reduction', CLIMATE_MITIGATION: 'Climate Mitigation', TECHNOLOGY_INNOVATION: 'Technology Innovation', SUSTAINABLE_SHIPPING: 'Sustainable Shipping', BLUE_CARBON: 'Blue Carbon', HABITAT_RESTORATION: 'Habitat Restoration', COMMUNITY_CAPACITY: 'Community Capacity', SUSTAINABLE_FISHING: 'Sustainable Fishing', CONSUMER_AWARENESS: 'Consumer Awareness', OCEAN_ACIDIFICATION: 'Ocean Acidification', OTHER: 'Other', } export default function ApplicantProjectPage() { 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 project = dashboardData?.project const projectId = project?.id const isIntakeOpen = dashboardData?.isIntakeOpen ?? false const { data: teamData, isLoading: teamLoading, refetch } = trpc.applicant.getTeamMembers.useQuery( { projectId: projectId! }, { enabled: !!projectId } ) const { data: logoUrl, refetch: refetchLogo } = trpc.applicant.getProjectLogoUrl.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: '', nationality: '', country: '', institution: '', sendInvite: true, }, }) 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 || !project) { return (

Project

No Project

Submit a project first to view details.

) } // 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 */}
{/* Project logo */}
{logoUrl ? ( {project.title} ) : ( )}

{project.title}

{project.teamName ? `Team: ${project.teamName}` : 'Project details and team management'}

{/* Project Details Card */}
Project Information {isIntakeOpen && ( Editable during intake )}
{/* Category & Ocean Issue badges */}
{project.competitionCategory && ( {project.competitionCategory === 'STARTUP' ? 'Start-up' : 'Business Concept'} )} {project.oceanIssue && ( {OCEAN_ISSUE_LABELS[project.oceanIssue] || project.oceanIssue.replace(/_/g, ' ')} )} {project.wantsMentorship && ( Wants Mentorship )}
{/* Description */} {project.description && (

Description

{project.description}

)} {/* Location, Institution, Founded */}
{(project.country || project.geographicZone) && (

Location

{project.geographicZone || project.country}

)} {project.institution && (

Institution

{project.institution}

)} {project.foundedAt && (

Founded

{formatDateOnly(project.foundedAt)}

)}
{/* Mentor info */} {project.mentorAssignment?.mentor && (

Assigned Mentor

{project.mentorAssignment.mentor.name} ({project.mentorAssignment.mentor.email})

)} {/* Tags */} {project.tags && project.tags.length > 0 && (

Tags

{project.tags.map((tag: string) => ( {tag} ))}
)}
{/* Project Logo */} {isTeamLead && projectId && ( Project Logo Click the image to upload or change your project logo. refetchLogo()} /> )} {/* Team Members List */}
Team ({teamData?.teamMembers.length || 0} members) Everyone on this list can view and collaborate on this project.
{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}

)}
form.setValue('nationality', v)} placeholder="Select nationality" />
form.setValue('country', v)} placeholder="Select country" />
form.setValue('sendInvite', !!checked)} />

What invited members can do:

  • Upload documents for submission rounds
  • View project status and competition progress
  • Receive email notifications about round updates

Only the Team Lead can invite or remove members.

)}
{teamData?.teamMembers.map((member) => { const StatusIcon = statusLabels[member.user.status]?.icon || AlertCircle return (
{member.role === 'LEAD' && (
)}
{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 && ( )}
)}
) }