533 lines
28 KiB
HTML
533 lines
28 KiB
HTML
{% load i18n %}
|
|
{% load survey_filters %}
|
|
{% load static %}
|
|
<!DOCTYPE html>
|
|
<html lang="{{ language }}" dir="{% if language == 'ar' %}rtl{% else %}ltr{% endif %}">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
<title>{% if language == 'ar' %}استبيان رضا المرضى{% else %}Patient Satisfaction Survey{% endif %} - PX360</title>
|
|
<link rel="stylesheet" href="{% static 'dist/css/tailwind.css' %}">
|
|
<style>
|
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
|
body { font-family: 'Inter', sans-serif; }
|
|
|
|
.rating-star { transition: all 0.2s; cursor: pointer; }
|
|
.rating-star svg { transition: all 0.15s ease; }
|
|
.rating-star:hover svg { transform: scale(1.2); }
|
|
.nps-button { transition: all 0.15s; }
|
|
.nps-button.selected {
|
|
background: #005696;
|
|
color: white;
|
|
border-color: #005696;
|
|
transform: scale(1.08);
|
|
}
|
|
.likert-option.selected { color: #005696; font-weight: 600; }
|
|
|
|
.form-input {
|
|
border: 2px solid #e2e8f0;
|
|
border-radius: 0.75rem;
|
|
padding: 0.75rem 1rem;
|
|
width: 100%;
|
|
transition: all 0.2s;
|
|
outline: none;
|
|
}
|
|
.form-input:focus {
|
|
border-color: #005696;
|
|
box-shadow: 0 0 0 3px rgba(0, 86, 150, 0.1);
|
|
}
|
|
|
|
.question-block {
|
|
border-bottom: 1px solid #e2e8f0;
|
|
padding-bottom: 1.5rem;
|
|
}
|
|
.question-block:last-of-type {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.validation-error {
|
|
color: #ef4444;
|
|
font-size: 0.875rem;
|
|
font-weight: 500;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
.form-hidden {
|
|
display: none !important;
|
|
}
|
|
.form-visible {
|
|
display: block;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="bg-gray-50 min-h-screen">
|
|
|
|
<!-- Language Toggle -->
|
|
<div class="fixed top-4 {% if language == 'ar' %}left-4{% else %}right-4{% endif %} z-50">
|
|
<a href="?lang={% if language == 'ar' %}en{% else %}ar{% endif %}"
|
|
class="flex items-center gap-1.5 bg-white text-navy px-4 py-2 rounded-lg shadow-sm hover:shadow transition-shadow text-sm font-semibold border border-gray-200">
|
|
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<circle cx="12" cy="12" r="10"/><path d="M2 12h20"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>
|
|
</svg>
|
|
{% if language == 'ar' %}English{% else %}العربية{% endif %}
|
|
</a>
|
|
</div>
|
|
|
|
<div class="max-w-3xl mx-auto w-full bg-white min-h-screen shadow-sm">
|
|
|
|
<!-- Survey Header -->
|
|
<div class="p-6 md:p-8 pb-4 flex justify-between items-start border-b border-gray-200 bg-white">
|
|
<div class="flex items-center">
|
|
<img src="{% static 'img/HH_P_H_Logo.png' %}" alt="HH Hospital" class="h-10 md:h-12 object-contain">
|
|
</div>
|
|
<div class="text-end">
|
|
<h1 class="text-xl md:text-2xl font-bold text-[#2c3e50]">{% if language == 'ar' %}استبيان رضا{% else %}Patient{% endif %}</h1>
|
|
<h1 class="text-xl md:text-2xl font-bold text-[#2c3e50]">{% if language == 'ar' %}المرضى{% else %}Satisfaction Survey{% endif %}</h1>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Instructions (if any) -->
|
|
{% if language == 'ar' and survey.survey_template.instructions_ar or language != 'ar' and survey.survey_template.instructions_en %}
|
|
<div class="px-6 md:px-8 py-4 bg-blue-50 border-b border-blue-100">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<svg class="w-5 h-5 text-navy" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/>
|
|
</svg>
|
|
<h2 class="font-bold text-navy">{% if language == 'ar' %}تعليمات{% else %}Instructions{% endif %}</h2>
|
|
</div>
|
|
<p class="text-sm text-gray-700 leading-relaxed whitespace-pre-line">
|
|
{% if language == 'ar' %}{{ survey.survey_template.instructions_ar }}{% else %}{{ survey.survey_template.instructions_en }}{% endif %}
|
|
</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Consent (if required) -->
|
|
{% if survey.survey_template.requires_consent %}
|
|
{% if language == 'ar' and survey.survey_template.consent_text_ar or language != 'ar' and survey.survey_template.consent_text_en %}
|
|
<div class="px-6 md:px-8 py-4 bg-amber-50 border-b border-amber-100">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<svg class="w-5 h-5 text-amber-600" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
|
</svg>
|
|
<h2 class="font-bold text-amber-800">{% if language == 'ar' %}الموافقة{% else %}Consent{% endif %}</h2>
|
|
</div>
|
|
<p class="text-sm text-gray-700 leading-relaxed whitespace-pre-line mb-3">
|
|
{% if language == 'ar' %}{{ survey.survey_template.consent_text_ar }}{% else %}{{ survey.survey_template.consent_text_en }}{% endif %}
|
|
</p>
|
|
<label class="flex items-center gap-2 cursor-pointer">
|
|
<input type="checkbox" id="consent-checkbox" class="w-5 h-5 accent-navy rounded">
|
|
<span class="text-sm font-semibold text-gray-800">
|
|
{% if language == 'ar' %}أوافق على الشروط المذكورة أعلاه{% else %}I agree to the terms above{% endif %}
|
|
</span>
|
|
</label>
|
|
<div id="consent-error" class="validation-error hidden mt-2">
|
|
{% if language == 'ar' %}يجب الموافقة قبل المتابعة{% else %}You must agree to continue{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
|
|
<!-- Survey Form -->
|
|
<form id="survey-form" class="px-6 md:px-8 py-6 space-y-6 {% if survey.survey_template.requires_consent %}form-hidden{% else %}form-visible{% endif %}">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="language" value="{{ language }}">
|
|
|
|
<!-- Questions -->
|
|
{% for question in questions %}
|
|
{% if not question.is_conditional %}
|
|
<div class="question-block" data-question-id="{{ question.id }}" data-required="{{ question.is_required|yesno:'true,false' }}">
|
|
<div class="flex items-start gap-3 mb-4">
|
|
<span class="font-bold text-navy text-xl flex-shrink-0">{{ forloop.counter }}.</span>
|
|
<div>
|
|
<p class="font-bold text-base leading-relaxed text-gray-800">
|
|
{% if language == 'ar' and question.text_ar %}{{ question.text_ar }}{% else %}{{ question.text }}{% endif %}
|
|
{% if question.is_required %}<span class="text-red-500 ms-1">*</span>{% endif %}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ms-8 md:ms-10">
|
|
{% if question.question_type == 'rating' %}
|
|
<div class="flex gap-3 justify-center flex-wrap">
|
|
{% for i in "12345" %}
|
|
<button type="button" class="rating-star text-gray-300 focus:outline-none" data-value="{{ i }}" onclick="selectRating(this)">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg>
|
|
</button>
|
|
{% endfor %}
|
|
</div>
|
|
<input type="hidden" name="question_{{ question.id }}" id="answer_{{ question.id }}" data-question-id="{{ question.id }}">
|
|
|
|
{% elif question.question_type == 'nps' %}
|
|
<div class="nps-scale flex gap-2 justify-center flex-wrap">
|
|
{% for i in "012345678910" %}
|
|
<button type="button" class="nps-button w-9 h-9 md:w-10 md:h-10 border border-gray-300 rounded-lg bg-white font-semibold text-sm focus:outline-none hover:border-navy hover:text-navy" data-value="{{ i }}" onclick="selectNPS(this)">
|
|
{{ i }}
|
|
</button>
|
|
{% endfor %}
|
|
</div>
|
|
<div class="flex justify-between text-xs text-gray-400 mt-2 px-2">
|
|
<span>{% if language == 'ar' %}غير محتمل{% else %}Not likely{% endif %}</span>
|
|
<span>{% if language == 'ar' %}محتمل جداً{% else %}Very likely{% endif %}</span>
|
|
</div>
|
|
<input type="hidden" name="question_{{ question.id }}" id="answer_{{ question.id }}" data-question-id="{{ question.id }}">
|
|
|
|
{% elif question.question_type == 'likert' %}
|
|
<div class="space-y-2">
|
|
{% if language == 'ar' %}
|
|
<label class="likert-option flex items-center cursor-pointer gap-3 py-2 hover:text-navy transition-colors text-base">
|
|
<input type="radio" name="radio_{{ question.id }}" value="1" class="w-5 h-5 accent-navy" onchange="selectOption(this)">
|
|
<span>1: أرفض بشدة</span>
|
|
</label>
|
|
<label class="likert-option flex items-center cursor-pointer gap-3 py-2 hover:text-navy transition-colors text-base">
|
|
<input type="radio" name="radio_{{ question.id }}" value="2" class="w-5 h-5 accent-navy" onchange="selectOption(this)">
|
|
<span>2: أرفض</span>
|
|
</label>
|
|
<label class="likert-option flex items-center cursor-pointer gap-3 py-2 hover:text-navy transition-colors text-base">
|
|
<input type="radio" name="radio_{{ question.id }}" value="3" class="w-5 h-5 accent-navy" onchange="selectOption(this)">
|
|
<span>3: محايد</span>
|
|
</label>
|
|
<label class="likert-option flex items-center cursor-pointer gap-3 py-2 hover:text-navy transition-colors text-base">
|
|
<input type="radio" name="radio_{{ question.id }}" value="4" class="w-5 h-5 accent-navy" onchange="selectOption(this)">
|
|
<span>4: أوافق</span>
|
|
</label>
|
|
<label class="likert-option flex items-center cursor-pointer gap-3 py-2 hover:text-navy transition-colors text-base">
|
|
<input type="radio" name="radio_{{ question.id }}" value="5" class="w-5 h-5 accent-navy" onchange="selectOption(this)">
|
|
<span>5: أوافق بشدة</span>
|
|
</label>
|
|
{% else %}
|
|
<label class="likert-option flex items-center cursor-pointer gap-3 py-2 hover:text-navy transition-colors text-base">
|
|
<input type="radio" name="radio_{{ question.id }}" value="1" class="w-5 h-5 accent-navy" onchange="selectOption(this)">
|
|
<span>1: Strongly Disagree</span>
|
|
</label>
|
|
<label class="likert-option flex items-center cursor-pointer gap-3 py-2 hover:text-navy transition-colors text-base">
|
|
<input type="radio" name="radio_{{ question.id }}" value="2" class="w-5 h-5 accent-navy" onchange="selectOption(this)">
|
|
<span>2: Disagree</span>
|
|
</label>
|
|
<label class="likert-option flex items-center cursor-pointer gap-3 py-2 hover:text-navy transition-colors text-base">
|
|
<input type="radio" name="radio_{{ question.id }}" value="3" class="w-5 h-5 accent-navy" onchange="selectOption(this)">
|
|
<span>3: Neutral</span>
|
|
</label>
|
|
<label class="likert-option flex items-center cursor-pointer gap-3 py-2 hover:text-navy transition-colors text-base">
|
|
<input type="radio" name="radio_{{ question.id }}" value="4" class="w-5 h-5 accent-navy" onchange="selectOption(this)">
|
|
<span>4: Agree</span>
|
|
</label>
|
|
<label class="likert-option flex items-center cursor-pointer gap-3 py-2 hover:text-navy transition-colors text-base">
|
|
<input type="radio" name="radio_{{ question.id }}" value="5" class="w-5 h-5 accent-navy" onchange="selectOption(this)">
|
|
<span>5: Strongly Agree</span>
|
|
</label>
|
|
{% endif %}
|
|
</div>
|
|
<input type="hidden" name="question_{{ question.id }}" id="answer_{{ question.id }}" data-question-id="{{ question.id }}">
|
|
|
|
{% elif question.question_type == 'yes_no' %}
|
|
<div class="flex gap-6">
|
|
<label class="flex items-center cursor-pointer gap-3 py-2">
|
|
<input type="radio" name="radio_{{ question.id }}" value="yes" class="w-5 h-5 accent-navy" onchange="selectOption(this)">
|
|
<span class="text-lg">{% if language == 'ar' %}نعم{% else %}Yes{% endif %}</span>
|
|
</label>
|
|
<label class="flex items-center cursor-pointer gap-3 py-2">
|
|
<input type="radio" name="radio_{{ question.id }}" value="no" class="w-5 h-5 accent-navy" onchange="selectOption(this)">
|
|
<span class="text-lg">{% if language == 'ar' %}لا{% else %}No{% endif %}</span>
|
|
</label>
|
|
</div>
|
|
<input type="hidden" name="question_{{ question.id }}" id="answer_{{ question.id }}" data-question-id="{{ question.id }}">
|
|
|
|
{% elif question.question_type == 'multiple_choice' %}
|
|
<div class="space-y-2">
|
|
{% for choice in question.choices_json %}
|
|
<label class="likert-option flex items-center cursor-pointer gap-3 py-2 hover:text-navy transition-colors text-base">
|
|
<input type="radio" name="radio_{{ question.id }}" value="{{ choice.value|default:choice }}" class="w-5 h-5 accent-navy" onchange="selectOption(this)">
|
|
<span>{% if language == 'ar' and choice.label_ar %}{{ choice.label_ar }}{% else %}{{ choice.label|default:choice }}{% endif %}</span>
|
|
</label>
|
|
{% endfor %}
|
|
</div>
|
|
<input type="hidden" name="question_{{ question.id }}" id="answer_{{ question.id }}" data-question-id="{{ question.id }}">
|
|
|
|
{% elif question.question_type == 'text' %}
|
|
<input type="text" name="question_{{ question.id }}" id="answer_{{ question.id }}" data-question-id="{{ question.id }}"
|
|
class="form-input text-base" placeholder="{% if language == 'ar' %}أدخل إجابتك هنا...{% else %}Type your answer here...{% endif %}">
|
|
|
|
{% elif question.question_type == 'textarea' %}
|
|
<textarea name="question_{{ question.id }}" id="answer_{{ question.id }}" data-question-id="{{ question.id }}"
|
|
class="form-input text-base resize-none" rows="3" placeholder="{% if language == 'ar' %}أدخل إجابتك هنا...{% else %}Type your answer here...{% endif %}"></textarea>
|
|
{% endif %}
|
|
|
|
<div class="validation-error hidden" id="error_{{ question.id }}">
|
|
{% if language == 'ar' %}هذا السؤال مطلوب{% else %}This question is required{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
<!-- Comment Section -->
|
|
<div class="question-block">
|
|
<p class="font-bold text-base flex items-center gap-2 mb-2">
|
|
{% if language == 'ar' %}
|
|
ملاحظات إضافية (اختياري)
|
|
{% else %}
|
|
Additional Comments (Optional)
|
|
{% endif %}
|
|
</p>
|
|
<p class="text-sm text-gray-500 mb-3">
|
|
{% if language == 'ar' %}
|
|
شارك أي ملاحظات أو اقتراحات إضافية حول تجربتك
|
|
{% else %}
|
|
Share any additional comments or suggestions about your experience
|
|
{% endif %}
|
|
</p>
|
|
<textarea name="comment" id="survey_comment"
|
|
class="form-input resize-none" rows="3"
|
|
placeholder="{% if language == 'ar' %}اكتب ملاحظاتك هنا...{% else %}Write your comments here...{% endif %}"></textarea>
|
|
</div>
|
|
|
|
<!-- Submit Button -->
|
|
<div class="pt-4">
|
|
<button type="submit" id="submitBtn" class="w-full bg-navy hover:bg-navy-dark text-white px-8 py-4 rounded-xl font-bold text-lg transition-all flex items-center justify-center gap-2" {% if survey.survey_template.requires_consent %}disabled{% endif %}>
|
|
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/>
|
|
</svg>
|
|
{% if language == 'ar' %}إرسال الاستبيان{% else %}Submit Survey{% endif %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
<!-- Footer -->
|
|
<div class="bg-gray-50 border-t border-gray-200 p-6 text-center space-y-1">
|
|
<p class="text-sm font-medium text-gray-600">
|
|
{% if language == 'ar' %}
|
|
شكراً لمشاركتك في تحسين تجربة المرضى
|
|
{% else %}
|
|
Thank you for taking the time to complete our patient satisfaction survey.
|
|
{% endif %}
|
|
</p>
|
|
<p class="text-sm font-medium text-gray-500">
|
|
{% if language == 'ar' %}
|
|
ملاحظاتك ستساعدنا في تقديم رعاية أفضل
|
|
{% else %}
|
|
Your feedback will help us improve patient experience.
|
|
{% endif %}
|
|
</p>
|
|
<p class="text-xs font-medium text-gray-400 mt-2">
|
|
Powered by <a href="https://tenhal.sa" target="_blank" class="text-gray-500 hover:underline">tenhal.sa</a>
|
|
</p>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
(function() {
|
|
var surveyToken = "{{ survey.access_token }}";
|
|
var language = "{{ language }}";
|
|
var isRtl = language === 'ar';
|
|
|
|
var requiresConsent = {{ survey.survey_template.requires_consent|yesno:"true,false" }};
|
|
|
|
function getCsrf() {
|
|
var el = document.querySelector('input[name=csrfmiddlewaretoken]');
|
|
return el ? el.value : '';
|
|
}
|
|
|
|
// Consent checkbox handler
|
|
if (requiresConsent) {
|
|
var consentCheckbox = document.getElementById('consent-checkbox');
|
|
var surveyForm = document.getElementById('survey-form');
|
|
var submitBtn = document.getElementById('submitBtn');
|
|
|
|
if (consentCheckbox && surveyForm && submitBtn) {
|
|
consentCheckbox.addEventListener('change', function() {
|
|
if (this.checked) {
|
|
surveyForm.classList.remove('form-hidden');
|
|
surveyForm.classList.add('form-visible');
|
|
submitBtn.disabled = false;
|
|
|
|
// Clear consent error if visible
|
|
var consentError = document.getElementById('consent-error');
|
|
if (consentError) consentError.classList.add('hidden');
|
|
|
|
// Smooth scroll to form
|
|
setTimeout(function() {
|
|
surveyForm.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
}, 100);
|
|
} else {
|
|
surveyForm.classList.remove('form-visible');
|
|
surveyForm.classList.add('form-hidden');
|
|
submitBtn.disabled = true;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Rating selection
|
|
window.selectRating = function(star) {
|
|
var value = parseInt(star.getAttribute('data-value'));
|
|
var container = star.parentElement;
|
|
var stars = container.querySelectorAll('.rating-star');
|
|
var hiddenInput = container.parentElement.querySelector('input[type="hidden"]');
|
|
|
|
stars.forEach(function(s, i) {
|
|
var svg = s.querySelector('svg');
|
|
if (i < value) {
|
|
svg.setAttribute('fill', '#fbbf24');
|
|
svg.setAttribute('stroke', '#fbbf24');
|
|
s.classList.add('active');
|
|
} else {
|
|
svg.setAttribute('fill', 'none');
|
|
svg.setAttribute('stroke', '#94a3b8');
|
|
s.classList.remove('active');
|
|
}
|
|
});
|
|
|
|
if (hiddenInput) hiddenInput.value = value;
|
|
clearError(hiddenInput);
|
|
};
|
|
|
|
// NPS selection
|
|
window.selectNPS = function(button) {
|
|
var container = button.parentElement;
|
|
container.querySelectorAll('.nps-button').forEach(function(b) { b.classList.remove('selected'); });
|
|
button.classList.add('selected');
|
|
var hiddenInput = container.parentElement.querySelector('input[type="hidden"]');
|
|
if (hiddenInput) {
|
|
hiddenInput.value = button.getAttribute('data-value');
|
|
clearError(hiddenInput);
|
|
}
|
|
};
|
|
|
|
// Radio option selection
|
|
window.selectOption = function(radio) {
|
|
var questionBlock = radio.closest('.question-block');
|
|
if (questionBlock) {
|
|
questionBlock.querySelectorAll('.likert-option').forEach(function(opt) { opt.classList.remove('selected'); });
|
|
var label = radio.closest('.likert-option');
|
|
if (label) label.classList.add('selected');
|
|
}
|
|
var hiddenInput = questionBlock.querySelector('input[type="hidden"]');
|
|
if (hiddenInput) {
|
|
hiddenInput.value = radio.value;
|
|
clearError(hiddenInput);
|
|
}
|
|
};
|
|
|
|
function clearError(input) {
|
|
if (!input) return;
|
|
var questionBlock = input.closest('.question-block');
|
|
if (questionBlock) {
|
|
var errorEl = questionBlock.querySelector('.validation-error');
|
|
if (errorEl) errorEl.classList.add('hidden');
|
|
}
|
|
}
|
|
|
|
// Form validation
|
|
function validateForm() {
|
|
var isValid = true;
|
|
var questionBlocks = document.querySelectorAll('.question-block[data-required="true"]');
|
|
|
|
questionBlocks.forEach(function(block) {
|
|
var hiddenInput = block.querySelector('input[type="hidden"][data-question-id]');
|
|
var textInput = block.querySelector('input[type="text"][data-question-id], textarea[data-question-id]');
|
|
var errorEl = block.querySelector('.validation-error');
|
|
var hasValue = false;
|
|
|
|
if (hiddenInput) {
|
|
hasValue = hiddenInput.value && hiddenInput.value.trim() !== '';
|
|
} else if (textInput) {
|
|
hasValue = textInput.value && textInput.value.trim() !== '';
|
|
}
|
|
|
|
if (!hasValue) {
|
|
isValid = false;
|
|
if (errorEl) errorEl.classList.remove('hidden');
|
|
} else {
|
|
if (errorEl) errorEl.classList.add('hidden');
|
|
}
|
|
});
|
|
|
|
// Validate consent if required
|
|
if (requiresConsent) {
|
|
var consentCb = document.getElementById('consent-checkbox');
|
|
var consentError = document.getElementById('consent-error');
|
|
if (consentCb && !consentCb.checked) {
|
|
isValid = false;
|
|
if (consentError) consentError.classList.remove('hidden');
|
|
} else if (consentError) {
|
|
consentError.classList.add('hidden');
|
|
}
|
|
}
|
|
|
|
return isValid;
|
|
}
|
|
|
|
// Track survey start
|
|
var surveyStarted = false;
|
|
function trackSurveyStart() {
|
|
if (surveyStarted) return;
|
|
fetch('/surveys/s/' + surveyToken + '/track-start/', {
|
|
method: 'POST',
|
|
headers: { 'X-CSRFToken': getCsrf(), 'Content-Type': 'application/json' },
|
|
credentials: 'same-origin'
|
|
}).catch(function() {});
|
|
surveyStarted = true;
|
|
}
|
|
|
|
// Form submission
|
|
document.getElementById('survey-form').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
trackSurveyStart();
|
|
|
|
if (!validateForm()) {
|
|
// Scroll to first error
|
|
var firstError = document.querySelector('.validation-error:not(.hidden)');
|
|
if (firstError) {
|
|
firstError.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
}
|
|
return;
|
|
}
|
|
|
|
var formData = new FormData(this);
|
|
|
|
fetch('/surveys/s/' + surveyToken + '/', {
|
|
method: 'POST',
|
|
body: formData,
|
|
headers: { 'X-CSRFToken': getCsrf() },
|
|
credentials: 'same-origin'
|
|
}).then(function(r) {
|
|
if (r.redirected) {
|
|
window.location.href = r.url;
|
|
} else {
|
|
window.location.href = '/surveys/s/' + surveyToken + '/thank-you/?lang=' + language;
|
|
}
|
|
}).catch(function() {
|
|
// Fallback: submit the form normally
|
|
var fallbackForm = document.createElement('form');
|
|
fallbackForm.method = 'POST';
|
|
fallbackForm.action = '/surveys/s/' + surveyToken + '/';
|
|
fallbackForm.innerHTML = '<input type="hidden" name="csrfmiddlewaretoken" value="' + getCsrf() + '">';
|
|
|
|
var formInputs = document.getElementById('survey-form').querySelectorAll('input, textarea');
|
|
formInputs.forEach(function(input) {
|
|
if (input.name && input.value) {
|
|
var hidden = document.createElement('input');
|
|
hidden.type = 'hidden';
|
|
hidden.name = input.name;
|
|
hidden.value = input.value;
|
|
fallbackForm.appendChild(hidden);
|
|
}
|
|
});
|
|
|
|
document.body.appendChild(fallbackForm);
|
|
fallbackForm.submit();
|
|
});
|
|
});
|
|
|
|
// Clear validation errors on input
|
|
document.querySelectorAll('input[type="text"], textarea').forEach(function(input) {
|
|
input.addEventListener('input', function() {
|
|
clearError(this);
|
|
});
|
|
});
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|