feat: rebuild questionnaire2 with radio buttons and checkboxes

This commit is contained in:
Anton
2026-05-12 21:49:04 +03:00
parent 03facb5db8
commit 0a634b6e20
2 changed files with 464 additions and 182 deletions
+351 -128
View File
@@ -3,13 +3,118 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; form-action https://formspree.io; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src https://formspree.io">
<title>Sunset Pass — Уточняющий опросник</title>
<title>Saarijärven koirapuisto — Уточняющий опросник</title>
<link rel="stylesheet" href="style.css">
<style>
.radio-group, .checkbox-group {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 8px;
}
.radio-group label, .checkbox-group label {
display: flex;
align-items: center;
gap: 10px;
font-weight: 400;
cursor: pointer;
padding: 10px 14px;
border: 1.5px solid #e0e0e0;
border-radius: 8px;
transition: border-color 0.15s, background 0.15s;
}
.radio-group label:hover, .checkbox-group label:hover {
border-color: #2d6a4f;
background: #f0faf4;
}
.radio-group input[type="radio"],
.checkbox-group input[type="checkbox"] {
width: 18px;
height: 18px;
accent-color: #2d6a4f;
flex-shrink: 0;
}
.radio-group input[type="radio"]:checked + span,
.checkbox-group input[type="checkbox"]:checked + span {
font-weight: 600;
color: #2d6a4f;
}
.radio-group label:has(input:checked),
.checkbox-group label:has(input:checked) {
border-color: #2d6a4f;
background: #f0faf4;
}
.other-input {
margin-top: 6px;
margin-left: 28px;
width: calc(100% - 28px);
padding: 8px 12px;
border: 1.5px solid #ddd;
border-radius: 8px;
font-size: 0.9rem;
font-family: inherit;
display: none;
}
.other-input.visible { display: block; }
.question-label {
font-weight: 500;
font-size: 0.95rem;
margin-bottom: 4px;
display: block;
}
.context-note {
font-size: 0.85rem;
color: #888;
font-style: italic;
margin-bottom: 16px;
padding: 10px 14px;
background: #f8f8f8;
border-radius: 8px;
border-left: 3px solid #c8e6c9;
}
.variant-card {
border: 1.5px solid #e0e0e0;
border-radius: 10px;
padding: 14px 18px;
margin-bottom: 10px;
cursor: pointer;
transition: border-color 0.15s, background 0.15s;
}
.variant-card:has(input:checked) {
border-color: #2d6a4f;
background: #f0faf4;
}
.variant-card.recommended {
border-color: #2d6a4f;
}
.variant-card label {
font-weight: 400;
cursor: pointer;
display: flex;
gap: 10px;
align-items: flex-start;
margin: 0;
padding: 0;
border: none;
background: none;
}
.variant-card label:hover { background: none; border: none; }
.variant-title { font-weight: 700; font-size: 0.95rem; }
.variant-desc { font-size: 0.85rem; color: #555; margin-top: 4px; }
.variant-badge {
display: inline-block;
font-size: 0.72rem;
padding: 2px 8px;
border-radius: 20px;
margin-left: 8px;
font-weight: 600;
}
.badge-green { background: #e8f5e9; color: #2d6a4f; }
.badge-orange { background: #fff3e0; color: #e65100; }
</style>
</head>
<body>
<!-- Шапка компании -->
<div class="company-header">
<img src="logo.jpg" alt="Kot Kanagrilli Oy" class="company-logo">
<div class="company-info">
@@ -18,29 +123,22 @@
</div>
</div>
<!-- Тулбар -->
<div class="toolbar">
<div class="toolbar-left">
<span class="toolbar-title">Saarijärven koirapuisto — Уточняющий опросник</span>
<span class="save-status" id="saveStatus"></span>
</div>
<div class="toolbar-right">
<button class="toolbar-btn" id="btnSave" title="Сохранить"><span class="btn-icon">💾</span><span class="btn-text"> Сохранить</span></button>
</div>
</div>
<!-- Flash сообщение -->
<div class="flash" id="flash"></div>
<!-- Основной макет -->
<div class="app-layout">
<div class="form-column">
<header>
<div class="progress-bar">
<div class="progress-fill" id="progressFill"></div>
</div>
<p class="progress-text" id="progressText">Заполнено: 0 из 16</p>
<p class="progress-text" id="progressText">Заполнено: 0 из 24</p>
</header>
<form id="questionnaireForm" action="https://formspree.io/f/xrejjoav" method="POST">
@@ -50,189 +148,316 @@
<input type="hidden" name="anton_time" id="anton_time">
<input type="hidden" name="questionnaire_number" value="2">
<!-- О воротах -->
<section class="section" data-section-id="gates">
<div class="section-header">
<h2>О воротах</h2>
</div>
<p style="font-size:0.88rem;color:#888;margin-bottom:16px;">Вы указали: <em>оборудования нет, нужно закупить с нуля.</em></p>
<!-- 1. Бронирование -->
<section class="section" data-section-id="booking">
<div class="section-header"><h2>Как работает бронирование</h2></div>
<div class="question" data-index="1">
<label for="gate_type">8а. Какой тип ворот на входе в парк?</label>
<textarea id="gate_type" name="gate_type" placeholder="Например: калитка / распашные / раздвижные"></textarea>
<span class="question-label">1. Как устроены слоты времени?</span>
<div class="radio-group">
<label><input type="radio" name="slot_type" value="fixed"><span>Фиксированные каждый час (9:00, 10:00, 11:00...)</span></label>
<label><input type="radio" name="slot_type" value="flexible"><span>Клиент сам выбирает любое время начала</span></label>
<label><input type="radio" name="slot_type" value="advice"><span>Не знаю, посоветуйте</span></label>
</div>
</div>
<div class="question" data-index="2">
<label for="gate_electricity">8б. Есть ли электричество рядом с воротами?</label>
<textarea id="gate_electricity" name="gate_electricity" placeholder="Например: да, розетка есть / нет, нужен аккумулятор"></textarea>
<span class="question-label">2. Можно ли взять несколько часов подряд одним бронированием?</span>
<div class="radio-group">
<label><input type="radio" name="multi_hour" value="yes"><span>Да, без ограничений</span></label>
<label><input type="radio" name="multi_hour" value="limited"><span>Да, но не больше X часов</span></label>
<label><input type="radio" name="multi_hour" value="no"><span>Нет, только по одному часу</span></label>
</div>
<input type="text" class="other-input" id="multi_hour_limit" name="multi_hour_limit" placeholder="Максимум сколько часов?">
</div>
<div class="question" data-index="3">
<label for="gate_internet">8в. Достигает ли WiFi или мобильный интернет до ворот?</label>
<textarea id="gate_internet" name="gate_internet" placeholder="Например: да, WiFi есть / есть мобильная сеть 4G"></textarea>
<span class="question-label">3. Что видит клиент в календаре?</span>
<div class="radio-group">
<label><input type="radio" name="calendar_view" value="free_only"><span>Только свободные слоты</span></label>
<label><input type="radio" name="calendar_view" value="both"><span>И свободные и занятые (занятые отмечены серым)</span></label>
</div>
</div>
<div class="question" data-index="4">
<label for="gate_camera">8г. Нужна ли камера видеонаблюдения у входа?</label>
<textarea id="gate_camera" name="gate_camera" placeholder="Например: да / нет / возможно позже"></textarea>
<span class="question-label">4. Как далеко вперёд можно бронировать?</span>
<div class="radio-group">
<label><input type="radio" name="advance_booking" value="7"><span>7 дней</span></label>
<label><input type="radio" name="advance_booking" value="14"><span>14 дней</span></label>
<label><input type="radio" name="advance_booking" value="30"><span>30 дней</span></label>
<label><input type="radio" name="advance_booking" value="60"><span>60 дней</span></label>
<label><input type="radio" name="advance_booking" value="unlimited"><span>Без ограничений</span></label>
</div>
</div>
<div class="question" data-index="5">
<span class="question-label">5. Клиент может сам отменить бронирование через приложение?</span>
<div class="radio-group">
<label><input type="radio" name="self_cancel" value="yes"><span>Да, самостоятельно через приложение</span></label>
<label><input type="radio" name="self_cancel" value="contact"><span>Нет, только написав мне</span></label>
</div>
</div>
</section>
<!-- Об оплате -->
<!-- 2. Оплата -->
<section class="section" data-section-id="payment">
<div class="section-header">
<h2>Об оплате</h2>
</div>
<p style="font-size:0.88rem;color:#888;margin-bottom:16px;">Вы указали: <em>онлайн.</em></p>
<div class="question" data-index="5">
<label for="payment_provider">5а. Какой платёжный сервис предпочитаете?</label>
<textarea id="payment_provider" name="payment_provider" placeholder="Например: Paytrail (финский стандарт, поддерживает все банки) / Stripe / другой"></textarea>
</div>
<div class="section-header"><h2>Оплата</h2></div>
<div class="question" data-index="6">
<label for="price_per_hour">5б. Сколько стоит один час аренды парка?</label>
<textarea id="price_per_hour" name="price_per_hour" placeholder="Например: 15€ за час"></textarea>
<span class="question-label">6. Цена за один час аренды парка (€)</span>
<div class="radio-group">
<label><input type="radio" name="price_per_hour" value="under10"><span>До 10€</span></label>
<label><input type="radio" name="price_per_hour" value="10-15"><span>1015€</span></label>
<label><input type="radio" name="price_per_hour" value="15-20"><span>1520€</span></label>
<label><input type="radio" name="price_per_hour" value="over20"><span>Больше 20€</span></label>
<label><input type="radio" name="price_per_hour" value="other"><span>Свой вариант</span></label>
</div>
<input type="text" class="other-input" id="price_other" name="price_other" placeholder="Напишите сумму...">
</div>
<div class="question" data-index="7">
<label for="price_tiers">. Будут ли разные тарифы?</label>
<textarea id="price_tiers" name="price_tiers" placeholder="Например: будни дешевле / выходные дороже / ночной тариф / один тариф для всех"></textarea>
<span class="question-label">7. Будут ли разные тарифы?</span>
<div class="radio-group">
<label><input type="radio" name="price_tiers" value="single"><span>Один тариф для всех</span></label>
<label><input type="radio" name="price_tiers" value="weekday_weekend"><span>Будни дешевле, выходные дороже</span></label>
<label><input type="radio" name="price_tiers" value="evening"><span>Вечерний/ночной тариф отдельно</span></label>
<label><input type="radio" name="price_tiers" value="complex"><span>Несколько разных тарифов</span></label>
<label><input type="radio" name="price_tiers" value="later"><span>Решу позже</span></label>
</div>
</div>
<div class="question" data-index="8">
<label for="invoice">5г. Нужен ли счёт-фактура (lasku) для корпоративных клиентов?</label>
<textarea id="invoice" name="invoice" placeholder="Например: да / нет / не знаю пока"></textarea>
<span class="question-label">8. При отмене за 24+ часа — что происходит с деньгами?</span>
<div class="radio-group">
<label><input type="radio" name="refund_policy" value="full"><span>Полный возврат автоматически</span></label>
<label><input type="radio" name="refund_policy" value="partial"><span>Возврат за вычетом комиссии</span></label>
<label><input type="radio" name="refund_policy" value="reschedule"><span>Нет возврата, только перенос на другую дату</span></label>
<label><input type="radio" name="refund_policy" value="no_refund"><span>Возврат не предусмотрен</span></label>
</div>
</div>
</section>
<!-- О бронировании -->
<section class="section" data-section-id="booking">
<div class="section-header">
<h2>О бронировании</h2>
</div>
<p style="font-size:0.88rem;color:#888;margin-bottom:16px;">Вы указали: <em>минимум 1 час, максимум 1 день.</em></p>
<div class="question" data-index="9">
<label for="closing_time">3а. Ворота закрываются автоматически в 23:00 или клиент может бронировать до конца суток?</label>
<textarea id="closing_time" name="closing_time" placeholder="Например: последний слот должен заканчиваться в 23:00 / можно до полуночи"></textarea>
</div>
<div class="question" data-index="10">
<label for="advance_booking">3б. Как далеко вперёд можно бронировать?</label>
<textarea id="advance_booking" name="advance_booking" placeholder="Например: максимум 30 дней / 2 месяца / без ограничений"></textarea>
<span class="question-label">9. Нужен ли счёт-фактура (lasku) для корпоративных клиентов?</span>
<div class="radio-group">
<label><input type="radio" name="invoice" value="yes"><span>Да</span></label>
<label><input type="radio" name="invoice" value="no"><span>Нет</span></label>
<label><input type="radio" name="invoice" value="later"><span>Решу позже</span></label>
</div>
</div>
</section>
<!-- О PIN-коде -->
<!-- 3. PIN и ворота -->
<section class="section" data-section-id="pin">
<div class="section-header">
<h2>О PIN-коде и уведомлениях</h2>
<div class="section-header"><h2>PIN-код и ворота</h2></div>
<p class="context-note">Наше приложение будет автоматически генерировать PIN и отправлять его в систему замка. Нам важно знать какую систему вы планируете установить — чтобы подключить к ней наше приложение.</p>
<div class="question" data-index="10">
<span class="question-label">10. Какую систему замка/ворот планируете установить?</span>
<div class="radio-group">
<label><input type="radio" name="lock_system" value="nuki"><span>Nuki</span></label>
<label><input type="radio" name="lock_system" value="yale"><span>Yale</span></label>
<label><input type="radio" name="lock_system" value="abloy"><span>Abloy (финский стандарт)</span></label>
<label><input type="radio" name="lock_system" value="unknown"><span>Ещё не выбрала, нужна рекомендация</span></label>
<label><input type="radio" name="lock_system" value="other"><span>Другая система</span></label>
</div>
<input type="text" class="other-input" id="lock_other" name="lock_other" placeholder="Напишите марку/модель...">
</div>
<p style="font-size:0.88rem;color:#888;margin-bottom:16px;">Вы указали: <em>система генерирует PIN автоматически после оплаты.</em></p>
<div class="question" data-index="11">
<label for="pin_delivery">9а. Как клиент получает PIN — по email или в мобильном приложении?</label>
<textarea id="pin_delivery" name="pin_delivery" placeholder="Например: по email / в приложении / SMS"></textarea>
<span class="question-label">11. PIN-код — сколько цифр?</span>
<div class="radio-group">
<label><input type="radio" name="pin_length" value="4"><span>4 цифры</span></label>
<label><input type="radio" name="pin_length" value="6"><span>6 цифр</span></label>
<label><input type="radio" name="pin_length" value="advice"><span>Посоветуйте</span></label>
</div>
</div>
<div class="question" data-index="12">
<label for="time_warning">9б. Нужно ли уведомление клиенту когда время заканчивается?</label>
<textarea id="time_warning" name="time_warning" placeholder="Например: да, за 15 минут / да, за 30 минут / нет"></textarea>
</div>
<div class="question" data-index="13">
<label for="park_signal">9в. Будет ли сигнал на территории парка когда время вышло?</label>
<textarea id="park_signal" name="park_signal" placeholder="Например: да, лампочка / звуковой сигнал / нет, только уведомление в телефоне"></textarea>
<span class="question-label">12. Как клиент получает PIN после оплаты?</span>
<div class="radio-group">
<label><input type="radio" name="pin_delivery" value="email"><span>По email</span></label>
<label><input type="radio" name="pin_delivery" value="app"><span>В приложении (пуш-уведомление)</span></label>
<label><input type="radio" name="pin_delivery" value="both"><span>И email и в приложении</span></label>
<label><input type="radio" name="pin_delivery" value="sms"><span>По SMS</span></label>
</div>
</div>
</section>
<!-- Об управлении -->
<section class="section" data-section-id="admin">
<div class="section-header">
<h2>Об управлении бронированиями</h2>
<!-- 4. Уведомления -->
<section class="section" data-section-id="notifications">
<div class="section-header"><h2>Уведомления</h2></div>
<div class="question" data-index="13">
<span class="question-label">13. Подтверждение бронирования клиенту сразу после оплаты?</span>
<div class="radio-group">
<label><input type="radio" name="booking_confirm" value="email"><span>Да, по email</span></label>
<label><input type="radio" name="booking_confirm" value="sms"><span>Да, по SMS</span></label>
<label><input type="radio" name="booking_confirm" value="both"><span>И email и SMS</span></label>
<label><input type="radio" name="booking_confirm" value="no"><span>Не нужно</span></label>
</div>
</div>
<div class="question" data-index="14">
<label for="admin_view">10а. Как вы хотите видеть список бронирований?</label>
<textarea id="admin_view" name="admin_view" placeholder="Например: достаточно письма на email / нужна отдельная панель управления / оба варианта"></textarea>
<span class="question-label">14. Напоминание клиенту перед началом слота — за сколько?</span>
<div class="radio-group">
<label><input type="radio" name="reminder_before" value="24h"><span>За 24 часа</span></label>
<label><input type="radio" name="reminder_before" value="1h"><span>За 1 час</span></label>
<label><input type="radio" name="reminder_before" value="30min"><span>За 30 минут</span></label>
<label><input type="radio" name="reminder_before" value="no"><span>Не нужно</span></label>
</div>
</div>
<div class="question" data-index="15">
<label for="block_dates">10б. Нужна ли возможность заблокировать определённые дни?</label>
<textarea id="block_dates" name="block_dates" placeholder="Например: да, для техработ и отпуска / нет"></textarea>
<span class="question-label">15. Предупреждение когда время заканчивается — за сколько минут?</span>
<div class="radio-group">
<label><input type="radio" name="time_warning" value="30min"><span>За 30 минут</span></label>
<label><input type="radio" name="time_warning" value="15min"><span>За 15 минут</span></label>
<label><input type="radio" name="time_warning" value="5min"><span>За 5 минут</span></label>
<label><input type="radio" name="time_warning" value="no"><span>Не нужно</span></label>
</div>
</div>
<div class="question" data-index="16">
<label for="support_phone">10в. Если PIN не сработает — есть ли телефон поддержки, который мы укажем на сайте?</label>
<textarea id="support_phone" name="support_phone" placeholder="Например: да, мой номер +358 XX XXX XXXX / нет, только email"></textarea>
<span class="question-label">16. Вы (Сирпа) получаете уведомление о каждом новом бронировании?</span>
<div class="radio-group">
<label><input type="radio" name="admin_notify" value="email"><span>Да, по email</span></label>
<label><input type="radio" name="admin_notify" value="sms"><span>Да, по SMS</span></label>
<label><input type="radio" name="admin_notify" value="both"><span>И email и SMS</span></label>
<label><input type="radio" name="admin_notify" value="no"><span>Нет, буду сама проверять</span></label>
</div>
</div>
</section>
<!-- Юридическое -->
<section class="section" data-section-id="legal">
<div class="section-header">
<h2>Юридическое</h2>
</div>
<p style="font-size:0.88rem;color:#888;margin-bottom:16px;">В Финляндии по закону ЕС (GDPR) сайт обязан иметь политику конфиденциальности.</p>
<!-- 5. Панель управления -->
<section class="section" data-section-id="admin">
<div class="section-header"><h2>Ваша панель управления</h2></div>
<div class="question">
<label for="legal_terms">13а. Вы составите текст правил пользования парком самостоятельно или нам подготовить шаблон?</label>
<textarea id="legal_terms" name="legal_terms" placeholder="Например: подготовьте шаблон, я дополню / составлю сам / что это вообще такое?"></textarea>
<div class="question" data-index="17">
<span class="question-label">17. Что хотите видеть в своей панели? (можно несколько)</span>
<div class="checkbox-group">
<label><input type="checkbox" name="admin_features" value="bookings_list"><span>Список всех бронирований</span></label>
<label><input type="checkbox" name="admin_features" value="revenue"><span>Статистику доходов</span></label>
<label><input type="checkbox" name="admin_features" value="manual_booking"><span>Создать/отменить бронирование вручную</span></label>
<label><input type="checkbox" name="admin_features" value="block_dates"><span>Блокировка дат (техработы, отпуск)</span></label>
<label><input type="checkbox" name="admin_features" value="change_price"><span>Изменение цен</span></label>
<label><input type="checkbox" name="admin_features" value="clients"><span>История клиентов</span></label>
</div>
</div>
<div class="question" data-index="18">
<span class="question-label">18. Нужна ли история клиентов (кто бронировал раньше)?</span>
<div class="radio-group">
<label><input type="radio" name="client_history" value="yes"><span>Да</span></label>
<label><input type="radio" name="client_history" value="no"><span>Нет</span></label>
<label><input type="radio" name="client_history" value="later"><span>Решу позже</span></label>
</div>
</div>
</section>
<!-- О дизайне -->
<!-- 6. Формат приложения -->
<section class="section" data-section-id="format">
<div class="section-header"><h2>Формат приложения</h2></div>
<div class="question" data-index="19">
<span class="question-label">19. Какой формат приложения?</span>
<div class="variant-card">
<label>
<input type="radio" name="product_format" value="web">
<div>
<div class="variant-title">Только сайт <span class="variant-badge badge-green">Быстро, дёшево</span></div>
<div class="variant-desc">Работает в браузере телефона, компьютера, планшета. Не нужно ничего устанавливать.</div>
</div>
</label>
</div>
<div class="variant-card recommended">
<label>
<input type="radio" name="product_format" value="web_pwa">
<div>
<div class="variant-title">Сайт + установка на телефон <span class="variant-badge badge-green">Рекомендуем</span></div>
<div class="variant-desc">Работает как приложение, но не требует App Store. Клиент добавляет на экран — и всё.</div>
</div>
</label>
</div>
<div class="variant-card">
<label>
<input type="radio" name="product_format" value="ios_android">
<div>
<div class="variant-title">Приложение для iPhone и Android <span class="variant-badge badge-orange">Дороже, дольше</span></div>
<div class="variant-desc">Полноценное приложение в App Store и Google Play. Максимальный комфорт для клиентов.</div>
</div>
</label>
</div>
</div>
<div class="question" data-index="20">
<span class="question-label">20. Язык приложения?</span>
<div class="radio-group">
<label><input type="radio" name="language" value="fi"><span>Только финский</span></label>
<label><input type="radio" name="language" value="fi_en"><span>Финский + английский</span></label>
<label><input type="radio" name="language" value="fi_en_ru"><span>Финский + английский + русский</span></label>
</div>
</div>
</section>
<!-- 7. Дизайн -->
<section class="section" data-section-id="design">
<div class="section-header">
<h2>О дизайне</h2>
</div>
<p style="font-size:0.88rem;color:#888;margin-bottom:16px;">Вы указали: <em>название Saarijärven koirapuisto, логотипа пока нет.</em></p>
<div class="section-header"><h2>Дизайн</h2></div>
<div class="question">
<label for="logo">14а. Логотип нужен? Если да — вы найдёте дизайнера или нам создать простой вариант?</label>
<textarea id="logo" name="logo" placeholder="Например: да, сделайте простой / найду дизайнера сам / логотип не нужен"></textarea>
<div class="question" data-index="21">
<span class="question-label">21. Логотип?</span>
<div class="radio-group">
<label><input type="radio" name="logo" value="make_simple"><span>Сделайте простой логотип</span></label>
<label><input type="radio" name="logo" value="own_designer"><span>Найду дизайнера сама</span></label>
<label><input type="radio" name="logo" value="not_needed"><span>Логотип не нужен</span></label>
<label><input type="radio" name="logo" value="later"><span>Решу позже</span></label>
</div>
</div>
<div class="question">
<label for="design_style">14б. Есть ли пожелания по цветам или стилю сайта?</label>
<textarea id="design_style" name="design_style" placeholder="Например: природные цвета, зелёный / современный минимализм / нет предпочтений"></textarea>
<div class="question" data-index="22">
<span class="question-label">22. Цвета и стиль сайта?</span>
<div class="radio-group">
<label><input type="radio" name="design_style" value="nature"><span>Природные (зелёный, коричневый, тёплые тона)</span></label>
<label><input type="radio" name="design_style" value="minimal"><span>Современный минимализм (белый/чёрный)</span></label>
<label><input type="radio" name="design_style" value="bright"><span>Яркий и дружелюбный</span></label>
<label><input type="radio" name="design_style" value="no_pref"><span>Нет предпочтений, доверяю вам</span></label>
<label><input type="radio" name="design_style" value="other"><span>Свой вариант</span></label>
</div>
<input type="text" class="other-input" id="design_other" name="design_other" placeholder="Опишите пожелания...">
</div>
</section>
<!-- О формате продукта -->
<section class="section" data-section-id="product">
<div class="section-header">
<h2>О формате продукта</h2>
</div>
<p style="font-size:0.88rem;color:#888;margin-bottom:16px;">Мы можем реализовать проект в нескольких вариантах:</p>
<!-- 8. Юридическое -->
<section class="section" data-section-id="legal">
<div class="section-header"><h2>Юридическое</h2></div>
<p class="context-note">По закону ЕС (GDPR) любой финский сайт обязан иметь политику конфиденциальности и правила использования.</p>
<div style="background:#f8fffe;border:1.5px solid #c8e6c9;border-radius:10px;padding:16px 20px;margin-bottom:12px;">
<strong>Вариант 1 — Простой сайт</strong> <em style="color:#888;">(быстро, дёшево)</em><br>
<span style="font-size:0.9rem;">Сайт на финском: календарь слотов, онлайн-оплата, PIN по email. Работает на любом телефоне через браузер.</span>
<div class="question" data-index="23">
<span class="question-label">23. Правила пользования парком — кто составляет?</span>
<div class="radio-group">
<label><input type="radio" name="terms" value="you_template"><span>Подготовьте шаблон, я дополню своими правилами</span></label>
<label><input type="radio" name="terms" value="me"><span>Составлю сама</span></label>
<label><input type="radio" name="terms" value="you_full"><span>Подготовьте полностью</span></label>
<label><input type="radio" name="terms" value="what_is_it"><span>Не знаю что это — объясните</span></label>
</div>
</div>
<div style="background:#f0faf4;border:2px solid #2d6a4f;border-radius:10px;padding:16px 20px;margin-bottom:12px;">
<strong>Вариант 2 — Продвинутый сайт</strong> <em style="color:#2d6a4f;">(рекомендуем)</em><br>
<span style="font-size:0.9rem;">То же + SMS-уведомления, напоминания перед концом слота, панель управления для вас (блокировка дат, просмотр бронирований, статистика).</span>
</div>
<div style="background:#fff8f0;border:1.5px solid #ffe0b2;border-radius:10px;padding:16px 20px;margin-bottom:20px;">
<strong>Вариант 3 — Сайт + мобильное приложение</strong> <em style="color:#888;">(максимум)</em><br>
<span style="font-size:0.9rem;">То же + приложение для iOS и Android. PIN приходит в приложение, уведомления через приложение. Самый удобный вариант для клиентов.</span>
</div>
<div class="question">
<label for="product_choice">16а. Какой вариант вам ближе?</label>
<textarea id="product_choice" name="product_choice" placeholder="Например: вариант 2 / вариант 3 / хочу обсудить на звонке / не знаю пока"></textarea>
<div class="question" data-index="24">
<span class="question-label">24. Политика конфиденциальности (GDPR) — кто составляет?</span>
<div class="radio-group">
<label><input type="radio" name="privacy" value="you_template"><span>Подготовьте шаблон, я дополню</span></label>
<label><input type="radio" name="privacy" value="me"><span>Составлю сама</span></label>
<label><input type="radio" name="privacy" value="you_full"><span>Подготовьте полностью</span></label>
<label><input type="radio" name="privacy" value="what_is_it"><span>Не знаю что это — объясните</span></label>
</div>
</div>
</section>
<!-- Контактные данные -->
<!-- Контакты -->
<section class="section" data-section-id="contacts">
<div class="section-header">
<h2>Ваши контактные данные</h2>
</div>
<div class="section-header"><h2>Ваши контактные данные</h2></div>
<div class="question">
<label for="client_name">Имя</label>
<input type="text" id="client_name" name="client_name" required placeholder="Ваше имя" value="Sirpa Lehtinen">
@@ -249,11 +474,10 @@
<button type="submit" id="submitBtn">Отправить ответы</button>
<p class="submit-note">После отправки мы свяжемся с вами в течение 1-2 рабочих дней.</p>
</form>
</div>
<!-- Панель предпросмотра -->
<!-- Предпросмотр -->
<div class="preview-column">
<div class="preview-panel">
<div class="preview-header">Предпросмотр</div>
@@ -262,7 +486,6 @@
</div>
</div>
</div>
</div>
<script src="script2.js"></script>
+113 -54
View File
@@ -1,29 +1,37 @@
// ─── Поля прогресса ───────────────────────────────────────────────────────────
const TRACKED_FIELDS = [
'gate_type','gate_electricity','gate_internet','gate_camera',
'payment_provider','price_per_hour','price_tiers','invoice',
'closing_time','advance_booking',
'pin_delivery','time_warning','park_signal',
'admin_view','block_dates','support_phone',
'product_choice'
];
const TOTAL = 24;
// ─── Инициализация ────────────────────────────────────────────────────────────
document.addEventListener('DOMContentLoaded', function () {
updateProgress();
updatePreview();
document.getElementById('client_email').addEventListener('input', function () {
document.getElementById('_replyto').value = this.value;
});
document.getElementById('_replyto').value = document.getElementById('client_email').value;
document.getElementById('questionnaireForm').addEventListener('change', function () {
updateProgress();
updatePreview();
handleConditionals();
});
document.getElementById('questionnaireForm').addEventListener('input', function () {
updateProgress();
updatePreview();
});
document.getElementById('questionnaireForm').addEventListener('submit', function () {
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();
@@ -42,67 +50,118 @@ document.addEventListener('DOMContentLoaded', function () {
}) + ' (UTC+3, Финляндия)';
}, true);
document.getElementById('btnSave').addEventListener('click', function () {
showFlash('Сохранено!');
});
handleConditionals();
});
// ─── Прогресс ─────────────────────────────────────────────────────────────────
function updateProgress() {
let filled = 0;
TRACKED_FIELDS.forEach(name => {
const el = document.getElementById(name);
if (el && el.value.trim()) filled++;
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');
}
});
const total = TRACKED_FIELDS.length;
const pct = Math.round((filled / total) * 100);
document.getElementById('progressFill').style.width = pct + '%';
document.getElementById('progressText').textContent = `Заполнено: ${filled} из ${total}`;
}
// ─── Предпросмотр ─────────────────────────────────────────────────────────────
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 sections = document.querySelectorAll('.section');
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 = '';
sections.forEach(section => {
const title = section.querySelector('h2');
const questions = section.querySelectorAll('.question');
let sectionHtml = '';
questions.forEach(q => {
const input = q.querySelector('textarea, input[type="text"], input[type="email"], input[type="tel"]');
const label = q.querySelector('label');
if (!input || !label) return;
const val = input.value.trim();
if (val) {
sectionHtml += `<div class="preview-item">
<div class="preview-item-label">${label.textContent.trim()}</div>
<div class="preview-item-value">${escapeHtml(val)}</div>
</div>`;
}
});
if (sectionHtml && title) {
html += `<div class="preview-section">
<div class="preview-section-title">${title.textContent.trim()}</div>
${sectionHtml}
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>';
}
// ─── Flash ────────────────────────────────────────────────────────────────────
function escapeHtml(str) {
return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}
function showFlash(msg) {
const el = document.getElementById('flash');
el.textContent = msg;
el.classList.add('show');
setTimeout(() => el.classList.remove('show'), 2500);
}
function escapeHtml(str) {
return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}