2025-11-24 15:32:08 +03:00
// Функция генерации HTML формы подтверждения заявления
// Основана на структуре из n8n Code node "Mini-app Подтверждение данных"
export function generateConfirmationFormHTML ( data : any ) : string {
2025-11-24 15:38:24 +03:00
// Данные уже нормализованы в компоненте, используем их напрямую
const caseObj = data . case || { } ;
// Извлекаем SMS данные
const smsInputData = {
2025-11-24 15:32:08 +03:00
prefix : data.sms_meta?.prefix || '' ,
2025-11-24 15:38:24 +03:00
session_token : data.session_token || '' ,
2025-11-24 15:32:08 +03:00
telegram_id : data.telegram_id || '' ,
claim_id : data.case?.meta?.claim_id || '' ,
unified_id : data.case?.meta?.unified_id || '' ,
user_id : data.case?.meta?.user_id || '' ,
2025-11-24 15:38:24 +03:00
status : data.case?.meta?.status || '' ,
2025-11-24 15:32:08 +03:00
} ;
// Утилиты
function safeGet ( . . . keys : any [ ] ) : any {
for ( const k of keys ) {
if ( k === undefined || k === null ) continue ;
if ( typeof k === 'object' ) {
if ( Object . keys ( k ) . length ) return k ;
continue ;
}
const s = String ( k ) . trim ( ) ;
if ( s !== '' ) return k ;
}
return '' ;
}
function tryParseJSON ( x : any ) : any {
try {
return typeof x === 'string' ? JSON . parse ( x ) : x ;
} catch {
return null ;
}
}
function escapeHtml ( str : any ) : string {
if ( str === undefined || str === null ) return '' ;
return String ( str )
. replace ( /&/g , '&' )
. replace ( /</g , '<' )
. replace ( />/g , '>' )
. replace ( /"/g , '"' )
. replace ( /'/g , ''' ) ;
}
// Нормализация денежной суммы
function normalizeMoney ( rawValue : any ) : number | null {
if ( ! rawValue ) return null ;
let s = String ( rawValue ) ;
s = s . replace ( /\s+/g , '' ) ;
s = s . replace ( /р у б (лей|ль|\.)?/gi , '' ) ;
s = s . replace ( /₽|р \.|р $/gi , '' ) ;
s = s . replace ( /[^0-9,.-]/g , '' ) ;
s = s . replace ( ',' , '.' ) ;
if ( ! /^-?[0-9]+(\.[0-9]{1,2})?$/ . test ( s ) ) return null ;
const result = parseFloat ( s ) ;
return result > 0 ? result : null ;
}
// Базовые схемы
const baseUser = {
firstname : null ,
secondname : null ,
lastname : null ,
mobile : null ,
email : null ,
tgid : null ,
birthday : null ,
birthplace : null ,
mailingstreet : null ,
inn : null ,
} ;
const baseProject = {
category : null ,
direction : null ,
agrprice : null ,
subject : null ,
agrdate : null ,
startdate : null ,
finishdate : null ,
period_text : null ,
description : null ,
reason : null ,
} ;
const baseOffender = {
accountname : null ,
address : null ,
email : null ,
website : null ,
phone : null ,
inn : null ,
ogrn : null ,
} ;
2025-11-24 15:58:17 +03:00
// Функция нормализации данных из формата propertyName в формат case
function normalizeData ( data : any ) : any {
console . log ( '=== НОРМАЛИЗАЦИЯ ДАННЫХ ===' ) ;
console . log ( 'Input data:' , data ) ;
console . log ( 'Has propertyName:' , ! ! data . propertyName ) ;
console . log ( 'Has applicant in propertyName:' , ! ! ( data . propertyName && data . propertyName . applicant ) ) ;
// Если данные приходят в новом формате (с propertyName)
if ( data . propertyName && data . propertyName . applicant ) {
console . log ( 'Using NEW format with propertyName' ) ;
const props = data . propertyName ;
const applicant = props . applicant || { } ;
const caseData = props . case || { } ;
const contract = props . contract_or_service || { } ;
const offenders = props . offenders || [ ] ;
const claim = props . claim || { } ;
const meta = props . meta || { } ;
console . log ( '=== ОТЛАДКА К О Н Т Р А К Т А ===' ) ;
console . log ( 'contract_or_service:' , contract ) ;
console . log ( 'subject:' , contract . subject ) ;
console . log ( 'agreement_date_fmt:' , contract . agreement_date_fmt ) ;
console . log ( 'agreement_date:' , contract . agreement_date ) ;
console . log ( 'period_start_fmt:' , contract . period_start_fmt ) ;
console . log ( 'period_end_fmt:' , contract . period_end_fmt ) ;
// Получаем список приложенных документов
const attachments = props . attachments_names || [ ] ;
console . log ( '=== ОТЛАДКА ПРИЛОЖЕНИЙ ===' ) ;
console . log ( 'attachments_names:' , attachments ) ;
return {
user : {
firstname : applicant.first_name || null ,
secondname : applicant.middle_name || null ,
lastname : applicant.last_name || null ,
mobile : applicant.phone || null ,
email : applicant.email || null ,
birthday : applicant.birth_date_fmt || applicant . birth_date || null ,
birthplace : applicant.birth_place || null ,
mailingstreet : applicant.address || null ,
inn : applicant.inn || null ,
tgid : null ,
} ,
project : {
category : caseData.category || null ,
direction : caseData.direction || null ,
agrprice : normalizeMoney ( contract . amount_paid_fmt || contract . amount_paid ) || null ,
subject : contract.subject || null ,
agrdate : contract.agreement_date_fmt || contract . agreement_date || null ,
startdate : contract.period_start_fmt || contract . period_start || null ,
finishdate : contract.period_end_fmt || contract . period_end || null ,
period_text : contract.period_text || null ,
description : claim.description || null ,
reason : claim.reason || caseData . category || null ,
} ,
attachments : attachments ,
offenders : offenders.map ( ( o : any ) = > ( {
accountname : o.name || null ,
address : o.address || null ,
email : o.email || null ,
website : o.website || null ,
phone : o.phone || null ,
inn : o.inn || null ,
ogrn : o.ogrn || null ,
} ) ) ,
meta : Object.assign ( { } , meta , {
session_token : data.session_token || meta . claim_id || null ,
prefix : data.prefix || null ,
telegram_id : data.telegram_id || null ,
claim_id : data.claim_id || meta . claim_id || null ,
unified_id : meta.unified_id || null ,
user_id : meta.user_id || null ,
} ) ,
} ;
}
// Если данные приходят в старом формате (прямо applicant, case, etc)
if ( data . applicant || data . case || data . contract_or_service ) {
const applicant = data . applicant || { } ;
const caseData = data . case || { } ;
const contract = data . contract_or_service || { } ;
const offenders = data . offenders || [ ] ;
const claim = data . claim || { } ;
console . log ( '=== ОТЛАДКА К О Н Т Р А К Т А (старый формат) ===' ) ;
console . log ( 'contract_or_service:' , contract ) ;
console . log ( 'subject:' , contract . subject ) ;
console . log ( 'agreement_date_fmt:' , contract . agreement_date_fmt ) ;
return {
user : {
firstname : applicant.first_name || null ,
secondname : applicant.middle_name || null ,
lastname : applicant.last_name || null ,
mobile : applicant.phone || null ,
email : applicant.email || null ,
birthday : applicant.birth_date_fmt || applicant . birth_date || null ,
birthplace : applicant.birth_place || null ,
mailingstreet : applicant.address || null ,
inn : applicant.inn || null ,
tgid : null ,
} ,
project : {
category : caseData.category || null ,
direction : caseData.direction || null ,
agrprice : normalizeMoney ( contract . amount_paid_fmt || contract . amount_paid ) || null ,
subject : contract.subject || null ,
agrdate : contract.agreement_date_fmt || contract . agreement_date || null ,
startdate : contract.period_start_fmt || contract . period_start || null ,
finishdate : contract.period_end_fmt || contract . period_end || null ,
period_text : contract.period_text || null ,
description : claim.description || null ,
reason : claim.reason || caseData . category || null ,
} ,
attachments : data.attachments_names || [ ] ,
offenders : offenders.map ( ( o : any ) = > Object . assign ( { } , baseOffender , o || { } ) ) ,
meta : data.meta || { } ,
} ;
}
// Если данные уже в формате case (наша текущая структура)
if ( data . case ) {
return data . case ;
}
// Старый формат (обратная совместимость)
return {
user : Object.assign ( { } , baseUser , tryParseJSON ( data . user ) || data . user || { } ) ,
project : Object.assign ( { } , baseProject , tryParseJSON ( data . project ) || data . project || { } ) ,
offenders : Array.isArray ( data . offenders ) ? data . offenders . map ( ( o : any ) = > Object . assign ( { } , baseOffender , o || { } ) ) : [ ] ,
meta : Object.assign ( { } , data . meta || { } ) ,
attachments : data.attachments || [ ] ,
} ;
}
// Нормализуем данные
let normalizedCaseObj : any ;
// Если данные приходят в формате propertyName (как из n8n)
if ( data . propertyName ) {
normalizedCaseObj = normalizeData ( data ) ;
} else if ( data . case ) {
// Данные уже в формате case
normalizedCaseObj = data . case ;
} else {
// Пытаемся нормализовать из любого формата
normalizedCaseObj = normalizeData ( data ) ;
}
2025-11-24 15:38:24 +03:00
// Убеждаемся, что есть базовые структуры
2025-11-24 15:58:17 +03:00
if ( ! normalizedCaseObj . offenders || ! normalizedCaseObj . offenders . length ) {
normalizedCaseObj . offenders = [ Object . assign ( { } , baseOffender ) ] ;
2025-11-24 15:32:08 +03:00
}
2025-11-24 15:58:17 +03:00
if ( ! normalizedCaseObj . user ) normalizedCaseObj . user = Object . assign ( { } , baseUser , normalizedCaseObj . user || { } ) ;
if ( ! normalizedCaseObj . project ) normalizedCaseObj . project = Object . assign ( { } , baseProject , normalizedCaseObj . project || { } ) ;
if ( ! normalizedCaseObj . meta ) normalizedCaseObj . meta = { } ;
if ( ! normalizedCaseObj . attachments ) normalizedCaseObj . attachments = [ ] ;
2025-11-24 15:39:14 +03:00
// Нормализуем сумму, если она пришла в виде строки
2025-11-24 15:58:17 +03:00
if ( normalizedCaseObj . project && normalizedCaseObj . project . agrprice && typeof normalizedCaseObj . project . agrprice === 'string' ) {
const normalized = normalizeMoney ( normalizedCaseObj . project . agrprice ) ;
2025-11-24 15:39:14 +03:00
if ( normalized !== null ) {
2025-11-24 15:58:17 +03:00
normalizedCaseObj . project . agrprice = normalized ;
2025-11-24 15:39:14 +03:00
}
}
2025-11-24 15:58:17 +03:00
// Используем нормализованные данные
const caseObj = normalizedCaseObj ;
2025-11-24 15:32:08 +03:00
// Сервисные поля
2025-11-24 15:38:24 +03:00
const sessionToken = String ( safeGet ( caseObj . meta ? . session_token , data . session_token , '' ) ) ;
const telegramId = String ( safeGet ( caseObj . user ? . tgid , data . telegram_id , '' ) ) ;
2025-11-24 15:32:08 +03:00
const smsMetaData = {
session_token : String ( safeGet ( smsInputData . session_token , sessionToken , '' ) ) ,
prefix : String ( safeGet ( smsInputData . prefix , '' ) ) ,
telegram_id : String ( safeGet ( smsInputData . telegram_id , telegramId , '' ) ) ,
claim_id : String ( safeGet ( smsInputData . claim_id , '' ) ) ,
unified_id : String ( safeGet ( smsInputData . unified_id , '' ) ) ,
user_id : String ( safeGet ( smsInputData . user_id , '' ) ) ,
} ;
// Безопасно встраиваем данные в HTML
let caseJson = JSON . stringify ( {
case : caseObj ,
session_token : sessionToken ,
telegram_id : telegramId ,
2025-11-24 15:38:24 +03:00
token : data.token || '' ,
2025-11-24 15:32:08 +03:00
sms_meta : smsMetaData ,
} ) ;
caseJson = caseJson . replace ( /</g , '\\u003c' ) ;
// HTML шаблон (используем упрощенную версию из предоставленного кода)
// Полный код слишком большой, поэтому используем ключевые части
const html = ` <!doctype html>
< html lang = "ru" >
< head >
< meta charset = "utf-8" / >
< meta name = "viewport" content = "width=device-width,initial-scale=1" / >
< title > П о д т в е р ж д е н и е д а н н ы х < / title >
< script src = "https://telegram.org/js/telegram-web-app.js" > < / script >
< style >
* { box - sizing :border - box }
body {
font - family : - apple - system , BlinkMacSystemFont , 'Segoe UI' , Roboto , Helvetica , Arial , sans - serif ;
background :linear - gradient ( 135 deg , # 667 eea 0 % , # 764 ba2 100 % ) ;
margin :0 ; padding :0 ; min - height :100vh ;
color : # 1 f2937 ;
}
. wrap {
max - width :1400px ; margin :0 auto ; padding :20px ;
min - height :100vh ; display :flex ; flex - direction :column ;
}
. header {
text - align :center ; margin - bottom :30px ; color :white ;
}
. header h1 {
font - size :28px ; font - weight :700 ; margin :0 0 8 px ;
text - shadow :0 2 px 4 px rgba ( 0 , 0 , 0 , 0.3 ) ;
}
. header p {
font - size :16px ; opacity :0.9 ; margin :0 ;
}
. statement - container {
background : # fff ; border - radius :16px ; padding :32px ;
box - shadow :0 4 px 20 px rgba ( 0 , 0 , 0 , 0.08 ) ;
line - height :1.8 ; font - size :15px ;
max - width :800px ; margin :0 auto ;
}
. statement - text {
font - family : 'Times New Roman' , serif ;
text - align :justify ;
}
. inline - field {
display :inline - block ; min - width :120px ; max - width :300px ;
border :2px solid # e5e7eb ; border - radius :6px ;
padding :4px 8 px ; margin :0 2 px ; background : # fff ;
font - size :inherit ; font - family :inherit ;
transition :all 0.2 s ease ;
vertical - align :baseline ;
}
. inline - field :focus {
outline :none ; border - color : # 667 eea ;
box - shadow :0 0 0 2 px rgba ( 102 , 126 , 234 , 0.1 ) ;
background : # f8fafc ;
}
. inline - field :hover { border - color : # d1d5db }
. inline - field . large {
min - width :200px ; max - width :500px ;
}
. inline - field . full - width {
display :block ; width :100 % ; min - width :auto ; max - width :none ;
margin :8px 0 ; padding :8px 12 px ;
}
. readonly - field {
background - color : # f9fafb ! important ;
border - color : # d1d5db ! important ;
color : # 6 b7280 ! important ;
cursor :not - allowed ! important ;
font - weight :500 ;
}
. date - field {
min - width :140px ! important ;
max - width :160px ! important ;
cursor :pointer ;
}
. section - break {
margin :24px 0 ; border - top :1px solid # e5e7eb ;
padding - top :16px ;
}
2025-11-24 15:47:16 +03:00
. checkbox - container {
display :flex ;
align - items :flex - start ;
gap :8px ;
cursor :pointer ;
padding :12px ;
margin :8px 0 ;
border - radius :8px ;
transition :background - color 0.2 s ease ;
}
. checkbox - container :hover {
background - color :rgba ( 102 , 126 , 234 , 0.05 ) ;
}
. checkbox - container . required - checkbox {
border :2px solid # e5e7eb ;
}
. checkbox - container . required - checkbox . error {
border - color : # ef4444 ;
background - color : # fef2f2 ;
}
. checkbox - field {
width :18px ;
height :18px ;
margin :0 ;
cursor :pointer ;
accent - color : # 667 eea ;
}
. checkmark {
width :18px ;
height :18px ;
border :2px solid # d1d5db ;
border - radius :4px ;
position :relative ;
transition :all 0.2 s ease ;
flex - shrink :0 ;
}
. checkbox - field :checked + . checkmark {
background - color : # 667 eea ;
border - color : # 667 eea ;
}
. checkbox - field :checked + . checkmark : : after {
content : '✓' ;
position :absolute ;
color :white ;
font - size :12px ;
font - weight :bold ;
left :50 % ;
top :50 % ;
transform :translate ( - 50 % , - 50 % ) ;
}
. checkbox - field {
position :absolute ;
opacity :0 ;
cursor :pointer ;
}
. checkbox - label {
font - size :14px ;
line - height :1.5 ;
color : # 374151 ;
cursor :pointer ;
}
. checkbox - label a {
color : # 667 eea ;
text - decoration :none ;
}
. checkbox - label a :hover {
text - decoration :underline ;
}
. fade - in {
animation :fadeIn 0.3 s ease ;
}
@keyframes fadeIn {
from { opacity :0 ; transform :translateY ( 10 px ) }
to { opacity :1 ; transform :translateY ( 0 ) }
}
2025-11-24 15:32:08 +03:00
. buttons {
display :flex ; gap :12px ; margin - top :24px ;
flex - wrap :wrap ;
justify - content :center ;
}
. btn {
appearance :none ; border :0 ; border - radius :12px ;
padding :12px 24 px ; font - weight :600 ; cursor :pointer ;
font - size :14px ; transition :all 0.2 s ease ;
display :flex ; align - items :center ; gap :8px ;
text - decoration :none ; justify - content :center ;
min - width :120px ;
}
. btn :disabled {
opacity :0.6 ; cursor :not - allowed ;
}
. btn - primary {
background :linear - gradient ( 135 deg , # 667 eea , # 764 ba2 ) ;
color :white ; box - shadow :0 4 px 12 px rgba ( 102 , 126 , 234 , 0.4 ) ;
}
. btn - primary :hover : not ( : disabled ) {
transform :translateY ( - 1 px ) ;
box - shadow :0 6 px 16 px rgba ( 102 , 126 , 234 , 0.5 ) ;
}
. btn - secondary {
background : # f3f4f6 ; color : # 374151 ;
border :1px solid # d1d5db ;
}
. btn - secondary :hover : not ( : disabled ) {
background : # e5e7eb ; transform :translateY ( - 1 px ) ;
}
@media ( max - width :768px ) {
. wrap { padding :16px }
. buttons { flex - direction :column }
. btn { width :100 % }
. header h1 { font - size :24px }
}
< / style >
< / head >
< body >
< div class = "wrap" >
< div class = "header" >
< h1 > 📋 Р е д а к т и р о в а н и е з а я в л е н и я < / h1 >
< p > П р о в е р ь т е и п р и н е о б х о д и м о с т и о т р е д а к т и р у й т е в с е п о л я < / p >
< / div >
< div class = "statement-container fade-in" >
< div id = "statement" class = "statement-text" > З а г р у з к а … < / div >
< div class = "buttons" style = "margin-top:32px;justify-content:center" >
< button id = "confirmBtn" class = "btn btn-primary" >
✅ П о д т в е р д и т ь и о т п р а в и т ь
< / button >
< / div >
< div id = "status" style = "margin-top:16px" > < / div >
< / div >
< / div >
< script id = "case-data" type = "application/json" > $ { caseJson } < / script >
< script >
( function ( ) {
console . log ( '=== СКРИПТ ЗАПУЩЕН ===' ) ;
function getData ( ) {
try {
var dataEl = document . getElementById ( 'case-data' ) ;
if ( ! dataEl ) {
console . error ( 'Элемент #case-data не найден!' ) ;
return { } ;
}
var textContent = dataEl . textContent || '{}' ;
var parsed = JSON . parse ( textContent ) ;
console . log ( 'Parsed data:' , parsed ) ;
return parsed ;
} catch ( e ) {
console . error ( 'ОШИБКА ПАРСИНГА JSON:' , e ) ;
return { } ;
}
}
2025-11-24 15:58:17 +03:00
function tryParseJSON ( x ) {
try {
return typeof x === 'string' ? JSON . parse ( x ) : x ;
} catch {
return null ;
}
}
function normalizeMoney ( rawValue ) {
if ( ! rawValue ) return null ;
console . log ( 'normalizeMoney: входящее значение:' , rawValue , 'тип:' , typeof rawValue ) ;
var s = String ( rawValue ) ;
s = s . replace ( /\\s+/g , '' ) ;
s = s . replace ( /р у б (лей|ль|\\\\.)?/gi , '' ) ;
s = s . replace ( /₽|р \\\\.|р $/gi , '' ) ;
s = s . replace ( /[^0-9,.-]/g , '' ) ;
s = s . replace ( ',' , '.' ) ;
console . log ( 'normalizeMoney: после очистки:' , s ) ;
if ( ! /^-?[0-9]+(\\.[0-9]{1,2})?$/ . test ( s ) ) {
console . log ( 'normalizeMoney: невалидный формат после очистки' ) ;
return null ;
}
var result = parseFloat ( s ) ;
console . log ( 'normalizeMoney: результат:' , result ) ;
return result > 0 ? result : null ;
}
// Функция нормализации данных из формата propertyName в формат case
function normalizeData ( data ) {
console . log ( '=== НОРМАЛИЗАЦИЯ ДАННЫХ ===' ) ;
console . log ( 'Input data:' , data ) ;
// Если данные приходят в новом формате (с propertyName)
if ( data . propertyName && data . propertyName . applicant ) {
console . log ( 'Using NEW format with propertyName' ) ;
var props = data . propertyName ;
var applicant = props . applicant || { } ;
var caseData = props . case || { } ;
var contract = props . contract_or_service || { } ;
var offenders = props . offenders || [ ] ;
var claim = props . claim || { } ;
var meta = props . meta || { } ;
var attachments = props . attachments_names || [ ] ;
return {
user : {
firstname : applicant.first_name || null ,
secondname : applicant.middle_name || null ,
lastname : applicant.last_name || null ,
mobile : applicant.phone || null ,
email : applicant.email || null ,
birthday : applicant.birth_date_fmt || applicant . birth_date || null ,
birthplace : applicant.birth_place || null ,
mailingstreet : applicant.address || null ,
inn : applicant.inn || null ,
tgid : null ,
} ,
project : {
category : caseData.category || null ,
direction : caseData.direction || null ,
agrprice : normalizeMoney ( contract . amount_paid_fmt || contract . amount_paid ) || null ,
subject : contract.subject || null ,
agrdate : contract.agreement_date_fmt || contract . agreement_date || null ,
startdate : contract.period_start_fmt || contract . period_start || null ,
finishdate : contract.period_end_fmt || contract . period_end || null ,
period_text : contract.period_text || null ,
description : claim.description || null ,
reason : claim.reason || caseData . category || null ,
} ,
attachments : attachments ,
offenders : offenders.map ( function ( o ) {
return {
accountname : o.name || null ,
address : o.address || null ,
email : o.email || null ,
website : o.website || null ,
phone : o.phone || null ,
inn : o.inn || null ,
ogrn : o.ogrn || null ,
} ;
} ) ,
meta : Object.assign ( { } , meta , {
session_token : data.session_token || meta . claim_id || null ,
prefix : data.prefix || null ,
telegram_id : data.telegram_id || null ,
claim_id : data.claim_id || meta . claim_id || null ,
unified_id : meta.unified_id || null ,
user_id : meta.user_id || null ,
} ) ,
} ;
}
// Если данные приходят в старом формате (прямо applicant, case, etc)
if ( data . applicant || data . case || data . contract_or_service ) {
var applicant = data . applicant || { } ;
var caseData = data . case || { } ;
var contract = data . contract_or_service || { } ;
var offenders = data . offenders || [ ] ;
var claim = data . claim || { } ;
return {
user : {
firstname : applicant.first_name || null ,
secondname : applicant.middle_name || null ,
lastname : applicant.last_name || null ,
mobile : applicant.phone || null ,
email : applicant.email || null ,
birthday : applicant.birth_date_fmt || applicant . birth_date || null ,
birthplace : applicant.birth_place || null ,
mailingstreet : applicant.address || null ,
inn : applicant.inn || null ,
tgid : null ,
} ,
project : {
category : caseData.category || null ,
direction : caseData.direction || null ,
agrprice : normalizeMoney ( contract . amount_paid_fmt || contract . amount_paid ) || null ,
subject : contract.subject || null ,
agrdate : contract.agreement_date_fmt || contract . agreement_date || null ,
startdate : contract.period_start_fmt || contract . period_start || null ,
finishdate : contract.period_end_fmt || contract . period_end || null ,
period_text : contract.period_text || null ,
description : claim.description || null ,
reason : claim.reason || caseData . category || null ,
} ,
attachments : data.attachments_names || [ ] ,
offenders : offenders.map ( function ( o ) {
return {
accountname : o.name || o . accountname || null ,
address : o.address || null ,
email : o.email || null ,
website : o.website || null ,
phone : o.phone || null ,
inn : o.inn || null ,
ogrn : o.ogrn || null ,
} ;
} ) ,
meta : data.meta || { } ,
} ;
}
// Если данные уже в формате case (наша текущая структура)
if ( data . case ) {
return data . case ;
}
// Старый формат (обратная совместимость)
return {
user : tryParseJSON ( data . user ) || data . user || { } ,
project : tryParseJSON ( data . project ) || data . project || { } ,
offenders : Array.isArray ( data . offenders ) ? data . offenders : [ ] ,
meta : data.meta || { } ,
attachments : data.attachments || [ ] ,
} ;
}
2025-11-24 15:32:08 +03:00
function esc ( s ) {
if ( s === null || s === undefined ) return '' ;
return String ( s ) . replace ( /&/g , '&' ) . replace ( /</g , '<' ) . replace ( /"/g , '"' ) ;
}
function createField ( root , key , value , placeholder , index ) {
var id = 'field_' + root + '_' + key + '_' + ( index !== undefined ? index + '_' : '' ) + Math . random ( ) . toString ( 36 ) . slice ( 2 ) ;
var dataIndex = index !== undefined ? ' data-index="' + index + '"' : '' ;
var extra = '' ;
if ( key === 'inn' ) {
var isIndividual = ( root === 'user' ) ;
if ( isIndividual ) {
extra = ' inputmode="numeric" pattern="[0-9]{12}" maxlength="12" autocomplete="off"' ;
} else {
extra = ' inputmode="numeric" pattern="[0-9]{10,12}" maxlength="12" autocomplete="off"' ;
}
}
var fieldHtml = '<input class="inline-field bind" data-root="' + esc ( root ) + '" data-key="' + esc ( key ) + '"' + dataIndex +
' id="' + id + '" value="' + esc ( value || '' ) + '" placeholder="' + esc ( placeholder || '' ) + '"' + extra + ' />' ;
return fieldHtml ;
}
function createReadonlyField ( root , key , value ) {
var id = 'field_' + root + '_' + key + '_readonly_' + Math . random ( ) . toString ( 36 ) . slice ( 2 ) ;
return '<input class="inline-field readonly-field" data-root="' + esc ( root ) + '" data-key="' + esc ( key ) + '" id="' + id + '" value="' + esc ( value || '' ) + '" readonly />' ;
}
function createDateField ( root , key , value ) {
var id = 'field_' + root + '_' + key + '_' + Math . random ( ) . toString ( 36 ) . slice ( 2 ) ;
var dateValue = '' ;
if ( value ) {
var cleanValue = String ( value ) . trim ( ) ;
if ( cleanValue . match ( /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/ ) ) {
dateValue = cleanValue ;
} else if ( cleanValue . match ( /^[0-9]{2}\.[0-9]{2}\.[0-9]{4}$/ ) ) {
var parts = cleanValue . split ( '.' ) ;
dateValue = parts [ 2 ] + '-' + parts [ 1 ] + '-' + parts [ 0 ] ;
}
}
return '<input type="date" class="inline-field bind date-field" data-root="' + esc ( root ) + '" data-key="' + esc ( key ) + '" id="' + id + '" value="' + esc ( dateValue ) + '" />' ;
}
function createMoneyField ( root , key , value ) {
var id = 'field_' + root + '_' + key + '_' + Math . random ( ) . toString ( 36 ) . slice ( 2 ) ;
return '<div style="display: flex; align-items: center; gap: 8px;">' +
'<input class="inline-field bind money-field" data-root="' + esc ( root ) + '" data-key="' + esc ( key ) + '"' +
' id="' + id + '" value="' + esc ( value || '' ) + '"' +
' inputmode="decimal" pattern="[0-9]*[.,]?[0-9]*" autocomplete="off" />' +
'<span style="color: #6b7280; font-size: 14px;">рублей</span>' +
'</div>' ;
}
function createTextarea ( root , key , value ) {
var id = 'field_' + root + '_' + key + '_' + Math . random ( ) . toString ( 36 ) . slice ( 2 ) ;
return '<textarea class="inline-field bind full-width" data-root="' + esc ( root ) + '" data-key="' + esc ( key ) + '" id="' + id + '">' + esc ( value || '' ) + '</textarea>' ;
}
2025-11-24 15:47:16 +03:00
function createCheckbox ( root , key , checked , labelText , required ) {
var id = 'field_' + root + '_' + key + '_' + Math . random ( ) . toString ( 36 ) . slice ( 2 ) ;
var checkedAttr = checked ? ' checked' : '' ;
var requiredClass = required ? ' required-checkbox' : '' ;
var checkboxHtml = '<label class="checkbox-container' + requiredClass + '" for="' + id + '">' ;
checkboxHtml += '<input type="checkbox" class="checkbox-field bind" data-root="' + esc ( root ) + '" data-key="' + esc ( key ) + '" id="' + id + '"' + checkedAttr + ' />' ;
checkboxHtml += '<span class="checkmark"></span>' ;
checkboxHtml += '<span class="checkbox-label">' + labelText + '</span>' ;
checkboxHtml += '</label>' ;
return checkboxHtml ;
}
2025-11-24 15:58:17 +03:00
// Получаем данные
2025-11-24 15:32:08 +03:00
var injected = getData ( ) ;
2025-11-24 15:58:17 +03:00
console . log ( '=== ПОЛУЧЕНЫ ДАННЫЕ ===' ) ;
console . log ( 'injected:' , injected ) ;
console . log ( 'injected.case:' , injected . case ) ;
console . log ( 'injected.propertyName:' , injected . propertyName ) ;
// Достаём объект кейса из «типичных» мест
var dataCandidate = null ;
if ( ! dataCandidate && injected . propertyName !== undefined ) {
// Если propertyName - это объект (как в вашем случае), берем е г о напрямую
if ( typeof injected . propertyName === 'object' && injected . propertyName !== null ) {
dataCandidate = injected . propertyName ;
} else if ( typeof injected . propertyName === 'string' ) {
dataCandidate = tryParseJSON ( injected . propertyName ) ;
}
}
if ( ! dataCandidate && injected . value !== undefined ) dataCandidate = tryParseJSON ( injected . value ) ;
if ( ! dataCandidate && ( injected . user || injected . project || injected . offenders || injected . meta ) ) dataCandidate = injected ;
if ( ! dataCandidate && injected . data ) dataCandidate = injected . data ;
if ( ! dataCandidate && injected . output ) dataCandidate = tryParseJSON ( injected . output ) || injected . output ;
if ( ! dataCandidate && injected . case ) dataCandidate = { case : injected . case } ;
dataCandidate = dataCandidate || injected ;
console . log ( 'dataCandidate:' , dataCandidate ) ;
console . log ( 'Type of dataCandidate:' , typeof dataCandidate ) ;
console . log ( 'Keys of dataCandidate:' , Object . keys ( dataCandidate || { } ) ) ;
// Если dataCandidate - массив, берем первый элемент
var dataToNormalize = Array . isArray ( dataCandidate ) ? dataCandidate [ 0 ] : dataCandidate ;
console . log ( 'Data to normalize:' , dataToNormalize ) ;
// Нормализуем данные
var normalizedCase = normalizeData ( dataToNormalize ) ;
console . log ( 'Normalized case:' , normalizedCase ) ;
// Формируем state из нормализованных данных
var state = normalizedCase || { user : { } , project : { } , offenders : [ { } ] , meta : { } , attachments : [ ] } ;
// Убеждаемся, что есть базовые структуры
if ( ! state . offenders || ! state . offenders . length ) {
state . offenders = [ { } ] ;
}
if ( ! state . user ) state . user = { } ;
if ( ! state . project ) state . project = { } ;
if ( ! state . meta ) state . meta = { } ;
if ( ! state . attachments ) state . attachments = [ ] ;
// Добавляем SMS данные из injected
if ( injected . sms_meta ) {
state . meta = Object . assign ( { } , state . meta , injected . sms_meta ) ;
}
if ( injected . session_token ) {
state . meta . session_token = injected . session_token ;
}
if ( injected . token ) {
state . meta . token = injected . token ;
}
console . log ( 'Final state:' , state ) ;
2025-11-24 15:32:08 +03:00
function renderStatement() {
var u = state . user || { } ;
var o = ( state . offenders && state . offenders [ 0 ] ) || { } ;
var p = state . project || { } ;
var html = '' ;
html += '<div style="text-align:center;margin-bottom:32px">' ;
html += '<h2 style="font-size:20px;margin:0 0 16px;color:#1f2937">В М О О «Клиентправ»</h2>' ;
html += '<p style="margin:0;color:#6b7280">help@clientright.ru</p>' ;
html += '</div>' ;
html += '<p><strong>Заявитель:</strong> ' ;
html += createField ( 'user' , 'lastname' , u . lastname , 'Фамилия (обязательно)' ) ;
html += ' ' ;
html += createField ( 'user' , 'firstname' , u . firstname , 'Имя (обязательно)' ) ;
html += ' ' ;
html += createField ( 'user' , 'secondname' , u . secondname , 'Отчество' ) ;
html += '</p>' ;
html += '<p><strong>Дата рождения:</strong> ' ;
html += createDateField ( 'user' , 'birthday' , u . birthday ) ;
html += '</p>' ;
html += '<p><strong>Место рождения:</strong> ' ;
html += createField ( 'user' , 'birthplace' , u . birthplace , 'Место рождения (обязательно)' ) ;
html += '</p>' ;
html += '<p><strong>ИНН:</strong> ' ;
html += createField ( 'user' , 'inn' , u . inn , '12-значный ИНН (обязательно)' ) ;
html += '</p>' ;
html += '<p><strong>Адрес:</strong> ' ;
html += createField ( 'user' , 'mailingstreet' , u . mailingstreet , 'Адрес регистрации как в паспорте (обязательно)' ) ;
html += '</p>' ;
html += '<p><strong>Телефон:</strong> ' ;
html += createReadonlyField ( 'user' , 'mobile' , u . mobile ) ;
html += '</p>' ;
html += '<p><strong>E-mail:</strong> ' ;
html += createField ( 'user' , 'email' , u . email , 'email@example.com' ) ;
html += '</p>' ;
html += '<div class="section-break"></div>' ;
2025-11-24 15:47:16 +03:00
// Возмещение
html += '<h3 style="font-size:16px;margin:0 0 16px;color:#1f2937">Возмещение:</h3>' ;
html += '<p>Выплата возмещения возможна по системе быстрых платежей (СБП) по номеру телефона заявителя: <strong id="phone-display">' + esc ( u . mobile || '' ) + '</strong></p>' ;
html += '<div class="section-break"></div>' ;
// Заявление
2025-11-24 15:32:08 +03:00
html += '<h3 style="font-size:16px;margin:0 0 16px;color:#1f2937;text-align:center">ЗАЯВЛЕНИЕ</h3>' ;
2025-11-24 15:47:16 +03:00
// Тема обращения (только для чтения)
2025-11-24 15:32:08 +03:00
html += '<p><strong>Тема обращения:</strong> ' ;
html += createReadonlyField ( 'project' , 'category' , p . category ) ;
html += '</p>' ;
2025-11-24 15:47:16 +03:00
// Название договора / предмет
2025-11-24 15:32:08 +03:00
html += '<p><strong>Предмет договора:</strong> ' ;
html += createField ( 'project' , 'subject' , p . subject , 'Название договора или предмет услуг' ) ;
html += '</p>' ;
2025-11-24 15:47:16 +03:00
// Дата события / заключения договора
2025-11-24 15:32:08 +03:00
html += '<p><strong>Дата события / заключения договора:</strong> ' ;
html += createDateField ( 'project' , 'agrdate' , p . agrdate ) ;
html += '</p>' ;
2025-11-24 15:47:16 +03:00
// Сумма оплаты / стоимость
2025-11-24 15:32:08 +03:00
html += '<p><strong>Сумма оплаты / стоимость:</strong> ' ;
html += createMoneyField ( 'project' , 'agrprice' , p . agrprice ) ;
html += '</p>' ;
2025-11-24 15:47:16 +03:00
// Период
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>' ;
2025-11-24 15:32:08 +03:00
html += '<div class="section-break"></div>' ;
2025-11-24 15:47:16 +03:00
// Контрагенты / участники
2025-11-24 15:32:08 +03:00
html += '<h4 style="font-size:14px;margin:16px 0 12px;color:#1f2937">Контрагенты / участники:</h4>' ;
for ( var i = 0 ; i < state . offenders . length ; i ++ ) {
var offender = state . offenders [ i ] ;
html += '<div style="margin-bottom:16px;padding:12px;border:1px solid #e5e7eb;border-radius:8px;">' ;
2025-11-24 15:47:16 +03:00
2025-11-24 15:32:08 +03:00
html += '<p><strong>Наименование:</strong> ' ;
html += createField ( 'offender' , 'accountname' , offender . accountname , 'Название организации' , i ) ;
html += '</p>' ;
2025-11-24 15:47:16 +03:00
2025-11-24 15:32:08 +03:00
html += '<p><strong>ИНН:</strong> ' ;
html += createField ( 'offender' , 'inn' , offender . inn , 'ИНН организации (10 или 12 цифр)' , i ) ;
html += '</p>' ;
2025-11-24 15:47:16 +03:00
2025-11-24 15:32:08 +03:00
html += '<p><strong>ОГРН:</strong> ' ;
html += createField ( 'offender' , 'ogrn' , offender . ogrn , 'ОГРН' , i ) ;
html += '</p>' ;
2025-11-24 15:47:16 +03:00
2025-11-24 15:32:08 +03:00
html += '<p><strong>Адрес:</strong> ' ;
html += createField ( 'offender' , 'address' , offender . address , 'Адрес' , i ) ;
html += '</p>' ;
2025-11-24 15:47:16 +03:00
html += '<p><strong>E-mail:</strong> ' ;
html += createField ( 'offender' , 'email' , offender . email , 'email@example.com' , i ) ;
html += '</p>' ;
html += '<p><strong>Телефон:</strong> ' ;
html += createField ( 'offender' , 'phone' , offender . phone , '+7 (___) ___-__-__' , i ) ;
html += '</p>' ;
html += '<p><strong>Сайт:</strong> ' ;
html += createField ( 'offender' , 'website' , offender . website , 'https://example.com' , i ) ;
html += '</p>' ;
2025-11-24 15:32:08 +03:00
html += '</div>' ;
}
html += '<div class="section-break"></div>' ;
2025-11-24 15:47:16 +03:00
// Причина обращения (редактируемая)
2025-11-24 15:32:08 +03:00
html += '<p><strong>Причина обращения:</strong> ' ;
html += createField ( 'project' , 'reason' , p . reason , 'Можете уточнить или изменить причину обращения' ) ;
html += '</p>' ;
html += '<p><strong>Описание проблемы:</strong></p>' ;
html += createTextarea ( 'project' , 'description' , p . description ) ;
2025-11-24 15:47:16 +03:00
html += '<p>Н а основании вышеизложенного и руководствуясь ст. 45 Закона «О защите прав потребителей», ст. 3, ч. 1 ст. 46 ГПК РФ, прошу вас защитить мои потребительские права, обратиться в суд с заявлением о защите моих потребительских прав и/или с коллективным иском о защите группы потребителей, и представлять мои интересы во всех судебных органах РФ, а также обращаться с заявлениями во все госорганы, подавать претензии, письма и жалобы.</p>' ;
html += '<div class="section-break"></div>' ;
// Согласие на обработку персональных данных
html += '<div style="margin:24px 0;">' ;
html += createCheckbox ( 'meta' , 'privacyConsent' , state . meta && state . meta . privacyConsent ,
'Я ознакомлен(а ) и согласен(а ) с <a href="https://clientright.ru/person" target="_blank">Политикой обработки персональных данных</a> и даю согласие на обработку моих персональных данных в соответствии с Федеральным законом от 27.07.2006 №152-ФЗ «О персональных данных»' ,
true ) ;
html += '</div>' ;
// Список приложенных документов
if ( state . attachments && state . attachments . length > 0 ) {
html += '<div class="section-break"></div>' ;
html += '<h4 style="font-size:14px;margin:16px 0 12px;color:#1f2937">Приложенные документы:</h4>' ;
html += '<div style="background:#f9fafb;padding:12px;border-radius:8px;border:1px solid #e5e7eb;">' ;
for ( var i = 0 ; i < state . attachments . length ; i ++ ) {
var fileName = state . attachments [ i ] ;
html += '<div style="display:flex;align-items:center;margin-bottom:8px;padding:8px;background:white;border-radius:6px;border:1px solid #e5e7eb;">' ;
html += '<span style="color:#3b82f6;margin-right:8px;">📎</span>' ;
html += '<span style="color:#374151;font-size:14px;">' + esc ( fileName ) + '</span>' ;
html += '</div>' ;
}
html += '<p style="margin:8px 0 0;color:#6b7280;font-size:12px;">' ;
html += 'В с е г о документов: ' + state . attachments . length ;
html += '</p>' ;
html += '</div>' ;
}
2025-11-24 15:32:08 +03:00
var statementEl = document . getElementById ( 'statement' ) ;
if ( statementEl ) {
2025-11-24 15:47:16 +03:00
console . log ( 'Setting innerHTML, HTML length:' , html . length ) ;
2025-11-24 15:32:08 +03:00
statementEl . innerHTML = html ;
2025-11-24 15:47:16 +03:00
console . log ( 'innerHTML set, calling attachBindings...' ) ;
2025-11-24 15:32:08 +03:00
attachBindings ( ) ;
2025-11-24 15:47:16 +03:00
console . log ( 'attachBindings completed' ) ;
// Обновляем состояние кнопки отправки
setTimeout ( function ( ) {
updateSubmitButton ( ) ;
} , 100 ) ;
console . log ( '=== РЕНДЕРИНГ ЗАВЕРШЕН УСПЕШНО ===' ) ;
} else {
console . error ( 'ОШИБКА: элемент #statement не найден в renderStatement!' ) ;
}
}
// Простые функции валидации
function isValidPhone ( phone ) {
if ( ! phone ) return false ;
var clean = phone . replace ( /\D/g , '' ) ;
return clean . length >= 10 && clean . length <= 11 ;
}
function isValidEmail ( email ) {
if ( ! email ) return false ;
return email . includes ( '@' ) && email . includes ( '.' ) && email . length > 5 ;
}
function isNotEmpty ( value ) {
return value && value . trim ( ) . length > 0 ;
}
// Преобразования дат
function parseDDMMYYYY ( s ) {
if ( ! /^[0-9]{2}\.[0-9]{2}\.[0-9]{4}$/ . test ( s ) ) return null ;
var [ d , m , y ] = s . split ( '.' ) . map ( Number ) ;
var dt = new Date ( y , m - 1 , d ) ;
var isValid = ( dt . getFullYear ( ) === y && dt . getMonth ( ) === m - 1 && dt . getDate ( ) === d ) ;
return isValid ? dt : null ;
}
function parseYMD ( s ) {
var cleanS = s . trim ( ) . replace ( /[\u2012\u2013\u2014\u2015\u2212\uFF0D]/g , '-' ) ;
if ( ! /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/ . test ( cleanS ) ) return null ;
var [ y , m , d ] = cleanS . split ( '-' ) . map ( Number ) ;
var dt = new Date ( y , m - 1 , d ) ;
var isValid = ( dt . getFullYear ( ) === y && dt . getMonth ( ) === m - 1 && dt . getDate ( ) === d ) ;
return isValid ? dt : null ;
}
function toYMD ( dt ) {
var y = dt . getFullYear ( ) ;
var m = String ( dt . getMonth ( ) + 1 ) . padStart ( 2 , '0' ) ;
var d = String ( dt . getDate ( ) ) . padStart ( 2 , '0' ) ;
return y + '-' + m + '-' + d ;
}
function isValidINN ( inn , isIndividual ) {
if ( ! inn ) return false ;
var clean = String ( inn ) . replace ( /\D/g , '' ) ;
if ( isIndividual ) {
return clean . length === 12 ;
} else {
return clean . length === 10 || clean . length === 12 ;
}
}
function isValidName ( name ) {
if ( ! name ) return false ;
var trimmed = name . trim ( ) ;
if ( trimmed . length < 2 ) return false ;
var hasCyrillic = /[а -яёА-ЯЁ]/ . test ( trimmed ) ;
var hasInvalidChars = /[a-zA-Z0-9]/ . test ( trimmed ) ;
return hasCyrillic && ! hasInvalidChars ;
}
function isValidAddress ( address ) {
if ( ! address ) return false ;
var trimmed = address . trim ( ) ;
if ( trimmed . length < 10 ) return false ;
var hasNumbers = /[0-9]/ . test ( trimmed ) ;
var cyrillicPattern = new RegExp ( '[а -яёА-ЯЁ]' ) ;
var hasLetters = cyrillicPattern . test ( trimmed ) ;
return hasNumbers && hasLetters ;
}
function isValidMoney ( v ) {
if ( ! v ) return false ;
var s = String ( v ) . replace ( /\s+/g , '' ) . replace ( ',' , '.' ) ;
if ( ! /^[0-9]+(\.[0-9]{1,2})?$/ . test ( s ) ) return false ;
return parseFloat ( s ) > 0 ;
}
// Функция проверки всех обязательных полей
function validateAllFields() {
var requiredFields = [
{ root : 'user' , key : 'lastname' , name : 'Фамилия' } ,
{ root : 'user' , key : 'firstname' , name : 'Имя' } ,
{ root : 'user' , key : 'birthday' , name : 'Дата рождения' } ,
{ root : 'user' , key : 'birthplace' , name : 'Место рождения' } ,
{ root : 'user' , key : 'mailingstreet' , name : 'Адрес' } ,
{ root : 'user' , key : 'inn' , name : 'ИНН' } ,
{ root : 'project' , key : 'agrdate' , name : 'Дата договора' } ,
{ root : 'project' , key : 'agrprice' , name : 'Сумма' } ,
{ root : 'project' , key : 'reason' , name : 'Причина обращения' } ,
{ root : 'project' , key : 'description' , name : 'Описание проблемы' } ,
{ root : 'offender' , key : 'accountname' , name : 'Название организации' } ,
{ root : 'offender' , key : 'address' , name : 'Адрес организации' }
] ;
var errors = [ ] ;
for ( var i = 0 ; i < requiredFields . length ; i ++ ) {
var field = requiredFields [ i ] ;
var value = '' ;
if ( field . root === 'user' ) {
value = state . user [ field . key ] || '' ;
} else if ( field . root === 'project' ) {
value = state . project [ field . key ] || '' ;
} else if ( field . root === 'offender' ) {
value = ( state . offenders [ 0 ] && state . offenders [ 0 ] [ field . key ] ) || '' ;
}
if ( ! value || ( typeof value === 'string' && value . trim ( ) === '' ) ) {
errors . push ( field . name ) ;
}
}
return errors ;
}
// Функция обновления состояния кнопки отправки
function updateSubmitButton() {
var confirmBtn = document . getElementById ( 'confirmBtn' ) ;
if ( ! confirmBtn ) return ;
var isConsentGiven = state . meta && state . meta . privacyConsent === true ;
var validationErrors = validateAllFields ( ) ;
if ( ! isConsentGiven ) {
confirmBtn . disabled = true ;
confirmBtn . style . opacity = '0.6' ;
confirmBtn . style . cursor = 'not-allowed' ;
confirmBtn . textContent = '✅ Подтвердить и отправить' ;
} else if ( validationErrors . length === 0 ) {
confirmBtn . disabled = false ;
confirmBtn . style . opacity = '1' ;
confirmBtn . style . cursor = 'pointer' ;
confirmBtn . textContent = '✅ Подтвердить и отправить' ;
} else {
confirmBtn . disabled = true ;
confirmBtn . style . opacity = '0.6' ;
confirmBtn . style . cursor = 'not-allowed' ;
confirmBtn . textContent = '❌ Заполните все поля (' + validationErrors . length + ')' ;
confirmBtn . title = 'Н е заполнены: ' + validationErrors . join ( ', ' ) ;
2025-11-24 15:32:08 +03:00
}
}
function attachBindings() {
2025-11-24 15:47:16 +03:00
console . log ( 'Attaching bindings...' ) ;
2025-11-24 15:32:08 +03:00
var fields = document . querySelectorAll ( '.bind' ) ;
2025-11-24 15:47:16 +03:00
console . log ( 'Found fields:' , fields . length ) ;
2025-11-24 15:32:08 +03:00
Array . prototype . forEach . call ( fields , function ( field ) {
2025-11-24 15:47:16 +03:00
// Обработка ввода
2025-11-24 15:32:08 +03:00
field . addEventListener ( 'input' , function ( ) {
2025-11-24 15:47:16 +03:00
// Автозамена запятой на точку для денежных полей
if ( this . classList . contains ( 'money-field' ) && this . value . includes ( ',' ) ) {
this . value = this . value . replace ( /,/g , '.' ) ;
}
2025-11-24 15:32:08 +03:00
var root = this . getAttribute ( 'data-root' ) ;
var key = this . getAttribute ( 'data-key' ) ;
var value = this . type === 'checkbox' ? this . checked : this.value ;
2025-11-24 15:47:16 +03:00
// Для полей дат конвертируем YYYY-MM-DD в DD.MM.YYYY для сохранения
if ( this . classList . contains ( 'date-field' ) && value ) {
var parts = value . split ( '-' ) ;
if ( parts . length === 3 ) {
value = parts [ 2 ] + '.' + parts [ 1 ] + '.' + parts [ 0 ] ;
}
}
console . log ( 'Field changed:' , root , key , value ) ;
// Обновляем состояние
2025-11-24 15:32:08 +03:00
if ( root === 'user' ) {
state . user = state . user || { } ;
state . user [ key ] = value ;
2025-11-24 15:47:16 +03:00
// Обновляем телефон в СБП
if ( key === 'mobile' ) {
var phoneDisplay = document . getElementById ( 'phone-display' ) ;
if ( phoneDisplay ) {
phoneDisplay . textContent = value ;
}
}
2025-11-24 15:32:08 +03:00
} else if ( root === 'project' ) {
state . project = state . project || { } ;
state . project [ key ] = value ;
} else if ( root === 'offender' ) {
var index = parseInt ( this . getAttribute ( 'data-index' ) || '0' , 10 ) ;
state . offenders [ index ] = state . offenders [ index ] || { } ;
state . offenders [ index ] [ key ] = value ;
2025-11-24 15:47:16 +03:00
} else if ( root === 'meta' ) {
state . meta = state . meta || { } ;
state . meta [ key ] = value ;
// Для чекбокса согласия обновляем состояние кнопки
if ( key === 'privacyConsent' ) {
updateSubmitButton ( ) ;
}
}
// Обновляем состояние кнопки при изменении любого поля
updateSubmitButton ( ) ;
} ) ;
// Блокировка нечисловых символов для ИНН
field . addEventListener ( 'keydown' , function ( e ) {
var key = this . getAttribute ( 'data-key' ) ;
if ( key === 'inn' ) {
var isDigit = ( e . key >= '0' && e . key <= '9' ) ;
var isServiceKey = [ 'Backspace' , 'Delete' , 'Tab' , 'ArrowLeft' , 'ArrowRight' , 'ArrowUp' , 'ArrowDown' , 'Home' , 'End' ] . includes ( e . key ) ;
var isCtrlKey = e . ctrlKey && [ 'a' , 'c' , 'v' , 'x' , 'z' ] . includes ( e . key . toLowerCase ( ) ) ;
if ( ! isDigit && ! isServiceKey && ! isCtrlKey ) {
e . preventDefault ( ) ;
}
}
} ) ;
// Фильтрация при вставке для ИНН
field . addEventListener ( 'paste' , function ( e ) {
var key = this . getAttribute ( 'data-key' ) ;
if ( key === 'inn' ) {
e . preventDefault ( ) ;
var paste = ( e . clipboardData || window . clipboardData ) . getData ( 'text' ) ;
var cleanPaste = paste . replace ( /\D/g , '' ) ;
var root = this . getAttribute ( 'data-root' ) ;
var isIndividual = ( root === 'user' ) ;
var maxLength = 12 ;
cleanPaste = cleanPaste . slice ( 0 , maxLength ) ;
this . value = cleanPaste ;
var inputEvent = new Event ( 'input' , { bubbles : true } ) ;
this . dispatchEvent ( inputEvent ) ;
2025-11-24 15:32:08 +03:00
}
} ) ;
2025-11-24 15:47:16 +03:00
// Валидация при потере фокуса
field . addEventListener ( 'blur' , function ( ) {
updateSubmitButton ( ) ;
} ) ;
// Дополнительная обработка для чекбоксов
if ( field . type === 'checkbox' ) {
field . addEventListener ( 'change' , function ( ) {
var root = this . getAttribute ( 'data-root' ) ;
var key = this . getAttribute ( 'data-key' ) ;
var value = this . checked ;
if ( root === 'meta' && key === 'privacyConsent' ) {
state . meta = state . meta || { } ;
state . meta [ key ] = value ;
updateSubmitButton ( ) ;
var container = this . closest ( '.checkbox-container' ) ;
if ( container ) {
if ( value ) {
container . classList . remove ( 'error' ) ;
} else {
container . classList . add ( 'error' ) ;
}
}
}
} ) ;
}
2025-11-24 15:32:08 +03:00
} ) ;
}
function initialize() {
2025-11-24 15:47:16 +03:00
try {
console . log ( '=== НАЧАЛО ИНИЦИАЛИЗАЦИИ ===' ) ;
console . log ( 'injected data:' , injected ) ;
console . log ( 'state before render:' , state ) ;
// Проверяем, что элемент statement существует
var statementEl = document . getElementById ( 'statement' ) ;
console . log ( 'statement element found:' , ! ! statementEl ) ;
if ( ! statementEl ) {
console . error ( 'КРИТИЧЕСКАЯ ОШИБКА: элемент #statement не найден!' ) ;
return ;
}
// Инициализируем поля для шаблонов, если их нет
if ( ! state . project . serviceType ) state . project . serviceType = 'education' ;
if ( ! state . project . customServiceType ) state . project . customServiceType = '' ;
if ( ! state . project . customReason ) state . project . customReason = '' ;
// Добавляем поле согласия на обработку персданных
if ( state . meta . privacyConsent === undefined ) state . meta . privacyConsent = false ;
console . log ( 'Calling renderStatement...' ) ;
renderStatement ( ) ;
console . log ( 'renderStatement completed' ) ;
// Валидируем уже заполненные поля
setTimeout ( function ( ) {
console . log ( 'Starting field validation...' ) ;
var fields = document . querySelectorAll ( '.bind' ) ;
console . log ( 'Found fields for validation:' , fields . length ) ;
Array . prototype . forEach . call ( fields , function ( field ) {
// Валидация будет выполнена при первом взаимодействии
} ) ;
console . log ( 'Initial validation completed' ) ;
} , 100 ) ;
var confirmBtn = document . getElementById ( 'confirmBtn' ) ;
console . log ( 'confirmBtn found:' , ! ! confirmBtn ) ;
if ( confirmBtn ) {
confirmBtn . addEventListener ( 'click' , function ( e ) {
e . preventDefault ( ) ;
console . log ( 'Confirm button clicked' ) ;
// Проверяем согласие
if ( ! state . meta || ! state . meta . privacyConsent ) {
alert ( 'Необходимо дать согласие на обработку персональных данных' ) ;
return ;
}
// Проверяем валидность полей
var validationErrors = validateAllFields ( ) ;
if ( validationErrors . length > 0 ) {
alert ( 'Заполните все обязательные поля: ' + validationErrors . join ( ', ' ) ) ;
return ;
}
window . parent . postMessage ( {
type : 'claim_confirmed' ,
data : {
user : state.user ,
project : state.project ,
offenders : state.offenders ,
meta : state.meta
} ,
originalData : injected
} , '*' ) ;
} ) ;
}
// Делегированная фильтрация ИНН
if ( ! window . __innFilterAttached ) {
document . addEventListener ( 'input' , function ( e ) {
var el = e . target ;
if ( ! el || ! el . classList ) return ;
var isInn = el . classList . contains ( 'bind' ) && el . getAttribute ( 'data-key' ) === 'inn' ;
if ( ! isInn ) return ;
// Логика из test/index.php - фильтрация по типу ИНН
var root = el . getAttribute ( 'data-root' ) ;
var isIndividual = ( root === 'user' ) ; // user = физлицо, offender = юрлицо
// Оставляем только цифры
var v = ( el . value || '' ) . replace ( /\D/g , '' ) ;
if ( isIndividual ) {
// Физлицо: максимум 12 цифр (как js-inn-mask: "999999999999")
v = v . slice ( 0 , 12 ) ;
} else {
// Юрлицо: максимум 12 цифр, но валидны 10 или 12 (как js-inn-mask2: "9{10,12}")
v = v . slice ( 0 , 12 ) ;
}
if ( el . value !== v ) el . value = v ;
// Синхронизируем state
var root = el . getAttribute ( 'data-root' ) ;
if ( root === 'user' ) {
state . user = state . user || { } ;
state . user . inn = v ;
} else if ( root === 'offender' ) {
var idx = parseInt ( el . getAttribute ( 'data-index' ) || '0' , 10 ) ;
state . offenders = state . offenders || [ ] ;
state . offenders [ idx ] = state . offenders [ idx ] || { } ;
state . offenders [ idx ] . inn = v ;
}
// Моментальная валидация и обновление кнопки
updateSubmitButton ( ) ;
} , { passive : true } ) ;
window . __innFilterAttached = true ;
}
// Отправляем сообщение родителю при загрузке
window . parent . postMessage ( { type : 'claim_form_loaded' } , '*' ) ;
console . log ( '=== ИНИЦИАЛИЗАЦИЯ ЗАВЕРШЕНА ===' ) ;
} catch ( e ) {
console . error ( '=== ОШИБКА ИНИЦИАЛИЗАЦИИ ===' ) ;
console . error ( 'Error details:' , e ) ;
console . error ( 'Error stack:' , e . stack ) ;
var statementEl = document . getElementById ( 'statement' ) ;
if ( statementEl ) {
statementEl . innerHTML = '<p style="color:red;padding:20px;">Ошибка загрузки: ' + e . message + '<br><br>Проверьте консоль браузера (F12) для деталей.</p>' ;
}
2025-11-24 15:32:08 +03:00
}
}
2025-11-24 15:47:16 +03:00
// Запускаем инициализацию когда DOM готов
2025-11-24 15:32:08 +03:00
if ( document . readyState === 'loading' ) {
2025-11-24 15:47:16 +03:00
console . log ( 'DOM еще загружается, ждем события DOMContentLoaded...' ) ;
document . addEventListener ( 'DOMContentLoaded' , function ( ) {
console . log ( 'DOMContentLoaded fired, starting initialization...' ) ;
initialize ( ) ;
} ) ;
2025-11-24 15:32:08 +03:00
} else {
2025-11-24 15:47:16 +03:00
console . log ( 'DOM уже готов, запускаем инициализацию сразу...' ) ;
2025-11-24 15:32:08 +03:00
initialize ( ) ;
}
} ) ( ) ;
< / script >
< / body >
< / html > ` ;
return html ;
}