'use client' import { useEffect, useState } from 'react' import { useRouter } from 'next/navigation' import PageLayout from '../../../components/PageLayout' import useAuthStore from '../../../store/authStore' import { useUserStatus } from '../../../hooks/useUserStatus' import { useToast } from '../../../components/toast/toastComponent' interface PersonalProfileData { dob: string nationality: string street: string postalCode: string city: string country: string accountHolder: string iban: string secondPhone: string emergencyName: string emergencyPhone: string } // Common nationalities list const NATIONALITIES = [ 'German', 'Austrian', 'Swiss', 'Italian', 'French', 'Spanish', 'Portuguese', 'Dutch', 'Belgian', 'Polish', 'Czech', 'Hungarian', 'Croatian', 'Slovenian', 'Slovak', 'British', 'Irish', 'Swedish', 'Norwegian', 'Danish', 'Finnish', 'Russian', 'Turkish', 'Greek', 'Romanian', 'Bulgarian', 'Serbian', 'Albanian', 'Bosnian', 'American', 'Canadian', 'Brazilian', 'Argentinian', 'Mexican', 'Chinese', 'Japanese', 'Indian', 'Pakistani', 'Australian', 'South African', 'Other' ] // Common countries list const COUNTRIES = [ 'Germany', 'Austria', 'Switzerland', 'Italy', 'France', 'Spain', 'Portugal', 'Netherlands', 'Belgium', 'Poland', 'Czech Republic', 'Hungary', 'Croatia', 'Slovenia', 'Slovakia', 'United Kingdom', 'Ireland', 'Sweden', 'Norway', 'Denmark', 'Finland', 'Russia', 'Turkey', 'Greece', 'Romania', 'Bulgaria', 'Serbia', 'Albania', 'Bosnia and Herzegovina', 'United States', 'Canada', 'Brazil', 'Argentina', 'Mexico', 'China', 'Japan', 'India', 'Pakistan', 'Australia', 'South Africa', 'Other' ] const initialData: PersonalProfileData = { dob: '', nationality: '', street: '', postalCode: '', city: '', country: '', accountHolder: '', iban: '', secondPhone: '', emergencyName: '', emergencyPhone: '' } export default function PersonalAdditionalInformationPage() { const router = useRouter() const { accessToken } = useAuthStore() const { refreshStatus } = useUserStatus() const { showToast } = useToast() const [form, setForm] = useState(initialData) const [loading, setLoading] = useState(false) const [success, setSuccess] = useState(false) const [error, setError] = useState('') // Prefill form if profile already exists useEffect(() => { let abort = false async function loadProfile() { if (!accessToken) return try { const res = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/api/me`, { method: 'GET', headers: { Authorization: `Bearer ${accessToken}` } }) if (!res.ok) return const data = await res.json().catch(() => null) const profile = data?.profile const user = data?.user if (!profile || abort) return const toDateInput = (d?: string) => { if (!d) return '' const dt = new Date(d) if (Number.isNaN(dt.getTime())) return '' return dt.toISOString().split('T')[0] } setForm(prev => ({ ...prev, dob: toDateInput(profile.date_of_birth || profile.dateOfBirth), nationality: profile.nationality || prev.nationality, street: profile.address || prev.street, postalCode: profile.zip_code || profile.zipCode || prev.postalCode, city: profile.city || prev.city, country: profile.country || prev.country, accountHolder: profile.account_holder_name || profile.accountHolderName || prev.accountHolder, // Prefer IBAN from users table (data.user.iban), fallback to profile if any iban: (user?.iban ?? profile.iban ?? prev.iban) as string, secondPhone: profile.phone_secondary || profile.phoneSecondary || prev.secondPhone, emergencyName: profile.emergency_contact_name || profile.emergencyContactName || prev.emergencyName, emergencyPhone: profile.emergency_contact_phone || profile.emergencyContactPhone || prev.emergencyPhone, })) } catch (_) { // ignore prefill errors; user can still fill manually } } loadProfile() return () => { abort = true } }, [accessToken]) const handleChange = (e: React.ChangeEvent) => { const { name, value } = e.target setForm(p => ({ ...p, [name]: value })) setError('') } const validateDateOfBirth = (dob: string) => { if (!dob) return false const birthDate = new Date(dob) const today = new Date() // Check if date is valid if (isNaN(birthDate.getTime())) return false // Check if birth date is not in the future if (birthDate > today) return false // Check minimum age (18 years) const minDate = new Date() minDate.setFullYear(today.getFullYear() - 18) if (birthDate > minDate) return false // Check maximum age (120 years) const maxDate = new Date() maxDate.setFullYear(today.getFullYear() - 120) if (birthDate < maxDate) return false return true } const validate = () => { const requiredKeys: (keyof PersonalProfileData)[] = [ 'dob','nationality','street','postalCode','city','country','accountHolder','iban' ] for (const k of requiredKeys) { if (!form[k].trim()) { const msg = 'Please fill in all required fields.' setError(msg) showToast({ variant: 'error', title: 'Missing information', message: msg, }) return false } } // Date of birth validation if (!validateDateOfBirth(form.dob)) { const msg = 'Invalid date of birth. You must be at least 18 years old.' setError(msg) showToast({ variant: 'error', title: 'Invalid date of birth', message: msg, }) return false } // very loose IBAN check if (!/^([A-Z]{2}\d{2}[A-Z0-9]{10,30})$/i.test(form.iban.replace(/\s+/g,''))) { const msg = 'Invalid IBAN.' setError(msg) showToast({ variant: 'error', title: 'Invalid IBAN', message: msg, }) return false } setError('') return true } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (loading || success) return if (!validate()) return if (!accessToken) { const msg = 'Not authenticated. Please log in again.' setError(msg) showToast({ variant: 'error', title: 'Authentication error', message: msg, }) return } setLoading(true) try { // Prepare data for backend with correct field names const profileData = { dateOfBirth: form.dob, nationality: form.nationality, address: form.street, // Backend expects 'address', not nested object zip_code: form.postalCode, // Backend expects 'zip_code' city: form.city, country: form.country, phoneSecondary: form.secondPhone || null, // Backend expects 'phoneSecondary' emergencyContactName: form.emergencyName || null, emergencyContactPhone: form.emergencyPhone || null, accountHolderName: form.accountHolder, // Backend expects 'accountHolderName' iban: form.iban.replace(/\s+/g, '') // Remove spaces from IBAN } const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/api/profile/personal/complete`, { method: 'POST', headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify(profileData) }) if (!response.ok) { const errorData = await response.json().catch(() => ({ message: 'Save failed' })) throw new Error(errorData.message || 'Save failed') } setSuccess(true) showToast({ variant: 'success', title: 'Profile saved', message: 'Your personal profile has been saved successfully.', }) // Refresh user status to update profile completion state await refreshStatus() // Redirect to next step after short delay setTimeout(() => { // Check if we came from tutorial const urlParams = new URLSearchParams(window.location.search) const fromTutorial = urlParams.get('tutorial') === 'true' if (fromTutorial) { router.push('/quickaction-dashboard?tutorial=true') } else { router.push('/quickaction-dashboard/register-sign-contract/personal') } }, 1500) } catch (error: any) { console.error('Personal profile save error:', error) const msg = error.message || 'Save failed. Please try again.' setError(msg) showToast({ variant: 'error', title: 'Save failed', message: msg, }) } finally { setLoading(false) } } return (
{/* Animated background (same as dashboard) */}
{/* Soft gradient blobs */}
{/* Subtle radial highlight */}

Complete Your Profile

{/* Personal Information */}

Personal Information


{/* Bank Details */}

Bank Details


{/* Additional Information */}

Additional Information

{error && (
{error}
)} {success && (
Data saved. Redirecting shortly…
)}
) }