Simplify hotel registration form to single step
Remove legal entity section (ОПФ, ИНН, КПП), two-step flow, and confirm password. Keep: hotel name, address, contact person, email, phone, password. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -122,75 +122,21 @@ function LoginForm({ onSwitch }: { onSwitch: () => void }) {
|
|||||||
|
|
||||||
// ── Register form ──────────────────────────────────────────────────────────────
|
// ── Register form ──────────────────────────────────────────────────────────────
|
||||||
function RegisterForm({ onSwitch }: { onSwitch: () => void }) {
|
function RegisterForm({ onSwitch }: { onSwitch: () => void }) {
|
||||||
const [step, setStep] = useState<1 | 2 | 'done'>(1)
|
const [done, setDone] = useState(false)
|
||||||
|
const [hotelName, setHotelName] = useState('')
|
||||||
|
const [address, setAddress] = useState('')
|
||||||
|
const [contact, setContact] = useState('')
|
||||||
|
const [email, setEmail] = useState('')
|
||||||
|
const [phone, setPhone] = useState('')
|
||||||
|
const [password, setPassword] = useState('')
|
||||||
|
const [showPass, setShowPass] = useState(false)
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [errors, setErrors] = useState<Record<string, string>>({})
|
||||||
|
|
||||||
// Step 1 — Hotel + Legal entity
|
const Field = ({ label, error, children }: { label: string; error?: string; children: React.ReactNode }) => (
|
||||||
const [hotelName, setHotelName] = useState('')
|
|
||||||
const [legalType, setLegalType] = useState<LegalType>('ooo')
|
|
||||||
const [legalName, setLegalName] = useState('')
|
|
||||||
const [inn, setInn] = useState('')
|
|
||||||
const [kpp, setKpp] = useState('')
|
|
||||||
const [address, setAddress] = useState('')
|
|
||||||
|
|
||||||
// Step 2 — Account
|
|
||||||
const [firstName, setFirstName] = useState('')
|
|
||||||
const [lastName, setLastName] = useState('')
|
|
||||||
const [email, setEmail] = useState('')
|
|
||||||
const [phone, setPhone] = useState('')
|
|
||||||
const [password, setPassword] = useState('')
|
|
||||||
const [confirm, setConfirm] = useState('')
|
|
||||||
const [showPass, setShowPass] = useState(false)
|
|
||||||
const [showConfirm, setShowConfirm] = useState(false)
|
|
||||||
const [loading, setLoading] = useState(false)
|
|
||||||
const [errors, setErrors] = useState<Record<string, string>>({})
|
|
||||||
|
|
||||||
const isIp = legalType === 'ip'
|
|
||||||
|
|
||||||
const validateStep1 = () => {
|
|
||||||
const e: Record<string, string> = {}
|
|
||||||
if (!hotelName.trim()) e.hotelName = 'Введите название отеля'
|
|
||||||
if (!legalName.trim()) e.legalName = 'Введите наименование юрлица'
|
|
||||||
if (!inn.trim()) e.inn = 'Введите ИНН'
|
|
||||||
else if (!/^\d{10,12}$/.test(inn.replace(/\s/g, '')))
|
|
||||||
e.inn = 'ИНН — 10 цифр (юрлицо) или 12 (ИП)'
|
|
||||||
if (!isIp && !kpp.trim()) e.kpp = 'Введите КПП'
|
|
||||||
setErrors(e)
|
|
||||||
return Object.keys(e).length === 0
|
|
||||||
}
|
|
||||||
|
|
||||||
const validateStep2 = () => {
|
|
||||||
const e: Record<string, string> = {}
|
|
||||||
if (!firstName.trim()) e.firstName = 'Введите имя'
|
|
||||||
if (!lastName.trim()) e.lastName = 'Введите фамилию'
|
|
||||||
if (!email.trim()) e.email = 'Введите email'
|
|
||||||
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) e.email = 'Некорректный email'
|
|
||||||
if (password.length < 8) e.password = 'Минимум 8 символов'
|
|
||||||
if (password !== confirm) e.confirm = 'Пароли не совпадают'
|
|
||||||
setErrors(e)
|
|
||||||
return Object.keys(e).length === 0
|
|
||||||
}
|
|
||||||
|
|
||||||
const nextStep = () => {
|
|
||||||
if (validateStep1()) setStep(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
|
||||||
e.preventDefault()
|
|
||||||
if (!validateStep2()) return
|
|
||||||
setLoading(true)
|
|
||||||
// Simulate API call
|
|
||||||
await new Promise(r => setTimeout(r, 1200))
|
|
||||||
setLoading(false)
|
|
||||||
setStep('done')
|
|
||||||
}
|
|
||||||
|
|
||||||
const Field = ({
|
|
||||||
label, error, children, hint,
|
|
||||||
}: { label: string; error?: string; children: React.ReactNode; hint?: string }) => (
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">{label}</label>
|
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">{label}</label>
|
||||||
{children}
|
{children}
|
||||||
{hint && !error && <p className="mt-1 text-xs text-slate-400">{hint}</p>}
|
|
||||||
{error && (
|
{error && (
|
||||||
<p className="mt-1 text-xs text-red-600 dark:text-red-400 flex items-center gap-1">
|
<p className="mt-1 text-xs text-red-600 dark:text-red-400 flex items-center gap-1">
|
||||||
<AlertCircle size={11} /> {error}
|
<AlertCircle size={11} /> {error}
|
||||||
@@ -199,21 +145,35 @@ function RegisterForm({ onSwitch }: { onSwitch: () => void }) {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
if (step === 'done') {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
const err: Record<string, string> = {}
|
||||||
|
if (!hotelName.trim()) err.hotelName = 'Введите название отеля'
|
||||||
|
if (!contact.trim()) err.contact = 'Введите контактное лицо'
|
||||||
|
if (!email.trim()) err.email = 'Введите email'
|
||||||
|
else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) err.email = 'Некорректный email'
|
||||||
|
if (password.length < 8) err.password = 'Минимум 8 символов'
|
||||||
|
setErrors(err)
|
||||||
|
if (Object.keys(err).length > 0) return
|
||||||
|
setLoading(true)
|
||||||
|
await new Promise(r => setTimeout(r, 1000))
|
||||||
|
setLoading(false)
|
||||||
|
setDone(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (done) {
|
||||||
return (
|
return (
|
||||||
<div className="text-center py-6">
|
<div className="text-center py-6">
|
||||||
<div className="w-16 h-16 rounded-full bg-emerald-100 dark:bg-emerald-900/40 flex items-center justify-center mx-auto mb-5">
|
<div className="w-16 h-16 rounded-full bg-emerald-100 dark:bg-emerald-900/40 flex items-center justify-center mx-auto mb-5">
|
||||||
<CheckCircle2 size={32} className="text-emerald-600 dark:text-emerald-400" />
|
<CheckCircle2 size={32} className="text-emerald-600 dark:text-emerald-400" />
|
||||||
</div>
|
</div>
|
||||||
<h2 className="text-2xl font-bold text-slate-900 dark:text-slate-100 mb-2">
|
<h2 className="text-2xl font-bold text-slate-900 dark:text-slate-100 mb-2">Заявка отправлена!</h2>
|
||||||
Заявка отправлена!
|
|
||||||
</h2>
|
|
||||||
<p className="text-slate-500 dark:text-slate-400 mb-2">
|
<p className="text-slate-500 dark:text-slate-400 mb-2">
|
||||||
Мы получили данные отеля <span className="font-semibold text-slate-700 dark:text-slate-300">{hotelName}</span>.
|
Мы получили данные отеля <span className="font-semibold text-slate-700 dark:text-slate-300">{hotelName}</span>.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm text-slate-500 dark:text-slate-400 mb-8">
|
<p className="text-sm text-slate-500 dark:text-slate-400 mb-8">
|
||||||
На адрес <span className="font-medium text-slate-700 dark:text-slate-300">{email}</span> отправлено
|
На адрес <span className="font-medium text-slate-700 dark:text-slate-300">{email}</span> отправлено
|
||||||
письмо с подтверждением. После проверки данных менеджер HotelSync активирует аккаунт в течение одного рабочего дня.
|
письмо с подтверждением. Менеджер HotelSync активирует аккаунт в течение одного рабочего дня.
|
||||||
</p>
|
</p>
|
||||||
<button onClick={onSwitch} className="btn-primary w-full justify-center py-2.5">
|
<button onClick={onSwitch} className="btn-primary w-full justify-center py-2.5">
|
||||||
Перейти к входу
|
Перейти к входу
|
||||||
@@ -224,240 +184,89 @@ function RegisterForm({ onSwitch }: { onSwitch: () => void }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center justify-between mb-6">
|
<h2 className="text-2xl font-bold text-slate-900 dark:text-slate-100 mb-1">Регистрация отеля</h2>
|
||||||
<div>
|
<p className="text-slate-500 dark:text-slate-400 mb-6">Заполните данные — мы активируем аккаунт в течение дня</p>
|
||||||
<h2 className="text-2xl font-bold text-slate-900 dark:text-slate-100">
|
|
||||||
{step === 1 ? 'Регистрация отеля' : 'Данные аккаунта'}
|
|
||||||
</h2>
|
|
||||||
<p className="text-sm text-slate-500 dark:text-slate-400 mt-0.5">Шаг {step} из 2</p>
|
|
||||||
</div>
|
|
||||||
{/* Step indicator */}
|
|
||||||
<div className="flex items-center gap-1.5">
|
|
||||||
{[1, 2].map(s => (
|
|
||||||
<div key={s} className={cn(
|
|
||||||
'w-8 h-8 rounded-full flex items-center justify-center text-xs font-bold transition-all',
|
|
||||||
step === s
|
|
||||||
? 'bg-brand-600 text-white'
|
|
||||||
: s < step
|
|
||||||
? 'bg-emerald-500 text-white'
|
|
||||||
: 'bg-slate-200 dark:bg-slate-700 text-slate-500 dark:text-slate-400',
|
|
||||||
)}>
|
|
||||||
{s < step ? <CheckCircle2 size={14} /> : s}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* ── Step 1: Hotel + Legal entity ── */}
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
{step === 1 && (
|
<Field label="Название отеля *" error={errors.hotelName}>
|
||||||
<div className="space-y-4">
|
<input
|
||||||
<Field label="Название отеля *" error={errors.hotelName}>
|
type="text"
|
||||||
|
className={cn('input', errors.hotelName && 'border-red-400 focus:ring-red-400')}
|
||||||
|
placeholder="Grand Palace Hotel"
|
||||||
|
value={hotelName} onChange={e => setHotelName(e.target.value)}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field label="Адрес" error={errors.address}>
|
||||||
|
<input
|
||||||
|
type="text" className="input"
|
||||||
|
placeholder="г. Москва, ул. Примерная, д. 1"
|
||||||
|
value={address} onChange={e => setAddress(e.target.value)}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field label="Контактное лицо *" error={errors.contact}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className={cn('input', errors.contact && 'border-red-400 focus:ring-red-400')}
|
||||||
|
placeholder="Иванов Иван Иванович"
|
||||||
|
value={contact} onChange={e => setContact(e.target.value)}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field label="Email *" error={errors.email}>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
className={cn('input', errors.email && 'border-red-400 focus:ring-red-400')}
|
||||||
|
placeholder="director@myhotel.ru"
|
||||||
|
value={email} onChange={e => setEmail(e.target.value)}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field label="Телефон" error={errors.phone}>
|
||||||
|
<input
|
||||||
|
type="tel" className="input"
|
||||||
|
placeholder="+7 (999) 000-00-00"
|
||||||
|
value={phone} onChange={e => setPhone(e.target.value)}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<Field label="Пароль *" error={errors.password}>
|
||||||
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
type="text" className={cn('input', errors.hotelName && 'border-red-400 focus:ring-red-400')}
|
type={showPass ? 'text' : 'password'}
|
||||||
placeholder="Grand Palace Hotel"
|
className={cn('input pr-10', errors.password && 'border-red-400 focus:ring-red-400')}
|
||||||
value={hotelName} onChange={e => setHotelName(e.target.value)}
|
placeholder="Минимум 8 символов"
|
||||||
|
value={password} onChange={e => setPassword(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</Field>
|
<button type="button" onClick={() => setShowPass(v => !v)}
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-slate-600">
|
||||||
<div className="pt-1 border-t border-slate-100 dark:border-slate-700">
|
{showPass ? <EyeOff size={16} /> : <Eye size={16} />}
|
||||||
<div className="flex items-center gap-2 mb-3">
|
|
||||||
<Building2 size={14} className="text-slate-500" />
|
|
||||||
<p className="text-sm font-semibold text-slate-700 dark:text-slate-300">Юридическое лицо</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Legal type selector */}
|
|
||||||
<div className="mb-4">
|
|
||||||
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">
|
|
||||||
Организационно-правовая форма *
|
|
||||||
</label>
|
|
||||||
<div className="flex gap-1.5 flex-wrap">
|
|
||||||
{LEGAL_TYPES.map(lt => (
|
|
||||||
<button
|
|
||||||
key={lt.id}
|
|
||||||
type="button"
|
|
||||||
onClick={() => setLegalType(lt.id)}
|
|
||||||
className={cn(
|
|
||||||
'px-3 py-1.5 rounded-lg text-sm font-medium border transition-colors',
|
|
||||||
legalType === lt.id
|
|
||||||
? 'bg-brand-600 border-brand-600 text-white'
|
|
||||||
: 'bg-white dark:bg-slate-700 border-slate-200 dark:border-slate-600 text-slate-700 dark:text-slate-300 hover:border-brand-400',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{lt.label}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-3">
|
|
||||||
<Field
|
|
||||||
label={isIp ? 'ФИО индивидуального предпринимателя *' : 'Полное наименование организации *'}
|
|
||||||
error={errors.legalName}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className={cn('input', errors.legalName && 'border-red-400 focus:ring-red-400')}
|
|
||||||
placeholder={isIp ? 'Иванов Иван Иванович' : 'Общество с ограниченной ответственностью «Название»'}
|
|
||||||
value={legalName} onChange={e => setLegalName(e.target.value)}
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
|
|
||||||
<div className={cn('grid gap-3', isIp ? 'grid-cols-1' : 'grid-cols-2')}>
|
|
||||||
<Field
|
|
||||||
label="ИНН *"
|
|
||||||
error={errors.inn}
|
|
||||||
hint={isIp ? '12 цифр' : '10 цифр'}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className={cn('input font-mono', errors.inn && 'border-red-400 focus:ring-red-400')}
|
|
||||||
placeholder={isIp ? '123456789012' : '1234567890'}
|
|
||||||
maxLength={isIp ? 12 : 10}
|
|
||||||
value={inn} onChange={e => setInn(e.target.value.replace(/\D/g, ''))}
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
{!isIp && (
|
|
||||||
<Field label="КПП *" error={errors.kpp} hint="9 цифр">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className={cn('input font-mono', errors.kpp && 'border-red-400 focus:ring-red-400')}
|
|
||||||
placeholder="123456789"
|
|
||||||
maxLength={9}
|
|
||||||
value={kpp} onChange={e => setKpp(e.target.value.replace(/\D/g, ''))}
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Field label="Юридический адрес" error={errors.address}>
|
|
||||||
<input
|
|
||||||
type="text" className="input"
|
|
||||||
placeholder="г. Москва, ул. Примерная, д. 1"
|
|
||||||
value={address} onChange={e => setAddress(e.target.value)}
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="button" onClick={nextStep} className="btn-primary w-full justify-center py-2.5 mt-2">
|
|
||||||
Далее
|
|
||||||
<ChevronRight size={16} />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<p className="text-center text-sm text-slate-500 dark:text-slate-400">
|
|
||||||
Уже есть аккаунт?{' '}
|
|
||||||
<button onClick={onSwitch} className="text-brand-600 dark:text-brand-400 font-medium hover:underline">
|
|
||||||
Войти
|
|
||||||
</button>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* ── Step 2: Account ── */}
|
|
||||||
{step === 2 && (
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
|
||||||
<div className="grid grid-cols-2 gap-3">
|
|
||||||
<Field label="Имя *" error={errors.firstName}>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className={cn('input', errors.firstName && 'border-red-400 focus:ring-red-400')}
|
|
||||||
placeholder="Иван"
|
|
||||||
value={firstName} onChange={e => setFirstName(e.target.value)}
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
<Field label="Фамилия *" error={errors.lastName}>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className={cn('input', errors.lastName && 'border-red-400 focus:ring-red-400')}
|
|
||||||
placeholder="Иванов"
|
|
||||||
value={lastName} onChange={e => setLastName(e.target.value)}
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Field label="Рабочий email *" error={errors.email}>
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
className={cn('input', errors.email && 'border-red-400 focus:ring-red-400')}
|
|
||||||
placeholder="director@myhotel.ru"
|
|
||||||
value={email} onChange={e => setEmail(e.target.value)}
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
|
|
||||||
<Field label="Телефон" error={errors.phone}>
|
|
||||||
<input
|
|
||||||
type="tel" className="input"
|
|
||||||
placeholder="+7 (999) 000-00-00"
|
|
||||||
value={phone} onChange={e => setPhone(e.target.value)}
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
|
|
||||||
<Field label="Пароль *" error={errors.password} hint="Минимум 8 символов">
|
|
||||||
<div className="relative">
|
|
||||||
<input
|
|
||||||
type={showPass ? 'text' : 'password'}
|
|
||||||
className={cn('input pr-10', errors.password && 'border-red-400 focus:ring-red-400')}
|
|
||||||
placeholder="••••••••"
|
|
||||||
value={password} onChange={e => setPassword(e.target.value)}
|
|
||||||
/>
|
|
||||||
<button type="button" onClick={() => setShowPass(v => !v)}
|
|
||||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-slate-600">
|
|
||||||
{showPass ? <EyeOff size={16} /> : <Eye size={16} />}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{password && (
|
|
||||||
<div className="mt-1.5 flex gap-1">
|
|
||||||
{[
|
|
||||||
password.length >= 8,
|
|
||||||
/[A-Z]/.test(password),
|
|
||||||
/[0-9]/.test(password),
|
|
||||||
].map((ok, i) => (
|
|
||||||
<div key={i} className={cn('h-1 flex-1 rounded-full transition-colors', ok ? 'bg-emerald-500' : 'bg-slate-200 dark:bg-slate-600')} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
|
|
||||||
<Field label="Подтверждение пароля *" error={errors.confirm}>
|
|
||||||
<div className="relative">
|
|
||||||
<input
|
|
||||||
type={showConfirm ? 'text' : 'password'}
|
|
||||||
className={cn('input pr-10', errors.confirm && 'border-red-400 focus:ring-red-400')}
|
|
||||||
placeholder="••••••••"
|
|
||||||
value={confirm} onChange={e => setConfirm(e.target.value)}
|
|
||||||
/>
|
|
||||||
<button type="button" onClick={() => setShowConfirm(v => !v)}
|
|
||||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-slate-600">
|
|
||||||
{showConfirm ? <EyeOff size={16} /> : <Eye size={16} />}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</Field>
|
|
||||||
|
|
||||||
<p className="text-xs text-slate-400 dark:text-slate-500">
|
|
||||||
Нажимая «Создать аккаунт», вы соглашаетесь с{' '}
|
|
||||||
<span className="text-brand-600 dark:text-brand-400 cursor-pointer hover:underline">условиями использования</span>{' '}
|
|
||||||
и{' '}
|
|
||||||
<span className="text-brand-600 dark:text-brand-400 cursor-pointer hover:underline">политикой конфиденциальности</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="flex gap-3">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => { setErrors({}); setStep(1) }}
|
|
||||||
className="btn-secondary flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
<ChevronLeft size={16} />
|
|
||||||
Назад
|
|
||||||
</button>
|
|
||||||
<button type="submit" disabled={loading} className="btn-primary flex-1 justify-center py-2.5">
|
|
||||||
{loading
|
|
||||||
? <span className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
|
||||||
: <><User size={15} /> Создать аккаунт</>
|
|
||||||
}
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
{password && (
|
||||||
)}
|
<div className="mt-1.5 flex gap-1">
|
||||||
|
{[password.length >= 8, /[A-Z]/.test(password), /[0-9]/.test(password)].map((ok, i) => (
|
||||||
|
<div key={i} className={cn('h-1 flex-1 rounded-full transition-colors', ok ? 'bg-emerald-500' : 'bg-slate-200 dark:bg-slate-600')} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<button type="submit" disabled={loading} className="btn-primary w-full justify-center py-2.5 mt-2">
|
||||||
|
{loading
|
||||||
|
? <span className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
||||||
|
: 'Зарегистрировать отель'
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<p className="mt-5 text-center text-sm text-slate-500 dark:text-slate-400">
|
||||||
|
Уже есть аккаунт?{' '}
|
||||||
|
<button onClick={onSwitch} className="text-brand-600 dark:text-brand-400 font-medium hover:underline">
|
||||||
|
Войти
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user