253 lines
10 KiB
JavaScript
253 lines
10 KiB
JavaScript
const TOTAL = 24;
|
||
|
||
document.addEventListener('DOMContentLoaded', function () {
|
||
// Добавить поле комментария под каждый вопрос
|
||
document.querySelectorAll('.question[data-index]').forEach(q => {
|
||
const name = (q.querySelector('[name]') || {}).name || '';
|
||
const ta = document.createElement('textarea');
|
||
ta.className = 'question-comment';
|
||
ta.name = name + '_comment';
|
||
ta.placeholder = 'Свой комментарий или уточнение (необязательно)...';
|
||
q.appendChild(ta);
|
||
});
|
||
|
||
updateProgress();
|
||
|
||
document.getElementById('client_email').addEventListener('input', function () {
|
||
document.getElementById('_replyto').value = this.value;
|
||
});
|
||
document.getElementById('_replyto').value = document.getElementById('client_email').value;
|
||
|
||
document.getElementById('btnReset').addEventListener('click', function () {
|
||
if (!confirm('Очистить всю форму? Это действие нельзя отменить.')) return;
|
||
document.getElementById('questionnaireForm').querySelectorAll('input[type="radio"], input[type="checkbox"]').forEach(el => el.checked = false);
|
||
document.getElementById('questionnaireForm').querySelectorAll('textarea, input[type="text"], input[type="tel"], input[type="email"]').forEach(el => { if (!['client_name','client_email'].includes(el.id)) el.value = ''; });
|
||
document.querySelectorAll('.other-input').forEach(el => el.classList.remove('visible'));
|
||
updateProgress(); updatePreview();
|
||
showFlash('Форма очищена');
|
||
});
|
||
|
||
document.getElementById('btnSave').addEventListener('click', function () {
|
||
updateURL(); showFlash('Сохранено!');
|
||
document.getElementById('saveStatus').textContent = 'Сохранено ' + formatTime();
|
||
});
|
||
|
||
document.getElementById('btnShare').addEventListener('click', function () {
|
||
updateURL();
|
||
const url = window.location.href;
|
||
if (navigator.clipboard) {
|
||
navigator.clipboard.writeText(url).then(() => showFlash('Ссылка скопирована!'));
|
||
} else {
|
||
const ta = document.createElement('textarea');
|
||
ta.value = url; ta.style.position = 'fixed'; ta.style.opacity = '0';
|
||
document.body.appendChild(ta); ta.select(); document.execCommand('copy');
|
||
document.body.removeChild(ta); showFlash('Ссылка скопирована!');
|
||
}
|
||
});
|
||
|
||
// Модальное окно
|
||
const generalForm = document.getElementById('generalForm');
|
||
if (generalForm) {
|
||
generalForm.addEventListener('submit', async function (e) {
|
||
e.preventDefault();
|
||
const btn = document.getElementById('generalSubmitBtn');
|
||
btn.disabled = true; btn.textContent = 'Отправляем...';
|
||
document.getElementById('general_replyto').value = document.getElementById('g_email').value;
|
||
try {
|
||
const res = await fetch(generalForm.action, { method: 'POST', body: new FormData(generalForm), headers: { 'Accept': 'application/json' } });
|
||
if (res.ok) { generalForm.style.display = 'none'; document.getElementById('modalSuccess').style.display = 'block'; }
|
||
else throw new Error();
|
||
} catch { btn.disabled = false; btn.textContent = 'Отправить обращение'; showFlash('Ошибка. Попробуйте ещё раз.'); }
|
||
});
|
||
}
|
||
document.addEventListener('keydown', e => { if (e.key === 'Escape') closeModal(); });
|
||
|
||
document.getElementById('questionnaireForm').addEventListener('change', function () {
|
||
updateProgress();
|
||
updatePreview();
|
||
handleConditionals();
|
||
});
|
||
|
||
document.getElementById('questionnaireForm').addEventListener('input', function () {
|
||
updateProgress();
|
||
updatePreview();
|
||
});
|
||
|
||
document.getElementById('questionnaireForm').addEventListener('submit', function (e) {
|
||
// Собрать чекбоксы в одно скрытое поле
|
||
const checked = [...document.querySelectorAll('input[name="admin_features"]:checked')].map(el => el.value);
|
||
let hidden = document.getElementById('admin_features_combined');
|
||
if (!hidden) {
|
||
hidden = document.createElement('input');
|
||
hidden.type = 'hidden';
|
||
hidden.id = 'admin_features_combined';
|
||
hidden.name = 'admin_features_combined';
|
||
document.getElementById('questionnaireForm').appendChild(hidden);
|
||
}
|
||
hidden.value = checked.join(', ');
|
||
|
||
const now = new Date();
|
||
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||
const offsetMin = -now.getTimezoneOffset();
|
||
const offsetSign = offsetMin >= 0 ? '+' : '-';
|
||
const offsetHours = String(Math.floor(Math.abs(offsetMin) / 60)).padStart(2, '0');
|
||
const offsetMins = String(Math.abs(offsetMin) % 60).padStart(2, '0');
|
||
document.getElementById('sender_time').value =
|
||
now.toLocaleString('ru-RU', { hour12: false }) +
|
||
` (UTC${offsetSign}${offsetHours}:${offsetMins}, ${tz})`;
|
||
document.getElementById('sender_timezone').value = tz;
|
||
document.getElementById('anton_time').value =
|
||
now.toLocaleString('ru-RU', {
|
||
timeZone: 'Europe/Helsinki', hour12: false,
|
||
year: 'numeric', month: '2-digit', day: '2-digit',
|
||
hour: '2-digit', minute: '2-digit', second: '2-digit'
|
||
}) + ' (UTC+3, Финляндия)';
|
||
}, true);
|
||
|
||
handleConditionals();
|
||
});
|
||
|
||
function handleConditionals() {
|
||
// Показать поле "свой вариант" для радио с value="other"/"limited"
|
||
const conditionals = [
|
||
{ radio: 'price_per_hour', value: 'other', inputId: 'price_other' },
|
||
{ radio: 'lock_system', value: 'other', inputId: 'lock_other' },
|
||
{ radio: 'design_style', value: 'other', inputId: 'design_other' },
|
||
{ radio: 'multi_hour', value: 'limited', inputId: 'multi_hour_limit' },
|
||
];
|
||
|
||
conditionals.forEach(({ radio, value, inputId }) => {
|
||
const selected = document.querySelector(`input[name="${radio}"]:checked`);
|
||
const input = document.getElementById(inputId);
|
||
if (!input) return;
|
||
if (selected && selected.value === value) {
|
||
input.classList.add('visible');
|
||
} else {
|
||
input.classList.remove('visible');
|
||
}
|
||
});
|
||
}
|
||
|
||
function updateProgress() {
|
||
const radioGroups = [
|
||
'slot_type','multi_hour','calendar_view','advance_booking','self_cancel',
|
||
'price_per_hour','price_tiers','refund_policy','invoice',
|
||
'lock_system','pin_length','pin_delivery',
|
||
'booking_confirm','reminder_before','time_warning','admin_notify',
|
||
'client_history',
|
||
'product_format','language',
|
||
'logo','design_style',
|
||
'terms','privacy'
|
||
];
|
||
|
||
let filled = 0;
|
||
|
||
// Радио (23 группы)
|
||
radioGroups.forEach(name => {
|
||
if (document.querySelector(`input[name="${name}"]:checked`)) filled++;
|
||
});
|
||
|
||
// Чекбокс (1 группа)
|
||
if (document.querySelector('input[name="admin_features"]:checked')) filled++;
|
||
|
||
const pct = Math.round((filled / TOTAL) * 100);
|
||
document.getElementById('progressFill').style.width = pct + '%';
|
||
document.getElementById('progressText').textContent = `Заполнено: ${filled} из ${TOTAL}`;
|
||
}
|
||
|
||
function updatePreview() {
|
||
const container = document.getElementById('previewContent');
|
||
const labels = {
|
||
slot_type: 'Тип слотов',
|
||
multi_hour: 'Несколько часов подряд',
|
||
calendar_view: 'Вид календаря',
|
||
advance_booking: 'Бронирование вперёд',
|
||
self_cancel: 'Самостоятельная отмена',
|
||
price_per_hour: 'Цена за час',
|
||
price_tiers: 'Тарифы',
|
||
refund_policy: 'Возврат при отмене',
|
||
invoice: 'Счёт-фактура',
|
||
lock_system: 'Система замка',
|
||
pin_length: 'Длина PIN',
|
||
pin_delivery: 'Доставка PIN',
|
||
booking_confirm: 'Подтверждение бронирования',
|
||
reminder_before: 'Напоминание перед слотом',
|
||
time_warning: 'Предупреждение о конце',
|
||
admin_notify: 'Уведомления для вас',
|
||
client_history: 'История клиентов',
|
||
product_format: 'Формат приложения',
|
||
language: 'Язык',
|
||
logo: 'Логотип',
|
||
design_style: 'Стиль дизайна',
|
||
terms: 'Правила пользования',
|
||
privacy: 'Политика конфиденциальности',
|
||
};
|
||
|
||
let html = '';
|
||
|
||
Object.entries(labels).forEach(([name, label]) => {
|
||
const checked = document.querySelector(`input[name="${name}"]:checked`);
|
||
if (checked) {
|
||
const spanText = checked.closest('label').querySelector('span')?.textContent || checked.value;
|
||
html += `<div class="preview-item">
|
||
<div class="preview-item-label">${label}</div>
|
||
<div class="preview-item-value">${escapeHtml(spanText)}</div>
|
||
</div>`;
|
||
}
|
||
});
|
||
|
||
// Чекбоксы
|
||
const checkedBoxes = [...document.querySelectorAll('input[name="admin_features"]:checked')];
|
||
if (checkedBoxes.length > 0) {
|
||
const vals = checkedBoxes.map(el => el.closest('label').querySelector('span')?.textContent || el.value).join(', ');
|
||
html += `<div class="preview-item">
|
||
<div class="preview-item-label">Панель управления</div>
|
||
<div class="preview-item-value">${escapeHtml(vals)}</div>
|
||
</div>`;
|
||
}
|
||
|
||
container.innerHTML = html || '<p class="preview-empty">Начните заполнять форму — здесь появится предпросмотр</p>';
|
||
}
|
||
|
||
function escapeHtml(str) {
|
||
return str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
||
}
|
||
|
||
function updateURL() {
|
||
const data = {};
|
||
document.querySelectorAll('input[type="radio"]:checked, input[type="checkbox"]:checked').forEach(el => {
|
||
if (!data[el.name]) data[el.name] = [];
|
||
data[el.name].push(el.value);
|
||
});
|
||
const b64 = btoa(unescape(encodeURIComponent(JSON.stringify(data))));
|
||
const url = new URL(window.location.href);
|
||
url.searchParams.set('d', b64);
|
||
window.history.replaceState(null, '', url.toString());
|
||
}
|
||
|
||
function formatTime() {
|
||
const d = new Date();
|
||
return d.getHours().toString().padStart(2,'0') + ':' + d.getMinutes().toString().padStart(2,'0');
|
||
}
|
||
|
||
function openModal() {
|
||
document.getElementById('modalOverlay').classList.add('open');
|
||
document.body.style.overflow = 'hidden';
|
||
}
|
||
|
||
function closeModal() {
|
||
document.getElementById('modalOverlay').classList.remove('open');
|
||
document.body.style.overflow = '';
|
||
}
|
||
|
||
function closeModalOutside(e) {
|
||
if (e.target === document.getElementById('modalOverlay')) closeModal();
|
||
}
|
||
|
||
function showFlash(msg) {
|
||
const el = document.getElementById('flash');
|
||
el.textContent = msg;
|
||
el.classList.add('show');
|
||
setTimeout(() => el.classList.remove('show'), 2500);
|
||
}
|