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={
Период: '; - 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: ( +