feat: add personal and company profile fields with phone number validation
This commit is contained in:
parent
71f4fdfd02
commit
860e88c3be
@ -7,9 +7,14 @@ import useAuthStore from '../../../store/authStore'
|
|||||||
import { useUserStatus } from '../../../hooks/useUserStatus'
|
import { useUserStatus } from '../../../hooks/useUserStatus'
|
||||||
import { useToast } from '../../../components/toast/toastComponent'
|
import { useToast } from '../../../components/toast/toastComponent'
|
||||||
import { ChevronDownIcon } from '@heroicons/react/20/solid' // NEW
|
import { ChevronDownIcon } from '@heroicons/react/20/solid' // NEW
|
||||||
|
import TelephoneInput, { TelephoneInputHandle } from '../../../components/phone/telephoneInput'
|
||||||
|
|
||||||
interface CompanyProfileData {
|
interface CompanyProfileData {
|
||||||
companyName: string
|
companyName: string
|
||||||
|
companyEmail: string
|
||||||
|
companyPhone: string
|
||||||
|
contactPersonName: string
|
||||||
|
contactPersonPhone: string
|
||||||
vatNumber: string
|
vatNumber: string
|
||||||
street: string
|
street: string
|
||||||
postalCode: string
|
postalCode: string
|
||||||
@ -35,6 +40,10 @@ const COUNTRIES = [
|
|||||||
|
|
||||||
const init: CompanyProfileData = {
|
const init: CompanyProfileData = {
|
||||||
companyName: '',
|
companyName: '',
|
||||||
|
companyEmail: '',
|
||||||
|
companyPhone: '',
|
||||||
|
contactPersonName: '',
|
||||||
|
contactPersonPhone: '',
|
||||||
vatNumber: '',
|
vatNumber: '',
|
||||||
street: '',
|
street: '',
|
||||||
postalCode: '',
|
postalCode: '',
|
||||||
@ -175,12 +184,54 @@ export default function CompanyAdditionalInformationPage() {
|
|||||||
const { accessToken } = useAuthStore()
|
const { accessToken } = useAuthStore()
|
||||||
const { userStatus, loading: statusLoading, refreshStatus } = useUserStatus()
|
const { userStatus, loading: statusLoading, refreshStatus } = useUserStatus()
|
||||||
const { showToast } = useToast()
|
const { showToast } = useToast()
|
||||||
|
const companyPhoneRef = useRef<TelephoneInputHandle | null>(null)
|
||||||
|
const contactPhoneRef = useRef<TelephoneInputHandle | null>(null)
|
||||||
|
const secondPhoneRef = useRef<TelephoneInputHandle | null>(null)
|
||||||
|
const emergencyPhoneRef = useRef<TelephoneInputHandle | null>(null)
|
||||||
|
|
||||||
const [form, setForm] = useState(init)
|
const [form, setForm] = useState(init)
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [success, setSuccess] = useState(false)
|
const [success, setSuccess] = useState(false)
|
||||||
const [error, setError] = useState('')
|
const [error, setError] = useState('')
|
||||||
|
|
||||||
|
// Prefill form with existing profile/user data
|
||||||
|
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 me = data?.user
|
||||||
|
if ((!profile && !me) || abort) return
|
||||||
|
setForm(prev => ({
|
||||||
|
...prev,
|
||||||
|
companyName: profile?.company_name || me?.companyName || prev.companyName,
|
||||||
|
companyEmail: me?.email || prev.companyEmail,
|
||||||
|
companyPhone: profile?.phone || me?.companyPhone || prev.companyPhone,
|
||||||
|
contactPersonName: profile?.contact_person_name || me?.contactPersonName || prev.contactPersonName,
|
||||||
|
contactPersonPhone: profile?.contact_person_phone || me?.contactPersonPhone || prev.contactPersonPhone,
|
||||||
|
vatNumber: profile?.registration_number || prev.vatNumber,
|
||||||
|
street: profile?.address || prev.street,
|
||||||
|
postalCode: profile?.zip_code || prev.postalCode,
|
||||||
|
city: profile?.city || prev.city,
|
||||||
|
country: profile?.country || prev.country,
|
||||||
|
accountHolder: profile?.account_holder_name || prev.accountHolder,
|
||||||
|
iban: (me?.iban ?? prev.iban) as string,
|
||||||
|
}))
|
||||||
|
} catch (_) {
|
||||||
|
// ignore prefill errors; user can still fill manually
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadProfile()
|
||||||
|
return () => { abort = true }
|
||||||
|
}, [accessToken])
|
||||||
|
|
||||||
// NEW: smooth redirect
|
// NEW: smooth redirect
|
||||||
const [redirectTo, setRedirectTo] = useState<string | null>(null)
|
const [redirectTo, setRedirectTo] = useState<string | null>(null)
|
||||||
const redirectOnceRef = useRef(false)
|
const redirectOnceRef = useRef(false)
|
||||||
@ -223,7 +274,8 @@ export default function CompanyAdditionalInformationPage() {
|
|||||||
|
|
||||||
const validate = () => {
|
const validate = () => {
|
||||||
const required: (keyof CompanyProfileData)[] = [
|
const required: (keyof CompanyProfileData)[] = [
|
||||||
'companyName','vatNumber','street','postalCode','city','country','accountHolder','iban'
|
'companyName','companyEmail','companyPhone','contactPersonName','contactPersonPhone',
|
||||||
|
'vatNumber','street','postalCode','city','country','accountHolder','iban'
|
||||||
]
|
]
|
||||||
for (const k of required) {
|
for (const k of required) {
|
||||||
if (!form[k].trim()) {
|
if (!form[k].trim()) {
|
||||||
@ -237,6 +289,77 @@ export default function CompanyAdditionalInformationPage() {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const companyApi = companyPhoneRef.current
|
||||||
|
const contactApi = contactPhoneRef.current
|
||||||
|
const companyDialCode = companyApi?.getDialCode?.()
|
||||||
|
const contactDialCode = contactApi?.getDialCode?.()
|
||||||
|
const companyNumber = companyApi?.getNumber() || ''
|
||||||
|
const contactNumber = contactApi?.getNumber() || ''
|
||||||
|
const companyValid = companyApi?.isValid() ?? false
|
||||||
|
const contactValid = contactApi?.isValid() ?? false
|
||||||
|
|
||||||
|
if (!companyDialCode || !contactDialCode) {
|
||||||
|
const msg = 'Please select country codes for company and contact phone numbers.'
|
||||||
|
setError(msg)
|
||||||
|
showToast({
|
||||||
|
variant: 'error',
|
||||||
|
title: 'Missing country code',
|
||||||
|
message: msg,
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!companyNumber || !contactNumber) {
|
||||||
|
const msg = 'Please enter both company and contact phone numbers.'
|
||||||
|
setError(msg)
|
||||||
|
showToast({
|
||||||
|
variant: 'error',
|
||||||
|
title: 'Missing phone numbers',
|
||||||
|
message: msg,
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!companyValid || !contactValid) {
|
||||||
|
const msg = 'Please enter valid phone numbers for company and contact person.'
|
||||||
|
setError(msg)
|
||||||
|
showToast({
|
||||||
|
variant: 'error',
|
||||||
|
title: 'Invalid phone numbers',
|
||||||
|
message: msg,
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const optionalSecond = form.secondPhone.trim()
|
||||||
|
if (optionalSecond) {
|
||||||
|
const secondApi = secondPhoneRef.current
|
||||||
|
const ok = secondApi?.isValid?.() ?? false
|
||||||
|
if (!ok) {
|
||||||
|
const msg = 'Please enter a valid second phone number.'
|
||||||
|
setError(msg)
|
||||||
|
showToast({
|
||||||
|
variant: 'error',
|
||||||
|
title: 'Invalid phone number',
|
||||||
|
message: msg,
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const optionalEmergency = form.emergencyPhone.trim()
|
||||||
|
if (optionalEmergency) {
|
||||||
|
const emergencyApi = emergencyPhoneRef.current
|
||||||
|
const ok = emergencyApi?.isValid?.() ?? false
|
||||||
|
if (!ok) {
|
||||||
|
const msg = 'Please enter a valid emergency phone number.'
|
||||||
|
setError(msg)
|
||||||
|
showToast({
|
||||||
|
variant: 'error',
|
||||||
|
title: 'Invalid phone number',
|
||||||
|
message: msg,
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!/^([A-Z]{2}\d{2}[A-Z0-9]{10,30})$/i.test(form.iban.replace(/\s+/g,''))) {
|
if (!/^([A-Z]{2}\d{2}[A-Z0-9]{10,30})$/i.test(form.iban.replace(/\s+/g,''))) {
|
||||||
const msg = 'Ungültige IBAN.'
|
const msg = 'Ungültige IBAN.'
|
||||||
setError(msg)
|
setError(msg)
|
||||||
@ -270,9 +393,17 @@ export default function CompanyAdditionalInformationPage() {
|
|||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const normalizedCompanyPhone = companyPhoneRef.current?.getNumber() || form.companyPhone
|
||||||
|
const normalizedContactPhone = contactPhoneRef.current?.getNumber() || form.contactPersonPhone
|
||||||
|
const normalizedSecondPhone = secondPhoneRef.current?.getNumber() || form.secondPhone
|
||||||
|
const normalizedEmergencyPhone = emergencyPhoneRef.current?.getNumber() || form.emergencyPhone
|
||||||
|
|
||||||
// Prepare data for backend with correct field names
|
// Prepare data for backend with correct field names
|
||||||
const profileData = {
|
const profileData = {
|
||||||
companyName: user?.companyName || '',
|
companyName: form.companyName,
|
||||||
|
companyPhone: normalizedCompanyPhone,
|
||||||
|
contactPersonName: form.contactPersonName,
|
||||||
|
contactPersonPhone: normalizedContactPhone,
|
||||||
address: form.street, // Backend expects 'address', not nested object
|
address: form.street, // Backend expects 'address', not nested object
|
||||||
zip_code: form.postalCode, // Backend expects 'zip_code'
|
zip_code: form.postalCode, // Backend expects 'zip_code'
|
||||||
city: form.city,
|
city: form.city,
|
||||||
@ -282,7 +413,10 @@ export default function CompanyAdditionalInformationPage() {
|
|||||||
branch: null, // Not collected in form, set to null
|
branch: null, // Not collected in form, set to null
|
||||||
numberOfEmployees: null, // Not collected in form, set to null
|
numberOfEmployees: null, // Not collected in form, set to null
|
||||||
accountHolderName: form.accountHolder, // Backend expects 'accountHolderName'
|
accountHolderName: form.accountHolder, // Backend expects 'accountHolderName'
|
||||||
iban: form.iban.replace(/\s+/g, '') // Remove spaces from IBAN
|
iban: form.iban.replace(/\s+/g, ''), // Remove spaces from IBAN
|
||||||
|
secondPhone: normalizedSecondPhone || null,
|
||||||
|
emergencyContactName: form.emergencyName || null,
|
||||||
|
emergencyContactPhone: normalizedEmergencyPhone || null
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/api/profile/company/complete`, {
|
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/api/profile/company/complete`, {
|
||||||
@ -385,6 +519,57 @@ export default function CompanyAdditionalInformationPage() {
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="sm:col-span-2 lg:col-span-3">
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
Company Email *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="companyEmail"
|
||||||
|
value={form.companyEmail}
|
||||||
|
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>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
Company Phone *
|
||||||
|
</label>
|
||||||
|
<TelephoneInput
|
||||||
|
name="companyPhone"
|
||||||
|
value={form.companyPhone}
|
||||||
|
onChange={handleChange}
|
||||||
|
placeholder="e.g. +43 1 234567"
|
||||||
|
ref={companyPhoneRef}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
Contact Person *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
name="contactPersonName"
|
||||||
|
value={form.contactPersonName}
|
||||||
|
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">
|
||||||
|
Contact Person Phone *
|
||||||
|
</label>
|
||||||
|
<TelephoneInput
|
||||||
|
name="contactPersonPhone"
|
||||||
|
value={form.contactPersonPhone}
|
||||||
|
onChange={handleChange}
|
||||||
|
placeholder="e.g. +43 676 1234567"
|
||||||
|
ref={contactPhoneRef}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
VAT / Reg No. *
|
VAT / Reg No. *
|
||||||
@ -507,12 +692,12 @@ export default function CompanyAdditionalInformationPage() {
|
|||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
Second Phone (optional)
|
Second Phone (optional)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<TelephoneInput
|
||||||
name="secondPhone"
|
name="secondPhone"
|
||||||
value={form.secondPhone}
|
value={form.secondPhone}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
placeholder="+49 123 456 7890"
|
placeholder="+49 123 456 7890"
|
||||||
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent"
|
ref={secondPhoneRef}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@ -531,12 +716,12 @@ export default function CompanyAdditionalInformationPage() {
|
|||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
Emergency Contact Phone
|
Emergency Contact Phone
|
||||||
</label>
|
</label>
|
||||||
<input
|
<TelephoneInput
|
||||||
name="emergencyPhone"
|
name="emergencyPhone"
|
||||||
value={form.emergencyPhone}
|
value={form.emergencyPhone}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
placeholder="+49 123 456 7890"
|
placeholder="+49 123 456 7890"
|
||||||
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent"
|
ref={emergencyPhoneRef}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden lg:block" />
|
<div className="hidden lg:block" />
|
||||||
|
|||||||
@ -7,8 +7,13 @@ import useAuthStore from '../../../store/authStore'
|
|||||||
import { useUserStatus } from '../../../hooks/useUserStatus'
|
import { useUserStatus } from '../../../hooks/useUserStatus'
|
||||||
import { useToast } from '../../../components/toast/toastComponent'
|
import { useToast } from '../../../components/toast/toastComponent'
|
||||||
import { ChevronDownIcon } from '@heroicons/react/20/solid'
|
import { ChevronDownIcon } from '@heroicons/react/20/solid'
|
||||||
|
import TelephoneInput, { TelephoneInputHandle } from '../../../components/phone/telephoneInput'
|
||||||
|
|
||||||
interface PersonalProfileData {
|
interface PersonalProfileData {
|
||||||
|
firstName: string
|
||||||
|
lastName: string
|
||||||
|
email: string
|
||||||
|
phone: string
|
||||||
dob: string
|
dob: string
|
||||||
nationality: string
|
nationality: string
|
||||||
street: string
|
street: string
|
||||||
@ -43,6 +48,10 @@ const COUNTRIES = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const initialData: PersonalProfileData = {
|
const initialData: PersonalProfileData = {
|
||||||
|
firstName: '',
|
||||||
|
lastName: '',
|
||||||
|
email: '',
|
||||||
|
phone: '',
|
||||||
dob: '',
|
dob: '',
|
||||||
nationality: '',
|
nationality: '',
|
||||||
street: '',
|
street: '',
|
||||||
@ -204,6 +213,9 @@ export default function PersonalAdditionalInformationPage() {
|
|||||||
const { accessToken } = useAuthStore()
|
const { accessToken } = useAuthStore()
|
||||||
const { userStatus, loading: statusLoading, refreshStatus } = useUserStatus()
|
const { userStatus, loading: statusLoading, refreshStatus } = useUserStatus()
|
||||||
const { showToast } = useToast()
|
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 [form, setForm] = useState(initialData)
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
@ -224,7 +236,7 @@ export default function PersonalAdditionalInformationPage() {
|
|||||||
const data = await res.json().catch(() => null)
|
const data = await res.json().catch(() => null)
|
||||||
const profile = data?.profile
|
const profile = data?.profile
|
||||||
const user = data?.user
|
const user = data?.user
|
||||||
if (!profile || abort) return
|
if ((!profile && !user) || abort) return
|
||||||
const toDateInput = (d?: string) => {
|
const toDateInput = (d?: string) => {
|
||||||
if (!d) return ''
|
if (!d) return ''
|
||||||
const dt = new Date(d)
|
const dt = new Date(d)
|
||||||
@ -233,18 +245,22 @@ export default function PersonalAdditionalInformationPage() {
|
|||||||
}
|
}
|
||||||
setForm(prev => ({
|
setForm(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
dob: toDateInput(profile.date_of_birth || profile.dateOfBirth),
|
firstName: user?.firstName || profile?.first_name || prev.firstName,
|
||||||
nationality: profile.nationality || prev.nationality,
|
lastName: user?.lastName || profile?.last_name || prev.lastName,
|
||||||
street: profile.address || prev.street,
|
email: user?.email || prev.email,
|
||||||
postalCode: profile.zip_code || profile.zipCode || prev.postalCode,
|
phone: user?.phone || profile?.phone || prev.phone,
|
||||||
city: profile.city || prev.city,
|
dob: toDateInput(profile?.date_of_birth || profile?.dateOfBirth),
|
||||||
country: profile.country || prev.country,
|
nationality: profile?.nationality || prev.nationality,
|
||||||
accountHolder: profile.account_holder_name || profile.accountHolderName || prev.accountHolder,
|
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
|
// Prefer IBAN from users table (data.user.iban), fallback to profile if any
|
||||||
iban: (user?.iban ?? profile.iban ?? prev.iban) as string,
|
iban: (user?.iban ?? profile?.iban ?? prev.iban) as string,
|
||||||
secondPhone: profile.phone_secondary || profile.phoneSecondary || prev.secondPhone,
|
secondPhone: profile?.phone_secondary || profile?.phoneSecondary || prev.secondPhone,
|
||||||
emergencyName: profile.emergency_contact_name || profile.emergencyContactName || prev.emergencyName,
|
emergencyName: profile?.emergency_contact_name || profile?.emergencyContactName || prev.emergencyName,
|
||||||
emergencyPhone: profile.emergency_contact_phone || profile.emergencyContactPhone || prev.emergencyPhone,
|
emergencyPhone: profile?.emergency_contact_phone || profile?.emergencyContactPhone || prev.emergencyPhone,
|
||||||
}))
|
}))
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// ignore prefill errors; user can still fill manually
|
// ignore prefill errors; user can still fill manually
|
||||||
@ -287,6 +303,7 @@ export default function PersonalAdditionalInformationPage() {
|
|||||||
|
|
||||||
const validate = () => {
|
const validate = () => {
|
||||||
const requiredKeys: (keyof PersonalProfileData)[] = [
|
const requiredKeys: (keyof PersonalProfileData)[] = [
|
||||||
|
'firstName','lastName','email','phone',
|
||||||
'dob','nationality','street','postalCode','city','country','accountHolder','iban'
|
'dob','nationality','street','postalCode','city','country','accountHolder','iban'
|
||||||
]
|
]
|
||||||
for (const k of requiredKeys) {
|
for (const k of requiredKeys) {
|
||||||
@ -325,6 +342,72 @@ export default function PersonalAdditionalInformationPage() {
|
|||||||
})
|
})
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const phoneApi = phoneRef.current
|
||||||
|
const dialCode = phoneApi?.getDialCode?.()
|
||||||
|
const intlNumber = phoneApi?.getNumber() || ''
|
||||||
|
const valid = phoneApi?.isValid() ?? false
|
||||||
|
if (!dialCode) {
|
||||||
|
const msg = 'Please select a country code for your phone number.'
|
||||||
|
setError(msg)
|
||||||
|
showToast({
|
||||||
|
variant: 'error',
|
||||||
|
title: 'Missing country code',
|
||||||
|
message: msg,
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!intlNumber) {
|
||||||
|
const msg = 'Please enter your phone number.'
|
||||||
|
setError(msg)
|
||||||
|
showToast({
|
||||||
|
variant: 'error',
|
||||||
|
title: 'Missing phone number',
|
||||||
|
message: msg,
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!valid) {
|
||||||
|
const msg = 'Please enter a valid phone number.'
|
||||||
|
setError(msg)
|
||||||
|
showToast({
|
||||||
|
variant: 'error',
|
||||||
|
title: 'Invalid phone number',
|
||||||
|
message: msg,
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const optionalSecond = form.secondPhone.trim()
|
||||||
|
if (optionalSecond) {
|
||||||
|
const secondApi = secondPhoneRef.current
|
||||||
|
const ok = secondApi?.isValid?.() ?? false
|
||||||
|
if (!ok) {
|
||||||
|
const msg = 'Please enter a valid second phone number.'
|
||||||
|
setError(msg)
|
||||||
|
showToast({
|
||||||
|
variant: 'error',
|
||||||
|
title: 'Invalid phone number',
|
||||||
|
message: msg,
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const optionalEmergency = form.emergencyPhone.trim()
|
||||||
|
if (optionalEmergency) {
|
||||||
|
const emergencyApi = emergencyPhoneRef.current
|
||||||
|
const ok = emergencyApi?.isValid?.() ?? false
|
||||||
|
if (!ok) {
|
||||||
|
const msg = 'Please enter a valid emergency phone number.'
|
||||||
|
setError(msg)
|
||||||
|
showToast({
|
||||||
|
variant: 'error',
|
||||||
|
title: 'Invalid phone number',
|
||||||
|
message: msg,
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
setError('')
|
setError('')
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -348,17 +431,24 @@ export default function PersonalAdditionalInformationPage() {
|
|||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
|
||||||
try {
|
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
|
// Prepare data for backend with correct field names
|
||||||
const profileData = {
|
const profileData = {
|
||||||
|
firstName: form.firstName,
|
||||||
|
lastName: form.lastName,
|
||||||
|
phone: normalizedPhone,
|
||||||
dateOfBirth: form.dob,
|
dateOfBirth: form.dob,
|
||||||
nationality: form.nationality,
|
nationality: form.nationality,
|
||||||
address: form.street, // Backend expects 'address', not nested object
|
address: form.street, // Backend expects 'address', not nested object
|
||||||
zip_code: form.postalCode, // Backend expects 'zip_code'
|
zip_code: form.postalCode, // Backend expects 'zip_code'
|
||||||
city: form.city,
|
city: form.city,
|
||||||
country: form.country,
|
country: form.country,
|
||||||
phoneSecondary: form.secondPhone || null, // Backend expects 'phoneSecondary'
|
phoneSecondary: normalizedSecondPhone || null, // Backend expects 'phoneSecondary'
|
||||||
emergencyContactName: form.emergencyName || null,
|
emergencyContactName: form.emergencyName || null,
|
||||||
emergencyContactPhone: form.emergencyPhone || null,
|
emergencyContactPhone: normalizedEmergencyPhone || null,
|
||||||
accountHolderName: form.accountHolder, // Backend expects 'accountHolderName'
|
accountHolderName: form.accountHolder, // Backend expects 'accountHolderName'
|
||||||
iban: form.iban.replace(/\s+/g, '') // Remove spaces from IBAN
|
iban: form.iban.replace(/\s+/g, '') // Remove spaces from IBAN
|
||||||
}
|
}
|
||||||
@ -485,6 +575,56 @@ export default function PersonalAdditionalInformationPage() {
|
|||||||
Personal Information
|
Personal Information
|
||||||
</h2>
|
</h2>
|
||||||
<div className="grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
|
<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">
|
||||||
|
First Name *
|
||||||
|
</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">
|
||||||
|
Last Name *
|
||||||
|
</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">
|
||||||
|
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">
|
||||||
|
Phone Number *
|
||||||
|
</label>
|
||||||
|
<TelephoneInput
|
||||||
|
name="phone"
|
||||||
|
value={form.phone}
|
||||||
|
onChange={handleChange}
|
||||||
|
placeholder="e.g. +43 676 1234567"
|
||||||
|
ref={phoneRef}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
Date of Birth *
|
Date of Birth *
|
||||||
@ -609,12 +749,12 @@ export default function PersonalAdditionalInformationPage() {
|
|||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
Second Phone Number (optional)
|
Second Phone Number (optional)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<TelephoneInput
|
||||||
name="secondPhone"
|
name="secondPhone"
|
||||||
value={form.secondPhone}
|
value={form.secondPhone}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
placeholder="+43 660 1234567"
|
placeholder="+43 660 1234567"
|
||||||
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent"
|
ref={secondPhoneRef}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@ -633,12 +773,12 @@ export default function PersonalAdditionalInformationPage() {
|
|||||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
Emergency Contact Phone
|
Emergency Contact Phone
|
||||||
</label>
|
</label>
|
||||||
<input
|
<TelephoneInput
|
||||||
name="emergencyPhone"
|
name="emergencyPhone"
|
||||||
value={form.emergencyPhone}
|
value={form.emergencyPhone}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
placeholder="+43 660 1234567"
|
placeholder="+43 660 1234567"
|
||||||
className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent"
|
ref={emergencyPhoneRef}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden lg:block" />
|
<div className="hidden lg:block" />
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user