HH/templates/surveys/public_form.html
2026-02-22 08:35:53 +03:00

459 lines
23 KiB
HTML

{% load i18n %}
{% load survey_filters %}
<!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>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
background: linear-gradient(135deg, #005696 0%, #007bbd 100%);
}
.rating-star {
transition: all 0.2s;
}
.rating-star.active {
color: #fbbf24;
transform: scale(1.1);
}
.rating-star:hover {
color: #fbbf24;
transform: scale(1.15);
}
.nps-button:hover {
transform: scale(1.05);
}
.nps-button.selected {
background: #005696;
color: white;
border-color: #005696;
transform: scale(1.05);
}
.likert-option:hover {
border-color: #005696;
background: #eef6fb;
}
.likert-option.selected {
border-color: #005696;
background: #e0f0fa;
}
.yes-no-button:hover {
border-color: #005696;
transform: translateY(-2px);
}
.yes-no-button.selected {
background: #005696;
color: white;
border-color: #005696;
}
.question-card {
transition: all 0.2s;
}
.form-input:focus {
border-color: #005696;
box-shadow: 0 0 0 3px rgba(0, 86, 150, 0.1);
}
</style>
</head>
<body class="min-h-screen">
<!-- Language Toggle -->
<div class="fixed top-4 {% if language == 'ar' %}left-4{% else %}right-4{% endif %} bg-white rounded-full px-4 py-2 shadow-lg z-50">
{% if language == 'ar' %}
<a href="?lang=en" class="text-navy hover:text-navy font-medium text-sm flex items-center gap-1">
<i data-lucide="languages" class="w-4 h-4"></i> English
</a>
{% else %}
<a href="?lang=ar" class="text-navy hover:text-navy font-medium text-sm flex items-center gap-1">
<i data-lucide="languages" class="w-4 h-4"></i> العربية
</a>
{% endif %}
</div>
<div class="max-w-2xl mx-auto px-4 py-6 sm:py-8">
<div class="bg-white rounded-3xl shadow-xl overflow-hidden mb-6">
<!-- Survey Header -->
<div class="bg-gradient-to-br from-navy to-blue text-white p-8 text-center">
<div class="flex justify-center mb-4">
<div class="bg-white/20 p-3 rounded-2xl backdrop-blur-sm">
<i data-lucide="clipboard-list" class="w-10 h-10"></i>
</div>
</div>
<h1 class="text-2xl sm:text-3xl font-bold mb-2">
{% if language == 'ar' %}
{{ survey.survey_template.name_ar|default:survey.survey_template.name }}
{% else %}
{{ survey.survey_template.name }}
{% endif %}
</h1>
<p class="text-white/80 text-sm sm:text-base">
{% if language == 'ar' %}
شكراً لمشاركتك في تحسين تجربة المرضى
{% else %}
Thank you for helping us improve patient experience
{% endif %}
</p>
<!-- Progress Bar -->
<div class="bg-white/20 rounded-full h-2 mt-6 overflow-hidden">
<div class="bg-white h-full rounded-full transition-all duration-300" id="progressBar" style="width: 0%"></div>
</div>
</div>
<!-- Survey Body -->
<div class="p-6 sm:p-8">
<!-- Error Messages -->
{% if errors %}
<div class="bg-red-50 border-l-4 border-red-500 text-red-700 p-4 rounded-xl mb-6">
<strong class="flex items-center gap-2 mb-2">
<i data-lucide="alert-circle" class="w-5 h-5"></i>
{% if language == 'ar' %}
يرجى تصحيح الأخطاء التالية:
{% else %}
Please correct the following errors:
{% endif %}
</strong>
<ul class="mb-0 mt-2 space-y-1">
{% for error in errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
<!-- Survey Form -->
<form method="post" id="surveyForm">
{% csrf_token %}
<input type="hidden" name="language" value="{{ language }}">
{% for question in questions %}
<div class="question-card mb-8 pb-8 border-b border-gray-100 last:border-b-0 last:pb-0 last:mb-0" data-question="{{ forloop.counter }}">
<div class="flex items-center gap-3 mb-4">
<span class="bg-navy text-white w-8 h-8 rounded-full flex items-center justify-center font-bold text-sm flex-shrink-0">
{{ forloop.counter }}
</span>
<div>
<h3 class="text-lg font-semibold 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 ml-1">*</span>
{% endif %}
</h3>
</div>
</div>
{% if question.help_text or question.help_text_ar %}
<small class="text-gray-500 block mb-4 ml-11">
{% if language == 'ar' and question.help_text_ar %}
{{ question.help_text_ar }}
{% else %}
{{ question.help_text }}
{% endif %}
</small>
{% endif %}
<!-- Rating Question -->
{% if question.question_type == 'rating' %}
<div class="ml-11">
<div class="rating-stars flex gap-3 justify-center flex-wrap">
{% for i in "12345" %}
<button type="button" class="rating-star text-gray-300 text-4xl focus:outline-none"
data-value="{{ forloop.counter }}"
onclick="selectRating(this, 'question_{{ question.id }}')">
<i data-lucide="star" class="w-10 h-10 fill-current"></i>
</button>
{% endfor %}
</div>
<input type="hidden" name="question_{{ question.id }}" id="question_{{ question.id }}"
{% if question.is_required %}required{% endif %}>
</div>
<!-- NPS Question -->
{% elif question.question_type == 'nps' %}
<div class="ml-11">
<div class="nps-scale flex gap-2 justify-center flex-wrap">
{% for i in "012345678910" %}
<button type="button" class="nps-button w-10 h-10 sm:w-12 sm:h-12 border-2 border-gray-300 rounded-xl bg-white font-bold focus:outline-none transition-all"
data-value="{{ forloop.counter0 }}"
onclick="selectNPS(this, 'question_{{ question.id }}')">
{{ forloop.counter0 }}
</button>
{% endfor %}
</div>
<div class="flex justify-between text-xs text-gray-500 mt-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="question_{{ question.id }}"
{% if question.is_required %}required{% endif %}>
</div>
<!-- Likert Scale -->
{% elif question.question_type == 'likert' %}
<div class="ml-11 space-y-2">
{% for value, label in "1:Strongly Disagree,2:Disagree,3:Neutral,4:Agree,5:Strongly Agree"|split:"," %}
{% with parts=label|split:":" %}
<label class="likert-option flex items-center gap-3 p-3 border-2 border-gray-200 rounded-xl cursor-pointer transition-all">
<input type="radio" name="question_{{ question.id }}" value="{{ parts.0 }}"
{% if question.is_required %}required{% endif %}
class="w-5 h-5 text-navy focus:ring-navy"
onchange="selectLikert(this)">
<span class="text-gray-700">{{ parts.1 }}</span>
</label>
{% endwith %}
{% endfor %}
</div>
<!-- Yes/No Question -->
{% elif question.question_type == 'yes_no' %}
<div class="ml-11">
<div class="yes-no-buttons flex gap-4 justify-center">
<button type="button" class="yes-no-button flex-1 max-w-[140px] px-6 py-3 border-2 border-gray-200 rounded-xl bg-white font-semibold focus:outline-none transition-all"
data-value="yes"
onclick="selectYesNo(this, 'question_{{ question.id }}')">
{% if language == 'ar' %}نعم{% else %}Yes{% endif %}
</button>
<button type="button" class="yes-no-button flex-1 max-w-[140px] px-6 py-3 border-2 border-gray-200 rounded-xl bg-white font-semibold focus:outline-none transition-all"
data-value="no"
onclick="selectYesNo(this, 'question_{{ question.id }}')">
{% if language == 'ar' %}لا{% else %}No{% endif %}
</button>
</div>
<input type="hidden" name="question_{{ question.id }}" id="question_{{ question.id }}"
{% if question.is_required %}required{% endif %}>
</div>
<!-- Multiple Choice -->
{% elif question.question_type == 'multiple_choice' %}
<div class="ml-11 space-y-2">
{% for choice in question.choices_json %}
<label class="likert-option flex items-center gap-3 p-3 border-2 border-gray-200 rounded-xl cursor-pointer transition-all">
<input type="radio" name="question_{{ question.id }}" value="{{ choice.value }}"
{% if question.is_required %}required{% endif %}
class="w-5 h-5 text-navy focus:ring-navy"
onchange="selectLikert(this)">
<span class="text-gray-700">
{% if language == 'ar' and choice.label_ar %}
{{ choice.label_ar }}
{% else %}
{{ choice.label }}
{% endif %}
</span>
</label>
{% endfor %}
</div>
<!-- Text Input -->
{% elif question.question_type == 'text' %}
<div class="ml-11">
<input type="text" name="question_{{ question.id }}"
class="form-input w-full px-4 py-3 border-2 border-gray-200 rounded-xl text-gray-800 focus:outline-none transition-all"
{% if question.is_required %}required{% endif %}
placeholder="{% if language == 'ar' %}أدخل إجابتك هنا{% else %}Enter your answer here{% endif %}">
</div>
<!-- Text Area -->
{% elif question.question_type == 'textarea' %}
<div class="ml-11">
<textarea name="question_{{ question.id }}" class="form-input w-full px-4 py-3 border-2 border-gray-200 rounded-xl text-gray-800 focus:outline-none transition-all" rows="4"
{% if question.is_required %}required{% endif %}
placeholder="{% if language == 'ar' %}أدخل إجابتك هنا{% else %}Enter your answer here{% endif %}"></textarea>
</div>
{% endif %}
</div>
{% endfor %}
<!-- Optional Comment Section -->
<div class="question-card mb-8 pb-8 border-b border-gray-100">
<h3 class="text-lg font-semibold text-gray-800 mb-4 flex items-center gap-2">
<i data-lucide="message-circle" class="w-5 h-5 text-gray-400"></i>
{% if language == 'ar' %}
ملاحظات إضافية (اختياري)
{% else %}
Additional Comments (Optional)
{% endif %}
</h3>
<small class="text-gray-500 block mb-3">
{% if language == 'ar' %}
شارك أي ملاحظات أو اقتراحات إضافية حول تجربتك
{% else %}
Share any additional comments or suggestions about your experience
{% endif %}
</small>
<textarea name="comment" id="survey_comment" class="form-input w-full px-4 py-3 border-2 border-gray-200 rounded-xl text-gray-800 focus:outline-none transition-all" rows="4"
placeholder="{% if language == 'ar' %}اكتب ملاحظاتك هنا...{% else %}Write your comments here...{% endif %}"></textarea>
</div>
<!-- Submit Button -->
<button type="submit" class="w-full bg-gradient-to-br from-navy to-blue text-white px-8 py-4 rounded-2xl font-bold text-lg hover:shadow-lg hover:shadow-blue-200 transition-all transform hover:-translate-y-1 flex items-center justify-center gap-2">
<i data-lucide="send" class="w-5 h-5"></i>
{% if language == 'ar' %}
إرسال الاستبيان
{% else %}
Submit Survey
{% endif %}
</button>
</form>
</div>
</div>
<!-- Footer -->
<div class="text-center text-white">
<div class="flex items-center justify-center gap-2 mb-2">
<i data-lucide="heart" class="w-4 h-4 fill-current"></i>
<span class="text-sm font-medium">PX360</span>
</div>
<p class="text-white/80 text-xs">
{% if language == 'ar' %}
شكراً لمشاركتك في تحسين تجربة المرضى
{% else %}
Thank you for helping us improve patient experience
{% endif %}
</p>
</div>
</div>
<script>
// Initialize Lucide icons
lucide.createIcons();
// Survey Start Tracking
let surveyStarted = false;
const surveyToken = "{{ survey.access_token }}";
function trackSurveyStart() {
if (surveyStarted) return;
fetch(`/surveys/s/${surveyToken}/track-start/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
'Content-Type': 'application/json',
},
credentials: 'same-origin'
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
console.log('Survey started tracked:', data.survey_status);
}
})
.catch(error => {
console.error('Error tracking survey start:', error);
});
surveyStarted = true;
}
// Rating Stars
function selectRating(star, fieldId) {
const value = star.getAttribute('data-value');
const stars = star.parentElement.querySelectorAll('.rating-star');
stars.forEach((s, index) => {
if (index < value) {
s.classList.add('active');
} else {
s.classList.remove('active');
}
});
document.getElementById(fieldId).value = value;
updateProgress();
}
// NPS Selection
function selectNPS(button, fieldId) {
const value = button.getAttribute('data-value');
const buttons = button.parentElement.querySelectorAll('.nps-button');
buttons.forEach(b => b.classList.remove('selected'));
button.classList.add('selected');
document.getElementById(fieldId).value = value;
updateProgress();
}
// Likert Selection
function selectLikert(radio) {
const options = radio.closest('.likert-option').parentElement.querySelectorAll('.likert-option');
options.forEach(opt => opt.classList.remove('selected'));
radio.closest('.likert-option').classList.add('selected');
updateProgress();
}
// Yes/No Selection
function selectYesNo(button, fieldId) {
const value = button.getAttribute('data-value');
const buttons = button.parentElement.querySelectorAll('.yes-no-button');
buttons.forEach(b => b.classList.remove('selected'));
button.classList.add('selected');
document.getElementById(fieldId).value = value;
updateProgress();
}
// Update Progress Bar
function updateProgress() {
const form = document.getElementById('surveyForm');
const totalQuestions = '{{ total_questions }}';
const answeredQuestions = form.querySelectorAll('input[type="hidden"]:not([value=""]), input[type="radio"]:checked, input[type="text"]:not([value=""]), textarea:not([value=""])').length;
const progress = (answeredQuestions / totalQuestions) * 100;
document.getElementById('progressBar').style.width = progress + '%';
}
// Track first interaction with form
const surveyForm = document.getElementById('surveyForm');
// Track when user first interacts with any question
['click', 'input', 'change'].forEach(eventType => {
surveyForm.addEventListener(eventType, function(e) {
// Only track if it's a question interaction
const questionCard = e.target.closest('.question-card');
if (questionCard && !surveyStarted) {
trackSurveyStart();
}
}, { once: true });
});
// Form Submission
document.getElementById('surveyForm').addEventListener('submit', function(e) {
// Validate all required fields
const requiredFields = this.querySelectorAll('[required]');
let allValid = true;
requiredFields.forEach(field => {
if (!field.value) {
allValid = false;
field.closest('.question-card').style.borderLeft = '4px solid #ef4444';
} else {
field.closest('.question-card').style.borderLeft = 'none';
}
});
if (!allValid) {
e.preventDefault();
alert('{% if language == "ar" %}يرجى الإجابة على جميع الأسئلة المطلوبة{% else %}Please answer all required questions{% endif %}');
window.scrollTo({ top: 0, behavior: 'smooth' });
}
});
// Initialize progress on page load
updateProgress();
</script>
</body>
</html>