760 lines
39 KiB
HTML
760 lines
39 KiB
HTML
{% extends "layouts/base.html" %}
|
|
{% load i18n %}
|
|
{% load static %}
|
|
{% load survey_filters %}
|
|
|
|
{% block title %}{% trans "Survey" %} #{{ survey.id|slice:":8" }} - PX360{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.response-card {
|
|
transition: all 0.3s ease;
|
|
}
|
|
.response-card:hover {
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
|
}
|
|
.rating-stars {
|
|
color: #fbbf24;
|
|
}
|
|
.choice-option-bar {
|
|
transition: width 0.3s ease;
|
|
}
|
|
.lang-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 1px 6px;
|
|
border-radius: 4px;
|
|
font-size: 10px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.5px;
|
|
text-transform: uppercase;
|
|
line-height: 1.4;
|
|
}
|
|
.lang-badge-en {
|
|
background: #e2e8f0;
|
|
color: #475569;
|
|
}
|
|
.lang-badge-ar {
|
|
background: #dbeafe;
|
|
color: #1e40af;
|
|
}
|
|
.lang-ar-block {
|
|
direction: rtl;
|
|
text-align: right;
|
|
padding: 8px 12px;
|
|
margin-top: 8px;
|
|
background: #f0f9ff;
|
|
border-radius: 8px;
|
|
border-right: 3px solid #3b82f6;
|
|
font-size: 0.9rem;
|
|
line-height: 1.7;
|
|
color: #1e3a5f;
|
|
}
|
|
.response-box {
|
|
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 12px;
|
|
padding: 16px 20px;
|
|
position: relative;
|
|
}
|
|
.response-box::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 3px;
|
|
background: linear-gradient(90deg, #3b82f6 0%, #8b5cf6 100%);
|
|
border-radius: 12px 12px 0 0;
|
|
}
|
|
.response-label {
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
color: #64748b;
|
|
margin-bottom: 8px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
.response-content {
|
|
font-size: 1rem;
|
|
color: #1e293b;
|
|
font-weight: 500;
|
|
}
|
|
.response-content.ar {
|
|
direction: rtl;
|
|
text-align: right;
|
|
}
|
|
.response-language-indicator {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
padding: 3px 8px;
|
|
background: #f0f9ff;
|
|
border: 1px solid #bae6fd;
|
|
border-radius: 6px;
|
|
font-size: 0.7rem;
|
|
font-weight: 600;
|
|
color: #0369a1;
|
|
}
|
|
.response-language-indicator i {
|
|
width: 12px;
|
|
height: 12px;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<!-- Back Button -->
|
|
<div class="mb-6">
|
|
<a href="{% url 'surveys:instance_list' %}" class="inline-flex items-center gap-2 px-4 py-2 border border-slate-200 rounded-xl text-slate hover:text-navy hover:bg-light transition text-sm font-semibold">
|
|
<i data-lucide="arrow-left" class="w-4 h-4"></i> {% trans "Back to Surveys" %}
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Breadcrumb & Header -->
|
|
<header class="mb-6">
|
|
<div class="flex items-center gap-2 text-sm text-slate mb-2">
|
|
<a href="{% url 'surveys:instance_list' %}">{% trans "Surveys" %}</a>
|
|
<i data-lucide="chevron-right" class="w-4 h-4"></i>
|
|
<span class="font-bold text-navy">#{{ survey.id|slice:":8" }}</span>
|
|
<span class="ml-2 px-2 py-0.5 rounded-full text-[10px] font-bold uppercase tracking-wider
|
|
{% if survey.status == 'completed' %}bg-green-100 text-green-700
|
|
{% elif survey.status == 'sent' %}bg-yellow-100 text-yellow-700
|
|
{% elif survey.status == 'active' %}bg-blue-100 text-blue-700
|
|
{% else %}bg-gray-100 text-gray-700{% endif %}">
|
|
{{ survey.get_status_display }}
|
|
</span>
|
|
</div>
|
|
<div class="flex items-center justify-between">
|
|
<h1 class="text-2xl font-bold text-navy">{{ survey.survey_template.name }}</h1>
|
|
<div class="flex items-center gap-3">
|
|
<span class="px-2.5 py-1 rounded-lg text-xs font-bold bg-light text-navy">{{ survey.survey_template.get_survey_type_display }}</span>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Score Comparison Banner (if completed) -->
|
|
{% if survey.status == 'completed' and survey.total_score %}
|
|
<div class="bg-gradient-to-br from-navy to-blue rounded-2xl p-6 mb-6 text-white shadow-lg">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div class="text-center">
|
|
<div class="text-white/80 text-sm font-semibold mb-2">{% trans "Patient Score" %}</div>
|
|
<div class="text-5xl font-bold mb-1">{{ survey.total_score|floatformat:1 }}</div>
|
|
<div class="text-xl text-white/70">/ 5.0</div>
|
|
</div>
|
|
<div class="text-center md:border-l md:border-white/25">
|
|
<div class="text-white/80 text-sm font-semibold mb-2">{% trans "Template Average" %}</div>
|
|
<div class="text-5xl font-bold mb-1">{{ template_average|floatformat:1 }}</div>
|
|
<div class="text-xl text-white/70">/ 5.0</div>
|
|
</div>
|
|
</div>
|
|
<div class="text-center mt-4">
|
|
{% if survey.total_score >= template_average %}
|
|
<span class="px-4 py-2 bg-white text-green-600 rounded-xl font-bold text-sm inline-flex items-center gap-1">
|
|
<i data-lucide="arrow-up" class="w-4 h-4"></i> {% trans "Above average" %}
|
|
</span>
|
|
{% else %}
|
|
<span class="px-4 py-2 bg-white text-orange-600 rounded-xl font-bold text-sm inline-flex items-center gap-1">
|
|
<i data-lucide="arrow-down" class="w-4 h-4"></i> {% trans "Below average" %}
|
|
</span>
|
|
{% endif %}
|
|
{% if survey.is_negative %}
|
|
<span class="px-4 py-2 bg-red-500 text-white rounded-xl font-bold text-sm inline-flex items-center gap-1 ml-2">
|
|
<i data-lucide="alert-triangle" class="w-4 h-4"></i> {% trans "Negative feedback" %}
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Main Content Grid -->
|
|
<div class="grid grid-cols-12 gap-8">
|
|
<!-- Left Column (Content) -->
|
|
<div class="col-span-8 space-y-6">
|
|
|
|
<!-- Survey Responses Card -->
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
|
|
<div class="flex items-center justify-between mb-6">
|
|
<h3 class="font-bold text-navy flex items-center gap-2 text-lg">
|
|
<i data-lucide="message-square" class="w-5 h-5 text-blue"></i>
|
|
{% trans "Survey Responses" %}
|
|
</h3>
|
|
{% if survey.completed_language %}
|
|
<div class="flex items-center gap-2">
|
|
<span class="text-sm text-slate">{% trans "Completed in" %}:</span>
|
|
<span class="response-language-indicator">
|
|
<i data-lucide="{% if survey.completed_language == 'ar' %}languages{% else %}globe{% endif %}"></i>
|
|
{% if survey.completed_language == 'ar' %}{% trans "Arabic" %}{% else %}{% trans "English" %}{% endif %}
|
|
</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% for response in responses %}
|
|
<div class="response-card bg-white border border-slate-200 rounded-xl p-5 mb-6 shadow-sm">
|
|
<!-- Question Header -->
|
|
<div class="mb-4 pb-4 border-b border-slate-100">
|
|
<div class="flex items-center gap-2 mb-3">
|
|
<span class="px-2.5 py-1 rounded-lg text-xs font-bold bg-light text-navy">Q{{ forloop.counter }}</span>
|
|
<span class="lang-badge lang-badge-en">EN</span>
|
|
<span class="px-2 py-0.5 rounded-md text-xs font-semibold bg-slate-50 text-slate-500">{{ response.question.get_question_type_display }}</span>
|
|
</div>
|
|
<p class="text-navy font-semibold leading-relaxed text-base">{{ response.question.text }}</p>
|
|
{% if response.question.text_ar %}
|
|
<div class="lang-ar-block">
|
|
<span class="lang-badge lang-badge-ar mb-1">AR</span>
|
|
<div class="mt-1">{{ response.question.text_ar }}</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Response Section -->
|
|
<div class="response-box">
|
|
<div class="response-label">
|
|
<i data-lucide="{% if response.question.question_type == 'rating' %}star{% elif response.question.question_type in 'multiple_choice,single_choice' %}check-circle{% else %}message-square{% endif %}" class="w-3.5 h-3.5"></i>
|
|
{% trans "Patient Response" %}
|
|
</div>
|
|
|
|
<!-- Rating Visualization -->
|
|
{% if response.question.question_type == 'rating' %}
|
|
<div class="flex flex-col sm:flex-row sm:items-center gap-4">
|
|
<div class="flex items-center gap-2">
|
|
{% for i in "12345" %}
|
|
{% if forloop.counter <= response.numeric_value|floatformat:0 %}
|
|
<i data-lucide="star" class="w-8 h-8" style="fill: #fbbf24; color: #fbbf24;"></i>
|
|
{% else %}
|
|
<i data-lucide="star" class="w-8 h-8 text-slate-300"></i>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<span class="text-3xl font-bold {% if response.numeric_value >= 4 %}text-green-600{% elif response.numeric_value >= 3 %}text-orange-500{% else %}text-red-500{% endif %}">
|
|
{{ response.numeric_value|floatformat:1 }}
|
|
</span>
|
|
<span class="text-slate text-sm">/ 5</span>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- NPS Visualization -->
|
|
{% if response.question.question_type == 'nps' %}
|
|
<div class="flex flex-col sm:flex-row sm:items-center gap-4">
|
|
<div class="inline-flex items-center">
|
|
<span class="text-4xl font-bold {% if response.numeric_value >= 9 %}text-green-600{% elif response.numeric_value >= 7 %}text-blue-600{% else %}text-red-500{% endif %} px-4 py-2 bg-white rounded-lg border-2 {% if response.numeric_value >= 9 %}border-green-200{% elif response.numeric_value >= 7 %}border-blue-200{% else %}border-red-200{% endif %}">
|
|
{{ response.numeric_value|floatformat:0 }}
|
|
</span>
|
|
</div>
|
|
<div class="text-sm">
|
|
<span class="text-slate">{% trans "Score" %}: </span>
|
|
<span class="font-semibold {% if response.numeric_value >= 9 %}text-green-600{% elif response.numeric_value >= 7 %}text-blue-600{% else %}text-red-500{% endif %}">
|
|
{% if response.numeric_value >= 9 %}{% trans "Promoter" %}
|
|
{% elif response.numeric_value >= 7 %}{% trans "Passive" %}
|
|
{% else %}{% trans "Detractor" %}
|
|
{% endif %}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Yes/No Response -->
|
|
{% if response.question.question_type == 'yes_no' %}
|
|
<div class="response-content">
|
|
{% if response.choice_value == 'yes' %}
|
|
<span class="inline-flex items-center gap-2 px-4 py-2 bg-green-100 text-green-700 rounded-lg font-semibold">
|
|
<i data-lucide="check" class="w-5 h-5"></i>
|
|
{% trans "Yes" %}
|
|
</span>
|
|
{% else %}
|
|
<span class="inline-flex items-center gap-2 px-4 py-2 bg-red-100 text-red-700 rounded-lg font-semibold">
|
|
<i data-lucide="x" class="w-5 h-5"></i>
|
|
{% trans "No" %}
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Choice Response -->
|
|
{% if response.question.question_type in 'multiple_choice,single_choice' %}
|
|
<div class="response-content {% if survey.completed_language == 'ar' %}ar{% endif %}">
|
|
{{ response.text_value }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Text Response -->
|
|
{% if response.question.question_type in 'text,textarea' and response.text_value %}
|
|
<div class="response-content {% if survey.completed_language == 'ar' %}ar{% endif %} text-slate-700 leading-relaxed">
|
|
{{ response.text_value|linebreaks }}
|
|
</div>
|
|
<div class="mt-3 text-right">
|
|
<span class="text-xs text-slate-400">{{ response.text_value|length }} {% trans "characters" %}</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Statistics Comparison (for applicable types) -->
|
|
{% if response.question.id in question_stats %}
|
|
{% with question_stat=question_stats|get_item:response.question.id %}
|
|
<div class="mt-4 pt-4 border-t border-slate-100">
|
|
<div class="flex items-center justify-between text-sm mb-2">
|
|
<span class="text-slate flex items-center gap-2">
|
|
<i data-lucide="bar-chart-2" class="w-4 h-4"></i>
|
|
{% trans "Template Average" %}
|
|
</span>
|
|
<span class="font-semibold text-navy">{{ question_stat.average|floatformat:1 }}</span>
|
|
</div>
|
|
{% if response.question.question_type == 'rating' %}
|
|
<div class="h-2 bg-slate-100 rounded-full overflow-hidden flex">
|
|
<div class="h-full {% if response.numeric_value >= question_stat.average %}bg-green-500{% else %}bg-orange-500{% endif %}"
|
|
style="width: {{ response.numeric_value|mul:20 }}%"
|
|
title="{% trans 'Patient Rating' %}">
|
|
</div>
|
|
<div class="h-full bg-slate-300"
|
|
style="width: {{ question_stat.average|mul:20 }}%"
|
|
title="{% trans 'Average' %}">
|
|
</div>
|
|
</div>
|
|
<div class="flex justify-between text-xs text-slate-400 mt-1">
|
|
<span>{% trans "Patient" %}</span>
|
|
<span>{% trans "Average" %}</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endwith %}
|
|
{% endif %}
|
|
|
|
<!-- Response Distribution (for choice questions) -->
|
|
{% if response.question.question_type in 'multiple_choice,single_choice' and response.question.id in question_stats %}
|
|
{% with question_stat=question_stats|get_item:response.question.id %}
|
|
{% if question_stat.type == 'choice' %}
|
|
<div class="mt-4 pt-4 border-t border-slate-100">
|
|
<div class="text-sm font-semibold text-slate mb-3 flex items-center gap-2">
|
|
<i data-lucide="pie-chart" class="w-4 h-4"></i>
|
|
{% trans "Response Distribution" %}
|
|
</div>
|
|
{% for option in question_stat.options %}
|
|
<div class="mb-3">
|
|
<div class="flex justify-between text-sm mb-1">
|
|
<span class="text-slate">
|
|
{{ option.value }}
|
|
</span>
|
|
<span class="text-slate text-xs">{{ option.count }} ({{ option.percentage }}%)</span>
|
|
</div>
|
|
<div class="h-2 bg-slate-100 rounded-full overflow-hidden">
|
|
<div class="choice-option-bar h-full {% if option.value == response.choice_value %}bg-blue-600{% else %}bg-slate-300{% endif %}"
|
|
style="width: {{ option.percentage }}%">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
{% endwith %}
|
|
{% endif %}
|
|
</div>
|
|
{% empty %}
|
|
<div class="text-center py-12">
|
|
<div class="w-20 h-20 mx-auto mb-4 rounded-full bg-slate-50 flex items-center justify-center">
|
|
<i data-lucide="clipboard-list" class="w-10 h-10 text-slate-300"></i>
|
|
</div>
|
|
<p class="text-slate font-semibold">{% trans "No responses yet" %}</p>
|
|
<p class="text-slate-400 text-sm mt-1">{% trans "The patient hasn't submitted any answers" %}</p>
|
|
</div>
|
|
{% endfor %}
|
|
</section>
|
|
|
|
<!-- Patient Comment & AI Analysis -->
|
|
{% if survey.status == 'completed' %}
|
|
<section id="patient-comment" class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
|
|
<h3 class="font-bold text-navy flex items-center gap-2 mb-6 text-lg">
|
|
<i data-lucide="chat-quote" class="w-5 h-5 text-blue"></i>
|
|
{% trans "Patient Comment" %}
|
|
</h3>
|
|
|
|
{% if survey.comment %}
|
|
<div class="mb-6">
|
|
<div class="bg-slate-50 rounded-xl p-5">
|
|
<p class="text-slate mb-0">{{ survey.comment|linebreaks }}</p>
|
|
</div>
|
|
<span class="text-slate text-sm mt-2 block">
|
|
<i data-lucide="clock" class="w-4 h-4 inline mr-1"></i>{% trans "Submitted" %}: {{ survey.completed_at|date:"M d, Y H:i" }}
|
|
</span>
|
|
</div>
|
|
|
|
<!-- AI Analysis -->
|
|
{% if survey.comment_analyzed and survey.comment_analysis %}
|
|
<div class="bg-light border border-blue-200 rounded-xl p-6">
|
|
<h4 class="font-bold text-navy mb-4 flex items-center gap-2">
|
|
<i data-lucide="sparkles" class="w-5 h-5 text-blue"></i>{% trans "AI Analysis" %}
|
|
</h4>
|
|
|
|
<!-- Sentiment -->
|
|
<div class="mb-4">
|
|
<strong class="text-slate">{% trans "Sentiment" %}:</strong>
|
|
<span class="ml-2 px-2.5 py-1 rounded-lg text-xs font-bold {% if survey.comment_analysis.sentiment == 'positive' %}bg-green-100 text-green-600{% elif survey.comment_analysis.sentiment == 'negative' %}bg-red-100 text-red-600{% else %}bg-slate-100 text-slate-600{% endif %}">
|
|
{{ survey.comment_analysis.sentiment|title }}
|
|
</span>
|
|
{% if survey.comment_analysis.sentiment_score %}
|
|
<span class="text-slate text-sm ml-2">
|
|
(Score: {{ survey.comment_analysis.sentiment_score|floatformat:2 }})
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Emotion -->
|
|
{% if survey.comment_analysis.emotion %}
|
|
<div class="mb-4">
|
|
<strong class="text-slate">{% trans "Emotion" %}:</strong>
|
|
<span class="ml-2 px-2.5 py-1 rounded-lg text-xs font-bold bg-blue-100 text-blue-600">
|
|
{{ survey.comment_analysis.emotion|title }}
|
|
</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Summary -->
|
|
{% if survey.comment_analysis.summary_en %}
|
|
<div class="mb-4">
|
|
<strong class="text-slate">{% trans "Summary" %}:</strong>
|
|
<p class="text-slate mt-2 mb-0">{{ survey.comment_analysis.summary_en }}</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Topics -->
|
|
{% if survey.comment_analysis.topics_en %}
|
|
<div class="mb-0">
|
|
<strong class="text-slate">{% trans "Key Topics" %}:</strong>
|
|
<div class="mt-2 flex flex-wrap gap-2">
|
|
{% for topic in survey.comment_analysis.topics_en %}
|
|
<span class="px-3 py-1 bg-white border border-slate-200 text-slate rounded-lg text-sm font-medium">{{ topic }}</span>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% elif survey.comment_analyzed %}
|
|
<div class="bg-amber-50 border border-amber-200 rounded-xl p-4 flex items-center gap-3">
|
|
<i data-lucide="alert-triangle" class="w-6 h-6 text-amber-500 flex-shrink-0"></i>
|
|
<span class="text-amber-700">{% trans "Comment analysis failed. Check system logs for details." %}</span>
|
|
</div>
|
|
{% else %}
|
|
<div class="bg-slate-50 rounded-xl p-4 flex items-center gap-3">
|
|
<i data-lucide="hourglass" class="w-6 h-6 text-slate-400 flex-shrink-0"></i>
|
|
<span class="text-slate">{% trans "Comment is being analyzed by AI..." %}</span>
|
|
</div>
|
|
{% endif %}
|
|
{% else %}
|
|
<div class="text-center py-8">
|
|
<i data-lucide="message-circle" class="w-12 h-12 mx-auto mb-3 text-slate-300"></i>
|
|
<p class="text-slate mb-0">{% trans "No comment provided by patient" %}</p>
|
|
</div>
|
|
{% endif %}
|
|
</section>
|
|
{% endif %}
|
|
|
|
<!-- Related Surveys -->
|
|
{% if related_surveys %}
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
|
|
<h3 class="font-bold text-navy flex items-center gap-2 mb-6 text-lg">
|
|
<i data-lucide="layers" class="w-5 h-5 text-blue"></i>
|
|
{% trans "Related Surveys from Patient" %}
|
|
</h3>
|
|
<div class="overflow-x-auto">
|
|
<table class="w-full">
|
|
<thead class="bg-slate-50 border-b border-slate-100">
|
|
<tr class="text-xs font-bold text-slate uppercase tracking-wider">
|
|
<th class="px-4 py-3 text-left">{% trans "Survey" %}</th>
|
|
<th class="px-4 py-3 text-left">{% trans "Type" %}</th>
|
|
<th class="px-4 py-3 text-left">{% trans "Score" %}</th>
|
|
<th class="px-4 py-3 text-left">{% trans "Date" %}</th>
|
|
<th class="px-4 py-3 text-left">{% trans "Actions" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-slate-100 text-sm">
|
|
{% for related in related_surveys %}
|
|
<tr class="hover:bg-light/30 transition">
|
|
<td class="px-4 py-3 text-slate-800">{{ related.survey_template.name }}</td>
|
|
<td class="px-4 py-3">
|
|
<span class="px-2 py-1 rounded-lg text-xs font-bold bg-light text-navy">{{ related.survey_template.get_survey_type_display }}</span>
|
|
</td>
|
|
<td class="px-4 py-3">
|
|
<span class="font-bold {% if related.total_score < 3 %}text-red-500{% elif related.total_score < 4 %}text-orange-500{% else %}text-green-600{% endif %}">
|
|
{{ related.total_score|floatformat:1 }}
|
|
</span>
|
|
</td>
|
|
<td class="px-4 py-3 text-slate text-xs">{{ related.completed_at|date:"M d, Y" }}</td>
|
|
<td class="px-4 py-3">
|
|
<a href="{% url 'surveys:instance_detail' related.id %}"
|
|
class="p-2 text-navy hover:bg-light rounded-lg transition inline-flex items-center gap-1">
|
|
<i data-lucide="eye" class="w-4 h-4"></i>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Right Column (Sidebar) -->
|
|
<div class="col-span-4 space-y-6">
|
|
|
|
<!-- Survey Link -->
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
|
|
<h3 class="font-bold text-navy mb-4 text-sm flex items-center gap-2">
|
|
<i data-lucide="link" class="w-4 h-4 text-blue"></i>
|
|
{% trans "Survey Link" %}
|
|
</h3>
|
|
<div class="bg-slate-50 rounded-lg p-3 mb-3">
|
|
<code id="survey-url" class="text-xs text-slate break-all">{{ survey_url }}</code>
|
|
</div>
|
|
<button onclick="navigator.clipboard.writeText(document.getElementById('survey-url').textContent); this.textContent='{% trans "Copied!" %}'; setTimeout(() => this.textContent='{% trans "Copy Link" %}', 2000)"
|
|
class="w-full px-4 py-2.5 bg-blue text-white rounded-lg text-sm font-bold flex items-center justify-center gap-2 hover:bg-blue-600 transition">
|
|
<i data-lucide="copy" class="w-4 h-4"></i> {% trans "Copy Link" %}
|
|
</button>
|
|
{% if survey.token_expires_at %}
|
|
<div class="text-xs text-slate mt-2 flex items-center gap-1">
|
|
<i data-lucide="clock" class="w-3 h-3"></i>
|
|
{% trans "Expires" %}: {{ survey.token_expires_at|date:"M d, Y H:i" }}
|
|
</div>
|
|
{% endif %}
|
|
</section>
|
|
|
|
{% if survey.status == 'completed' and survey.comment %}
|
|
<a href="{% url 'surveys:survey_comments_list' %}" class="flex items-center gap-2 px-5 py-3.5 bg-white rounded-2xl shadow-sm border border-slate-100 text-sm font-bold text-navy hover:bg-light transition">
|
|
<i data-lucide="message-circle" class="w-4 h-4 text-blue"></i>
|
|
{% trans "Survey Comments" %}
|
|
<i data-lucide="arrow-right" class="w-3 h-3 text-slate ml-auto"></i>
|
|
</a>
|
|
{% endif %}
|
|
|
|
<!-- Survey Information -->
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
|
|
<h3 class="font-bold text-navy mb-4 text-sm flex items-center gap-2">
|
|
<i data-lucide="info" class="w-4 h-4 text-blue"></i>
|
|
{% trans "Survey Information" %}
|
|
</h3>
|
|
<ul class="space-y-3 text-sm">
|
|
<li class="flex justify-between border-b border-slate-100 pb-2">
|
|
<span class="text-slate">{% trans "Status" %}</span>
|
|
<span class="font-bold">
|
|
{% if survey.status == 'completed' %}
|
|
<span class="px-2 py-0.5 rounded-lg text-xs font-bold bg-green-100 text-green-600">{{ survey.get_status_display }}</span>
|
|
{% elif survey.status == 'sent' %}
|
|
<span class="px-2 py-0.5 rounded-lg text-xs font-bold bg-yellow-100 text-yellow-600">{{ survey.get_status_display }}</span>
|
|
{% elif survey.status == 'active' %}
|
|
<span class="px-2 py-0.5 rounded-lg text-xs font-bold bg-blue-100 text-blue-600">{{ survey.get_status_display }}</span>
|
|
{% else %}
|
|
<span class="px-2 py-0.5 rounded-lg text-xs font-bold bg-slate-100 text-slate-600">{{ survey.get_status_display }}</span>
|
|
{% endif %}
|
|
</span>
|
|
</li>
|
|
{% if survey.total_score %}
|
|
<li class="flex justify-between border-b border-slate-100 pb-2">
|
|
<span class="text-slate">{% trans "Total Score" %}</span>
|
|
<span class="font-bold {% if survey.is_negative %}text-red-500{% else %}text-green-600{% endif %}">
|
|
{{ survey.total_score|floatformat:1 }}/5.0
|
|
</span>
|
|
</li>
|
|
{% endif %}
|
|
{% if survey.sent_at %}
|
|
<li class="flex justify-between border-b border-slate-100 pb-2">
|
|
<span class="text-slate">{% trans "Sent" %}</span>
|
|
<span class="font-bold text-slate-800">{{ survey.sent_at|date:"M d, Y H:i" }}</span>
|
|
</li>
|
|
{% endif %}
|
|
{% if survey.completed_at %}
|
|
<li class="flex justify-between border-b border-slate-100 pb-2">
|
|
<span class="text-slate">{% trans "Completed" %}</span>
|
|
<span class="font-bold text-slate-800">{{ survey.completed_at|date:"M d, Y H:i" }}</span>
|
|
</li>
|
|
{% endif %}
|
|
<li class="flex justify-between">
|
|
<span class="text-slate">{% trans "Type" %}</span>
|
|
<span class="px-2 py-0.5 rounded-lg text-xs font-bold bg-light text-navy">{{ survey.survey_template.get_survey_type_display }}</span>
|
|
</li>
|
|
{% if survey.completed_language %}
|
|
<li class="flex justify-between">
|
|
<span class="text-slate">{% trans "Language" %}</span>
|
|
<span class="font-bold text-slate-800 flex items-center gap-1">
|
|
{% if survey.completed_language == 'ar' %}
|
|
<span class="text-blue-600">العربية</span>
|
|
{% else %}
|
|
English
|
|
{% endif %}
|
|
</span>
|
|
</li>
|
|
{% endif %}
|
|
{% if survey.survey_template.hospital %}
|
|
<li class="flex justify-between border-t border-slate-100 pt-2 mt-2">
|
|
<span class="text-slate">{% trans "Hospital" %}</span>
|
|
<span class="font-bold text-slate-800">{{ survey.survey_template.hospital.name }}</span>
|
|
</li>
|
|
{% endif %}
|
|
</ul>
|
|
</section>
|
|
|
|
<!-- Patient Information -->
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
|
|
<h3 class="font-bold text-navy mb-4 text-sm flex items-center gap-2">
|
|
<i data-lucide="user" class="w-4 h-4 text-blue"></i>
|
|
{% trans "Patient Information" %}
|
|
</h3>
|
|
<ul class="space-y-3 text-sm">
|
|
<li class="flex justify-between border-b border-slate-100 pb-2">
|
|
<span class="text-slate">{% trans "Name" %}</span>
|
|
<span class="font-bold text-navy">{{ survey.patient.get_full_name }}</span>
|
|
</li>
|
|
<li class="flex justify-between border-b border-slate-100 pb-2">
|
|
<span class="text-slate">{% trans "Phone" %}</span>
|
|
<span class="font-bold text-slate-800">{{ survey.patient.phone }}</span>
|
|
</li>
|
|
<li class="flex justify-between">
|
|
<span class="text-slate">{% trans "MRN" %}</span>
|
|
<span class="font-bold text-slate-800">{{ survey.patient.mrn }}</span>
|
|
</li>
|
|
{% if survey.patient.email %}
|
|
<li class="flex justify-between border-t border-slate-100 pt-2 mt-2">
|
|
<span class="text-slate">{% trans "Email" %}</span>
|
|
<span class="font-bold text-slate-800 text-xs">{{ survey.patient.email }}</span>
|
|
</li>
|
|
{% endif %}
|
|
</ul>
|
|
</section>
|
|
|
|
<!-- Visit Timeline -->
|
|
{% if visit_timeline %}
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
|
|
<h3 class="font-bold text-navy mb-4 text-sm flex items-center gap-2">
|
|
<i data-lucide="clock" class="w-4 h-4 text-blue"></i>
|
|
{% trans "Visit Timeline" %}
|
|
</h3>
|
|
<div class="space-y-0">
|
|
{% for visit in visit_timeline %}
|
|
<div class="relative pl-6 pb-4 {% if not forloop.last %}border-l-2 border-slate-200 ml-2{% endif %}">
|
|
<div class="absolute -left-1.5 top-0 w-3 h-3 rounded-full {% if visit.visit_category == 'ED' %}bg-red-500{% elif visit.visit_category == 'IP' %}bg-blue-500{% elif visit.visit_category == 'OP' %}bg-green-500{% else %}bg-slate-400{% endif %}"></div>
|
|
<div class="text-xs text-slate-500 mb-0.5">{{ visit.bill_date }}</div>
|
|
<div class="text-sm font-medium text-navy">{{ visit.type }}</div>
|
|
<div class="text-xs text-slate-400 mt-0.5">
|
|
<span class="px-1.5 py-0.5 rounded text-xs font-semibold {% if visit.visit_category == 'ED' %}bg-red-100 text-red-700{% elif visit.visit_category == 'IP' %}bg-blue-100 text-blue-700{% elif visit.visit_category == 'OP' %}bg-green-100 text-green-700{% else %}bg-slate-100 text-slate-600{% endif %}">
|
|
{{ visit.patient_type }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</section>
|
|
{% endif %}
|
|
|
|
<!-- Journey Information (if applicable) -->
|
|
{% if survey.journey_instance %}
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
|
|
<h3 class="font-bold text-navy mb-4 text-sm flex items-center gap-2">
|
|
<i data-lucide="git-branch" class="w-4 h-4 text-green-600"></i>
|
|
{% trans "Journey Information" %}
|
|
</h3>
|
|
<ul class="space-y-3 text-sm mb-4">
|
|
<li class="flex justify-between border-b border-slate-100 pb-2">
|
|
<span class="text-slate">{% trans "Journey" %}</span>
|
|
<span class="font-bold text-slate-800">{{ survey.journey_instance.journey_template.name }}</span>
|
|
</li>
|
|
{% if survey.journey_stage_instance %}
|
|
<li class="flex justify-between">
|
|
<span class="text-slate">{% trans "Stage" %}</span>
|
|
<span class="font-bold text-slate-800">{{ survey.journey_stage_instance.stage_template.name }}</span>
|
|
</li>
|
|
{% endif %}
|
|
</ul>
|
|
<a href="{% url 'journeys:instance_detail' survey.journey_instance.id %}"
|
|
class="w-full px-4 py-3 bg-navy text-white rounded-xl font-semibold hover:bg-blue transition flex items-center justify-center gap-2 text-sm">
|
|
<i data-lucide="git-branch" class="w-4 h-4"></i>{% trans "View Journey" %}
|
|
</a>
|
|
</section>
|
|
{% endif %}
|
|
|
|
<!-- Follow-up Actions (for negative surveys) -->
|
|
{% if survey.is_negative %}
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border-2 border-red-200">
|
|
<h3 class="font-bold text-red-600 mb-4 text-sm flex items-center gap-2">
|
|
<i data-lucide="alert-triangle" class="w-4 h-4"></i>
|
|
{% trans "Follow-up Actions" %}
|
|
</h3>
|
|
|
|
{% if not survey.patient_contacted %}
|
|
<div class="bg-red-50 border border-red-200 rounded-xl p-4 mb-4 flex items-start gap-3">
|
|
<i data-lucide="info" class="w-5 h-5 text-red-500 flex-shrink-0 mt-0.5"></i>
|
|
<div>
|
|
<strong class="text-red-800 block text-sm">{% trans "Action Required" %}</strong>
|
|
<span class="text-red-700 text-xs">{% trans "Contact patient to discuss negative feedback" %}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<form method="post" action="{% url 'surveys:log_patient_contact' survey.id %}">
|
|
{% csrf_token %}
|
|
<div class="mb-4">
|
|
<label for="contact_notes" class="block text-xs font-semibold text-slate uppercase mb-2">{% trans "Contact Notes" %} <span class="text-red-500">*</span></label>
|
|
<textarea class="w-full px-4 py-3 border border-slate-200 rounded-xl text-sm focus:ring-2 focus:ring-navy focus:border-transparent transition resize-none"
|
|
id="contact_notes" name="contact_notes" rows="3" required
|
|
placeholder="{% trans 'Document your conversation...' %}"></textarea>
|
|
</div>
|
|
<div class="mb-4 flex items-center gap-2">
|
|
<input type="checkbox" id="issue_resolved" name="issue_resolved"
|
|
class="w-4 h-4 rounded border-slate-300 text-navy focus:ring-navy">
|
|
<label for="issue_resolved" class="text-sm text-slate">
|
|
{% trans "Issue resolved" %}
|
|
</label>
|
|
</div>
|
|
<button type="submit"
|
|
class="w-full px-4 py-3 bg-navy text-white rounded-xl font-semibold hover:bg-blue transition flex items-center justify-center gap-2 text-sm">
|
|
<i data-lucide="phone" class="w-4 h-4"></i>{% trans "Log Contact" %}
|
|
</button>
|
|
</form>
|
|
{% else %}
|
|
<div class="bg-green-50 border border-green-200 rounded-xl p-4 mb-4 flex items-start gap-3">
|
|
<i data-lucide="check-circle" class="w-5 h-5 text-green-600 flex-shrink-0 mt-0.5"></i>
|
|
<div>
|
|
<strong class="text-green-800 block text-sm">{% trans "Patient Contacted" %}</strong>
|
|
<span class="text-green-700 text-xs">
|
|
{{ survey.patient_contacted_by.get_full_name }} • {{ survey.patient_contacted_at|date:"M d, Y" }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<strong class="text-slate text-xs uppercase block mb-1">{% trans "Notes" %}</strong>
|
|
<p class="text-slate text-sm">{{ survey.contact_notes }}</p>
|
|
</div>
|
|
|
|
{% if not survey.satisfaction_feedback_sent %}
|
|
<form method="post" action="{% url 'surveys:send_satisfaction_feedback' survey.id %}">
|
|
{% csrf_token %}
|
|
<button type="submit"
|
|
class="w-full px-4 py-3 bg-navy text-white rounded-xl font-semibold hover:bg-blue transition flex items-center justify-center gap-2 text-sm">
|
|
<i data-lucide="send" class="w-4 h-4"></i>{% trans "Send Satisfaction Feedback" %}
|
|
</button>
|
|
</form>
|
|
{% else %}
|
|
<div class="bg-blue-50 border border-blue-200 rounded-xl p-4 flex items-start gap-3">
|
|
<i data-lucide="check-circle" class="w-5 h-5 text-blue-600 flex-shrink-0 mt-0.5"></i>
|
|
<div>
|
|
<strong class="text-blue-800 block text-sm">{% trans "Feedback Sent" %}</strong>
|
|
<span class="text-blue-700 text-xs">{{ survey.satisfaction_feedback_sent_at|date:"M d, Y" }}</span>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
</section>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
lucide.createIcons();
|
|
</script>
|
|
{% endblock %}
|