1005 lines
36 KiB
TypeScript
1005 lines
36 KiB
TypeScript
'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>
|
||
)
|
||
}
|