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
}
}