HH/templates/surveys/public_form.html
2025-12-24 12:42:31 +03:00

627 lines
23 KiB
HTML

{% load i18n %}
<!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>
<!-- Bootstrap 5 CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap{% if language == 'ar' %}.rtl{% endif %}.min.css" rel="stylesheet">
<!-- Bootstrap Icons -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
<style>
:root {
--primary-color: #667eea;
--secondary-color: #764ba2;
--success-color: #4caf50;
--warning-color: #ff9800;
--danger-color: #f44336;
}
body {
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
min-height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
{% if language == 'ar' %}
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
{% endif %}
}
.survey-container {
max-width: 600px;
margin: 20px auto;
padding: 0 15px;
}
.survey-card {
background: white;
border-radius: 20px;
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
overflow: hidden;
margin-bottom: 20px;
}
.survey-header {
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
color: white;
padding: 30px 20px;
text-align: center;
}
.survey-header h1 {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 10px;
}
.survey-header p {
font-size: 0.9rem;
opacity: 0.9;
margin-bottom: 0;
}
.progress-bar-container {
background: rgba(255,255,255,0.2);
height: 8px;
border-radius: 4px;
margin-top: 15px;
overflow: hidden;
}
.progress-bar-fill {
background: white;
height: 100%;
border-radius: 4px;
transition: width 0.3s ease;
}
.survey-body {
padding: 30px 20px;
}
.question-card {
margin-bottom: 30px;
padding-bottom: 30px;
border-bottom: 1px solid #e0e0e0;
}
.question-card:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.question-number {
display: inline-block;
background: var(--primary-color);
color: white;
width: 28px;
height: 28px;
border-radius: 50%;
text-align: center;
line-height: 28px;
font-size: 0.85rem;
font-weight: 600;
margin-bottom: 10px;
}
.question-text {
font-size: 1.1rem;
font-weight: 500;
color: #333;
margin-bottom: 15px;
line-height: 1.5;
}
.required-indicator {
color: var(--danger-color);
margin-left: 5px;
}
/* Rating Stars */
.rating-stars {
display: flex;
gap: 10px;
justify-content: center;
flex-wrap: wrap;
}
.rating-star {
font-size: 2.5rem;
color: #ddd;
cursor: pointer;
transition: all 0.2s;
}
.rating-star:hover,
.rating-star.active {
color: #ffc107;
transform: scale(1.1);
}
/* NPS Scale */
.nps-scale {
display: flex;
gap: 8px;
justify-content: center;
flex-wrap: wrap;
margin-bottom: 10px;
}
.nps-button {
width: 45px;
height: 45px;
border: 2px solid #ddd;
border-radius: 8px;
background: white;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.nps-button:hover {
border-color: var(--primary-color);
transform: scale(1.05);
}
.nps-button.selected {
background: var(--primary-color);
color: white;
border-color: var(--primary-color);
}
.nps-labels {
display: flex;
justify-content: space-between;
font-size: 0.75rem;
color: #666;
margin-top: 5px;
}
/* Likert Scale */
.likert-scale {
display: flex;
flex-direction: column;
gap: 10px;
}
.likert-option {
display: flex;
align-items: center;
padding: 12px 15px;
border: 2px solid #e0e0e0;
border-radius: 10px;
cursor: pointer;
transition: all 0.2s;
}
.likert-option:hover {
border-color: var(--primary-color);
background: #f5f5f5;
}
.likert-option input[type="radio"] {
margin-right: 10px;
width: 20px;
height: 20px;
}
.likert-option.selected {
border-color: var(--primary-color);
background: #f0f4ff;
}
/* Yes/No Buttons */
.yes-no-buttons {
display: flex;
gap: 15px;
justify-content: center;
}
.yes-no-button {
flex: 1;
max-width: 150px;
padding: 15px;
border: 2px solid #e0e0e0;
border-radius: 12px;
background: white;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
text-align: center;
}
.yes-no-button:hover {
border-color: var(--primary-color);
transform: translateY(-2px);
}
.yes-no-button.selected {
background: var(--success-color);
color: white;
border-color: var(--success-color);
}
/* Text Input */
.form-control {
border: 2px solid #e0e0e0;
border-radius: 10px;
padding: 12px 15px;
font-size: 1rem;
transition: all 0.2s;
}
.form-control:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25);
}
/* Submit Button */
.submit-button {
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
color: white;
border: none;
padding: 15px 40px;
border-radius: 50px;
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
width: 100%;
margin-top: 20px;
}
.submit-button:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
}
/* Language Toggle */
.language-toggle {
position: fixed;
top: 20px;
{% if language == 'ar' %}left{% else %}right{% endif %}: 20px;
background: white;
border-radius: 50px;
padding: 8px 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
z-index: 1000;
}
.language-toggle a {
color: var(--primary-color);
text-decoration: none;
font-weight: 600;
font-size: 0.9rem;
}
/* Error Messages */
.error-message {
background: #ffebee;
color: #c62828;
padding: 12px 15px;
border-radius: 10px;
margin-bottom: 20px;
border-left: 4px solid #c62828;
}
/* Mobile Optimizations */
@media (max-width: 576px) {
.survey-header h1 {
font-size: 1.25rem;
}
.rating-star {
font-size: 2rem;
}
.nps-button {
width: 40px;
height: 40px;
font-size: 0.9rem;
}
.question-text {
font-size: 1rem;
}
}
</style>
</head>
<body>
<!-- Language Toggle -->
<div class="language-toggle">
{% if language == 'ar' %}
<a href="?lang=en"><i class="bi bi-translate me-1"></i> English</a>
{% else %}
<a href="?lang=ar"><i class="bi bi-translate me-1"></i> العربية</a>
{% endif %}
</div>
<div class="survey-container">
<div class="survey-card">
<!-- Survey Header -->
<div class="survey-header">
<h1>
{% if language == 'ar' %}
{{ survey.survey_template.name_ar|default:survey.survey_template.name }}
{% else %}
{{ survey.survey_template.name }}
{% endif %}
</h1>
<p>
{% if language == 'ar' %}
{{ survey.survey_template.description_ar|default:survey.survey_template.description }}
{% else %}
{{ survey.survey_template.description }}
{% endif %}
</p>
<!-- Progress Bar -->
<div class="progress-bar-container">
<div class="progress-bar-fill" id="progressBar" style="width: 0%"></div>
</div>
</div>
<!-- Survey Body -->
<div class="survey-body">
<!-- Error Messages -->
{% if errors %}
<div class="error-message">
<strong>
{% if language == 'ar' %}
يرجى تصحيح الأخطاء التالية:
{% else %}
Please correct the following errors:
{% endif %}
</strong>
<ul class="mb-0 mt-2">
{% 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" data-question="{{ forloop.counter }}">
<div class="question-number">{{ forloop.counter }}</div>
<div class="question-text">
{% if language == 'ar' and question.text_ar %}
{{ question.text_ar }}
{% else %}
{{ question.text }}
{% endif %}
{% if question.is_required %}
<span class="required-indicator">*</span>
{% endif %}
</div>
{% if question.help_text or question.help_text_ar %}
<small class="text-muted d-block mb-3">
{% 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="rating-stars">
{% for i in "12345" %}
<i class="bi bi-star-fill rating-star" data-value="{{ forloop.counter }}"
onclick="selectRating(this, 'question_{{ question.id }}')"></i>
{% endfor %}
</div>
<input type="hidden" name="question_{{ question.id }}" id="question_{{ question.id }}"
{% if question.is_required %}required{% endif %}>
<!-- NPS Question -->
{% elif question.question_type == 'nps' %}
<div class="nps-scale">
{% for i in "012345678910" %}
<button type="button" class="nps-button" data-value="{{ forloop.counter0 }}"
onclick="selectNPS(this, 'question_{{ question.id }}')">
{{ forloop.counter0 }}
</button>
{% endfor %}
</div>
<div class="nps-labels">
<span>
{% if language == 'ar' %}غير محتمل على الإطلاق{% else %}Not at all likely{% endif %}
</span>
<span>
{% if language == 'ar' %}محتمل جداً{% else %}Extremely likely{% endif %}
</span>
</div>
<input type="hidden" name="question_{{ question.id }}" id="question_{{ question.id }}"
{% if question.is_required %}required{% endif %}>
<!-- Likert Scale -->
{% elif question.question_type == 'likert' %}
<div class="likert-scale">
{% 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">
<input type="radio" name="question_{{ question.id }}" value="{{ parts.0 }}"
{% if question.is_required %}required{% endif %}
onchange="selectLikert(this)">
<span>{{ parts.1 }}</span>
</label>
{% endwith %}
{% endfor %}
</div>
<!-- Yes/No Question -->
{% elif question.question_type == 'yes_no' %}
<div class="yes-no-buttons">
<button type="button" class="yes-no-button" data-value="yes"
onclick="selectYesNo(this, 'question_{{ question.id }}')">
{% if language == 'ar' %}نعم{% else %}Yes{% endif %}
</button>
<button type="button" class="yes-no-button" 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 %}>
<!-- Multiple Choice -->
{% elif question.question_type == 'multiple_choice' %}
<div class="likert-scale">
{% for choice in question.choices_json %}
<label class="likert-option">
<input type="radio" name="question_{{ question.id }}" value="{{ choice.value }}"
{% if question.is_required %}required{% endif %}
onchange="selectLikert(this)">
<span>
{% 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' %}
<input type="text" name="question_{{ question.id }}" class="form-control"
{% if question.is_required %}required{% endif %}
placeholder="{% if language == 'ar' %}أدخل إجابتك هنا{% else %}Enter your answer here{% endif %}">
<!-- Text Area -->
{% elif question.question_type == 'textarea' %}
<textarea name="question_{{ question.id }}" class="form-control" rows="4"
{% if question.is_required %}required{% endif %}
placeholder="{% if language == 'ar' %}أدخل إجابتك هنا{% else %}Enter your answer here{% endif %}"></textarea>
{% endif %}
</div>
{% endfor %}
<!-- Submit Button -->
<button type="submit" class="submit-button">
<i class="bi bi-check-circle me-2"></i>
{% if language == 'ar' %}
إرسال الاستبيان
{% else %}
Submit Survey
{% endif %}
</button>
</form>
</div>
</div>
<!-- Footer -->
<div class="text-center text-white mt-3 mb-4">
<small>
{% if language == 'ar' %}
شكراً لمشاركتك في تحسين تجربة المرضى
{% else %}
Thank you for helping us improve patient experience
{% endif %}
</small>
</div>
</div>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
// 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-scale').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 + '%';
}
// 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 #f44336';
} 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>