import { useEffect, useState } from 'react'; import { Button, Card, Descriptions, Form, Input, Spin, Typography, message } from 'antd'; import { User } from 'lucide-react'; import './Profile.css'; const { Title, Text } = Typography; /** Поля профиля из CRM (поддержка snake_case и camelCase). Все кроме phone редактируемые при verification="0". */ const PROFILE_FIELDS: Array<{ key: string; keys: string[]; label: string; editable: boolean }> = [ { key: 'last_name', keys: ['last_name', 'lastName'], label: 'Фамилия', editable: true }, { key: 'first_name', keys: ['first_name', 'firstName'], label: 'Имя', editable: true }, { key: 'middle_name', keys: ['middle_name', 'middleName', 'otchestvo'], label: 'Отчество', editable: true }, { key: 'birth_date', keys: ['birth_date', 'birthDate', 'birthday'], label: 'Дата рождения', editable: true }, { key: 'birth_place', keys: ['birth_place', 'birthPlace'], label: 'Место рождения', editable: true }, { key: 'inn', keys: ['inn'], label: 'ИНН', editable: true }, { key: 'email', keys: ['email'], label: 'Электронная почта', editable: true }, { key: 'registration_address', keys: ['registration_address', 'address', 'mailingstreet'], label: 'Адрес регистрации', editable: true }, { key: 'mailing_address', keys: ['mailing_address', 'postal_address'], label: 'Почтовый адрес', editable: true }, { key: 'bank_for_compensation', keys: ['bank_for_compensation', 'bank'], label: 'Банк для получения возмещения', editable: true }, { key: 'phone', keys: ['phone', 'mobile', 'mobile_phone'], label: 'Мобильный телефон', editable: false }, ]; function getValue(obj: Record, keys: string[]): string { for (const k of keys) { const v = obj[k]; if (v != null && String(v).trim() !== '') return String(v).trim(); } return ''; } /** verification === "0" — профиль можно редактировать (ответ n8n). Иначе — только просмотр. */ function canEditProfile(contact: Record): boolean { const v = contact?.verification ?? contact?.Verification; return v === '0' || v === 0; } interface ProfileProps { onNavigate?: (path: string) => void; } export default function Profile({ onNavigate }: ProfileProps) { const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); const [contact, setContact] = useState | null>(null); const [form] = Form.useForm(); useEffect(() => { let cancelled = false; const token = (typeof sessionStorage !== 'undefined' ? sessionStorage.getItem('session_token') : null) || localStorage.getItem('session_token'); if (!token) { setLoading(false); onNavigate?.('/hello'); return; } const entryChannel = (typeof window !== 'undefined' && (window as any).Telegram?.WebApp?.initData) ? 'telegram' : (typeof window !== 'undefined' && (window as any).WebApp?.initData) ? 'max' : 'web'; const chatId = (() => { if (typeof window === 'undefined') return undefined; const tg = (window as any).Telegram?.WebApp?.initDataUnsafe?.user?.id; if (tg != null) return String(tg); const max = (window as any).WebApp?.initDataUnsafe?.user?.id; if (max != null) return String(max); return undefined; })(); setLoading(true); setError(null); const params = new URLSearchParams({ session_token: token, entry_channel: entryChannel }); if (chatId) params.set('chat_id', chatId); fetch(`/api/v1/profile/contact?${params.toString()}`) .then((res) => { if (!res.ok) { if (res.status === 401) { try { sessionStorage.removeItem('session_token'); } catch (_) {} localStorage.removeItem('session_token'); throw new Error('Сессия истекла'); } throw new Error('Ошибка загрузки'); } return res.json(); }) .then((data: { items?: unknown[] }) => { if (cancelled) return; const items = Array.isArray(data?.items) ? data.items : []; const first = items.length > 0 && typeof items[0] === 'object' && items[0] !== null ? (items[0] as Record) : null; setContact(first); if (first && canEditProfile(first)) { const initial: Record = {}; PROFILE_FIELDS.forEach(({ key, keys }) => { initial[key] = getValue(first, keys) || ''; }); form.setFieldsValue(initial); } }) .catch((e) => { if (!cancelled) setError(e?.message || 'Не удалось загрузить данные'); }) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, [onNavigate, form]); const handleSave = async () => { if (!contact || !canEditProfile(contact)) return; const token = sessionStorage.getItem('session_token') || localStorage.getItem('session_token'); if (!token) { message.error('Сессия истекла'); onNavigate?.('/hello'); return; } const entryChannel = (typeof window !== 'undefined' && (window as any).Telegram?.WebApp?.initData) ? 'telegram' : (typeof window !== 'undefined' && (window as any).WebApp?.initData) ? 'max' : 'web'; let values: Record; try { values = await form.validateFields(); } catch { return; } setSaving(true); try { const res = await fetch('/api/v1/profile/contact/update', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ session_token: token, entry_channel: entryChannel, last_name: values.last_name ?? '', first_name: values.first_name ?? '', middle_name: values.middle_name ?? '', birth_date: values.birth_date ?? '', birth_place: values.birth_place ?? '', inn: values.inn ?? '', email: values.email ?? '', registration_address: values.registration_address ?? '', mailing_address: values.mailing_address ?? '', bank_for_compensation: values.bank_for_compensation ?? '', phone: getValue(contact, ['phone', 'mobile', 'mobile_phone']) || undefined, }), }); const data = await res.json().catch(() => ({})); if (!res.ok) { const detail = typeof data?.detail === 'string' ? data.detail : data?.detail?.[0]?.msg || 'Не удалось сохранить профиль'; message.error(detail); return; } message.success('Профиль сохранён'); setContact({ ...contact, ...values }); } catch (e) { message.error('Не удалось сохранить профиль, попробуйте позже'); } finally { setSaving(false); } }; if (loading) { return (
); } if (error) { return (
Профиль {error}
); } if (!contact) { return (
Профиль Контактных данных пока нет. Они появятся после обработки ваших обращений.
); } const canEdit = canEditProfile(contact); if (canEdit) { return (
Профиль} extra={onNavigate ? : null} >
{PROFILE_FIELDS.map(({ key, keys, label, editable }) => ( ))}
); } const items = PROFILE_FIELDS.map(({ keys, label }) => ({ key: keys[0], label, children: getValue(contact, keys) || '—', })); return (
Профиль}> {items.map((item) => ( {item.children} ))}
); }