Compare commits
3 Commits
1a653f2154
...
73524465fd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73524465fd | ||
|
|
f7d27388a0 | ||
|
|
56516fdd7d |
99
GIT_STATUS.md
Normal file
99
GIT_STATUS.md
Normal file
@@ -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
|
||||
|
||||
@@ -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)}")
|
||||
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
# Получаем актуальный токен
|
||||
|
||||
39
docker-compose.dev.yml
Normal file
39
docker-compose.dev.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
aiform_frontend_dev:
|
||||
container_name: aiform_frontend_dev
|
||||
build: ./frontend
|
||||
ports:
|
||||
- "5177:3000"
|
||||
environment:
|
||||
- VITE_API_URL=http://localhost:8201
|
||||
- NODE_ENV=development
|
||||
volumes:
|
||||
- ./frontend/src:/app/src:ro
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
networks:
|
||||
- aiform-dev-network
|
||||
restart: unless-stopped
|
||||
|
||||
aiform_backend_dev:
|
||||
container_name: aiform_backend_dev
|
||||
build: ./backend
|
||||
ports:
|
||||
- "8201:8200"
|
||||
env_file:
|
||||
- ./backend/.env
|
||||
environment:
|
||||
- APP_ENV=development
|
||||
- DEBUG=true
|
||||
networks:
|
||||
- aiform-dev-network
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
aiform-dev-network:
|
||||
driver: bridge
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState } from 'react';
|
||||
import { Form, Input, Button, message, Space } from 'antd';
|
||||
import { PhoneOutlined, SafetyOutlined } from '@ant-design/icons';
|
||||
import { Form, Input, Button, message, Space, Modal } from 'antd';
|
||||
import { PhoneOutlined, SafetyOutlined, CopyOutlined } from '@ant-design/icons';
|
||||
|
||||
interface Props {
|
||||
formData: any;
|
||||
@@ -23,6 +23,8 @@ export default function Step1Phone({
|
||||
const [codeSent, setCodeSent] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [verifyLoading, setVerifyLoading] = useState(false);
|
||||
const [debugCode, setDebugCode] = useState<string | null>(null);
|
||||
const [showDebugModal, setShowDebugModal] = useState(false);
|
||||
|
||||
const sendCode = async () => {
|
||||
try {
|
||||
@@ -49,7 +51,12 @@ export default function Step1Phone({
|
||||
message.success('Код отправлен на ваш телефон');
|
||||
setCodeSent(true);
|
||||
updateFormData({ phone });
|
||||
// DEBUG код не показываем в продакшене
|
||||
|
||||
// 🔧 DEV MODE: показываем debug код в модалке
|
||||
if (result.debug_code) {
|
||||
setDebugCode(result.debug_code);
|
||||
setShowDebugModal(true);
|
||||
}
|
||||
} else {
|
||||
addDebugEvent?.('sms', 'error', `❌ Ошибка SMS: ${result.detail}`, { error: result.detail });
|
||||
message.error(result.detail || 'Ошибка отправки кода');
|
||||
@@ -334,7 +341,58 @@ export default function Step1Phone({
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
{/* DEV MODE секция удалена для продакшена */}
|
||||
{/* 🔧 DEV MODE: Модалка с SMS кодом */}
|
||||
<Modal
|
||||
title="🔧 DEV MODE - SMS Код"
|
||||
open={showDebugModal}
|
||||
onCancel={() => setShowDebugModal(false)}
|
||||
footer={[
|
||||
<Button
|
||||
key="copy"
|
||||
icon={<CopyOutlined />}
|
||||
onClick={() => {
|
||||
if (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('Код скопирован!');
|
||||
}
|
||||
}}
|
||||
>
|
||||
Скопировать
|
||||
</Button>,
|
||||
<Button key="close" type="primary" onClick={() => setShowDebugModal(false)}>
|
||||
Закрыть
|
||||
</Button>
|
||||
]}
|
||||
>
|
||||
<div style={{ textAlign: 'center', padding: '20px 0' }}>
|
||||
<p style={{ marginBottom: 16, color: '#666' }}>
|
||||
Это DEV режим. SMS не отправляется реально.
|
||||
</p>
|
||||
<div style={{
|
||||
fontSize: 32,
|
||||
fontWeight: 'bold',
|
||||
fontFamily: 'monospace',
|
||||
background: '#f5f5f5',
|
||||
padding: '16px 32px',
|
||||
borderRadius: 8,
|
||||
display: 'inline-block',
|
||||
letterSpacing: 8
|
||||
}}>
|
||||
{debugCode}
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -1064,17 +1064,7 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed:
|
||||
html += createMoneyField('project', 'agrprice', p.agrprice);
|
||||
html += '<span class="required-marker">*</span></p>';
|
||||
|
||||
// Период
|
||||
html += '<p><strong>Период:</strong> ';
|
||||
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 += '</p>';
|
||||
// Период - УДАЛЕНО по требованию
|
||||
|
||||
html += '<div class="section-break"></div>';
|
||||
|
||||
@@ -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 = '<option value="">Ошибка: нет валидных банков</option>';
|
||||
}
|
||||
});
|
||||
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 = '<option value="">Ошибка загрузки банков. Обновите страницу.</option>';
|
||||
}
|
||||
// Показываем placeholder с ошибкой
|
||||
if (input.placeholder) {
|
||||
input.placeholder = 'Ошибка загрузки списка банков';
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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: (
|
||||
<Step3Payment
|
||||
formData={formData}
|
||||
updateFormData={updateFormData}
|
||||
onPrev={prevStep}
|
||||
onSubmit={handleSubmit}
|
||||
isPhoneVerified={isPhoneVerified}
|
||||
setIsPhoneVerified={setIsPhoneVerified}
|
||||
addDebugEvent={addDebugEvent}
|
||||
/>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
// Последний шаг: Payment (всегда)
|
||||
stepsArray.push({
|
||||
title: 'Заявление',
|
||||
description: 'Подтверждение',
|
||||
content: (
|
||||
<Step3Payment
|
||||
formData={formData} // ✅ claim_id уже в formData
|
||||
updateFormData={updateFormData}
|
||||
onPrev={prevStep}
|
||||
onSubmit={handleSubmit}
|
||||
isPhoneVerified={isPhoneVerified}
|
||||
setIsPhoneVerified={setIsPhoneVerified}
|
||||
addDebugEvent={addDebugEvent}
|
||||
/>
|
||||
),
|
||||
});
|
||||
|
||||
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 (
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user