471 lines
24 KiB
HTML
471 lines
24 KiB
HTML
{% extends "layouts/base.html" %}
|
|
{% load i18n %}
|
|
{% load static %}
|
|
{% load survey_filters %}
|
|
|
|
{% block title %}{{ _("Survey") }} #{{ survey.id|slice:":8" }} - PX360{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.response-card {
|
|
transition: all 0.2s ease;
|
|
}
|
|
.response-card:hover {
|
|
box-shadow: var(--hh-shadow);
|
|
}
|
|
.rating-stars {
|
|
color: #ffd700;
|
|
}
|
|
.choice-option-bar {
|
|
transition: width 0.3s ease;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<!-- Back Button -->
|
|
<div class="mb-3">
|
|
<a href="{% url 'surveys:instance_list' %}" class="btn btn-outline-secondary btn-sm">
|
|
<i class="bi bi-arrow-left me-1"></i> {{ _("Back to Surveys")}}
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Page Header -->
|
|
<div class="mb-4">
|
|
<h2 class="mb-1">
|
|
<i class="bi bi-clipboard-data text-info me-2"></i>
|
|
{{ survey.survey_template.name }}
|
|
</h2>
|
|
<p class="text-muted mb-0">
|
|
{{ _("Survey") }} #{{ survey.id|slice:":8" }} •
|
|
<span class="badge badge-soft-primary">{{ survey.survey_template.get_survey_type_display }}</span>
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Score Comparison Banner (if completed) -->
|
|
{% if survey.status == 'completed' and survey.total_score %}
|
|
<div class="card mb-4 border-0 bg-gradient-teal">
|
|
<div class="card-body text-white">
|
|
<div class="row align-items-center">
|
|
<div class="col-md-6 text-center">
|
|
<div class="mb-2">{{ _("Patient Score") }}</div>
|
|
<h1 class="display-4 mb-0">{{ survey.total_score|floatformat:1 }}</h1>
|
|
<div class="fs-4 opacity-75">/ 5.0</div>
|
|
</div>
|
|
<div class="col-md-6 text-center border-start border-light border-opacity-25">
|
|
<div class="mb-2">{{ _("Template Average") }}</div>
|
|
<h1 class="display-4 mb-0">{{ template_average|floatformat:1 }}</h1>
|
|
<div class="fs-4 opacity-75">/ 5.0</div>
|
|
</div>
|
|
</div>
|
|
<div class="text-center mt-3">
|
|
{% if survey.total_score >= template_average %}
|
|
<span class="badge bg-success fs-6">
|
|
<i class="bi bi-arrow-up me-1"></i>
|
|
{% trans "Above average" %}
|
|
</span>
|
|
{% else %}
|
|
<span class="badge bg-warning fs-6">
|
|
<i class="bi bi-arrow-down me-1"></i>
|
|
{% trans "Below average" %}
|
|
</span>
|
|
{% endif %}
|
|
{% if survey.is_negative %}
|
|
<span class="badge bg-danger fs-6 ms-2">
|
|
<i class="bi bi-exclamation-triangle me-1"></i>
|
|
{% trans "Negative feedback" %}
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="row">
|
|
<!-- Main Content: Survey Responses -->
|
|
<div class="col-lg-8">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h6 class="card-title mb-0">
|
|
<i class="bi bi-question-circle text-info me-2"></i>
|
|
{% trans "Survey Responses" %}
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
{% for response in responses %}
|
|
<div class="response-card card mb-3 border">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-start mb-3">
|
|
<div class="flex-grow-1">
|
|
<span class="badge badge-soft-primary me-2">Q{{ forloop.counter }}</span>
|
|
<strong>{{ response.question.text }}</strong>
|
|
<div class="text-muted small mt-1">
|
|
{% trans "Question type" %}: {{ response.question.get_question_type_display }}
|
|
</div>
|
|
</div>
|
|
{% if response.numeric_value %}
|
|
<div class="text-end">
|
|
<div class="display-6 fw-bold {% if response.numeric_value >= 4 %}text-success{% elif response.numeric_value >= 3 %}text-warning{% else %}text-danger{% endif %}">
|
|
{{ response.numeric_value }}
|
|
</div>
|
|
<div class="text-muted small">{% trans "out of" %} 5</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Rating Visualization -->
|
|
{% if response.question.question_type == 'rating' %}
|
|
<div class="mb-3">
|
|
<div class="d-flex align-items-center mb-2">
|
|
<span class="me-3">{% trans "Your rating" %}:</span>
|
|
{% for i in "12345" %}
|
|
{% if forloop.counter <= response.numeric_value|floatformat:0 %}
|
|
<i class="bi bi-star-fill rating-stars fs-4"></i>
|
|
{% else %}
|
|
<i class="bi bi-star rating-stars text-muted fs-4"></i>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</div>
|
|
|
|
{% if response.question.id in question_stats %}
|
|
{% with question_stat=question_stats|get_item:response.question.id %}
|
|
<div class="bg-light rounded p-2">
|
|
<div class="d-flex justify-content-between">
|
|
<span class="text-muted">{% trans "Average" %}: <strong>{{ question_stat.average }}</strong></span>
|
|
<span class="text-muted">{{ question_stat.total_responses }} {% trans "responses" %}</span>
|
|
</div>
|
|
<div class="progress mt-2" style="height: 8px;">
|
|
<div class="progress-bar {% if response.numeric_value >= question_stat.average %}bg-success{% else %}bg-warning{% endif %}"
|
|
style="width: {{ response.numeric_value|mul:20 }}%"
|
|
title="{% trans "Your rating" %}">
|
|
</div>
|
|
<div class="progress-bar bg-secondary"
|
|
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-3">
|
|
<div class="alert alert-light mb-2">
|
|
<strong>{% trans "Your 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-3">
|
|
<strong class="d-block mb-2">{% trans "Response Distribution" %}:</strong>
|
|
{% for option in question_stats|get_item:response.question.id.options %}
|
|
<div class="mb-2">
|
|
<div class="d-flex justify-content-between mb-1">
|
|
<small>{{ option.value }}</small>
|
|
<small>{{ option.count }} ({{ option.percentage }}%)</small>
|
|
</div>
|
|
<div class="progress" style="height: 6px;">
|
|
<div class="progress-bar choice-option-bar {% if option.value == response.choice_value %}bg-primary{% else %}bg-light{% endif %}"
|
|
style="width: {{ option.percentage }}%">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Text Response -->
|
|
{% if response.text_value %}
|
|
<div class="bg-light rounded p-3 mb-0">
|
|
<div class="d-flex justify-content-between mb-2">
|
|
<strong>{% trans "Comment" %}</strong>
|
|
<small class="text-muted">{{ response.text_value|length }} {% trans "characters" %}</small>
|
|
</div>
|
|
<p class="mb-0">{{ response.text_value|linebreaks }}</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% empty %}
|
|
<div class="text-center py-5">
|
|
<i class="bi bi-clipboard" style="font-size: 3rem; color: #ccc;"></i>
|
|
<p class="text-muted mt-3">{{ _("No responses yet")}}</p>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Related Surveys -->
|
|
{% if related_surveys %}
|
|
<div class="card mt-4">
|
|
<div class="card-header">
|
|
<h6 class="card-title mb-0">
|
|
<i class="bi bi-collection text-primary me-2"></i>
|
|
{% trans "Related Surveys from Patient" %}
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-sm">
|
|
<thead>
|
|
<tr>
|
|
<th>{% trans "Survey" %}</th>
|
|
<th>{% trans "Type" %}</th>
|
|
<th>{% trans "Score" %}</th>
|
|
<th>{% trans "Date" %}</th>
|
|
<th>{% trans "Actions" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for related in related_surveys %}
|
|
<tr>
|
|
<td>{{ related.survey_template.name }}</td>
|
|
<td><span class="badge badge-soft-primary">{{ related.survey_template.get_survey_type_display }}</span></td>
|
|
<td>
|
|
<span class="{% if related.total_score < 3 %}text-danger{% elif related.total_score < 4 %}text-warning{% else %}text-success{% endif %} fw-bold">
|
|
{{ related.total_score|floatformat:1 }}
|
|
</span>
|
|
</td>
|
|
<td>{{ related.completed_at|date:"M d, Y" }}</td>
|
|
<td>
|
|
<a href="{% url 'surveys:instance_detail' related.id %}" class="btn btn-sm btn-outline-primary">
|
|
<i class="bi bi-eye"></i>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Sidebar: Survey Info & Actions -->
|
|
<div class="col-lg-4">
|
|
<!-- Survey Information -->
|
|
<div class="card mb-3">
|
|
<div class="card-header">
|
|
<h6 class="card-title mb-0">
|
|
<i class="bi bi-info-circle text-info me-2"></i>
|
|
{% trans "Survey Information" %}
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<strong>{% trans "Status" %}:</strong><br>
|
|
{% if survey.status == 'completed' %}
|
|
<span class="badge bg-success">{{ survey.get_status_display }}</span>
|
|
{% elif survey.status == 'sent' %}
|
|
<span class="badge bg-warning">{{ survey.get_status_display }}</span>
|
|
{% elif survey.status == 'active' %}
|
|
<span class="badge bg-info">{{ survey.get_status_display }}</span>
|
|
{% elif survey.status == 'cancelled' %}
|
|
<span class="badge bg-danger">{{ survey.get_status_display }}</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary">{{ survey.get_status_display }}</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if survey.total_score %}
|
|
<div class="mb-3">
|
|
<strong>{% trans "Total Score" %}:</strong><br>
|
|
<h3 class="mb-0 {% if survey.is_negative %}text-danger{% else %}text-success{% endif %}">
|
|
{{ survey.total_score|floatformat:1 }}/5.0
|
|
</h3>
|
|
{% if survey.is_negative %}
|
|
<span class="badge bg-danger mt-2">{% trans "Negative Feedback" %}</span>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if survey.sent_at %}
|
|
<div class="mb-3">
|
|
<strong>{% trans "Sent" %}:</strong><br>
|
|
<small>{{ survey.sent_at|date:"M d, Y H:i" }}</small>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if survey.completed_at %}
|
|
<div class="mb-3">
|
|
<strong>{% trans "Completed" %}:</strong><br>
|
|
<small>{{ survey.completed_at|date:"M d, Y H:i" }}</small>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="mb-3">
|
|
<strong>{% trans "Survey Type" %}:</strong><br>
|
|
<span class="badge badge-soft-primary">{{ survey.survey_template.get_survey_type_display }}</span>
|
|
</div>
|
|
|
|
{% if survey.survey_template.hospital %}
|
|
<div class="mb-0">
|
|
<strong>{% trans "Hospital" %}:</strong><br>
|
|
<small>{{ survey.survey_template.hospital.name }}</small>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Patient Information -->
|
|
<div class="card mb-3">
|
|
<div class="card-header">
|
|
<h6 class="card-title mb-0">
|
|
<i class="bi bi-person text-primary me-2"></i>
|
|
{% trans "Patient Information" %}
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-2">
|
|
<strong>{% trans "Name" %}:</strong><br>
|
|
{{ survey.patient.get_full_name }}
|
|
</div>
|
|
<div class="mb-2">
|
|
<strong>{% trans "Phone" %}:</strong><br>
|
|
{{ survey.patient.phone }}
|
|
</div>
|
|
<div class="mb-2">
|
|
<strong>{% trans "MRN" %}:</strong><br>
|
|
{{ survey.patient.mrn }}
|
|
</div>
|
|
{% if survey.patient.email %}
|
|
<div class="mb-0">
|
|
<strong>{% trans "Email" %}:</strong><br>
|
|
<small>{{ survey.patient.email }}</small>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Journey Information (if applicable) -->
|
|
{% if survey.journey_instance %}
|
|
<div class="card mb-3">
|
|
<div class="card-header">
|
|
<h6 class="card-title mb-0">
|
|
<i class="bi bi-diagram-3 text-success me-2"></i>
|
|
{% trans "Journey Information" %}
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-2">
|
|
<strong>{% trans "Journey" %}:</strong><br>
|
|
<small>{{ survey.journey_instance.journey_template.name }}</small>
|
|
</div>
|
|
{% if survey.journey_stage_instance %}
|
|
<div class="mb-2">
|
|
<strong>{% trans "Stage" %}:</strong><br>
|
|
<small>{{ survey.journey_stage_instance.stage_template.name }}</small>
|
|
</div>
|
|
{% endif %}
|
|
<div class="mb-0">
|
|
<a href="{% url 'journeys:instance_detail' survey.journey_instance.id %}" class="btn btn-sm btn-outline-success">
|
|
<i class="bi bi-diagram-3 me-1"></i>
|
|
{% trans "View Journey" %}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Follow-up Actions (for negative surveys) -->
|
|
{% if survey.is_negative %}
|
|
<div class="card border-warning mb-3">
|
|
<div class="card-header bg-warning text-dark">
|
|
<h6 class="card-title mb-0">
|
|
<i class="bi bi-exclamation-triangle me-2"></i>
|
|
{% trans "Follow-up Actions" %}
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if not survey.patient_contacted %}
|
|
<div class="alert alert-warning mb-3">
|
|
<i class="bi bi-info-circle me-2"></i>
|
|
<strong>{% trans "Action Required" %}:</strong> {% trans "Contact patient to discuss negative feedback" %}.
|
|
</div>
|
|
|
|
<form method="post" action="{% url 'surveys:log_patient_contact' survey.id %}">
|
|
{% csrf_token %}
|
|
<div class="mb-3">
|
|
<label for="contact_notes" class="form-label">{% trans "Contact Notes *" %}</label>
|
|
<textarea class="form-control" id="contact_notes" name="contact_notes" rows="4" required
|
|
placeholder="{% trans 'Document your conversation with patient...' %}"></textarea>
|
|
</div>
|
|
<div class="form-check mb-3">
|
|
<input class="form-check-input" type="checkbox" id="issue_resolved" name="issue_resolved">
|
|
<label class="form-check-label" for="issue_resolved">
|
|
{% trans "Issue resolved or explained to patient" %}
|
|
</label>
|
|
</div>
|
|
<button type="submit" class="btn btn-warning w-100">
|
|
<i class="bi bi-telephone me-2"></i>{% trans "Log Patient Contact" %}
|
|
</button>
|
|
</form>
|
|
{% else %}
|
|
<div class="alert alert-success mb-3">
|
|
<i class="bi bi-check-circle me-2"></i>
|
|
<strong>{% trans "Patient Contacted" %}</strong><br>
|
|
<small>{% trans "By" %} {{ survey.patient_contacted_by.get_full_name }} {% trans "on" %} {{ survey.patient_contacted_at|date:"M d, Y H:i" }}</small>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<strong>{% trans "Contact Notes" %}:</strong>
|
|
<p class="mb-0 mt-2">{{ survey.contact_notes }}</p>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<strong>{% trans "Status" %}:</strong><br>
|
|
{% if survey.issue_resolved %}
|
|
<span class="badge bg-success">{% trans "Issue Resolved" %}</span>
|
|
{% else %}
|
|
<span class="badge bg-warning">{% trans "Issue Discussed" %}</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if not survey.satisfaction_feedback_sent %}
|
|
<hr>
|
|
<h6 class="mb-3">{% trans "Send Satisfaction Feedback" %}</h6>
|
|
<p class="text-muted small mb-3">
|
|
{% trans "Send a feedback form to patient to assess their satisfaction with how their concerns were addressed" %}.
|
|
</p>
|
|
<form method="post" action="{% url 'surveys:send_satisfaction_feedback' survey.id %}">
|
|
{% csrf_token %}
|
|
<button type="submit" class="btn btn-primary w-100">
|
|
<i class="bi bi-send me-2"></i>{% trans "Send Satisfaction Feedback" %}
|
|
</button>
|
|
</form>
|
|
{% else %}
|
|
<hr>
|
|
<div class="alert alert-info mb-0">
|
|
<i class="bi bi-check-circle me-2"></i>
|
|
<strong>{% trans "Satisfaction Feedback Sent" %}</strong><br>
|
|
<small>{{ survey.satisfaction_feedback_sent_at|date:"M d, Y H:i" }}</small>
|
|
</div>
|
|
|
|
{% if survey.follow_up_feedbacks.exists %}
|
|
<div class="mt-3">
|
|
<strong>{% trans "Related Feedback" %}:</strong>
|
|
{% for feedback in survey.follow_up_feedbacks.all %}
|
|
<div class="mt-2">
|
|
<a href="{% url 'feedback:feedback_detail' feedback.id %}" class="btn btn-sm btn-outline-primary">
|
|
<i class="bi bi-chat-left-text me-1"></i>{% trans "View Feedback" %} #{{ feedback.id|slice:":8" }}
|
|
</a>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|