From 73524465fd6f5d93ca11154167fa9c7b3b77188b Mon Sep 17 00:00:00 2001 From: Fedor Date: Fri, 2 Jan 2026 17:37:37 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20?= =?UTF-8?q?=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=D0=B4=D0=BD=D0=B5=D0=B3=D0=BE=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BC=D0=BC=D0=B8=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Изменения в backend: - Обновления в n8n_proxy.py - Изменения в SMS API - Обновления конфигурации - Улучшения SMS сервиса Изменения в frontend: - Обновления Step1Phone компонента - Изменения в Step3Payment - Улучшения generateConfirmationFormHTML - Обновления ClaimForm страницы - Изменения в vite.config.ts Статистика: +242 строки, -81 строка --- GIT_STATUS.md | 99 ++++++++++++++++ backend/app/api/n8n_proxy.py | 12 +- backend/app/api/sms.py | 2 +- backend/app/config.py | 4 +- backend/app/services/sms_service.py | 14 ++- frontend/src/components/form/Step1Phone.tsx | 13 ++- frontend/src/components/form/Step3Payment.tsx | 59 ++++++---- .../form/generateConfirmationFormHTML.ts | 109 ++++++++++++++---- frontend/src/pages/ClaimForm.tsx | 106 +++++++++++++---- frontend/vite.config.ts | 4 +- 10 files changed, 341 insertions(+), 81 deletions(-) create mode 100644 GIT_STATUS.md diff --git a/GIT_STATUS.md b/GIT_STATUS.md new file mode 100644 index 0000000..6c41b74 --- /dev/null +++ b/GIT_STATUS.md @@ -0,0 +1,99 @@ +# 📊 Статус Git репозитория DEV + +**Дата проверки:** 2 января 2025 + +--- + +## 📅 Последний коммит + +**Дата:** 29 декабря 2025, 10:59:21 +**Автор:** Fedor (fedor@clientright.ru) +**Сообщение:** `feat: Add SMS debug code modal for dev environment` +**Хеш:** `f7d27388a0b62380e4f1bdeba3c997f50ff10587` + +--- + +## ⚠️ Незакоммиченные изменения + +**Всего изменено файлов:** 9 + +### Backend (4 файла): +- `backend/app/api/n8n_proxy.py` - изменён +- `backend/app/api/sms.py` - изменён +- `backend/app/config.py` - изменён +- `backend/app/services/sms_service.py` - изменён + +### Frontend (5 файлов): +- `frontend/src/components/form/Step1Phone.tsx` - изменён +- `frontend/src/components/form/Step3Payment.tsx` - изменён +- `frontend/src/components/form/generateConfirmationFormHTML.ts` - изменён +- `frontend/src/pages/ClaimForm.tsx` - изменён +- `frontend/vite.config.ts` - изменён + +**Статистика изменений:** +- Добавлено: ~242 строки +- Удалено: ~81 строка +- Чистое изменение: +161 строка + +--- + +## 📤 Статус с remote + +**Ветка:** `main` +**Remote:** `origin/main` +**Статус:** Есть локальные изменения, которые не запушены в remote + +**Не запушенные изменения:** +- `backend/app/api/n8n_proxy.py` +- `backend/app/api/sms.py` +- `backend/app/config.py` +- `backend/app/services/sms_service.py` + +--- + +## 🔄 История коммитов (последние 5) + +1. **2025-12-29** - `feat: Add SMS debug code modal for dev environment` +2. **2025-12-29** - `Add docker-compose.dev.yml for dev environment (ports 5177, 8201)` +3. **2025-12-29** - `docs: Move session log to root` +4. **2025-12-29** - `Add session log 2025-12-29` +5. **2025-12-29** - `Production fixes: n8n workflow auto-restart, user-friendly messages, fixed navigation buttons` + +--- + +## 💡 Рекомендации + +### 1. Закоммитить изменения + +```bash +cd /var/www/fastuser/data/www/crm.clientright.ru/aiform_dev + +# Посмотреть что изменилось +git diff + +# Добавить все изменения +git add . + +# Закоммитить +git commit -m "feat: Описание изменений" +``` + +### 2. Запушить в remote + +```bash +# Отправить в dev репозиторий +git push origin main + +# Или если remote называется aiform_dev +git push aiform_dev main +``` + +### 3. Перенести в PROD (если нужно) + +После коммита и пуша, можно перенести изменения в PROD папку. + +--- + +**Автор:** AI Assistant + Фёдор +**Дата:** 2 января 2025 + diff --git a/backend/app/api/n8n_proxy.py b/backend/app/api/n8n_proxy.py index 80d2443..1fe5ab3 100644 --- a/backend/app/api/n8n_proxy.py +++ b/backend/app/api/n8n_proxy.py @@ -15,11 +15,11 @@ logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/n8n", tags=["n8n-proxy"]) -# URL webhooks из .env (будут добавлены) -N8N_POLICY_CHECK_WEBHOOK = getattr(settings, 'n8n_policy_check_webhook', None) -N8N_FILE_UPLOAD_WEBHOOK = getattr(settings, 'n8n_file_upload_webhook', None) -N8N_CREATE_CONTACT_WEBHOOK = getattr(settings, 'n8n_create_contact_webhook', 'https://n8n.clientright.pro/webhook/511fde97-88bb-4fb4-bea5-cafdc364be27') -N8N_CREATE_CLAIM_WEBHOOK = getattr(settings, 'n8n_create_claim_webhook', 'https://n8n.clientright.pro/webhook/d5bf4ca6-9e44-44b9-9714-3186ea703e7d') +# URL webhooks - берём из settings (defaults в config.py) +N8N_POLICY_CHECK_WEBHOOK = settings.n8n_policy_check_webhook or None +N8N_FILE_UPLOAD_WEBHOOK = settings.n8n_file_upload_webhook or None +N8N_CREATE_CONTACT_WEBHOOK = settings.n8n_create_contact_webhook +N8N_CREATE_CLAIM_WEBHOOK = settings.n8n_create_claim_webhook @router.post("/policy/check") @@ -124,7 +124,9 @@ async def proxy_create_contact(request: Request): logger.error("⏱️ N8N webhook timeout") raise HTTPException(status_code=504, detail="Таймаут подключения к n8n") except Exception as e: + import traceback logger.error(f"❌ Error proxying to n8n: {e}") + logger.error(f"❌ Traceback: {traceback.format_exc()}") raise HTTPException(status_code=500, detail=f"Ошибка создания контакта: {str(e)}") diff --git a/backend/app/api/sms.py b/backend/app/api/sms.py index f7ebf62..c7b154b 100644 --- a/backend/app/api/sms.py +++ b/backend/app/api/sms.py @@ -21,7 +21,7 @@ async def send_sms_code(request: SMSSendRequest): return { "success": True, "message": "Код отправлен на указанный номер", - "debug_code": code if sms_service.enabled else None # Показываем код только в dev + "debug_code": code # Всегда возвращаем код для dev модалки } else: raise HTTPException( diff --git a/backend/app/config.py b/backend/app/config.py index 3c37554..5b55b7d 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -177,8 +177,8 @@ class Settings(BaseSettings): n8n_api_key: str = "" # Нужно задать в .env n8n_policy_check_webhook: str = "" n8n_file_upload_webhook: str = "" - n8n_create_contact_webhook: str = "" - n8n_create_claim_webhook: str = "" + n8n_create_contact_webhook: str = "https://n8n.clientright.pro/webhook/511fde97-88bb-4fb4-bea5-cafdc364be27" + n8n_create_claim_webhook: str = "https://n8n.clientright.pro/webhook/d5bf4ca6-9e44-44b9-9714-3186ea703e7d" # ============================================ # LOGGING diff --git a/backend/app/services/sms_service.py b/backend/app/services/sms_service.py index 388c099..29ea1d1 100644 --- a/backend/app/services/sms_service.py +++ b/backend/app/services/sms_service.py @@ -65,11 +65,17 @@ class SMSService: logger.warning("SMS отправка отключена в конфигурации") return False + # 🔧 DEV: ПРИНУДИТЕЛЬНО ОТКЛЮЧЕНА ОТПРАВКА SMS + # Раскомментировать для продакшена! + logger.info(f"🔧 DEV MODE: SMS to {phone} ЗАБЛОКИРОВАНА (экономим бюджет!)") + logger.info(f"📱 Message: {message}") + return True + # DEBUG MODE: Не отправляем реальные SMS, экономим бюджет - if settings.debug or settings.app_env == "development": - logger.info(f"🔧 DEBUG MODE: SMS to {phone} not sent (saving money!)") - logger.info(f"📱 Message would be: {message}") - return True + # if settings.debug or settings.app_env == "development": + # logger.info(f"🔧 DEBUG MODE: SMS to {phone} not sent (saving money!)") + # logger.info(f"📱 Message would be: {message}") + # return True try: # Получаем актуальный токен diff --git a/frontend/src/components/form/Step1Phone.tsx b/frontend/src/components/form/Step1Phone.tsx index f3dda4d..3a44345 100644 --- a/frontend/src/components/form/Step1Phone.tsx +++ b/frontend/src/components/form/Step1Phone.tsx @@ -352,7 +352,18 @@ export default function Step1Phone({ icon={} onClick={() => { if (debugCode) { - navigator.clipboard.writeText(debugCode); + // Fallback для HTTP (clipboard API требует HTTPS) + if (navigator.clipboard && window.isSecureContext) { + navigator.clipboard.writeText(debugCode); + } else { + // Fallback: копируем через textarea + const textArea = document.createElement('textarea'); + textArea.value = debugCode; + document.body.appendChild(textArea); + textArea.select(); + document.execCommand('copy'); + document.body.removeChild(textArea); + } message.success('Код скопирован!'); } }} diff --git a/frontend/src/components/form/Step3Payment.tsx b/frontend/src/components/form/Step3Payment.tsx index a231394..43fb4b0 100644 --- a/frontend/src/components/form/Step3Payment.tsx +++ b/frontend/src/components/form/Step3Payment.tsx @@ -51,10 +51,17 @@ export default function Step3Payment({ throw new Error(`HTTP ${response.status}`); } - const banksData: Bank[] = await response.json(); + let banksData: Bank[] = await response.json(); + + // ✅ Фильтруем банки без названия + banksData = banksData.filter(bank => bank && bank.bankname && typeof bank.bankname === 'string'); // Сортируем по названию для удобства - banksData.sort((a, b) => a.bankname.localeCompare(b.bankname, 'ru')); + banksData.sort((a, b) => { + const nameA = (a.bankname || '').toString(); + const nameB = (b.bankname || '').toString(); + return nameA.localeCompare(nameB, 'ru'); + }); setBanks(banksData); addDebugEvent?.('banks', 'success', `✅ Загружено ${banksData.length} банков`, { count: banksData.length }); @@ -62,29 +69,31 @@ export default function Step3Payment({ // Если есть сохранённый bankName или bankId - восстанавливаем значения if (formData.bankName) { const foundBank = banksData.find(b => - b.bankname.toLowerCase() === formData.bankName.toLowerCase() || - b.bankname.toLowerCase().includes(formData.bankName.toLowerCase()) + b && b.bankname && ( + b.bankname.toLowerCase() === formData.bankName.toLowerCase() || + b.bankname.toLowerCase().includes(formData.bankName.toLowerCase()) + ) ); - if (foundBank) { + if (foundBank && foundBank.bankname) { updateFormData({ - bankId: foundBank.bankid, + bankId: foundBank.bankid || '', bankName: foundBank.bankname }); form.setFieldsValue({ - bankId: foundBank.bankid, + bankId: foundBank.bankid || '', bankName: foundBank.bankname }); } } else if (formData.bankId) { // Если есть только bankId, находим по ID - const foundBank = banksData.find(b => b.bankid === formData.bankId); - if (foundBank) { + const foundBank = banksData.find(b => b && b.bankid === formData.bankId); + if (foundBank && foundBank.bankname) { updateFormData({ - bankId: foundBank.bankid, + bankId: foundBank.bankid || '', bankName: foundBank.bankname }); form.setFieldsValue({ - bankId: foundBank.bankid, + bankId: foundBank.bankid || '', bankName: foundBank.bankname }); } @@ -414,7 +423,7 @@ export default function Step3Payment({ return Promise.resolve(); } const foundBank = banks.find(b => - b.bankname.toLowerCase() === value.toLowerCase() + b && b.bankname && b.bankname.toLowerCase() === value.toLowerCase() ); if (!foundBank) { return Promise.reject(new Error('Выберите банк из списка')); @@ -429,38 +438,40 @@ export default function Step3Payment({ size="large" loading={banksLoading} notFoundContent={banksLoading ? "Загрузка..." : "Банк не найден. Попробуйте ввести другое название"} - options={banks.map((bank) => ({ - value: bank.bankname, - label: bank.bankname, - }))} + options={banks + .filter(bank => bank && bank.bankname) + .map((bank) => ({ + value: bank.bankname, + label: bank.bankname, + }))} filterOption={(inputValue, option) => { if (!option?.label) return false; return option.label.toLowerCase().includes(inputValue.toLowerCase()); }} onSelect={(value) => { // При выборе из списка находим банк и сохраняем оба поля - const selectedBank = banks.find(b => b.bankname === value); - if (selectedBank) { + const selectedBank = banks.find(b => b && b.bankname && b.bankname === value); + if (selectedBank && selectedBank.bankname) { updateFormData({ - bankId: selectedBank.bankid, + bankId: selectedBank.bankid || '', bankName: selectedBank.bankname }); // Устанавливаем bankId в скрытое поле - form.setFieldsValue({ bankId: selectedBank.bankid }); + form.setFieldsValue({ bankId: selectedBank.bankid || '' }); } }} onChange={(value) => { // При вводе текста ищем точное совпадение по названию if (typeof value === 'string') { const foundBank = banks.find(b => - b.bankname.toLowerCase() === value.toLowerCase() + b && b.bankname && b.bankname.toLowerCase() === value.toLowerCase() ); - if (foundBank) { + if (foundBank && foundBank.bankname) { updateFormData({ - bankId: foundBank.bankid, + bankId: foundBank.bankid || '', bankName: foundBank.bankname }); - form.setFieldsValue({ bankId: foundBank.bankid }); + form.setFieldsValue({ bankId: foundBank.bankid || '' }); } else if (value === '') { // Если поле очищено, очищаем и bankId updateFormData({ bankId: undefined, bankName: undefined }); diff --git a/frontend/src/components/form/generateConfirmationFormHTML.ts b/frontend/src/components/form/generateConfirmationFormHTML.ts index aa1fd14..d17d28a 100644 --- a/frontend/src/components/form/generateConfirmationFormHTML.ts +++ b/frontend/src/components/form/generateConfirmationFormHTML.ts @@ -1064,17 +1064,7 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed: html += createMoneyField('project', 'agrprice', p.agrprice); html += '*

'; - // Период - html += '

Период: '; - if (p.startdate || p.finishdate) { - html += 'с '; - html += createDateField('project', 'startdate', p.startdate); - html += ' по '; - html += createDateField('project', 'finishdate', p.finishdate); - } else { - html += createField('project', 'period_text', p.period_text, 'Период действия'); - } - html += '

'; + // Период - УДАЛЕНО по требованию html += '
'; @@ -1685,11 +1675,49 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed: .then(function(banks) { console.log('Loaded ' + banks.length + ' banks'); + // ✅ Нормализуем данные: API возвращает bankId/bankName, приводим к bankid/bankname + if (banks.length > 0) { + console.log('🔍 Первый банк до нормализации:', JSON.stringify(banks[0])); + } + banks = banks.map(function(bank) { + if (!bank) return null; + return { + bankid: bank.bankId || bank.bankid || '', + bankname: bank.bankName || bank.bankname || '' + }; + }); + if (banks.length > 0 && banks[0]) { + console.log('🔍 Первый банк после нормализации:', JSON.stringify(banks[0])); + } + + // ✅ Фильтруем банки без названия и сортируем по названию + var initialCount = banks.length; + banks = banks.filter(function(bank) { + return bank && bank.bankname && typeof bank.bankname === 'string' && bank.bankname.trim() !== ''; + }); + console.log('✅ Фильтрация банков: было ' + initialCount + ', стало ' + banks.length); + + if (banks.length === 0) { + console.error('❌ Нет валидных банков после фильтрации!'); + Array.prototype.forEach.call(bankInputs, function(input) { + var datalistId = input.getAttribute('list'); + var datalist = document.getElementById(datalistId); + if (datalist) { + datalist.innerHTML = ''; + } + }); + return; + } + // Сортируем по названию banks.sort(function(a, b) { - return a.bankname.localeCompare(b.bankname, 'ru'); + const nameA = (a.bankname || '').toString(); + const nameB = (b.bankname || '').toString(); + return nameA.localeCompare(nameB, 'ru'); }); + console.log('✅ Банки отфильтрованы и отсортированы: ' + banks.length + ' шт.'); + // Сохраняем список банков глобально для поиска window.__banksList = banks; @@ -1703,19 +1731,29 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed: var currentBankName = ''; if (!datalist) { - console.error('Datalist not found for input:', input.id); + console.error('❌ Datalist not found for input:', input.id, 'datalistId:', datalistId); return; } + console.log('📋 Заполняю datalist для input:', input.id, 'datalistId:', datalistId); + // Очищаем datalist datalist.innerHTML = ''; // Заполняем datalist опциями + var optionsAdded = 0; banks.forEach(function(bank) { + // ✅ Проверяем наличие bankname перед использованием + if (!bank || !bank.bankname) { + console.warn('⚠️ Пропущен банк без названия:', bank); + return; + } + var option = document.createElement('option'); option.value = bank.bankname; - option.setAttribute('data-bank-id', bank.bankid); + option.setAttribute('data-bank-id', bank.bankid || ''); datalist.appendChild(option); + optionsAdded++; // Если это текущий банк, устанавливаем значение if (bank.bankid === currentBankId) { @@ -1723,6 +1761,22 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed: } }); + console.log('✅ Добавлено опций в datalist:', optionsAdded, 'для input:', input.id); + + // Проверяем, что опции действительно добавлены + var actualOptionsCount = datalist.querySelectorAll('option').length; + console.log('🔍 Проверка datalist:', { + datalistId: datalistId, + optionsAdded: optionsAdded, + actualOptionsInDOM: actualOptionsCount, + inputId: input.id, + inputListAttr: input.getAttribute('list') + }); + + if (actualOptionsCount === 0 && optionsAdded > 0) { + console.error('❌ КРИТИЧЕСКАЯ ОШИБКА: опции не добавлены в DOM!'); + } + // Устанавливаем текущее значение если есть if (currentBankName) { input.value = currentBankName; @@ -1744,17 +1798,17 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed: // Ищем точное совпадение if (inputValue) { foundBank = banks.find(function(b) { - return b.bankname.toLowerCase() === inputValue.toLowerCase(); + return b && b.bankname && b.bankname.toLowerCase() === inputValue.toLowerCase(); }); } - if (foundBank) { + if (foundBank && foundBank.bankname) { // Найден банк - сохраняем ID и название if (hiddenField) { - hiddenField.value = foundBank.bankid; + hiddenField.value = foundBank.bankid || ''; } state.user = state.user || {}; - state.user.bank_id = foundBank.bankid; + state.user.bank_id = foundBank.bankid || ''; state.user.bank_name = foundBank.bankname; // ✅ Сохраняем название банка this.classList.add('filled'); } else { @@ -1775,15 +1829,15 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed: input.addEventListener('change', function() { var inputValue = this.value.trim(); var foundBank = banks.find(function(b) { - return b.bankname.toLowerCase() === inputValue.toLowerCase(); + return b && b.bankname && b.bankname.toLowerCase() === inputValue.toLowerCase(); }); - if (foundBank) { + if (foundBank && foundBank.bankname) { if (hiddenField) { - hiddenField.value = foundBank.bankid; + hiddenField.value = foundBank.bankid || ''; } state.user = state.user || {}; - state.user.bank_id = foundBank.bankid; + state.user.bank_id = foundBank.bankid || ''; state.user.bank_name = foundBank.bankname; // ✅ Сохраняем название банка this.classList.add('filled'); updateFieldStyle(this); @@ -1793,12 +1847,23 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed: }) .catch(function(error) { console.error('Error loading banks:', error); + console.error('Error details:', { + message: error.message, + stack: error.stack, + name: error.name + }); + + // Показываем сообщение об ошибке пользователю Array.prototype.forEach.call(bankInputs, function(input) { var datalistId = input.getAttribute('list'); var datalist = document.getElementById(datalistId); if (datalist) { datalist.innerHTML = ''; } + // Показываем placeholder с ошибкой + if (input.placeholder) { + input.placeholder = 'Ошибка загрузки списка банков'; + } }); }); } diff --git a/frontend/src/pages/ClaimForm.tsx b/frontend/src/pages/ClaimForm.tsx index e0e5fd1..21acff6 100644 --- a/frontend/src/pages/ClaimForm.tsx +++ b/frontend/src/pages/ClaimForm.tsx @@ -108,7 +108,7 @@ export default function ClaimForm() { useEffect(() => { // 🔥 VERSION CHECK: Если видишь это в консоли - фронт обновился! - console.log('🔥 ClaimForm v3.8 - 2025-11-20 15:10 - Fix session_id priority in loadDraft'); + console.log('🔥 ClaimForm v3.9 - 2025-12-29 - Auto redirect to drafts after success'); }, []); // ✅ Восстановление сессии при загрузке страницы @@ -998,6 +998,42 @@ export default function ClaimForm() { setCurrentStep(1); // ✅ Переходим к описанию (индекс 1) }, [updateFormData, currentStep, isPhoneVerified, formData.unified_id, formData.phone]); + // ✅ Автоматический редирект на экран черновиков после успешной отправки + useEffect(() => { + if (isSubmitted) { + console.log('✅ Обращение успешно отправлено, ждём 2.5 секунды перед редиректом на черновики...'); + + const redirectTimer = setTimeout(async () => { + console.log('🔄 Выполняем редирект на экран черновиков'); + + // Проверяем наличие черновиков + const hasDraftsResult = await checkDrafts( + formData.unified_id, + formData.phone, + sessionIdRef.current + ); + + console.log('🔍 Результат проверки черновиков:', hasDraftsResult); + + // Переходим на экран черновиков + setShowDraftSelection(true); + setHasDrafts(hasDraftsResult); + setIsSubmitted(false); // Сбрасываем флаг отправки + setSelectedDraftId(null); // Сбрасываем выбранный черновик + + // Переходим на шаг 0 (черновики) + setTimeout(() => { + setCurrentStep(0); + console.log('✅ Переход на экран черновиков выполнен'); + }, 100); + }, 2500); // Задержка 2.5 секунды + + return () => { + clearTimeout(redirectTimer); + }; + } + }, [isSubmitted, formData.unified_id, formData.phone, checkDrafts]); + const handleSubmit = useCallback(async () => { try { addDebugEvent('form', 'info', '📤 Отправка заявки в n8n через backend'); @@ -1211,6 +1247,7 @@ export default function ClaimForm() { }); // Шаг подтверждения заявления (показывается после получения данных из claim:plan) + // ✅ НОВЫЙ ФЛОУ: StepClaimConfirmation с SMS подтверждением if (formData.showClaimConfirmation && formData.claimPlanData) { stepsArray.push({ title: 'Подтверждение', @@ -1225,25 +1262,26 @@ export default function ClaimForm() { /> ), }); + } else { + // ✅ СТАРЫЙ ФЛОУ: Step3Payment (только если нет StepClaimConfirmation) + // Используется как fallback, если данные claim:plan не получены + stepsArray.push({ + title: 'Заявление', + description: 'Подтверждение', + content: ( + + ), + }); } - // Последний шаг: Payment (всегда) - stepsArray.push({ - title: 'Заявление', - description: 'Подтверждение', - content: ( - - ), - }); - return stepsArray; }, [formData, isPhoneVerified, nextStep, prevStep, updateFormData, handleSubmit, setIsPhoneVerified, addDebugEvent, showDraftSelection, selectedDraftId, hasDrafts, handleSelectDraft, handleNewClaim, checkDrafts]); @@ -1290,10 +1328,38 @@ export default function ClaimForm() { // Удаляем session_token из localStorage localStorage.removeItem('session_token'); - // Сбрасываем форму - handleReset(); + // ✅ Полный сброс: очищаем все данные авторизации и черновиков + setIsSubmitted(false); + setShowDraftSelection(false); + setHasDrafts(false); + setSelectedDraftId(null); + + // ✅ Генерируем новую сессию для нового пользователя + const newSessionId = `sess-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; + sessionIdRef.current = newSessionId; + + // ✅ Полностью очищаем formData, включая unified_id и phone + setFormData({ + voucher: '', + claim_id: undefined, + session_id: newSessionId, + paymentMethod: 'sbp', + unified_id: undefined, // ✅ Очищаем unified_id + phone: undefined, // ✅ Очищаем phone + contact_id: undefined, // ✅ Очищаем contact_id + is_new_contact: undefined, + isPhoneVerified: false, + }); + + // ✅ Сбрасываем флаг верификации телефона + setIsPhoneVerified(false); + + // ✅ Переходим на экран входа (Step1Phone) + // Если showDraftSelection = false и нет unified_id, то шаг 0 будет Step1Phone + setCurrentStep(0); message.info('Сессия завершена. До свидания!'); + addDebugEvent('system', 'info', '🔄 Форма сброшена'); }, [formData.session_id, addDebugEvent]); return ( diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 474e8de..a8c8386 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -12,7 +12,7 @@ export default defineConfig({ port: 3000, proxy: { '/api': { - target: 'http://host.docker.internal:8200', + target: 'http://host.docker.internal:8201', changeOrigin: true, // SSE support configure: (proxy) => { @@ -24,7 +24,7 @@ export default defineConfig({ } }, '/events': { - target: 'http://host.docker.internal:8200', + target: 'http://host.docker.internal:8201', changeOrigin: true } }