
🎨 Унификация UI: - Полная унификация визуала вкладок Рефералы и Мои контрагенты - Исправлены React Hooks ошибки в sidebar.tsx - Убрана лишняя обертка glass-card в partners-dashboard.tsx - Исправлена цветовая схема (purple → yellow) - Табличный формат вместо карточного grid-layout - Компактные блоки статистики (4 метрики в ряд) - Правильная прозрачность glass-morphism эффектов 📚 Документация: - Переименован referral-system-rules.md → partners-rules.md - Детальные UI/UX правила в partners-rules.md - Правила унификации в visual-design-rules.md - Обновлен current-session.md - Создан development-diary.md 🚀 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
170 lines
5.9 KiB
TypeScript
170 lines
5.9 KiB
TypeScript
'use client'
|
||
|
||
import { useMutation } from '@apollo/client'
|
||
import { Phone, ArrowRight } from 'lucide-react'
|
||
import { useState } from 'react'
|
||
|
||
import { Button } from '@/components/ui/button'
|
||
import { GlassInput } from '@/components/ui/input'
|
||
import { Label } from '@/components/ui/label'
|
||
import { SEND_SMS_CODE } from '@/graphql/mutations'
|
||
|
||
import { AuthLayout } from './auth-layout'
|
||
|
||
interface PhoneStepProps {
|
||
onNext: (phone: string) => void
|
||
registrationType?: 'REFERRAL' | 'PARTNER' | null
|
||
referrerCode?: string | null
|
||
}
|
||
|
||
export function PhoneStep({ onNext, registrationType }: PhoneStepProps) {
|
||
const [phone, setPhone] = useState('')
|
||
const [isLoading, setIsLoading] = useState(false)
|
||
const [error, setError] = useState<string | null>(null)
|
||
|
||
const [sendSmsCode] = useMutation(SEND_SMS_CODE)
|
||
|
||
const formatPhoneNumber = (value: string) => {
|
||
const numbers = value.replace(/\D/g, '')
|
||
|
||
if (numbers.length === 0) return ''
|
||
if (numbers[0] === '8') {
|
||
const withoutFirst = numbers.slice(1)
|
||
return formatRussianNumber('7' + withoutFirst)
|
||
}
|
||
if (numbers[0] === '7') {
|
||
return formatRussianNumber(numbers)
|
||
}
|
||
|
||
return formatRussianNumber('7' + numbers)
|
||
}
|
||
|
||
const formatRussianNumber = (numbers: string) => {
|
||
if (numbers.length <= 1) return '+7'
|
||
if (numbers.length <= 4) return `+7 (${numbers.slice(1)}`
|
||
if (numbers.length <= 7) return `+7 (${numbers.slice(1, 4)}) ${numbers.slice(4)}`
|
||
if (numbers.length <= 9) return `+7 (${numbers.slice(1, 4)}) ${numbers.slice(4, 7)}-${numbers.slice(7)}`
|
||
return `+7 (${numbers.slice(1, 4)}) ${numbers.slice(4, 7)}-${numbers.slice(7, 9)}-${numbers.slice(9, 11)}`
|
||
}
|
||
|
||
const handlePhoneChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||
const formatted = formatPhoneNumber(e.target.value)
|
||
setPhone(formatted)
|
||
setError(null)
|
||
}
|
||
|
||
const isValidPhone = (phone: string) => {
|
||
const numbers = phone.replace(/\D/g, '')
|
||
return numbers.length === 11 && (numbers.startsWith('7') || numbers.startsWith('8'))
|
||
}
|
||
|
||
const handleSubmit = async (e: React.FormEvent) => {
|
||
e.preventDefault()
|
||
|
||
if (!isValidPhone(phone)) {
|
||
setError('Введите корректный номер телефона')
|
||
return
|
||
}
|
||
|
||
setIsLoading(true)
|
||
setError(null)
|
||
|
||
try {
|
||
const cleanPhone = phone.replace(/\D/g, '')
|
||
const formattedPhone = cleanPhone.startsWith('8') ? '7' + cleanPhone.slice(1) : cleanPhone
|
||
|
||
const { data } = await sendSmsCode({
|
||
variables: { phone: formattedPhone },
|
||
})
|
||
|
||
if (data.sendSmsCode.success) {
|
||
onNext(phone)
|
||
} else {
|
||
setError('Ошибка отправки SMS. Попробуйте позже.')
|
||
}
|
||
} catch (error: unknown) {
|
||
console.error('SMS sending error:', error)
|
||
setError(error instanceof Error ? error.message : 'Ошибка отправки SMS. Попробуйте позже.')
|
||
} finally {
|
||
setIsLoading(false)
|
||
}
|
||
}
|
||
|
||
return (
|
||
<AuthLayout
|
||
title="Добро пожаловать!"
|
||
description="Введите номер телефона для входа в систему"
|
||
currentStep={1}
|
||
totalSteps={5}
|
||
stepName="Авторизация"
|
||
>
|
||
{/* Индикатор типа регистрации */}
|
||
{registrationType && (
|
||
<div className="mb-6 p-4 rounded-xl bg-gradient-to-r from-white/5 to-white/10 border border-white/20">
|
||
<div className="flex items-center gap-3">
|
||
<div className={`p-2 rounded-lg ${
|
||
registrationType === 'PARTNER'
|
||
? 'bg-purple-500/20 border border-purple-500/30'
|
||
: 'bg-blue-500/20 border border-blue-500/30'
|
||
}`}>
|
||
<span className="text-xl">
|
||
{registrationType === 'PARTNER' ? '🤝' : '📎'}
|
||
</span>
|
||
</div>
|
||
<div className="flex-1">
|
||
<p className="text-sm font-medium text-white">
|
||
{registrationType === 'PARTNER'
|
||
? 'Регистрация по партнерской ссылке'
|
||
: 'Регистрация по реферальной ссылке'}
|
||
</p>
|
||
<p className="text-xs text-white/60 mt-1">
|
||
{registrationType === 'PARTNER'
|
||
? 'Вы получите +100 сфер ⚡ и автоматически станете партнером'
|
||
: 'Вы получите +100 сфер ⚡ за регистрацию'}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
<form onSubmit={handleSubmit} className="space-y-4">
|
||
<div className="space-y-2">
|
||
<Label htmlFor="phone" className="text-white text-sm font-medium flex items-center gap-2">
|
||
<Phone className="h-4 w-4" />
|
||
Номер телефона
|
||
</Label>
|
||
<GlassInput
|
||
id="phone"
|
||
type="tel"
|
||
placeholder="+7 (___) ___-__-__"
|
||
value={phone}
|
||
onChange={handlePhoneChange}
|
||
className={`h-12 text-lg ${error ? 'border-red-400/50' : ''}`}
|
||
style={{ caretColor: 'white' }}
|
||
onFocus={(e) => {
|
||
// Устанавливаем курсор в начало если поле пустое или содержит только +7
|
||
if (phone === '' || phone === '+7') {
|
||
setTimeout(() => {
|
||
e.target.setSelectionRange(0, 0)
|
||
}, 0)
|
||
}
|
||
}}
|
||
/>
|
||
{error && <p className="text-red-400 text-xs">{error}</p>}
|
||
</div>
|
||
|
||
<Button
|
||
type="submit"
|
||
variant="glass"
|
||
size="lg"
|
||
className="w-full h-12 flex items-center gap-2"
|
||
disabled={!isValidPhone(phone) || isLoading}
|
||
>
|
||
{isLoading ? 'Отправка...' : 'Получить SMS код'}
|
||
<ArrowRight className="h-4 w-4" />
|
||
</Button>
|
||
</form>
|
||
</AuthLayout>
|
||
)
|
||
}
|