diff --git a/ticket_form/frontend/src/components/form/generateConfirmationFormHTML.ts b/ticket_form/frontend/src/components/form/generateConfirmationFormHTML.ts index 2f116820..589088f5 100644 --- a/ticket_form/frontend/src/components/form/generateConfirmationFormHTML.ts +++ b/ticket_form/frontend/src/components/form/generateConfirmationFormHTML.ts @@ -373,6 +373,58 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed: box-shadow:0 0 0 2px rgba(239,68,68,0.1) !important; background-color:#fef2f2 !important; } + /* ⚠️ Желтая рамка для незаполненных обязательных полей */ + .inline-field.required-empty{ + border-color:#f59e0b !important; + background-color:#fffbeb !important; + border-width:2px !important; + } + .inline-field.required-empty:focus{ + border-color:#f59e0b !important; + box-shadow:0 0 0 3px rgba(245,158,11,0.2) !important; + background-color:#fffbeb !important; + } + /* Звёздочка для обязательных полей */ + .required-marker{ + color:#ef4444; + font-weight:bold; + margin-left:2px; + } + /* Блок с предупреждением о незаполненных полях */ + .validation-warning{ + margin:16px 0; + padding:12px 16px; + background:#fffbeb; + border:2px solid #f59e0b; + border-radius:8px; + font-size:14px; + color:#92400e; + } + .validation-warning-title{ + font-weight:600; + margin-bottom:8px; + display:flex; + align-items:center; + gap:8px; + } + .validation-warning-list{ + margin:0; + padding-left:20px; + list-style:none; + } + .validation-warning-list li{ + margin:4px 0; + padding-left:20px; + position:relative; + } + .validation-warning-list li:before{ + content:'•'; + position:absolute; + left:0; + color:#f59e0b; + font-weight:bold; + font-size:18px; + } .inline-field.large{ min-width:200px;max-width:500px; } @@ -925,28 +977,28 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed: html += ''; html += '

Заявитель: '; - html += createField('user', 'lastname', u.lastname, 'Фамилия (обязательно)'); + html += createField('user', 'lastname', u.lastname, 'Фамилия') + '*'; html += ' '; - html += createField('user', 'firstname', u.firstname, 'Имя (обязательно)'); + html += createField('user', 'firstname', u.firstname, 'Имя') + '*'; html += ' '; html += createField('user', 'secondname', u.secondname, 'Отчество'); html += '

'; html += '

Дата рождения: '; html += createDateField('user', 'birthday', u.birthday); - html += '

'; + html += '*

'; html += '

Место рождения: '; - html += createField('user', 'birthplace', u.birthplace, 'Место рождения (обязательно)'); - html += '

'; + html += createField('user', 'birthplace', u.birthplace, 'Место рождения'); + html += '*

'; html += '

ИНН: '; - html += createField('user', 'inn', u.inn, '12-значный ИНН (обязательно)'); - html += '

'; + html += createField('user', 'inn', u.inn, '12-значный ИНН'); + html += '*

'; html += '

Адрес: '; - html += createField('user', 'mailingstreet', u.mailingstreet, 'Адрес регистрации как в паспорте (обязательно)'); - html += '

'; + html += createField('user', 'mailingstreet', u.mailingstreet, 'Адрес регистрации как в паспорте'); + html += '*

'; html += '

Телефон: '; html += createReadonlyField('user', 'mobile', u.mobile); @@ -961,9 +1013,9 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed: // Возмещение html += '

Возмещение:

'; html += '

Выплата возмещения возможна по системе быстрых платежей (СБП) по номеру телефона заявителя: ' + esc(u.mobile || '') + '

'; - html += '

Банк для получения выплаты (обязательно): '; + html += '

Банк для получения выплаты: '; html += createBankSelect('user', 'bank_id', u.bank_id || ''); - html += '

'; + html += '*

'; html += '
'; @@ -983,12 +1035,12 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed: // Дата события / заключения договора html += '

Дата события / заключения договора: '; html += createDateField('project', 'agrdate', p.agrdate); - html += '

'; + html += '*

'; // Сумма оплаты / стоимость html += '

Сумма оплаты / стоимость: '; html += createMoneyField('project', 'agrprice', p.agrprice); - html += '

'; + html += '*

'; // Период html += '

Период: '; @@ -1013,19 +1065,19 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed: html += '

Наименование: '; html += createField('offender', 'accountname', offender.accountname, 'Название организации', i); - html += '

'; + html += '*

'; html += '

ИНН: '; - html += createField('offender', 'inn', offender.inn, 'ИНН организации (10 или 12 цифр) (обязательно)', i); - html += '

'; + html += createField('offender', 'inn', offender.inn, 'ИНН организации (10 или 12 цифр)', i); + html += '*

'; html += '

Адрес: '; - html += createField('offender', 'address', offender.address, 'Адрес (обязательно)', i); - html += '

'; + html += createField('offender', 'address', offender.address, 'Адрес', i); + html += '*

'; html += '

E-mail: '; - html += createField('offender', 'email', offender.email, 'email@example.com (обязательно)', i); - html += '

'; + html += createField('offender', 'email', offender.email, 'email@example.com', i); + html += '*

'; html += '

Телефон: '; html += createField('offender', 'phone', offender.phone, '+7 (___) ___-__-__', i); @@ -1043,15 +1095,18 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed: // Причина обращения (редактируемая) html += '

Причина обращения: '; html += createField('project', 'reason', p.reason, 'Можете уточнить или изменить причину обращения'); - html += '

'; + html += '*

'; - html += '

Описание проблемы:

'; + html += '

Описание проблемы: *

'; html += createTextarea('project', 'description', p.description); html += '

На основании вышеизложенного и руководствуясь ст. 45 Закона «О защите прав потребителей», ст. 3, ч. 1 ст. 46 ГПК РФ, прошу вас защитить мои потребительские права, обратиться в суд с заявлением о защите моих потребительских прав и/или с коллективным иском о защите группы потребителей, и представлять мои интересы во всех судебных органах РФ, а также обращаться с заявлениями во все госорганы, подавать претензии, письма и жалобы.

'; html += '
'; + // Блок с предупреждением о незаполненных полях (будет обновляться динамически) + html += ''; + // Согласие на обработку персональных данных html += '
'; html += createCheckbox('meta', 'privacyConsent', state.meta && state.meta.privacyConsent, @@ -1183,39 +1238,62 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed: return parseFloat(s) > 0; } + // Список обязательных полей + var requiredFieldsList = [ + { 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: 'inn', name: 'ИНН организации' }, + { root: 'offender', key: 'address', name: 'Адрес организации' }, + { root: 'offender', key: 'email', name: 'E-mail организации' }, + { root: 'user', key: 'bank_id', name: 'Банк для получения выплаты' } + ]; + + // Функция проверки, является ли поле обязательным + function isRequiredField(root, key) { + return requiredFieldsList.some(function(f) { + return f.root === root && f.key === key; + }); + } + + // Функция получения значения поля + function getFieldValue(root, key, index) { + if (root === 'user') { + return state.user[key] || ''; + } else if (root === 'project') { + return state.project[key] || ''; + } else if (root === 'offender') { + var offender = state.offenders[index || 0]; + return (offender && offender[key]) || ''; + } + return ''; + } + + // Функция проверки, заполнено ли поле + function isFieldFilled(root, key, index) { + var value = getFieldValue(root, key, index); + if (value === null || value === undefined) return false; + if (typeof value === 'string') { + return value.trim().length > 0; + } + return !!value; + } + // Функция проверки всех обязательных полей 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: 'inn', name: 'ИНН организации' }, - { root: 'offender', key: 'address', name: 'Адрес организации' }, - { root: 'offender', key: 'email', name: 'E-mail организации' }, - { root: 'user', key: 'bank_id', 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() === '')) { + for (var i = 0; i < requiredFieldsList.length; i++) { + var field = requiredFieldsList[i]; + if (!isFieldFilled(field.root, field.key, field.root === 'offender' ? 0 : undefined)) { errors.push(field.name); } } @@ -1223,6 +1301,34 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed: return errors; } + // Функция обновления блока с предупреждением о незаполненных полях + function updateValidationWarning() { + var warningBlock = document.getElementById('validation-warning-block'); + if (!warningBlock) return; + + var validationErrors = validateAllFields(); + + if (validationErrors.length === 0) { + warningBlock.style.display = 'none'; + return; + } + + // Формируем HTML для предупреждения + var warningHtml = '
'; + warningHtml += '
'; + warningHtml += '⚠️ Пожалуйста, заполните все обязательные поля (' + validationErrors.length + '):'; + warningHtml += '
'; + warningHtml += ''; + warningHtml += '
'; + + warningBlock.innerHTML = warningHtml; + warningBlock.style.display = 'block'; + } + // Функция обновления состояния кнопки отправки function updateSubmitButton() { var confirmBtn = document.getElementById('confirmBtn'); @@ -1231,6 +1337,15 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed: var isConsentGiven = state.meta && state.meta.privacyConsent === true; var validationErrors = validateAllFields(); + // Обновляем блок с предупреждением + updateValidationWarning(); + + // Обновляем стили всех полей + var fields = document.querySelectorAll('.bind'); + Array.prototype.forEach.call(fields, function(field) { + updateFieldStyle(field); + }); + if (!isConsentGiven) { confirmBtn.disabled = true; confirmBtn.style.opacity = '0.6'; @@ -1245,7 +1360,7 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed: confirmBtn.disabled = true; confirmBtn.style.opacity = '0.6'; confirmBtn.style.cursor = 'not-allowed'; - confirmBtn.textContent = '❌ Заполните все поля (' + validationErrors.length + ')'; + confirmBtn.textContent = '❌ Заполните все обязательные поля (' + validationErrors.length + ')'; confirmBtn.title = 'Не заполнены: ' + validationErrors.join(', '); } } @@ -1256,10 +1371,16 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed: var hasValue = field.type === 'checkbox' ? value : value.length > 0; var key = field.getAttribute('data-key'); var root = field.getAttribute('data-root'); + var index = field.getAttribute('data-index'); + var fieldIndex = index !== null ? parseInt(index, 10) : undefined; - // Убираем оба класса сначала + // Убираем все классы сначала field.classList.remove('filled'); field.classList.remove('invalid'); + field.classList.remove('required-empty'); + + // Проверяем, является ли поле обязательным + var isRequired = isRequiredField(root, key); if (hasValue) { // Проверяем валидность для телефона и email @@ -1283,10 +1404,16 @@ export function generateConfirmationFormHTML(data: any, contact_data_confirmed: } if (isValid) { - field.classList.add('filled'); - } else { + field.classList.add('filled'); + } else { field.classList.add('invalid'); } + } else { + // Поле не заполнено + if (isRequired) { + // Обязательное поле не заполнено - подсвечиваем жёлтым + field.classList.add('required-empty'); + } } }