feat: rebuild questionnaire2 with radio buttons and checkboxes
This commit is contained in:
+351
-128
@@ -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>10–15€</span></label>
|
||||
<label><input type="radio" name="price_per_hour" value="15-20"><span>15–20€</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">5в. Будут ли разные тарифы?</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
@@ -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,'&').replace(/</g,'<').replace(/>/g,'>');
|
||||
}
|
||||
|
||||
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,'&').replace(/</g,'<').replace(/>/g,'>');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user