profit-planet-frontend/src/app/quickaction-dashboard/register-additional-information/personal/page.tsx
DeathKaioken 4074ea4eee Bibelbumser
Co-authored-by: Copilot <copilot@github.com>
2026-05-04 23:48:09 +02:00

862 lines
36 KiB
TypeScript

'use client'
import { useEffect, useMemo, useRef, useState, useCallback } 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'
import { useTranslation } from '../../../i18n/useTranslation'
import { ChevronDownIcon } from '@heroicons/react/20/solid'
import TelephoneInput, { TelephoneInputHandle } from '../../../components/phone/telephoneInput'
interface PersonalProfileData {
firstName: string
lastName: string
email: string
phone: string
dob: string
nationality: string
street: string
postalCode: string
city: string
country: string
accountHolder: string
iban: string
secondPhone: string
emergencyName: string
emergencyPhone: string
}
const NATIONALITY_CODES = [
'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', 'southAfrican', 'other'
] as const
const COUNTRY_CODES = [
'germany', 'austria', 'switzerland', 'italy', 'france', 'spain', 'portugal', 'netherlands',
'belgium', 'poland', 'czechRepublic', 'hungary', 'croatia', 'slovenia', 'slovakia',
'unitedKingdom', 'ireland', 'sweden', 'norway', 'denmark', 'finland', 'russia',
'turkey', 'greece', 'romania', 'bulgaria', 'serbia', 'albania', 'bosniaHerzegovina',
'unitedStates', 'canada', 'brazil', 'argentina', 'mexico', 'china', 'japan',
'india', 'pakistan', 'australia', 'southAfrica', 'other'
] as const
const NATIONALITY_VALUE_BY_CODE: Record<(typeof NATIONALITY_CODES)[number], string> = {
german: 'German', austrian: 'Austrian', swiss: 'Swiss', italian: 'Italian', french: 'French', spanish: 'Spanish', portuguese: 'Portuguese', dutch: 'Dutch', belgian: 'Belgian', polish: 'Polish', czech: 'Czech', hungarian: 'Hungarian', croatian: 'Croatian', slovenian: 'Slovenian', slovak: 'Slovak', british: 'British', irish: 'Irish', swedish: 'Swedish', norwegian: 'Norwegian', danish: 'Danish', finnish: 'Finnish', russian: 'Russian', turkish: 'Turkish', greek: 'Greek', romanian: 'Romanian', bulgarian: 'Bulgarian', serbian: 'Serbian', albanian: 'Albanian', bosnian: 'Bosnian', american: 'American', canadian: 'Canadian', brazilian: 'Brazilian', argentinian: 'Argentinian', mexican: 'Mexican', chinese: 'Chinese', japanese: 'Japanese', indian: 'Indian', pakistani: 'Pakistani', australian: 'Australian', southAfrican: 'South African', other: 'Other'
}
const COUNTRY_VALUE_BY_CODE: Record<(typeof COUNTRY_CODES)[number], string> = {
germany: 'Germany', austria: 'Austria', switzerland: 'Switzerland', italy: 'Italy', france: 'France', spain: 'Spain', portugal: 'Portugal', netherlands: 'Netherlands', belgium: 'Belgium', poland: 'Poland', czechRepublic: 'Czech Republic', hungary: 'Hungary', croatia: 'Croatia', slovenia: 'Slovenia', slovakia: 'Slovakia', unitedKingdom: 'United Kingdom', ireland: 'Ireland', sweden: 'Sweden', norway: 'Norway', denmark: 'Denmark', finland: 'Finland', russia: 'Russia', turkey: 'Turkey', greece: 'Greece', romania: 'Romania', bulgaria: 'Bulgaria', serbia: 'Serbia', albania: 'Albania', bosniaHerzegovina: 'Bosnia and Herzegovina', unitedStates: 'United States', canada: 'Canada', brazil: 'Brazil', argentina: 'Argentina', mexico: 'Mexico', china: 'China', japan: 'Japan', india: 'India', pakistan: 'Pakistan', australia: 'Australia', southAfrica: 'South Africa', other: 'Other'
}
const NATIONALITIES = NATIONALITY_CODES.map(code => NATIONALITY_VALUE_BY_CODE[code])
const COUNTRIES = COUNTRY_CODES.map(code => COUNTRY_VALUE_BY_CODE[code])
const normalizeOptionValue = (value: string) => value.toLowerCase().replace(/[^a-z]/g, '')
const initialData: PersonalProfileData = {
firstName: '',
lastName: '',
email: '',
phone: '',
dob: '',
nationality: '',
street: '',
postalCode: '',
city: '',
country: '',
accountHolder: '',
iban: '',
secondPhone: '',
emergencyName: '',
emergencyPhone: ''
}
type SelectOption = { value: string; label: string }
function ModernSelect({
label,
placeholder,
searchPlaceholder = 'Search…',
noResults = 'No results',
value,
onChange,
options,
}: {
label: string
placeholder?: string
searchPlaceholder?: string
noResults?: string
value: string
onChange: (next: string) => void
options: SelectOption[]
}) {
const { t } = useTranslation();
const [open, setOpen] = useState(false)
const [query, setQuery] = useState('')
const btnRef = useRef<HTMLButtonElement | null>(null)
const [pos, setPos] = useState({ left: 16, top: 0, width: 320 })
const resolvedPlaceholder = placeholder ?? t('autofix.ka5bf342b')
const selected = useMemo(
() => options.find(o => o.value === value) || null,
[options, value]
)
const filtered = useMemo(() => {
const q = query.trim().toLowerCase()
if (!q) return options
return options.filter(o => o.label.toLowerCase().includes(q))
}, [options, query])
useEffect(() => {
if (!open) return
const update = () => {
const el = btnRef.current
if (!el) return
const r = el.getBoundingClientRect()
const padding = 16
const width = Math.min(r.width, window.innerWidth - padding * 2)
const left = Math.max(padding, Math.min(r.left, window.innerWidth - width - padding))
const top = r.bottom + 8
setPos({ left, top, width })
}
update()
window.addEventListener('resize', update)
window.addEventListener('scroll', update, true)
return () => {
window.removeEventListener('resize', update)
window.removeEventListener('scroll', update, true)
}
}, [open])
// close on escape
useEffect(() => {
if (!open) return
const onKey = (e: KeyboardEvent) => {
if (e.key === 'Escape') setOpen(false)
}
window.addEventListener('keydown', onKey)
return () => window.removeEventListener('keydown', onKey)
}, [open])
return (
<div className="relative">
<label className="block text-sm font-medium text-gray-700 mb-1">{label} *</label>
<button
ref={btnRef}
type="button"
onClick={() => setOpen(v => !v)}
className="w-full rounded-lg border border-gray-300 bg-white/70 px-3 py-2 text-sm text-left
shadow-sm hover:border-gray-400
focus:outline-none focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent
inline-flex items-center justify-between gap-3"
aria-haspopup="listbox"
aria-expanded={open}
>
<span className={selected ? 'text-gray-900' : 'text-gray-500'}>
{selected ? selected.label : resolvedPlaceholder}
</span>
<ChevronDownIcon className={`h-5 w-5 text-gray-500 transition-transform ${open ? 'rotate-180' : ''}`} />
</button>
{open && (
<>
{/* click-away overlay */}
<div className="fixed inset-0 z-[90]" onClick={() => setOpen(false)} aria-hidden />
{/* dropdown (fixed so it “pops out under” even on mobile) */}
<div
className="fixed z-[100] overflow-hidden rounded-xl border border-gray-200 bg-white shadow-2xl"
style={{ left: pos.left, top: pos.top, width: pos.width }}
>
<div className="p-2 border-b border-gray-100">
<input
value={query}
onChange={e => setQuery(e.target.value)}
placeholder={searchPlaceholder}
className="w-full rounded-lg border border-gray-200 px-3 py-2 text-sm
focus:outline-none focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent"
autoFocus
/>
</div>
<div className="max-h-[42vh] overflow-auto p-1">
{filtered.length === 0 ? (
<div className="px-3 py-2 text-sm text-gray-500">{noResults}</div>
) : (
filtered.map(o => {
const active = o.value === value
return (
<button
key={o.value}
type="button"
onClick={() => {
onChange(o.value)
setQuery('')
setOpen(false)
}}
className={`w-full text-left px-3 py-2 rounded-lg text-sm transition-colors
${active ? 'bg-[#8D6B1D]/10 text-[#7A5E1A] font-semibold' : 'text-gray-800 hover:bg-gray-50'}`}
role="option"
aria-selected={active}
>
{o.label}
</button>
)
})
)}
</div>
</div>
</>
)}
</div>
)
}
export default function PersonalAdditionalInformationPage() {
const { t } = useTranslation();
const router = useRouter()
const user = useAuthStore(s => s.user) // NEW
const isAuthReady = useAuthStore(s => (s as any).isAuthReady) // NEW
const { accessToken } = useAuthStore()
const { userStatus, loading: statusLoading, refreshStatus } = useUserStatus()
const { showToast } = useToast()
const phoneRef = useRef<TelephoneInputHandle | null>(null)
const secondPhoneRef = useRef<TelephoneInputHandle | null>(null)
const emergencyPhoneRef = useRef<TelephoneInputHandle | null>(null)
const [form, setForm] = useState(initialData)
const [loading, setLoading] = useState(false)
const [success, setSuccess] = useState(false)
const [error, setError] = useState('')
const nationalityOptions = useMemo(
() => NATIONALITY_CODES.map(code => ({ value: code, label: t(`quickactionDashboard.additionalInfo.nationalities.${code}`) })),
[t]
)
const countryOptions = useMemo(
() => COUNTRY_CODES.map(code => ({ value: code, label: t(`quickactionDashboard.additionalInfo.countries.${code}`) })),
[t]
)
// 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 && !user) || 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,
firstName: user?.firstName || profile?.first_name || prev.firstName,
lastName: user?.lastName || profile?.last_name || prev.lastName,
email: user?.email || prev.email,
phone: user?.phone || profile?.phone || prev.phone,
dob: toDateInput(profile?.date_of_birth || profile?.dateOfBirth),
nationality: Object.entries(NATIONALITY_VALUE_BY_CODE).find(([, label]) => normalizeOptionValue(label) === normalizeOptionValue(profile?.nationality || ''))?.[0] || prev.nationality,
street: profile?.address || prev.street,
postalCode: profile?.zip_code || profile?.zipCode || prev.postalCode,
city: profile?.city || prev.city,
country: Object.entries(COUNTRY_VALUE_BY_CODE).find(([, label]) => normalizeOptionValue(label) === normalizeOptionValue(profile?.country || ''))?.[0] || 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<HTMLInputElement | HTMLSelectElement>) => {
const { name, value } = e.target
setForm(p => ({ ...p, [name]: value }))
setError('')
}
const handlePhoneInput = (e: React.FormEvent<HTMLInputElement>) => {
const { name, value } = e.currentTarget
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)[] = [
'firstName','lastName','email','phone',
'dob','nationality','street','postalCode','city','country','accountHolder','iban'
]
for (const k of requiredKeys) {
if (!form[k].trim()) {
const msg = t('quickactionDashboard.additionalInfo.fillRequiredFields')
setError(msg)
showToast({
variant: 'error',
title: t('quickactionDashboard.uploadId.missingInfoTitle'),
message: msg,
})
return false
}
}
// Date of birth validation
if (!validateDateOfBirth(form.dob)) {
const msg = t('quickactionDashboard.additionalInfo.invalidDateOfBirthMessage')
setError(msg)
showToast({
variant: 'error',
title: t('quickactionDashboard.additionalInfo.invalidDateOfBirthTitle'),
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 = t('quickactionDashboard.additionalInfo.invalidIbanMessage')
setError(msg)
showToast({
variant: 'error',
title: t('quickactionDashboard.additionalInfo.invalidIbanTitle'),
message: msg,
})
return false
}
const phoneApi = phoneRef.current
const dialCode = phoneApi?.getDialCode?.()
const intlNumber = phoneApi?.getNumber() || ''
const valid = phoneApi?.isValid() ?? false
if (!dialCode) {
const msg = t('quickactionDashboard.additionalInfo.missingCountryCodeMessage')
setError(msg)
showToast({
variant: 'error',
title: t('quickactionDashboard.additionalInfo.missingCountryCodeTitle'),
message: msg,
})
return false
}
if (!intlNumber) {
const msg = t('quickactionDashboard.additionalInfo.phoneNumberMissingMessage')
setError(msg)
showToast({
variant: 'error',
title: t('quickactionDashboard.additionalInfo.missingPhoneNumberTitle'),
message: msg,
})
return false
}
if (!valid) {
const msg = t('quickactionDashboard.additionalInfo.validPhoneNumberMessage')
setError(msg)
showToast({
variant: 'error',
title: t('quickactionDashboard.additionalInfo.invalidPhoneNumberTitle'),
message: msg,
})
return false
}
const optionalSecond = form.secondPhone.trim()
if (optionalSecond) {
const secondApi = secondPhoneRef.current
const ok = secondApi?.isValid?.() ?? false
if (!ok) {
const msg = t('quickactionDashboard.additionalInfo.validSecondPhoneNumberMessage')
setError(msg)
showToast({
variant: 'error',
title: t('quickactionDashboard.additionalInfo.invalidPhoneNumberTitle'),
message: msg,
})
return false
}
}
const optionalEmergency = form.emergencyPhone.trim()
if (optionalEmergency) {
const emergencyApi = emergencyPhoneRef.current
const ok = emergencyApi?.isValid?.() ?? false
if (!ok) {
const msg = t('quickactionDashboard.additionalInfo.validEmergencyPhoneNumberMessage')
setError(msg)
showToast({
variant: 'error',
title: t('quickactionDashboard.additionalInfo.invalidPhoneNumberTitle'),
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 = t('quickactionDashboard.additionalInfo.authErrorMessage')
setError(msg)
showToast({
variant: 'error',
title: t('quickactionDashboard.additionalInfo.authErrorTitle'),
message: msg,
})
return
}
setLoading(true)
try {
const normalizedPhone = phoneRef.current?.getNumber() || form.phone
const normalizedSecondPhone = secondPhoneRef.current?.getNumber() || form.secondPhone
const normalizedEmergencyPhone = emergencyPhoneRef.current?.getNumber() || form.emergencyPhone
// Prepare data for backend with correct field names
const profileData = {
firstName: form.firstName,
lastName: form.lastName,
phone: normalizedPhone,
dateOfBirth: form.dob,
nationality: NATIONALITY_VALUE_BY_CODE[form.nationality as keyof typeof NATIONALITY_VALUE_BY_CODE] || form.nationality,
address: form.street, // Backend expects 'address', not nested object
zip_code: form.postalCode, // Backend expects 'zip_code'
city: form.city,
country: COUNTRY_VALUE_BY_CODE[form.country as keyof typeof COUNTRY_VALUE_BY_CODE] || form.country,
phoneSecondary: normalizedSecondPhone || null, // Backend expects 'phoneSecondary'
emergencyContactName: form.emergencyName || null,
emergencyContactPhone: normalizedEmergencyPhone || 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: t('autofix.k481c2be7') }))
throw new Error(errorData.message || 'Save failed')
}
setSuccess(true)
showToast({
variant: 'success',
title: t('quickactionDashboard.additionalInfo.additionalInfoSuccessTitle'),
message: t('quickactionDashboard.additionalInfo.personalSuccessMessage'),
})
// Refresh user status to update profile completion state
suppressAutoRedirectRef.current = true
await refreshStatus()
// Redirect back to tutorial modal after short delay
setTimeout(() => {
smoothReplace('/quickaction-dashboard?tutorial=true')
}, 1500)
} catch (error: any) {
console.error('Personal profile save error:', error)
const msg = error.message || t('quickactionDashboard.additionalInfo.saveFailedMessage')
setError(msg)
showToast({
variant: 'error',
title: t('quickactionDashboard.additionalInfo.saveFailedTitle'),
message: msg,
})
} finally {
setLoading(false)
}
}
const setField = (name: keyof PersonalProfileData, value: string) => {
setForm(p => ({ ...p, [name]: value }))
setError('')
}
// NEW: smooth redirect
const [redirectTo, setRedirectTo] = useState<string | null>(null)
const redirectOnceRef = useRef(false)
const suppressAutoRedirectRef = useRef(false)
const smoothReplace = useCallback((to: string) => {
if (redirectOnceRef.current) return
redirectOnceRef.current = true
setRedirectTo(to)
window.setTimeout(() => router.replace(to), 200)
}, [router])
// NEW: hard block if step already done OR all steps done
useEffect(() => {
if (statusLoading || !userStatus) return
if (suppressAutoRedirectRef.current) return
const allDone =
!!userStatus.email_verified &&
!!userStatus.documents_uploaded &&
!!userStatus.profile_completed &&
!!userStatus.contract_signed
if (allDone) {
smoothReplace('/dashboard') // CHANGED
} else if (userStatus.profile_completed) {
smoothReplace('/quickaction-dashboard') // CHANGED
}
}, [statusLoading, userStatus, smoothReplace])
// NEW: must be logged in
useEffect(() => {
if (!isAuthReady) return
if (!user || !accessToken) smoothReplace('/login')
}, [isAuthReady, user, accessToken, smoothReplace])
return (
<PageLayout>
{/* NEW: smooth redirect overlay */}
{redirectTo && (
<div className="fixed inset-0 z-[200] flex items-center justify-center bg-white/70 backdrop-blur-sm transition-opacity duration-200 opacity-100">
<div className="rounded-xl bg-white px-5 py-4 shadow ring-1 ring-black/5">
<div className="text-sm font-medium text-gray-900">{t('quickactionDashboard.redirecting')}</div>
<div className="mt-1 text-xs text-gray-600">{t('quickactionDashboard.pleaseWait')}</div>
</div>
</div>
)}
<div className="relative min-h-screen overflow-hidden bg-slate-50">
{/* Animated background (same as dashboard) */}
<div className="pointer-events-none absolute inset-0 z-0">
{/* Soft gradient blobs */}
<div className="absolute -top-32 -left-20 h-80 w-80 rounded-full bg-blue-400/25 blur-3xl animate-pulse" />
<div className="absolute top-1/4 -right-24 h-96 w-96 rounded-full bg-emerald-400/20 blur-3xl animate-pulse" />
<div className="absolute bottom-[-6rem] left-1/4 h-96 w-96 rounded-full bg-indigo-400/20 blur-3xl animate-pulse" />
{/* Subtle radial highlight */}
<div className="absolute inset-0 bg-[radial-gradient(circle_at_top,_rgba(59,130,246,0.14),transparent_60%)]" />
</div>
<main className="relative z-10 flex flex-col flex-1 w-full px-5 sm:px-8 lg:px-12 py-12">
<form
onSubmit={handleSubmit}
className="relative max-w-6xl w-full mx-auto bg-white rounded-2xl shadow-xl ring-1 ring-black/10"
>
<div className="px-6 py-8 sm:px-10 lg:px-16">
<h1 className="text-center text-xl sm:text-2xl font-semibold text-[#0F172A] mb-6">
{t('quickactionDashboard.additionalInfo.title')}
</h1>
{/* Personal Information */}
<section>
<h2 className="text-sm font-semibold text-[#0F2460] mb-4">
{t('quickactionDashboard.additionalInfo.personalInformation')}
</h2>
<div className="grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
{t('quickactionDashboard.additionalInfo.firstName')}
</label>
<input
name="firstName"
value={form.firstName}
onChange={handleChange}
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
{t('quickactionDashboard.additionalInfo.lastName')}
</label>
<input
name="lastName"
value={form.lastName}
onChange={handleChange}
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent"
required
/>
</div>
<div className="sm:col-span-2 lg:col-span-3">
<label className="block text-sm font-medium text-gray-700 mb-1">
{t('quickactionDashboard.additionalInfo.email')}
</label>
<input
name="email"
value={form.email}
onChange={handleChange}
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm bg-gray-50 text-gray-700 focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent"
readOnly
required
/>
</div>
<div className="sm:col-span-2 lg:col-span-3">
<label className="block text-sm font-medium text-gray-700 mb-1">
{t('quickactionDashboard.additionalInfo.phoneNumber')}
</label>
<TelephoneInput
name="phone"
value={form.phone}
onChange={handleChange}
onInput={handlePhoneInput}
placeholder={t('autofix.k9f56d4ac')}
ref={phoneRef}
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
{t('quickactionDashboard.additionalInfo.dateOfBirth')}
</label>
<input
type="date"
name="dob"
value={form.dob}
onChange={handleChange}
min={new Date(new Date().getFullYear() - 120, 0, 1).toISOString().split('T')[0]}
max={new Date(new Date().getFullYear() - 18, 11, 31).toISOString().split('T')[0]}
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent"
required
/>
</div>
<div>
<ModernSelect
label={t('quickactionDashboard.additionalInfo.nationality')}
placeholder={t('quickactionDashboard.additionalInfo.selectNationality')}
searchPlaceholder={t('quickactionDashboard.additionalInfo.searchPlaceholder')}
noResults={t('quickactionDashboard.additionalInfo.noResults')}
value={form.nationality}
onChange={(v) => setField('nationality', v)}
options={NATIONALITIES.map(n => ({ value: n, label: n }))}
/>
</div>
<div className="sm:col-span-2 lg:col-span-3">
<label className="block text-sm font-medium text-gray-700 mb-1">
{t('quickactionDashboard.additionalInfo.streetHouseNumber')}
</label>
<input
name="street"
value={form.street}
onChange={handleChange}
placeholder={t('autofix.k96dbbe05')}
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
{t('quickactionDashboard.additionalInfo.postalCode')}
</label>
<input
name="postalCode"
value={form.postalCode}
onChange={handleChange}
placeholder={t('autofix.kf70b9896')}
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
{t('quickactionDashboard.additionalInfo.city')}
</label>
<input
name="city"
value={form.city}
onChange={handleChange}
placeholder={t('autofix.k62bc3c59')}
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent"
required
/>
</div>
<div>
<ModernSelect
label={t('quickactionDashboard.additionalInfo.country')}
placeholder={t('quickactionDashboard.additionalInfo.selectCountry')}
searchPlaceholder={t('quickactionDashboard.additionalInfo.searchPlaceholder')}
noResults={t('quickactionDashboard.additionalInfo.noResults')}
value={form.country}
onChange={(v) => setField('country', v)}
options={COUNTRIES.map(c => ({ value: c, label: c }))}
/>
</div>
</div>
</section>
<hr className="my-8 border-gray-200" />
{/* Bank Details */}
<section>
<h2 className="text-sm font-semibold text-[#0F2460] mb-4">
{t('quickactionDashboard.additionalInfo.bankDetails')}
</h2>
<div className="grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
{t('quickactionDashboard.additionalInfo.accountHolder')}
</label>
<input
name="accountHolder"
value={form.accountHolder}
onChange={handleChange}
placeholder={t('autofix.k28f1a9b1')}
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent"
required
/>
</div>
<div className="sm:col-span-1 lg:col-span-2">
<label className="block text-sm font-medium text-gray-700 mb-1">
{t('quickactionDashboard.additionalInfo.iban')}
</label>
<input
name="iban"
value={form.iban}
onChange={handleChange}
placeholder={t('autofix.k1a1ca621')}
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm tracking-wide uppercase focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent"
required
/>
</div>
</div>
</section>
<hr className="my-8 border-gray-200" />
{/* Additional Information */}
<section>
<h2 className="text-sm font-semibold text-[#0F2460] mb-4">
{t('quickactionDashboard.additionalInfo.additionalInformation')}
</h2>
<div className="grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
<div className="sm:col-span-2 lg:col-span-3">
<label className="block text-sm font-medium text-gray-700 mb-1">
{t('quickactionDashboard.additionalInfo.secondPhoneOptional')}
</label>
<TelephoneInput
name="secondPhone"
value={form.secondPhone}
onChange={handleChange}
onInput={handlePhoneInput}
placeholder="+43 660 1234567"
ref={secondPhoneRef}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
{t('quickactionDashboard.additionalInfo.emergencyContactName')}
</label>
<input
name="emergencyName"
value={form.emergencyName}
onChange={handleChange}
placeholder={t('autofix.k67dd8a82')}
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
{t('quickactionDashboard.additionalInfo.emergencyContactPhone')}
</label>
<TelephoneInput
name="emergencyPhone"
value={form.emergencyPhone}
onChange={handleChange}
onInput={handlePhoneInput}
placeholder="+43 660 1234567"
ref={emergencyPhoneRef}
/>
</div>
<div className="hidden lg:block" />
</div>
</section>
{error && (
<div className="mt-6 rounded-md border border-red-200 bg-red-50 px-4 py-3 text-xs text-red-600">
{error}
</div>
)}
{success && (
<div className="mt-6 rounded-md border border-green-300 bg-green-50 px-4 py-3 text-xs text-green-700">
{t('quickactionDashboard.additionalInfo.dataSavedRedirecting')}
</div>
)}
<div className="mt-10 flex items-center justify-between">
<button
type="button"
onClick={() => router.push('/quickaction-dashboard')}
className="inline-flex items-center rounded-md border border-[#8D6B1D]/40 px-4 py-2 text-sm font-semibold text-[#8D6B1D] bg-white hover:bg-[#8D6B1D]/10"
>
{t('quickactionDashboard.backToDashboard')}
</button>
<button
type="submit"
disabled={loading || success}
className="inline-flex items-center rounded-md bg-[#8D6B1D] px-6 py-2.5 text-sm font-semibold text-white shadow hover:bg-[#7A5E1A] focus:outline-none focus:ring-2 focus:ring-[#8D6B1D] focus:ring-offset-2 disabled:bg-gray-400 disabled:cursor-not-allowed"
>
{loading ? t('common.saving') : success ? t('common.saved') : t('quickactionDashboard.additionalInfo.saveContinue')}
</button>
</div>
</div>
</form>
</main>
</div>
</PageLayout>
)
}