Files
sfera/src/components/auth/phone-step.tsx
Veronika Smirnova 6b425d075f Унификация UI раздела Партнеры и создание системы документирования
🎨 Унификация 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>
2025-08-11 15:38:23 +03:00

170 lines
5.9 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 { 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>
)
}