Added console logs to track SMS code:
- Log SMS code when saveFormData is called
- Log payload with masked SMS code before sending to Redis
- This will help debug why SMS code might not appear in Redis channel
Files:
- frontend/src/components/form/StepClaimConfirmation.tsx
Added SMS code to form approval data:
- Frontend: Pass SMS code to saveFormData function
- Frontend: Include sms_code in payload sent to backend
- Backend: Include sms_code in Redis event data
This allows n8n workflow to access the verified SMS code for logging/audit purposes.
Files:
- frontend/src/components/form/StepClaimConfirmation.tsx
- backend/app/api/claims.py
Fixed warning: 'Instance created by useForm is not connected to any Form element'
- Removed resetFields() call before form is rendered
- Form is only rendered when smsCodeSent === true
- Added preserve={false} to Form to ensure clean state
- Updated handleCancelSMS to only reset if form was rendered
Files:
- frontend/src/components/form/StepClaimConfirmation.tsx
Fixed route conflict:
- Moved POST /approve route BEFORE GET /{claim_id}
- FastAPI processes routes in order, so specific routes must come before parameterized ones
- This fixes 405 Method Not Allowed error
Files:
- backend/app/api/claims.py
Fixed React Hooks order violation:
- Moved handleSendCode, handleVerifyCode, handleCancelSMS useCallback hooks BEFORE conditional render
- Changed handleCancelSMS to useCallback for consistency
- Moved phone/displayPhone calculations before conditional render
- All hooks now called in the same order on every render
This fixes the error: 'Rendered more hooks than during the previous render'
Files:
- frontend/src/components/form/StepClaimConfirmation.tsx
Changed from dynamic channel form_approve:{claim_id} to fixed channel:
- Channel: clientright:webform:approve
- Simpler n8n subscription (no need for dynamic channel name)
- All form approvals go to the same channel
Files:
- backend/app/api/claims.py
- frontend/src/components/form/StepClaimConfirmation.tsx
- docs/REDIS_FORM_APPROVE.md
Simplified approach:
- Removed backend endpoint /approve (will use direct webhook)
- Updated saveFormData to send data directly to n8n webhook
- Fire-and-forget approach: no waiting for response
- Show success message 'Ваше заявление отправлено!' after SMS verification
- Uses webhook URL: https://n8n.clientright.pro/webhook/eebe58d4-0bcd-4d09-9d62-39868b110960
Flow:
1. User confirms form → SMS modal appears
2. SMS code sent automatically
3. User enters code → verified
4. Data sent to webhook (fire-and-forget)
5. Success message shown
6. Navigate to next step
Files:
- frontend/src/components/form/StepClaimConfirmation.tsx
- backend/app/api/claims.py (removed /approve endpoint)
Implemented SMS verification modal before saving confirmed form data:
- Added SMS modal component in StepClaimConfirmation.tsx
- Intercept claim_confirmed event and show SMS modal
- Send SMS code automatically when form is confirmed
- Verify SMS code before proceeding to save data
- Phone number extracted from claimPlanData (applicant.phone, user.mobile, or phone)
- Added saveFormData placeholder for future implementation
Features:
- Modal with SMS code input field
- Auto-send SMS code on form confirmation
- Code validation (6 digits, numbers only)
- Resend code functionality
- Cancel option
- Proper error handling
TODO: Implement saveFormData function to save form data to backend/n8n
Files:
- frontend/src/components/form/StepClaimConfirmation.tsx
Changes:
1. Added green border styling for filled form fields (.filled class)
- Green border (#10b981) and light green background (#f0fdf4)
- Applied automatically when field has value
- Works for inputs, textareas, and checkboxes
2. Updated document display to use field_label instead of filename
- Changed ClaimForm.tsx to include field_label in attachments
- Updated normalizeData to use full attachments array with field_label
- Display shows field_label if available, falls back to filename
3. Added updateFieldStyle function
- Updates field styling on input, blur, and change events
- Automatically applies filled class when field has value
Files:
- frontend/src/pages/ClaimForm.tsx: Added field_label to attachments
- frontend/src/components/form/generateConfirmationFormHTML.ts:
- Added .filled CSS class with green border
- Added updateFieldStyle function
- Updated document display to use field_label
- Updated normalizeData to use attachments array
Problem:
- Form was empty because propertyName was not passed to iframe
- JavaScript code checked for injected.propertyName but it was undefined
- Only case (normalized) was passed, not the original propertyName
Solution:
- Added propertyName to caseJson object that gets embedded in HTML
- Now JavaScript code can access both case and propertyName
- This allows the form to properly display data from send_to_form_approve.draft
Files:
- frontend/src/components/form/generateConfirmationFormHTML.ts: Added propertyName to caseJson
Problem:
- Query only searched by ID: WHERE id = claim_id::uuid
- If record exists with different ID but same claim_id in payload,
it wasn't found and send_to_form_approve wasn't saved
Solution:
- Added claim_lookup CTE that searches both by ID and payload->>'claim_id':
WHERE c.id = claim_id::uuid OR c.payload->>'claim_id' = claim_id::text
- base now uses claim_lookup instead of direct query
- UPDATE uses claim_lookup.id instead of direct claim_id::uuid
- This ensures correct record is always used
Changes:
- Added claim_lookup CTE for record lookup
- base reads from claim_lookup instead of direct query
- UPDATE uses claim_lookup.id instead of claim_id::uuid
Files:
- docs/SQL_SEND_TO_FORM_APPROVE_FIXED.sql: Fixed version with claim_lookup
Error:
- missing FROM-clause entry for table "partial"
- claim_lookup CTE used partial.claim_id_str but didn't have FROM partial
Root Cause:
- In CTE claim_lookup, we referenced partial.claim_id_str in WHERE clause
- But didn't include FROM partial, so PostgreSQL couldn't find the table
Solution:
- Added FROM partial to claim_lookup CTE:
FROM clpr_claims c, partial
- Now partial is available in claim_lookup CTE
- Also prefixed columns with table aliases (c.id, c.payload) for clarity
Files:
- docs/SQL_CLAIMSAVE_FINAL_FIXED.sql: Added FROM partial to claim_lookup
Problem:
- Original query only searched by ID: WHERE c.id = partial.cid
- If record exists with different ID but same claim_id in payload,
it wasn't found and documents weren't updated
Solution:
- Added claim_lookup CTE that searches both by ID and payload->>'claim_id':
WHERE id::text = claim_id_str OR payload->>'claim_id' = claim_id_str
- Uses found ID from claim_lookup instead of partial.cid
- This ensures correct record is always used
Changes:
- partial now accepts $2::text (instead of $2::uuid)
- Added claim_lookup CTE for record lookup
- upd_claim uses claim_lookup.id instead of partial.cid
- docs uses claim_lookup.id instead of partial.cid
Files:
- docs/SQL_CLAIMSAVE_FINAL_FIXED.sql: Fixed version with claim_lookup
Error:
- column "created_at" does not exist
- There is a column named "created_at" in table "clpr_claims", but it cannot be referenced from this part of the query
Root Cause:
- existing_claim CTE only selected id and payload
- But INSERT query tried to use: (SELECT created_at FROM existing_claim)
- PostgreSQL couldn't find created_at in existing_claim CTE
Solution:
- Added created_at to existing_claim CTE SELECT clause
- Now created_at is available for use in INSERT query
Files:
- docs/SQL_CLAIMSAVE_UPSERT_SIMPLE.sql: Added created_at to existing_claim CTE
Problem:
- Multiple records created with same claim_id but different IDs
- Example: ID=0eb051ec... (correct) vs ID=b532b1b3... (duplicate)
- Different SQL queries used different approaches:
* Some used claim_id as UUID for ID (partial.claim_id_str::uuid)
* Others searched by payload->>'claim_id' and created new UUID if not found
Root Cause:
- SQL_CLAIMSAVE_UPSERT_SIMPLE.sql only searched by ID:
WHERE id = claim_id_str::uuid
- If record existed with different ID but same claim_id in payload,
it wasn't found and new record was created
Solution:
1. existing_claim now searches both by ID and payload->>'claim_id':
WHERE id = claim_id_str::uuid OR payload->>'claim_id' = claim_id_str
ORDER BY priority (ID match first), then updated_at DESC
2. INSERT uses ID from existing_claim if found:
COALESCE((SELECT id FROM existing_claim), partial.claim_id_str::uuid)
3. This ensures same record is always used, preventing duplicates
Files:
- docs/SQL_CLAIMSAVE_UPSERT_SIMPLE.sql: Fixed search logic and INSERT ID
Problem:
- Multiple records exist with same claim_id in DB
- One record (ID: 0eb051ec...) has send_to_form_approve.draft (updated: 2025-11-24)
- Another record (ID: b532b1b3...) doesn't have send_to_form_approve (updated: 2025-11-21)
- API query used LIMIT 1 without ORDER BY, could return wrong record
- Form was empty because wrong record (without send_to_form_approve) was returned
Solution:
- Added ORDER BY updated_at DESC to get latest record first
- This ensures we always get the record with send_to_form_approve.draft
- Latest record has correct data structure for confirmation form
Files:
- backend/app/api/claims.py: Added ORDER BY updated_at DESC
Problem:
- Data already exists in DB in propertyName format at payload.send_to_form_approve.draft
- We were trying to convert from wizard_answers instead of using existing data
- Form was empty because we ignored the correct data structure
Solution:
1. Added priority check for payload.send_to_form_approve.draft:
- If exists, use it directly (PRIORITY 1)
- If not, fall back to conversion from wizard_answers (PRIORITY 2)
2. Direct usage of send_to_form_approve.draft:
- Extract propertyName structure directly from draft
- Preserve all fields: applicant, case, contract_or_service, offenders, claim, meta, attachments
- Use unified_id from draft.meta or fallback to claim/formData
3. Enhanced logging:
- Logs if send_to_form_approve.draft found
- Logs draft data structure
- Logs result structure
Files:
- frontend/src/pages/ClaimForm.tsx: Use send_to_form_approve.draft directly
Problem:
- Data comes from DB in wizard_answers format (answers to wizard questions)
- Form confirmation expects propertyName format (structured data)
- transformDraftToClaimPlanFormat tried to extract propertyName from DB, but it doesn't exist
- Form shows empty because propertyName structure is empty
Solution:
1. Added complete field mapping from wizard_answers to propertyName:
- answersParsed.item → contract_or_service.subject
- answersParsed.price → contract_or_service.amount_paid (normalized)
- answersParsed.place_date → contract_or_service.agreement_date
- answersParsed.cancel_date → contract_or_service.period_start
- answersParsed.steps_taken → claim.description
- answersParsed.expectation → claim.reason
- payload.problem_description → claim.description and contract.subject
2. Added applicant data extraction:
- phone from claim.phone, payload.phone, body.phone, or formData.phone
- email from claim.email, payload.email, body.email, or formData.email
- Other applicant fields (FIO, birth_date, INN) will be filled in confirmation form
3. Added default values:
- case.category = 'consumer'
- case.direction = 'web_form'
4. Enhanced logging:
- Logs what data exists in DB
- Logs wizard_answers keys
- Logs conversion process
Files:
- frontend/src/pages/ClaimForm.tsx: Complete wizard_answers conversion
Problem:
- Data comes from DB in wizard_answers format (answers to wizard questions)
- Form confirmation expects propertyName format (structured data: applicant, case, contract_or_service)
- transformDraftToClaimPlanFormat tried to extract propertyName data from DB, but it doesn't exist there
- Form shows empty because propertyName structure is empty
Solution:
1. Added wizard_answers parsing from multiple sources:
- body.answers, payload.answers
- body.wizard_answers, payload.wizard_answers
2. Added basic field mapping from wizard_answers to propertyName:
- answersParsed.item → contract_or_service.subject
- answersParsed.price → contract_or_service.amount_paid
- answersParsed.place_date → contract_or_service.agreement_date
- answersParsed.steps_taken → claim.description
3. Added logging to debug data flow:
- Logs what data exists in DB
- Logs wizard_answers parsing
- Logs conversion process
Note: This is a basic mapping. Full conversion requires complete field mapping from wizard_answers to propertyName structure.
Files:
- frontend/src/pages/ClaimForm.tsx: Added wizard_answers conversion
Problem:
- Line 77 tried to access formData.case.meta
- formData structure changed to propertyName format
- formData.case doesn't exist, causing runtime error
- Form shows blank screen due to crash
Solution:
- Removed incorrect formData.case.meta access
- Added proper logging for formData and propertyName structure
- Form should now render correctly
Files:
- frontend/src/components/form/StepClaimConfirmation.tsx: Fixed formData access
Problem:
- When data comes as { propertyName: {...}, ... }, dataCandidate was set to propertyName object
- normalizeData expects { propertyName: {...} } format, not just propertyName
- Data normalization failed because structure didn't match
Solution:
- Changed logic to use entire injected object when propertyName exists
- This ensures normalizeData receives correct structure
- Data should now normalize correctly and populate form fields
Files:
- frontend/src/components/form/generateConfirmationFormHTML.ts: Fixed data extraction logic
Problem:
- Data was being passed in normalized 'case' format
- generateConfirmationFormHTML expects propertyName format for normalization
- Form fields were empty because normalization didn't work correctly
Solution:
- Changed StepClaimConfirmation to pass data in propertyName format
- This matches the structure expected by normalizeData function
- Function can now properly normalize data from propertyName to case format
- All fields should now populate correctly
Files:
- frontend/src/components/form/StepClaimConfirmation.tsx: Changed data format to propertyName
Problem:
- Form iframe was too small (800px fixed height)
- Only spinner visible, form content not fully visible
Solution:
1. Increased iframe container:
- Changed Card bodyStyle to use calc(100vh - 200px)
- Set minHeight to 800px
- Made iframe flex: 1 to fill available space
2. Added auto-resize functionality:
- Form sends iframe_resize messages with height
- Parent component listens and adjusts iframe height
- ResizeObserver watches for content changes
- Window resize handler updates iframe size
3. Improved iframe styling:
- Added sandbox attributes for security
- Made height responsive (100% of container)
- Minimum height ensures form is always visible
Files:
- frontend/src/components/form/StepClaimConfirmation.tsx: Increased iframe size, added resize handler
- frontend/src/components/form/generateConfirmationFormHTML.ts: Added auto-resize messaging
Problem:
- caseObj was declared twice: once at line 6 and again at line 270
- Caused esbuild compilation error
Solution:
- Removed first declaration at line 6
- Updated smsInputData extraction to handle multiple data formats
- caseObj is now only declared after normalization at line 270
Files:
- frontend/src/components/form/generateConfirmationFormHTML.ts: Fixed duplicate declaration
Problem:
- Form didn't handle data in propertyName format from n8n
- Missing normalization logic from n8n Code node
Solution:
1. Added normalizeData function in TypeScript:
- Handles propertyName format (new format from n8n)
- Handles old format (applicant, case, contract_or_service)
- Handles case format (current structure)
- Converts field names (first_name -> firstname, etc.)
- Normalizes money values
- Extracts attachments_names
2. Added normalizeData function in JavaScript:
- Same logic as TypeScript version
- Handles data extraction from multiple sources:
* propertyName (object or string)
* value
* data
* output
* case
- Supports array format (takes first element)
3. Enhanced data extraction logic:
- Checks multiple data sources
- Handles both array and object formats
- Merges SMS meta data from injected
- Ensures base structures exist
Files:
- frontend/src/components/form/generateConfirmationFormHTML.ts: Added normalization logic
Problem:
- Form was simplified and didn't match n8n implementation
- Missing validation, button state updates, checkbox handling
- Missing full statement text with consent checkbox
Solution:
1. Added complete renderStatement function:
- Возмещение section with SBP phone display
- Full ЗАЯВЛЕНИЕ text with all fields inline
- Period fields (startdate/finishdate)
- Full offender fields (email, phone, website)
- Consent checkbox for privacy policy
- Attachments list display
2. Added validation functions:
- validateAllFields - checks all required fields
- updateSubmitButton - updates button state based on consent and validation
- Field validation helpers (isValidPhone, isValidEmail, isValidINN, etc.)
3. Enhanced attachBindings:
- Date field conversion (YYYY-MM-DD <-> DD.MM.YYYY)
- Money field comma to dot replacement
- INN field filtering (numeric only, length limits)
- Checkbox handling for privacy consent
- Phone display update in SBP section
4. Added checkbox styles and createCheckbox function:
- Custom checkbox styling matching n8n
- Required checkbox error states
- Proper label and link styling
5. Improved initialization:
- Better error handling
- Field validation on load
- Delegated INN filtering
- Proper state initialization
Files:
- frontend/src/components/form/generateConfirmationFormHTML.ts: Complete form implementation
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
Problem:
- When uploading files on Step 3, wizard_plan was reset to NULL
- wizard_plan is created on Step 2 (StepDescription) and saved by claimsave_primary
- form_get workflow on Step 3 doesn't receive wizard_plan again
- Old SQL was overwriting wizard_plan with NULL
Solution:
- Add 'existing_claim' CTE to read current payload from DB
- Modified all parsers to fallback to DB values if field not in incoming payload:
* wizard_plan_parsed - preserves generated wizard plan
* answers_prefill_parsed - preserves AI-generated prefill
* coverage_report_parsed - preserves coverage analysis
* ai_agent1_facts_parsed - preserves fact extraction
* ai_agent13_rag_parsed - preserves RAG analysis
* problem_description_parsed - preserves user description
Flow:
1. Step 2: User describes problem → claimsave_primary saves wizard_plan ✅
2. Step 3: User uploads files → form_get/claimsave preserves wizard_plan from DB ✅
File: docs/SQL_CLAIMSAVE_UPSERT_SIMPLE.sql
Next: Update n8n workflow 'form_get' node 'claimsave' with this SQL
Database changes:
- Add unified_id, contact_id, phone columns to clpr_claims table
- Create indexes for fast lookup by these fields
- Migrate existing data from payload to new columns
- SQL migration: docs/SQL_ALTER_CLPR_CLAIMS_ADD_FIELDS.sql
SQL improvements:
- Simplify claimsave query: remove complex claim_lookup logic
- Use UPSERT (INSERT ON CONFLICT) with known claim_id
- Always return claim (fix NULL issue)
- Store unified_id, contact_id, phone directly in table columns
- SQL: docs/SQL_CLAIMSAVE_UPSERT_SIMPLE.sql
Workflow enhancements:
- Add branch for form submissions WITHOUT files
- Create 6 new nodes: extract, prepare, save, redis, respond
- Separate flow for has_files=false in IF node
- Instructions: docs/N8N_FORM_GET_NO_FILES_INSTRUCTIONS.md
- Node config: docs/N8N_FORM_GET_NO_FILES_BRANCH.json
Migration stats:
- Total claims: 81
- With unified_id: 77
- Migrated from payload: 2
Next steps:
1. Add 6 nodes to n8n workflow form_get (ID: 8ZVMTsuH7Cmw7snw)
2. Connect TRUE branch of IF node to extract_webhook_data_no_files
3. Test form submission without files
4. Verify PostgreSQL save and Redis event
- Добавлены логи в frontend (ClaimForm.tsx) для отслеживания unified_id и запросов к API
- Добавлены логи в backend (claims.py) для отладки SQL запросов
- Создан лог сессии с описанием проблемы и текущего состояния
- Проблема: API возвращает 0 черновиков, хотя в БД есть данные
Изменения в /api/n8n/documents/attach:
✅ Принимает массив документов (не одиночный объект)
✅ Умная обработка S3 путей:
- /bucket/path → https://s3.twcstorage.ru/bucket/path
- bucket/path → https://s3.twcstorage.ru/bucket/path
- https://... → без изменений
✅ Поддержка обоих форматов полей:
- file / file_url
- filename / file_name
✅ Batch-обработка с детальной статистикой
✅ Возвращает результаты для каждого документа отдельно
✅ Логирование успешных и неуспешных операций
Формат ответа:
{
total_processed: N,
successful: M,
failed: K,
results: [...],
errors: [...]
}
Тесты:
- TEST_REAL_DATA.sh - тест с реальными данными из n8n
- TEST_QUICK.sh - быстрые тесты
Документация обновлена с примерами batch-обработки