'use client' import { useState, useMemo, useEffect } from 'react' import { useRouter } from 'next/navigation' import Image from 'next/image' 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 { CountrySelect } from '@/components/ui/country-select' import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { cn } from '@/lib/utils' import { toast } from 'sonner' import { AvatarUpload } from '@/components/shared/avatar-upload' import { ProjectLogoUpload } from '@/components/shared/project-logo-upload' import { Textarea } from '@/components/ui/textarea' import { User, Bell, CheckCircle, Loader2, ArrowRight, ArrowLeft, Camera, Globe, FileText, Building2, Flag, ImageIcon, Compass, LayoutDashboard, Upload, ClipboardList, Users, Trophy, BookOpen, GraduationCap, } from 'lucide-react' import { AnimatedCard } from '@/components/shared/animated-container' import { UserAvatar } from '@/components/shared/user-avatar' type Step = | 'name' | 'photo' | 'nationality' | 'country' | 'institution' | 'bio' | 'logo' | 'preferences' | 'guide' | 'complete' type ApplicantWizardProps = { userData: { id: string name: string | null email: string role: string country: string | null nationality: string | null institution: string | null bio: string | null profileImageKey: string | null notificationPreference: string } avatarUrl: string | null | undefined refetchUser: () => void } export function ApplicantOnboardingWizard({ userData, avatarUrl, refetchUser, }: ApplicantWizardProps) { const router = useRouter() const [step, setStep] = useState('name') const [initialized, setInitialized] = useState(false) // Form state const [name, setName] = useState('') const [nationality, setNationality] = useState('') const [country, setCountry] = useState('') const [institution, setInstitution] = useState('') const [bio, setBio] = useState('') const [notificationPreference, setNotificationPreference] = useState< 'EMAIL' | 'WHATSAPP' | 'BOTH' | 'NONE' >('EMAIL') // Fetch onboarding context (project info) const { data: onboardingCtx } = trpc.applicant.getOnboardingContext.useQuery() const { data: logoUrl, refetch: refetchLogo } = trpc.applicant.getProjectLogoUrl.useQuery( { projectId: onboardingCtx?.projectId ?? '' }, { enabled: !!onboardingCtx?.projectId } ) // Initialize form with user data useEffect(() => { if (userData && !initialized) { if (userData.name) setName(userData.name) if (userData.country) setCountry(userData.country) if (userData.nationality) setNationality(userData.nationality) if (userData.institution) setInstitution(userData.institution) if (userData.bio) setBio(userData.bio) if (userData.notificationPreference) { setNotificationPreference(userData.notificationPreference as typeof notificationPreference) } setInitialized(true) } }, [userData, initialized]) // Prefill institution from project if user hasn't set one useEffect(() => { if (onboardingCtx?.institution && !institution && initialized) { setInstitution(onboardingCtx.institution) } }, [onboardingCtx, institution, initialized]) const utils = trpc.useUtils() const completeOnboarding = trpc.user.completeOnboarding.useMutation({ onSuccess: () => utils.user.me.invalidate(), }) const steps: Step[] = useMemo(() => { const base: Step[] = [ 'name', 'photo', 'nationality', 'country', 'institution', 'bio', ] // Only show logo step if applicant has a project if (onboardingCtx?.projectId) { base.push('logo') } base.push('preferences', 'guide', 'complete') return base }, [onboardingCtx?.projectId]) const currentIndex = steps.indexOf(step) const totalVisibleSteps = steps.length - 1 const goNext = () => { if (step === 'name' && !name.trim()) { toast.error('Please enter your name') return } const nextIndex = currentIndex + 1 if (nextIndex < steps.length) { setStep(steps[nextIndex]) } } const goBack = () => { const prevIndex = currentIndex - 1 if (prevIndex >= 0) { setStep(steps[prevIndex]) } } const handleComplete = async () => { try { await completeOnboarding.mutateAsync({ name, country: country || undefined, nationality: nationality || undefined, institution: institution || undefined, bio: bio || undefined, notificationPreference, }) setStep('complete') toast.success('Welcome to MOPC!') setTimeout(() => { router.push('/applicant') }, 2000) } catch (error) { toast.error(error instanceof Error ? error.message : 'Failed to complete onboarding') } } const stepLabels: Record = { name: 'Name', photo: 'Photo', nationality: 'Nationality', country: 'Residence', institution: 'Institution', bio: 'About', logo: 'Logo', preferences: 'Settings', guide: 'Guide', complete: 'Done', } return (
{/* Progress indicator */} {step !== 'complete' && (
{steps.slice(0, -1).map((s, i) => (
{stepLabels[s]}
))}

Step {currentIndex + 1} of {totalVisibleSteps}

)} {/* Step: Name */} {step === 'name' && ( <> Welcome to MOPC Let's get your profile set up. What should we call you?
setName(e.target.value)} placeholder="Enter your full name" autoFocus />
)} {/* Step: Profile Photo */} {step === 'photo' && ( <> Profile Photo Add a profile photo so others can recognize you. This step is optional.
refetchUser()} >

Click the avatar to upload a new photo.

)} {/* Step: Nationality */} {step === 'nationality' && ( <> Nationality Select your nationality.
)} {/* Step: Country of Residence */} {step === 'country' && ( <> Country of Residence Where are you currently based?
)} {/* Step: Institution */} {step === 'institution' && ( <> Institution Your organization or institution name.
setInstitution(e.target.value)} placeholder="e.g., Ocean Research Institute" />
)} {/* Step: Bio */} {step === 'bio' && ( <> About You Tell us a bit about yourself and your work. (Optional)