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

532 lines
28 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;
}
</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">
<h3 class="font-bold text-navy flex items-center gap-2 mb-6 text-lg">
<i data-lucide="message-square" class="w-5 h-5 text-blue"></i>
{% trans "Survey Responses" %}
</h3>
{% for response in responses %}
<div class="response-card bg-white border border-slate-200 rounded-xl p-5 mb-4">
<div class="flex justify-between items-start mb-4">
<div class="flex-1">
<span class="px-2.5 py-1 rounded-lg text-xs font-bold bg-light text-navy mr-2">Q{{ forloop.counter }}</span>
<strong class="text-navy">{{ response.question.text }}</strong>
<div class="text-slate text-sm mt-1">
{% trans "Type" %}: {{ response.question.get_question_type_display }}
</div>
</div>
{% if response.numeric_value %}
<div class="text-right">
<div 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 }}
</div>
<div class="text-slate text-sm">{% trans "out of" %} 5</div>
</div>
{% endif %}
</div>
<!-- Rating Visualization -->
{% if response.question.question_type == 'rating' %}
<div class="mb-4">
<div class="flex items-center gap-3 mb-3">
<span class="text-slate">{% trans "Rating" %}:</span>
{% for i in "12345" %}
{% if forloop.counter <= response.numeric_value|floatformat:0 %}
<i data-lucide="star" class="w-6 h-6 fill-amber-400 text-amber-400"></i>
{% else %}
<i data-lucide="star" class="w-6 h-6 text-slate-300"></i>
{% endif %}
{% endfor %}
</div>
{% if response.question.id in question_stats %}
{% with question_stat=question_stats|get_item:response.question.id %}
<div class="bg-slate-50 rounded-xl p-4">
<div class="flex justify-between text-sm mb-2">
<span class="text-slate">{% trans "Average" %}: <strong class="text-navy">{{ question_stat.average }}</strong></span>
<span class="text-slate">{{ question_stat.total_responses }} {% trans "responses" %}</span>
</div>
<div class="h-2 bg-slate-200 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 'Rating' %}">
</div>
<div class="h-full bg-slate-300"
style="width: {{ question_stat.average|mul:20 }}%"
title="{% trans 'Average' %}">
</div>
</div>
</div>
{% endwith %}
{% endif %}
</div>
{% endif %}
<!-- Choice Visualization -->
{% if response.question.question_type in 'multiple_choice,single_choice' %}
<div class="mb-4">
<div class="bg-blue-50 rounded-xl p-4 mb-3">
<strong class="text-blue-700">{% trans "Response" %}:</strong> {{ response.choice_value }}
</div>
{% if response.question.id in question_stats and question_stats|get_item:response.question.id.type == 'choice' %}
<div class="mt-4">
<strong class="block mb-3 text-slate">{% trans "Response Distribution" %}:</strong>
{% for option in question_stats|get_item:response.question.id.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">{{ option.count }} ({{ option.percentage }}%)</span>
</div>
<div class="h-1.5 bg-slate-100 rounded-full overflow-hidden">
<div class="choice-option-bar h-full {% if option.value == response.choice_value %}bg-navy{% else %}bg-slate-300{% endif %}"
style="width: {{ option.percentage }}%">
</div>
</div>
</div>
{% endfor %}
</div>
{% endif %}
</div>
{% endif %}
<!-- Text Response -->
{% if response.text_value %}
<div class="bg-slate-50 rounded-xl p-4">
<div class="flex justify-between mb-2">
<strong class="text-navy">{% trans "Comment" %}</strong>
<span class="text-slate text-sm">{{ response.text_value|length }} {% trans "characters" %}</span>
</div>
<p class="text-slate mb-0">{{ response.text_value|linebreaks }}</p>
</div>
{% endif %}
</div>
{% empty %}
<div class="text-center py-12">
<i data-lucide="clipboard" class="w-16 h-16 mx-auto mb-4 text-slate-300"></i>
<p class="text-slate font-semibold">{% trans "No responses yet" %}</p>
</div>
{% endfor %}
</section>
<!-- Patient Comment & AI Analysis -->
{% if survey.status == 'completed' %}
<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="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 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.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>
<!-- 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 %}