660 lines
24 KiB
HTML
660 lines
24 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>
|
|
|
|
<!-- 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;
|
|
|
|
}
|
|
|
|
.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;
|
|
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>
|
|
|
|
<!-- 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>
|
|
// 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-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 + '%';
|
|
}
|
|
|
|
// 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 #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>
|