Problem:
- Function was trying to normalize already normalized data
- Used undefined 'raw' variable after refactoring
- Data structure mismatch causing form not to render
Solution:
1. Removed redundant normalization logic:
- Data is already normalized in StepClaimConfirmation component
- Use caseObj directly from data.case
- Only ensure base structures exist (user, project, offenders, meta, attachments)
2. Fixed undefined variable:
- Changed raw.token to data.token
- Removed unused normalization code
3. Simplified data flow:
- Input: data.case (already normalized)
- Output: caseJson with same structure
- JavaScript in HTML uses injected.case directly
Files:
- frontend/src/components/form/generateConfirmationFormHTML.ts: Simplified normalization
Problem:
- User wants to use the provided n8n Code node structure for generating confirmation form HTML
- Current implementation doesn't match the expected structure
Solution:
1. Created separate file generateConfirmationFormHTML.ts:
- Implements data normalization from propertyName format
- Uses provided HTML template structure
- Includes field creation functions (createField, createDateField, etc.)
- Implements form rendering with editable inline fields
- Handles form data binding and submission via postMessage
2. Updated StepClaimConfirmation component:
- Imports generateConfirmationFormHTML from separate file
- Removed old inline HTML generation function
- Uses new structure-based generation
3. Data normalization:
- Transforms propertyName structure to form structure
- Maps applicant fields: first_name -> firstname
- Maps contract_or_service fields to project
- Maps claim fields to project (description, reason)
- Handles attachments and offenders arrays
4. Form features:
- Inline editable fields in statement text
- Date fields with calendar picker
- Money fields with рублей suffix
- Readonly fields for phone and category
- Form data collection and submission
Files:
- frontend/src/components/form/generateConfirmationFormHTML.ts: New file with form generation logic
- frontend/src/components/form/StepClaimConfirmation.tsx: Updated to use new generator
Problem:
- Only placeholder with Claim ID and Unified ID was shown
- No actual form for editing claim data
- User asked when the actual form will appear
Solution:
1. Created full HTML form with editable fields:
- Applicant data (name, birthday, birthplace, INN, address, phone, email)
- Case data (category, subject, date, amount, reason, description)
- Offender data (organization name, address, INN, OGRN, contacts)
- Attachments list (read-only display)
2. Fixed data mapping:
- Transform propertyName structure to form structure
- Map applicant fields: first_name -> firstname, etc.
- Map contract_or_service fields to project
- Map claim fields to project (description, reason)
3. Form features:
- All fields are editable except phone (read-only)
- Category is read-only (set by system)
- Form collects edited values on confirmation
- Sends form data back via postMessage
4. Removed n8n webhook call:
- HTML form is generated locally in React component
- No external dependencies for form generation
Files:
- frontend/src/components/form/StepClaimConfirmation.tsx: Full form implementation
Problem:
- Claim ID and Unified ID showing as 'не указан' in confirmation form
- transformDraftToClaimPlanFormat was returning array instead of object
- StepClaimConfirmation was not correctly extracting IDs from claimPlanData
Solution:
1. Changed transformDraftToClaimPlanFormat return type:
- Changed from array [{ propertyName, ... }] to object { propertyName, ... }
- This matches what StepClaimConfirmation expects
2. Enhanced ID extraction in StepClaimConfirmation:
- Added explicit claimId and unifiedId variables
- Better fallback chain: claimPlanData.claim_id -> propertyName.meta.claim_id
- Same for unified_id
3. Added comprehensive logging:
- Log claimPlanData structure on component mount
- Log extracted IDs before form generation
- Log transformDraftToClaimPlanFormat input/output
- Log claim.unified_id from API response
4. Improved data flow:
- claim.unified_id from API -> transformDraftToClaimPlanFormat -> StepClaimConfirmation
- Fallback to currentFormData.unified_id if claim.unified_id missing
Files:
- frontend/src/pages/ClaimForm.tsx: Changed return type, added logging
- frontend/src/components/form/StepClaimConfirmation.tsx: Enhanced ID extraction, added logging
Problem:
- problem_description not found in payload, but exists in draft list
- Completeness check fails because hasDescription = false
- Draft not recognized as ready for confirmation
Solution:
1. Enhanced problem_description extraction:
- Checks multiple locations: body.problem_description, payload.problem_description
- Also checks payload.body.problem_description for nested structures
- Added fallback to body.description and payload.description
2. Improved completeness logic:
- If problem_description not found but wizard_plan and answers exist,
infer that description was entered (plan is generated from description)
- This handles cases where description exists but not in expected payload location
3. Better logging:
- Shows if problem_description was found directly or inferred
- Logs all payload keys for debugging
Logic:
- hasDescription = !!problemDescription || (!!wizardPlan && !!answers)
- If plan and answers exist → description was entered earlier
- This allows drafts with plan+answers+documents to proceed to confirmation
Files:
- frontend/src/pages/ClaimForm.tsx: Enhanced problem_description detection
Problem:
- When draft is fully filled, we subscribed to Redis SSE channel claim:plan
- But all data already exists in PostgreSQL database
- No need to wait for n8n to publish data - we can load it directly
Solution:
1. Removed subscribeToClaimPlanForDraft() function
- No longer subscribes to SSE channel for drafts
- Removed EventSource cleanup code
2. Added transformDraftToClaimPlanFormat() function
- Transforms draft data from DB format to propertyName format
- Extracts data from payload/body (telegram/web_form formats)
- Maps documents_meta to attachments array
- Formats applicant, case, contract_or_service, offenders, claim, meta
- Returns data in array format expected by confirmation form
3. Updated loadDraft() logic:
- When draft is ready for confirmation (all steps filled + draft status)
- Calls transformDraftToClaimPlanFormat() instead of subscribing to SSE
- Immediately shows confirmation form with data from DB
Flow:
1. User selects fully filled draft
2. System checks completeness (description, plan, answers, documents)
3. If ready → transforms DB data to propertyName format
4. Shows confirmation form immediately (no SSE wait)
Benefits:
- ✅ Faster: no waiting for n8n to publish data
- ✅ More reliable: data always available from DB
- ✅ Simpler: no SSE connection management for drafts
- ✅ Works offline: doesn't depend on Redis pub/sub
Files:
- frontend/src/pages/ClaimForm.tsx: Added transform function, removed SSE subscription
Problem:
- When user selects a draft with all steps filled (description, plan, answers, documents)
- But claim status is still 'draft' (not submitted)
- User has to manually navigate through all steps again
Solution:
1. Added check in loadDraft() to detect fully filled drafts:
- hasDescription: problem_description exists
- hasWizardPlan: wizard_plan exists
- hasAnswers: answers exist and not empty
- hasDocuments: documents_meta array has items
- isDraft: status_code === 'draft'
- allStepsFilled: all checks pass
2. When draft is ready for confirmation:
- Automatically subscribe to claim:plan SSE channel
- Wait for claim data from n8n
- Show loading message while waiting
- On success: show confirmation form automatically
3. Added subscribeToClaimPlanForDraft() function:
- Subscribes to /api/v1/claim-plan/{session_token}
- Handles claim_plan_ready event
- Updates formData with claimPlanData
- Auto-navigates to confirmation step via useEffect
4. Added useEffect for auto-navigation:
- Watches formData.showClaimConfirmation and formData.claimPlanData
- When both true, navigates to step 3 (confirmation)
- Handles cleanup of EventSource on unmount
Flow:
1. User selects draft → loadDraft() checks completeness
2. If all filled + draft → subscribeToClaimPlanForDraft()
3. SSE receives data → updates formData
4. useEffect detects → navigates to confirmation step
5. User sees confirmation form immediately
Files:
- frontend/src/pages/ClaimForm.tsx: Added auto-navigation logic
Problem:
- After wizard form submission, need to wait for claim data from n8n
- Claim data comes via Redis channel claim:plan:{session_token}
- Need to display confirmation form with claim data
Solution:
1. Backend: Added SSE endpoint /api/v1/claim-plan/{session_token}
- Subscribes to Redis channel claim:plan:{session_token}
- Streams claim data from n8n to frontend
- Handles timeouts and errors gracefully
2. Frontend: Added subscription to claim:plan channel
- StepWizardPlan: After form submission, subscribes to SSE
- Waits for claim_plan_ready event
- Shows loading message while waiting
- On success: saves claimPlanData and shows confirmation step
3. New component: StepClaimConfirmation
- Displays claim confirmation form in iframe
- Receives claimPlanData from parent
- Generates HTML form (placeholder - should call n8n for real HTML)
- Handles confirmation/cancellation via postMessage
4. ClaimForm: Added conditional step for confirmation
- Shows StepClaimConfirmation when showClaimConfirmation=true
- Step appears after StepWizardPlan
- Only visible when claimPlanData is available
Flow:
1. User fills wizard form → submits
2. Form data sent to n8n via /api/v1/claims/wizard
3. Frontend subscribes to SSE /api/v1/claim-plan/{session_token}
4. n8n processes data → publishes to Redis claim:plan:{session_token}
5. Backend receives → streams to frontend via SSE
6. Frontend receives → shows StepClaimConfirmation
7. User confirms → proceeds to next step
Files:
- backend/app/api/events.py: Added stream_claim_plan endpoint
- frontend/src/components/form/StepWizardPlan.tsx: Added subscribeToClaimPlan
- frontend/src/components/form/StepClaimConfirmation.tsx: New component
- frontend/src/pages/ClaimForm.tsx: Added confirmation step to steps array
- Добавлены логи в frontend (ClaimForm.tsx) для отслеживания unified_id и запросов к API
- Добавлены логи в backend (claims.py) для отладки SQL запросов
- Создан лог сессии с описанием проблемы и текущего состояния
- Проблема: API возвращает 0 черновиков, хотя в БД есть данные
Проблема:
- Step1Phone делал запрос НАПРЯМУЮ к n8n (палил webhook URL)
- В backend логах не было видно что n8n возвращает для контакта
- Нельзя было отследить contact_id, claim_id, is_new_contact
Решение:
✅ Добавлен endpoint /api/n8n/contact/create в n8n_proxy.py
✅ Step1Phone.tsx теперь использует proxy вместо прямого URL
✅ Backend логирует полный response от n8n (contact_id, claim_id и тд)
Теперь весь трафик к n8n идёт через backend proxy!
Проблема:
- TypeScript игнорировал project_id, is_new_project, ticket_id, ticket_number
- Они не были объявлены в interface FormData
Исправление:
✅ Добавлены в FormData:
- project_id?: string (ID проекта в vTiger)
- is_new_project?: boolean (флаг создания)
- ticket_id?: string (ID заявки HelpDesk)
- ticket_number?: string (номер заявки)
Теперь updateFormData корректно сохраняет все данные от n8n!
Проблема:
- n8n возвращает [{success: true, result: {claim_id, contact_id, ...}}]
- Код пытался взять data.claim_id вместо data.result.claim_id
Исправление:
- ✅ Обработка массива от n8n
- ✅ Извлечение данных из result: const result = crmResult.result || crmResult
- ✅ Улучшенное логирование для отладки
- ✅ Проверка crmResult.success перед обработкой
Теперь formData корректно получает:
- claim_id (от n8n)
- contact_id (от CreateWebContact)
- is_new_contact (флаг)
- ✅ Добавлена обработка массива от n8n (как в Step2EventType)
- ✅ Сохранение project_id, is_new_project в formData
- ✅ Сохранение contact_id для консистентности
- ✅ Работает как для найденного, так и для не найденного полиса
Теперь formData содержит полную информацию:
- claim_id (из n8n)
- contact_id (из CreateWebContact)
- project_id (из CreateWebProject) ← НОВОЕ
- is_new_project (флаг создания) ← НОВОЕ
- ✅ Новый endpoint: POST /api/n8n/claim/create
- ✅ Проксирует запросы к n8n webhook создания заявки
- ✅ Frontend теперь использует /api/n8n/claim/create вместо прямого URL
- ✅ Решает проблему CORS и скрывает webhook URL
- ✅ Логирование запросов и ошибок
- ✅ n8n может вернуть [{success: true, ...}] вместо {success: true, ...}
- ✅ Добавлена проверка Array.isArray и извлечение первого элемента
- ✅ Теперь корректно обрабатывается ответ от webhook создания заявки
- ✅ Вызов n8n webhook после выбора типа события
- ✅ Формирование title из event_type + voucher
- ✅ Передача всех данных: claim_id, contact_id, project_id, event_type
- ✅ Сохранение ticket_id и ticket_number в formData
- ✅ Loading состояние кнопки
- ✅ Debug события для отслеживания
- Step1Phone теперь вызывает n8n webhook после SMS верификации
- Webhook создаёт/находит контакт в CRM через CreateWebContact
- Возвращает: contact_id, claim_id, is_new_contact
- Данные сохраняются в formData для дальнейшей работы
- Исправлена нормализация телефона в sms_service (убираем +)
- Отключен rate limiting SMS для тестирования
- Backend подключён к внешнему Redis (crm.clientright.ru:6379)
- Добавлены поля contact_id, is_new_contact в FormData
- Frontend пересобран с новым кодом
- docker-compose.yml: убраны локальные postgres/redis, только внешние
- Frontend: телефон в формате 79001234567 (без +)
- Готово к интеграции с n8n webhook для создания контакта в CRM
- CreateWebContact: только создание или возврат ID, БЕЗ обновления
Проблема: Frontend ждал event_type='ocr_completed', но n8n отправлял 'policy_ocr_completed'
Из-за этого событие не обрабатывалось и показывалась ошибка
Решение: Гибкая проверка event_type:
- ocr_completed (старый формат)
- policy_ocr_completed (новый формат из n8n)
- любой *_ocr_completed (includes)
Теперь работает с любыми workflow которые шлют разные типы событий
Проблема: Frontend в Docker не мог достучаться до http://localhost:8100
Решение: Используем '/api/n8n/*' - Vite proxy автоматически перенаправит на backend
Изменения:
- Step1Policy.tsx: fetch('/api/n8n/policy/check')
- Step1Policy.tsx: fetch('/api/n8n/upload/file')
- StepDocumentUpload.tsx: fetch('/api/n8n/upload/file')
Vite proxy настроен в vite.config.ts:
/api -> host.docker.internal:8100
/events -> host.docker.internal:8100
- Создан n8n_proxy.py для безопасного проксирования запросов
- Webhook URLs перенесены в .env (скрыты от фронтенда)
- Frontend теперь использует /api/n8n/* endpoints
- Добавлена документация SECURITY_N8N_PROXY.md
Преимущества:
- Webhook URLs не видны в DevTools
- Централизованное логирование
- Возможность добавить rate limiting и auth
- Легко менять URLs без пересборки фронтенда
- Обернул nextStep, prevStep, updateFormData, handleSubmit в useCallback
- Теперь функции стабильны и не пересоздаются при ререндере
- nextStep и prevStep используют functional update для setState
- Добавлено логирование навигации: '⏩ nextStep' и '⏪ prevStep'
- Исправлены зависимости useMemo для steps
ПРОБЛЕМА: prevStep вызывался, но setCurrentStep не обновлял стейт
РЕШЕНИЕ: useCallback гарантирует что функции стабильны
- Добавлен disabled={false} для основной кнопки Назад
- Добавлен disabled={false} для DEV кнопок (Назад и Пропустить)
- Добавлено логирование при нажатии для отладки
- Теперь кнопки гарантированно кликабельны
- Изменён console.error на console.log для нормального закрытия SSE
- Теперь показывается '✅ SSE закрыто после получения результата - всё ОК'
- Реальная ошибка выводится только если данные не получены
- Консоль больше не пугает красными ошибками при успешной работе
- Upload: добавлен disabled во время загрузки
- Upload onChange: правильная обработка newFileList
- Кнопка Назад: убран disabled - теперь всегда доступна
- DEV кнопка Назад: также убран disabled
- Добавлено логирование handleUpload и onChange для отладки
- Создан Step2EventType.tsx для выбора типа страхового случая
- Создан StepDocumentUpload.tsx - универсальный компонент для загрузки одного документа
- Создан constants/documentConfigs.ts с конфигурацией документов для всех типов событий
- Переделан ClaimForm.tsx на динамическое создание шагов через useMemo
- Прогресс-бар теперь показывает: [Полис] → [Тип] → [Док1] → [Док2] → [Оплата]
- Бэкап старого Step2Details сохранён как Step2Details.OLD_WIZARD_INLINE.tsx
- Каждый документ загружается на отдельном шаге с модалкой обработки
- SSE для каждого документа с уникальным event_type
- DEV MODE кнопки для быстрой навигации на всех шагах
Проблема: После строки 657 весь код компонента дублировался,
что вызывало синтаксическую ошибку 'Unexpected token' на строке 659.
Решение: Удалены строки 659-820 (дубликат кода).
🎯 Изменения:
- Документы загружаются по очереди (один за другим)
- После загрузки каждого документа открывается модалка с крутилкой
- SSE слушает конкретный event_type: {file_type}_processed
- Модалка показывает результат распознавания с извлечёнными данными
- Кнопка 'Продолжить' → переход к следующему документу
- Опциональные документы можно пропустить
- После обработки всех обязательных → 'Далее на Step 3'
📊 UX флоу:
1. Выбор типа события → показываются нужные документы
2. Документ 1: Выбрать файл → Загрузить → Модалка → Результат → Продолжить
3. Документ 2: Выбрать файл → Загрузить → Модалка → Результат → Продолжить
4. Документ 3 (опц): Загрузить ИЛИ Пропустить
5. Все обязательные обработаны → Далее на Step 3
🔑 Каждый документ получает свой уникальный event_type:
- frontend отправляет file_type
- n8n возвращает event_type = {file_type}_processed
- frontend слушает этот конкретный event_type через SSE
🤖 Переход на OCR/AI для извлечения данных из документов:
✅ Изменения:
- Убран ручной ввод полей (дата, номер рейса и тд)
- Добавлена умная загрузка документов в зависимости от типа события
- Каждый тип документа получает уникальный file_type для n8n
- Валидация обязательных документов перед переходом
📋 Типы документов и их file_type:
1. Задержка рейса:
- flight_delay_boarding_or_ticket (обяз)
- flight_delay_confirmation (обяз)
2. Отмена рейса:
- flight_cancel_ticket (обяз)
- flight_cancel_notice (обяз)
3. Пропуск стыковки:
- connection_arrival_boarding (обяз)
- connection_departure_boarding_or_ticket (обяз)
- connection_delay_proof (опц)
4. Поезд (задержка):
- train_ticket (обяз)
- train_delay_proof (обяз)
5. Поезд (отмена):
- train_ticket (обяз)
- train_cancel_proof (обяз)
6. Паром:
- ferry_ticket (обяз)
- ferry_delay_proof (обяз)
7. Запасной аэродром:
- emergency_boarding_or_ticket (обяз)
- emergency_landing_proof (обяз)
🔑 file_type позволяет n8n разделять потоки и применять разные AI промпты
для каждого типа документа.
Backup старой версии: Step2Details.OLD_MANUAL_INPUT.tsx
Создана копия Step2Details.OLD_MANUAL_INPUT.tsx перед переходом на
умную форму с автоматическим распознаванием документов через OCR/AI.
В случае необходимости можно вернуться к ручному вводу.
✅ При успешном распознавании полиса - кнопка 'Продолжить →' → переход на Step 2
❌ При ошибке распознавания - кнопка 'Загрузить другой файл' → возврат к форме загрузки
Проблема: Backend закрывает SSE после отправки события, браузер триггерит onerror,
фронтенд перезаписывал успешный результат сообщением 'Ошибка подключения к серверу'.
Решение: Проверяем в onerror что если уже получили результат (prev !== 'loading'),
не затираем его ошибкой.
🎯 Основные изменения:
Backend:
- Реализован SSE endpoint /events/{task_id} для real-time стриминга событий
- Интеграция Redis Pub/Sub для получения событий от n8n
- Исправлен путь к .env файлу (абсолютный путь)
- Убран префикс /api/v1 для events router
- Добавлено подробное логирование событий
Frontend:
- Переключён на Vite dev mode для работы proxy
- Настроен proxy /events -> backend:8100
- Реализована модалка с крутилкой при загрузке файла
- SSE клиент для получения OCR результатов в real-time
- Отображение результатов AI анализа в модалке
Docker:
- Frontend: изменён на npm run dev (Vite dev server)
- Добавлен host.docker.internal для доступа к backend
- Настроен proxy в docker-compose
Утилиты:
- monitor_redis_direct.py - мониторинг Redis Pub/Sub
- test_redis_publish_direct.py - тестирование публикации в Redis
🚀 Полная цепочка работает:
Frontend → Backend SSE → Redis Pub/Sub ← n8n → OCR/AI → Result