profit-planet-frontend/src/app/register/components/RegisterForm.tsx

1005 lines
36 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import { useState, useEffect, useRef } from 'react'
import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline'
import { useRegister } from '../hooks/useRegister'
import { useToast } from '../../components/toast/toastComponent'
import TelephoneInput, { TelephoneInputHandle } from '../../components/phone/telephoneInput'
interface RegisterFormProps {
mode: 'personal' | 'company' | 'guest'
setMode: (mode: 'personal' | 'company' | 'guest') => void
refToken: string | null
onRegistered: () => void
referrerEmail?: string
}
interface PersonalFormData {
firstName: string
lastName: string
email: string
confirmEmail: string
password: string
confirmPassword: string
phoneNumber: string
}
interface CompanyFormData {
companyName: string
companyEmail: string
confirmCompanyEmail: string
companyPhone: string
contactPersonName: string
contactPersonPhone: string
password: string
confirmPassword: string
}
export default function RegisterForm({
mode,
setMode,
refToken,
onRegistered,
referrerEmail
}: RegisterFormProps) {
// Personal form state
const [personalForm, setPersonalForm] = useState<PersonalFormData>({
firstName: '',
lastName: '',
email: '',
confirmEmail: '',
password: '',
confirmPassword: '',
phoneNumber: ''
})
// Company form state
const [companyForm, setCompanyForm] = useState<CompanyFormData>({
companyName: '',
companyEmail: '',
confirmCompanyEmail: '',
companyPhone: '',
contactPersonName: '',
contactPersonPhone: '',
password: '',
confirmPassword: ''
})
// UI state
const [showPersonalPassword, setShowPersonalPassword] = useState(false)
const [showCompanyPassword, setShowCompanyPassword] = useState(false)
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const [formFade, setFormFade] = useState('fade-in')
// Phone input refs (to access intl-tel-input via TelephoneInput)
const personalPhoneRef = useRef<TelephoneInputHandle | null>(null)
const companyPhoneRef = useRef<TelephoneInputHandle | null>(null)
const contactPhoneRef = useRef<TelephoneInputHandle | null>(null)
// Hook for backend calls
const { registerPersonalReferral, registerCompanyReferral, registerGuest, error: regError } = useRegister()
const { showToast } = useToast()
// Guest form state
const [guestForm, setGuestForm] = useState({
firstName: '',
lastName: '',
email: '',
confirmEmail: '',
password: '',
confirmPassword: '',
})
// Animate form when mode changes
useEffect(() => {
setFormFade('fade-out')
const timeout = setTimeout(() => {
setFormFade('fade-in')
}, 180)
return () => clearTimeout(timeout)
}, [mode])
// Add fade CSS
useEffect(() => {
const style = document.createElement('style')
style.innerHTML = `
.fade-in {
opacity: 1;
transform: translateY(0);
transition: opacity 180ms, transform 180ms;
}
.fade-out {
opacity: 0;
transform: translateY(20px);
transition: opacity 180ms, transform 180ms;
}
`
document.head.appendChild(style)
return () => {
document.head.removeChild(style)
}
}, [])
// Reset forms when switching modes
useEffect(() => {
setError('')
}, [mode])
// Validation helpers
const validatePersonalForm = (): boolean => {
if (!personalForm.firstName.trim() || !personalForm.lastName.trim() ||
!personalForm.email.trim() || !personalForm.confirmEmail.trim() ||
!personalForm.password.trim() || !personalForm.confirmPassword.trim()
) {
setError('All fields are required')
return false
}
if (personalForm.email !== personalForm.confirmEmail) {
setError('Email addresses do not match')
return false
}
if (personalForm.password !== personalForm.confirmPassword) {
setError('Passwords do not match')
return false
}
if (!/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{8,}$/.test(personalForm.password)) {
setError('Password must be at least 8 characters long and contain uppercase and lowercase letters, numbers and special characters')
return false
}
const phoneApi = personalPhoneRef.current
const dialCode = phoneApi?.getDialCode?.()
const intlNumber = phoneApi?.getNumber() || ''
const valid = phoneApi?.isValid() ?? false
if (!dialCode) {
setError('Please select a country code from the dropdown before continuing.')
return false
}
if (!intlNumber) {
setError('Please enter your phone number.')
return false
}
if (!valid) {
setError('Please enter a valid mobile phone number.')
return false
}
setError('')
return true
}
const validateCompanyForm = (): boolean => {
if (!companyForm.companyName.trim() || !companyForm.companyEmail.trim() ||
!companyForm.confirmCompanyEmail.trim() || !companyForm.contactPersonName.trim() ||
!companyForm.password.trim() || !companyForm.confirmPassword.trim()
) {
setError('All fields are required')
return false
}
if (companyForm.companyEmail !== companyForm.confirmCompanyEmail) {
setError('Email addresses do not match')
return false
}
if (companyForm.password !== companyForm.confirmPassword) {
setError('Passwords do not match')
return false
}
if (!/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{8,}$/.test(companyForm.password)) {
setError('Password must be at least 8 characters long and contain uppercase and lowercase letters, numbers and special characters')
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) {
setError('Please select country codes (dropdown) for both company and contact phone numbers.')
return false
}
if (!companyNumber || !contactNumber) {
setError('Please enter both company and contact phone numbers.')
return false
}
if (!companyValid || !contactValid) {
setError('Please enter valid phone numbers for company and contact person.')
return false
}
setError('')
return true
}
// Submit handlers
const handlePersonalSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (loading) return
if (!validatePersonalForm()) return
setLoading(true)
setError('')
try {
const normalizedPhone =
personalPhoneRef.current?.getNumber() || personalForm.phoneNumber
console.log('[RegisterForm] handlePersonalSubmit normalized phone', {
normalizedPhone,
})
const res = await registerPersonalReferral({
refToken: refToken || '',
firstName: personalForm.firstName,
lastName: personalForm.lastName,
email: personalForm.email,
password: personalForm.password,
phone: normalizedPhone,
})
if (res.ok) {
showToast({
variant: 'success',
title: 'Registration successful',
message: 'You can now log in with your new account.'
})
onRegistered()
} else {
const msg = res.message || 'Registration failed. Please try again.'
setError(msg)
showToast({
variant: 'error',
title: 'Registration failed',
message: msg
})
}
} catch (error) {
const msg = 'Registration failed. Please try again.'
setError(msg)
showToast({
variant: 'error',
title: 'Registration failed',
message: msg
})
} finally {
setLoading(false)
}
}
const handleCompanySubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (loading) return
if (!validateCompanyForm()) return
setLoading(true)
setError('')
try {
const normalizedCompanyPhone =
companyPhoneRef.current?.getNumber() || companyForm.companyPhone
const normalizedContactPhone =
contactPhoneRef.current?.getNumber() || companyForm.contactPersonPhone
console.log('[RegisterForm] handleCompanySubmit normalized phones', {
normalizedCompanyPhone,
normalizedContactPhone,
})
const res = await registerCompanyReferral({
refToken: refToken || '',
companyEmail: companyForm.companyEmail,
password: companyForm.password,
companyName: companyForm.companyName,
companyPhone: normalizedCompanyPhone,
contactPersonName: companyForm.contactPersonName,
contactPersonPhone: normalizedContactPhone,
})
if (res.ok) {
showToast({
variant: 'success',
title: 'Registration successful',
message: 'You can now log in with your new company account.'
})
onRegistered()
} else {
const msg = res.message || 'Registration failed. Please try again.'
setError(msg)
showToast({
variant: 'error',
title: 'Registration failed',
message: msg
})
}
} catch (error) {
const msg = 'Registration failed. Please try again.'
setError(msg)
showToast({
variant: 'error',
title: 'Registration failed',
message: msg
})
} finally {
setLoading(false)
}
}
// Surface hook error if present and no local error
useEffect(() => {
if (regError && !error) {
setError(regError)
showToast({
variant: 'error',
title: 'Registration failed',
message: regError
})
}
}, [regError]) // eslint-disable-line react-hooks/exhaustive-deps
// Input change handlers
const handlePersonalChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target
setPersonalForm(prev => ({ ...prev, [name]: value }))
}
const handleCompanyChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target
setCompanyForm(prev => ({ ...prev, [name]: value }))
}
const handleGuestChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target
setGuestForm(prev => ({ ...prev, [name]: value }))
}
const validateGuestForm = (): boolean => {
if (!guestForm.firstName.trim() || !guestForm.lastName.trim() ||
!guestForm.email.trim() || !guestForm.confirmEmail.trim() ||
!guestForm.password.trim() || !guestForm.confirmPassword.trim()
) {
setError('All fields are required')
return false
}
if (guestForm.email !== guestForm.confirmEmail) {
setError('Email addresses do not match')
return false
}
if (guestForm.password !== guestForm.confirmPassword) {
setError('Passwords do not match')
return false
}
if (!/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{8,}$/.test(guestForm.password)) {
setError('Password must be at least 8 characters long and contain uppercase and lowercase letters, numbers and special characters')
return false
}
setError('')
return true
}
const handleGuestSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (loading) return
if (!validateGuestForm()) return
setLoading(true)
setError('')
try {
const res = await registerGuest({
refToken: refToken || '',
firstName: guestForm.firstName,
lastName: guestForm.lastName,
email: guestForm.email,
password: guestForm.password,
})
if (res.ok) {
showToast({
variant: 'success',
title: 'Registration successful',
message: 'You can now log in to view your coffee abonnement.'
})
onRegistered()
} else {
const msg = res.message || 'Registration failed. Please try again.'
setError(msg)
showToast({ variant: 'error', title: 'Registration failed', message: msg })
}
} catch {
const msg = 'Registration failed. Please try again.'
setError(msg)
showToast({ variant: 'error', title: 'Registration failed', message: msg })
} finally {
setLoading(false)
}
}
// Password strength indicator
const getPasswordStrength = (password: string) => {
let strength = 0
if (password.length >= 8) strength++
if (/[a-z]/.test(password)) strength++
if (/[A-Z]/.test(password)) strength++
if (/\d/.test(password)) strength++
if (/[\W_]/.test(password)) strength++
return strength
}
const renderPasswordStrength = (password: string) => {
const strength = getPasswordStrength(password)
const rules = [
{ test: password.length >= 8, text: 'At least 8 characters' },
{ test: /[a-z]/.test(password), text: 'Lowercase letters (a-z)' },
{ test: /[A-Z]/.test(password), text: 'Uppercase letters (A-Z)' },
{ test: /\d/.test(password), text: 'Digits (0-9)' },
{ test: /[\W_]/.test(password), text: 'Special characters (!@#$...)' }
]
return (
<div className="mt-2">
<div className="text-sm text-slate-700 mb-2">Password requirements:</div>
<ul className="text-sm space-y-1">
{rules.map((rule, index) => (
<li
key={index}
className={`flex items-center gap-2 ${rule.test ? 'text-green-600' : 'text-slate-600'}`}
>
<span>{rule.test ? '✓' : '○'}</span>
<span>{rule.text}</span>
</li>
))}
</ul>
</div>
)
}
return (
// softened outer container, no own solid white card parent provides glass card
<div className="w-full">
{/* Header */}
<div className="mb-6 text-center">
<h2 className="text-2xl sm:text-3xl font-extrabold text-[#0F172A] mb-2">
Registration for Profit Planet
</h2>
{referrerEmail && (
<p className="text-base sm:text-sm text-[#8D6B1D] font-medium">
You were invited by <span className="font-semibold">{referrerEmail}</span>!
</p>
)}
</div>
{/* Mode Toggle */}
<div className="flex justify-center mb-8">
<div className="bg-white/40 backdrop-blur-[18px] border border-white/35 shadow-sm p-1 rounded-lg">
{mode === 'guest' ? (
<button
className="px-6 py-2 rounded-md font-semibold text-sm bg-[#8D6B1D] text-white shadow-sm cursor-default"
type="button"
>
Guest
</button>
) : (
<>
<button
className={`px-6 py-2 rounded-md font-semibold text-sm transition-all duration-200 ${
mode === 'personal'
? 'bg-[#8D6B1D] text-white shadow-sm'
: 'bg-transparent text-slate-700 hover:text-[#8D6B1D]'
}`}
onClick={() => setMode('personal')}
type="button"
>
Individual
</button>
<button
className={`px-6 py-2 rounded-md font-semibold text-sm transition-all duration-200 ${
mode === 'company'
? 'bg-[#8D6B1D] text-white shadow-sm'
: 'bg-transparent text-slate-700 hover:text-[#8D6B1D]'
}`}
onClick={() => setMode('company')}
type="button"
>
Company
</button>
</>
)}
</div>
</div>
{/* Error Message */}
{error && (
<div className="mb-6 p-4 bg-red-50/70 backdrop-blur-[18px] border border-red-200/70 rounded-lg">
<p className="text-red-600 text-sm font-medium">{error}</p>
</div>
)}
{/* Forms */}
<div className={formFade}>
{mode === 'personal' ? (
<form onSubmit={handlePersonalSubmit} className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label htmlFor="firstName" className="block text-sm font-medium text-[#0F172A] mb-2">
First name *
</label>
<input
type="text"
id="firstName"
name="firstName"
value={personalForm.firstName}
onChange={handlePersonalChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-white/60 focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent transition-colors text-primary"
required
/>
</div>
<div>
<label htmlFor="lastName" className="block text-sm font-medium text-[#0F172A] mb-2">
Last name *
</label>
<input
type="text"
id="lastName"
name="lastName"
value={personalForm.lastName}
onChange={handlePersonalChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-white/60 focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent transition-colors text-primary"
required
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label htmlFor="email" className="block text-sm font-medium text-[#0F172A] mb-2">
Email address *
</label>
<input
type="email"
id="email"
name="email"
value={personalForm.email}
onChange={handlePersonalChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-white/60 focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent transition-colors text-primary"
required
/>
</div>
<div>
<label htmlFor="confirmEmail" className="block text-sm font-medium text-[#0F172A] mb-2">
Confirm email *
</label>
<input
type="email"
id="confirmEmail"
name="confirmEmail"
value={personalForm.confirmEmail}
onChange={handlePersonalChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-white/60 focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent transition-colors text-primary"
required
/>
</div>
</div>
<div>
<label htmlFor="phoneNumber" className="block text-sm font-medium text-[#0F172A] mb-2">
Phone number *
</label>
<TelephoneInput
id="phoneNumber"
name="phoneNumber"
ref={personalPhoneRef}
autoComplete="tel"
placeholder="e.g. +43 676 1234567"
required
onChange={e =>
setPersonalForm(prev => ({ ...prev, phoneNumber: (e.target as HTMLInputElement).value }))
}
/>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label htmlFor="password" className="block text-sm font-medium text-[#0F172A] mb-2">
Password *
</label>
<div className="relative">
<input
type={showPersonalPassword ? 'text' : 'password'}
id="password"
name="password"
value={personalForm.password}
onChange={handlePersonalChange}
autoComplete="new-password"
className="w-full px-3 py-2 pr-10 border border-gray-300 rounded-lg bg-white/60 focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent transition-colors text-primary"
required
/>
<button
type="button"
onClick={() => setShowPersonalPassword(!showPersonalPassword)}
className="absolute inset-y-0 right-0 pr-3 flex items-center"
>
{showPersonalPassword ? (
<EyeSlashIcon className="h-4 w-4 text-slate-600" />
) : (
<EyeIcon className="h-4 w-4 text-slate-600" />
)}
</button>
</div>
{personalForm.password && renderPasswordStrength(personalForm.password)}
</div>
<div>
<label htmlFor="confirmPassword" className="block text-sm font-medium text-[#0F172A] mb-2">
Confirm password *
</label>
<input
type={showPersonalPassword ? 'text' : 'password'}
id="confirmPassword"
name="confirmPassword"
value={personalForm.confirmPassword}
onChange={handlePersonalChange}
autoComplete="new-password"
className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-white/60 focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent transition-colors text-primary"
required
/>
</div>
</div>
<button
type="submit"
disabled={loading}
className={`w-full flex items-center justify-center py-3 px-4 rounded-lg text-white font-semibold transition-colors ${
loading
? 'bg-gray-400 cursor-not-allowed'
: 'bg-[#8D6B1D] hover:bg-[#7A5E1A] focus:ring-2 focus:ring-[#8D6B1D] focus:ring-offset-2'
}`}
>
{loading ? (
<>
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
Registration in progress...
</>
) : (
'Register now'
)}
</button>
</form>
) : mode === 'company' ? (
<form onSubmit={handleCompanySubmit} className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label htmlFor="companyName" className="block text-sm font-medium text-[#0F172A] mb-2">
Company name *
</label>
<input
type="text"
id="companyName"
name="companyName"
value={companyForm.companyName}
onChange={handleCompanyChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-white/60 focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent transition-colors text-primary"
required
/>
</div>
<div>
<label htmlFor="contactPersonName" className="block text-sm font-medium text-[#0F172A] mb-2">
Contact person *
</label>
<input
type="text"
id="contactPersonName"
name="contactPersonName"
value={companyForm.contactPersonName}
onChange={handleCompanyChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-white/60 focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent transition-colors text-primary"
required
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label htmlFor="companyEmail" className="block text-sm font-medium text-[#0F172A] mb-2">
Company email *
</label>
<input
type="email"
id="companyEmail"
name="companyEmail"
value={companyForm.companyEmail}
onChange={handleCompanyChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-white/60 focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent transition-colors text-primary"
required
/>
</div>
<div>
<label htmlFor="confirmCompanyEmail" className="block text-sm font-medium text-[#0F172A] mb-2">
Confirm email *
</label>
<input
type="email"
id="confirmCompanyEmail"
name="confirmCompanyEmail"
value={companyForm.confirmCompanyEmail}
onChange={handleCompanyChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-white/60 focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent transition-colors text-primary"
required
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label htmlFor="companyPhone" className="block text-sm font-medium text-[#0F172A] mb-2">
Company phone *
</label>
<TelephoneInput
id="companyPhone"
name="companyPhone"
ref={companyPhoneRef}
autoComplete="tel"
placeholder="e.g. +43 1 234567"
required
onChange={e =>
setCompanyForm(prev => ({ ...prev, companyPhone: (e.target as HTMLInputElement).value }))
}
/>
</div>
<div>
<label htmlFor="contactPersonPhone" className="block text-sm font-medium text-[#0F172A] mb-2">
Contact person phone *
</label>
<TelephoneInput
id="contactPersonPhone"
name="contactPersonPhone"
ref={contactPhoneRef}
autoComplete="tel"
placeholder="e.g. +43 676 1234567"
required
onChange={e =>
setCompanyForm(prev => ({
...prev,
contactPersonPhone: (e.target as HTMLInputElement).value,
}))
}
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label htmlFor="password" className="block text-sm font-medium text-[#0F172A] mb-2">
Password *
</label>
<div className="relative">
<input
type={showCompanyPassword ? 'text' : 'password'}
id="password"
name="password"
value={companyForm.password}
onChange={handleCompanyChange}
autoComplete="new-password"
className="w-full px-3 py-2 pr-10 border border-gray-300 rounded-lg bg-white/60 focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent transition-colors text-primary"
required
/>
<button
type="button"
onClick={() => setShowCompanyPassword(!showCompanyPassword)}
className="absolute inset-y-0 right-0 pr-3 flex items-center"
>
{showCompanyPassword ? (
<EyeSlashIcon className="h-4 w-4 text-slate-600" />
) : (
<EyeIcon className="h-4 w-4 text-slate-600" />
)}
</button>
</div>
{companyForm.password && renderPasswordStrength(companyForm.password)}
</div>
<div>
<label htmlFor="confirmPassword" className="block text-sm font-medium text-[#0F172A] mb-2">
Confirm password *
</label>
<input
type={showCompanyPassword ? 'text' : 'password'}
id="confirmPassword"
name="confirmPassword"
value={companyForm.confirmPassword}
onChange={handleCompanyChange}
autoComplete="new-password"
className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-white/60 focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent transition-colors text-primary"
required
/>
</div>
</div>
<button
type="submit"
disabled={loading}
className={`w-full flex items-center justify-center py-3 px-4 rounded-lg text-white font-semibold transition-colors ${
loading
? 'bg-gray-400 cursor-not-allowed'
: 'bg-[#8D6B1D] hover:bg-[#7A5E1A] focus:ring-2 focus:ring-[#8D6B1D] focus:ring-offset-2'
}`}
>
{loading ? (
<>
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
Registration in progress...
</>
) : (
'Register company'
)}
</button>
</form>
) : (
<form onSubmit={handleGuestSubmit} className="space-y-6">
<div className="p-4 bg-amber-50/70 backdrop-blur-[18px] border border-amber-200/70 rounded-lg mb-2">
<p className="text-amber-800 text-sm font-medium">
You are registering as a guest. You will have access to your coffee abonnements only.
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label htmlFor="guestFirstName" className="block text-sm font-medium text-[#0F172A] mb-2">
First name *
</label>
<input
type="text"
id="guestFirstName"
name="firstName"
value={guestForm.firstName}
onChange={handleGuestChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-white/60 focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent transition-colors text-primary"
required
/>
</div>
<div>
<label htmlFor="guestLastName" className="block text-sm font-medium text-[#0F172A] mb-2">
Last name *
</label>
<input
type="text"
id="guestLastName"
name="lastName"
value={guestForm.lastName}
onChange={handleGuestChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-white/60 focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent transition-colors text-primary"
required
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label htmlFor="guestEmail" className="block text-sm font-medium text-[#0F172A] mb-2">
Email *
</label>
<input
type="email"
id="guestEmail"
name="email"
value={guestForm.email}
onChange={handleGuestChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-white/60 focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent transition-colors text-primary"
required
/>
</div>
<div>
<label htmlFor="guestConfirmEmail" className="block text-sm font-medium text-[#0F172A] mb-2">
Confirm email *
</label>
<input
type="email"
id="guestConfirmEmail"
name="confirmEmail"
value={guestForm.confirmEmail}
onChange={handleGuestChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-white/60 focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent transition-colors text-primary"
required
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label htmlFor="guestPassword" className="block text-sm font-medium text-[#0F172A] mb-2">
Password *
</label>
<div className="relative">
<input
type={showPersonalPassword ? 'text' : 'password'}
id="guestPassword"
name="password"
value={guestForm.password}
onChange={handleGuestChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-white/60 focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent transition-colors text-primary pr-10"
required
/>
<button
type="button"
onClick={() => setShowPersonalPassword(!showPersonalPassword)}
className="absolute inset-y-0 right-0 px-3 flex items-center text-slate-500 hover:text-[#8D6B1D]"
>
{showPersonalPassword ? (
<EyeSlashIcon className="h-5 w-5" />
) : (
<EyeIcon className="h-5 w-5" />
)}
</button>
</div>
{guestForm.password && renderPasswordStrength(guestForm.password)}
</div>
<div>
<label htmlFor="guestConfirmPassword" className="block text-sm font-medium text-[#0F172A] mb-2">
Confirm password *
</label>
<input
type="password"
id="guestConfirmPassword"
name="confirmPassword"
value={guestForm.confirmPassword}
onChange={handleGuestChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-white/60 focus:ring-2 focus:ring-[#8D6B1D] focus:border-transparent transition-colors text-primary"
required
/>
</div>
</div>
<button
type="submit"
disabled={loading}
className={`w-full flex items-center justify-center py-3 px-4 rounded-lg text-white font-semibold transition-colors ${
loading
? 'bg-gray-400 cursor-not-allowed'
: 'bg-[#8D6B1D] hover:bg-[#7A5E1A] focus:ring-2 focus:ring-[#8D6B1D] focus:ring-offset-2'
}`}
>
{loading ? (
<>
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
Registration in progress...
</>
) : (
'Register as Guest'
)}
</button>
</form>
)}
</div>
{/* Login Link */}
<div className="mt-8 text-center">
<p className="text-slate-700">
Already registered?{' '}
<a
href="/login"
className="text-[#8D6B1D] hover:text-[#7A5E1A] font-medium transition-colors"
>
Login here
</a>
</p>
</div>
</div>
)
}