import { useEffect, useMemo, useRef, useState } from 'react'; import { Box, Button, Center, Group, Paper, Stack, Text, Title, rem, } from '@mantine/core'; import { IconArrowLeft, IconArrowRight, IconCheck, IconCircleCheck, IconLogout2, IconMapPin, IconUser, } from '@tabler/icons-react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { useNavigate } from 'react-router-dom'; import { useApiMutation } from '@ema-platform/api'; import { notify } from '@ema-platform/ui'; import { authStorage, setUser, logout } from '@ema-platform/auth'; import { useAppDispatch, useAppSelector } from '../../../store/hooks'; import { ProfileFormContent, profileSchema, type ProfileValues, } from '../../profile/components/ProfileFormContent'; import { AddressFormContent, addressSchema, type AddressValues, } from '../../profile/components/AddressFormContent'; const STEPS = [ { label: 'Profile', icon: IconUser }, { label: 'Address', icon: IconMapPin }, ]; function StepIndicator({ active, completed }: { active: number; completed: number[] }) { return ( {STEPS.map((step, i) => { const isDone = completed.includes(i); const isCurrent = active === i; return ( {isDone ? ( ) : ( {i + 1} )} {step.label} {i < STEPS.length - 1 && ( )} ); })} ); } function inferUserType(professionName: string): string { const name = professionName.toLowerCase(); if (name.includes('seafarer')) return 'SEAFARER'; return 'EMPLOYEE'; } export function ProfileSetupPage() { const navigate = useNavigate(); const dispatch = useAppDispatch(); const user = useAppSelector((state) => state.auth.user); const [active, setActive] = useState(0); const [completed, setCompleted] = useState([]); const [submitting, setSubmitting] = useState(false); const [professions, setProfessions] = useState>([]); const [professionsLoading, setProfessionsLoading] = useState(true); const [profileTrigger] = useApiMutation<{ id: string }>(); const [addressTrigger] = useApiMutation(); const [meTrigger] = useApiMutation<{ id: string }>(); const [profileCheckTrigger] = useApiMutation<{ total: number; items: Array<{ id: string; user: { id: string }; isComplete: boolean }> }>(); const [fetchProfessions] = useApiMutation<{ count: number; items: Array<{ id: string; name: { en: string } }> }>(); const fetched = useRef(false); const checkedExistingProfile = useRef(false); useEffect(() => { if (fetched.current) return; fetched.current = true; fetchProfessions({ url: '/professions?take=100', method: 'GET' }) .unwrap() .then((data) => setProfessions(data.items ?? [])) .catch(() => setProfessions([])) .finally(() => setProfessionsLoading(false)); }, [fetchProfessions]); useEffect(() => { if (!user || checkedExistingProfile.current) return; checkedExistingProfile.current = true; profileCheckTrigger({ url: `/profiles?q=${encodeURIComponent('i=user')}`, method: 'GET' }) .unwrap() .then((data) => { const existing = data.items?.find((p) => p.user?.id === user.id); if (existing?.isComplete) { authStorage.setProfileId(existing.id); navigate('/dashboard', { replace: true }); } }) .catch(() => {}); }, [user, navigate, profileCheckTrigger]); const professionOptions = useMemo( () => professions.map((p) => ({ value: p.id, label: p.name.en })), [professions], ); const professionNameMap = useMemo(() => { const map: Record = {}; professions.forEach((p) => { map[p.id] = p.name.en; }); return map; }, [professions]); const nameParts = useMemo(() => (user?.name?.en || '').trim().split(/\s+/), [user]); const profileDefaults: ProfileValues = useMemo(() => ({ professionId: '', firstName: nameParts[0] || '', middleName: nameParts.length > 2 ? nameParts.slice(1, -1).join(' ') : '', lastName: nameParts.length > 1 ? nameParts[nameParts.length - 1] : '', gender: '', dob: '', pob: '', maritalStatus: '', }), [nameParts]); const addressDefaults: AddressValues = useMemo(() => ({ idType: '', idNumber: '', nationality: '', primaryPhoneNumber: user?.phoneNumber || '', secondaryPhoneNumber: '', email: user?.email || '', regionId: '', cityId: '', subcityId: '', woredaId: '', kebeleId: '', streetAddress: '', postalAddress: '', emergencyContactName: '', emergencyContactPhone: '', emergencyContactRelation: '', }), [user]); const { register: profileRegister, handleSubmit: profileHandleSubmit, formState: { errors: profileErrors }, setValue: profileSetValue, watch: profileWatch, trigger: profileTriggerValidation, } = useForm({ resolver: zodResolver(profileSchema), defaultValues: profileDefaults, }); const { register: addressRegister, handleSubmit: addressHandleSubmit, formState: { errors: addressErrors }, setValue: addressSetValue, watch: addressWatch, trigger: addressTriggerValidation, } = useForm({ resolver: zodResolver(addressSchema), defaultValues: addressDefaults, }); const onNext = async () => { const valid = await profileTriggerValidation(); if (!valid) return; setCompleted((prev) => (prev.includes(active) ? prev : [...prev, active])); setActive((c) => c + 1); }; const onSubmitAddress = async () => { const valid = await addressTriggerValidation(); if (!valid) return; setSubmitting(true); try { const pv = profileWatch(); const av = addressWatch(); const selectedProfessionName = professionNameMap[pv.professionId] ?? ''; const profileResult = await profileTrigger({ url: '/profiles', method: 'POST', body: { userId: user?.id, type: inferUserType(selectedProfessionName), professionId: pv.professionId, firstName: pv.firstName, middleName: pv.middleName, lastName: pv.lastName, gender: pv.gender, dob: pv.dob, pob: pv.pob || undefined, maritalStatus: pv.maritalStatus, }, }).unwrap(); await profileTrigger({ url: `/profiles/${profileResult.id}`, method: 'PUT', body: { isComplete: true }, }).unwrap(); authStorage.setProfileId(profileResult.id); await addressTrigger({ url: `/addresss/profile/${profileResult.id}`, method: 'POST', body: { idType: av.idType, idNumber: av.idNumber, nationality: av.nationality, primaryPhoneNumber: av.primaryPhoneNumber, secondaryPhoneNumber: av.secondaryPhoneNumber || undefined, email: av.email || undefined, regionId: av.regionId || undefined, cityId: av.cityId || undefined, subcityId: av.subcityId || undefined, woredaId: av.woredaId || undefined, kebeleId: av.kebeleId || undefined, streetAddress: av.streetAddress || undefined, postalAddess: av.postalAddress || undefined, emergencyContactName: av.emergencyContactName || undefined, emergencyContactPhone: av.emergencyContactPhone || undefined, emergencyContactRelation: av.emergencyContactRelation || undefined, }, }).unwrap(); const me = await meTrigger({ url: '/auth/me', method: 'GET' }).unwrap(); dispatch(setUser(me)); notify.success('Profile setup complete!'); navigate('/dashboard'); } catch { notify.error('Failed to save profile. Please try again.'); } finally { setSubmitting(false); } }; if (!user) { return (
Please log in first.
); } return (
Complete Your Profile Set up your profile and address to get started
{active === 0 && ( <> Personal Information )} {active === 1 && ( <> Identity & Contact )} {active > 0 && ( )} {active < STEPS.length - 1 ? ( ) : ( )}
); }