HH/templates/surveys/comment_list.html

508 lines
24 KiB
HTML

{% extends "layouts/base.html" %}
{% load i18n %}
{% block title %}{% trans "Survey Comments" %} - PX360{% endblock %}
{% block content %}
<div class="container-fluid">
<!-- Page Header -->
<div class="mb-4">
<h2 class="mb-1">
<i class="bi bi-chat-quote text-info me-2"></i>
{% trans "Survey Comments" %}
</h2>
<p class="text-muted mb-0">
{% trans "View all patient comments with AI-powered sentiment analysis" %}
</p>
</div>
<!-- Statistics Cards -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<h6 class="text-muted mb-1">{% trans "Total Comments" %}</h6>
<h3 class="mb-0">{{ stats.total }}</h3>
</div>
<div class="text-primary">
<i class="bi bi-chat-quote" style="font-size: 2rem;"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<h6 class="text-muted mb-1">{% trans "Positive" %}</h6>
<h3 class="mb-0 text-success">{{ stats.positive }}</h3>
</div>
<div class="text-success">
<i class="bi bi-emoji-smile" style="font-size: 2rem;"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<h6 class="text-muted mb-1">{% trans "Negative" %}</h6>
<h3 class="mb-0 text-danger">{{ stats.negative }}</h3>
</div>
<div class="text-danger">
<i class="bi bi-emoji-frown" style="font-size: 2rem;"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<h6 class="text-muted mb-1">{% trans "AI Analyzed" %}</h6>
<h3 class="mb-0 text-info">{{ stats.analyzed }}</h3>
</div>
<div class="text-info">
<i class="bi bi-robot" style="font-size: 2rem;"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Charts -->
<div class="row mb-4">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="bi bi-pie-chart me-2"></i>
{% trans "Patient Type Distribution" %}
</h5>
</div>
<div class="card-body">
<div id="patientTypeDistributionChart" style="min-height: 300px;"></div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="bi bi-bar-chart me-2"></i>
{% trans "Sentiment by Patient Type" %}
</h5>
</div>
<div class="card-body">
<div id="sentimentByPatientTypeChart" style="min-height: 300px;"></div>
</div>
</div>
</div>
</div>
<!-- Filters -->
<div class="card mb-4">
<div class="card-body">
<form method="get" class="row g-3">
<div class="col-md-3">
<label for="search" class="form-label">{% trans "Search" %}</label>
<input type="text" class="form-control" id="search" name="search"
placeholder="{% trans 'MRN, name, or comment...' %}"
value="{{ filters.search }}">
</div>
<div class="col-md-2">
<label for="sentiment" class="form-label">{% trans "Sentiment" %}</label>
<select class="form-select" id="sentiment" name="sentiment">
<option value="">{% trans "All" %}</option>
<option value="positive" {% if filters.sentiment == 'positive' %}selected{% endif %}>
{% trans "Positive" %}
</option>
<option value="negative" {% if filters.sentiment == 'negative' %}selected{% endif %}>
{% trans "Negative" %}
</option>
<option value="neutral" {% if filters.sentiment == 'neutral' %}selected{% endif %}>
{% trans "Neutral" %}
</option>
</select>
</div>
<div class="col-md-2">
<label for="survey_type" class="form-label">{% trans "Survey Type" %}</label>
<select class="form-select" id="survey_type" name="survey_type">
<option value="">{% trans "All" %}</option>
<option value="stage" {% if filters.survey_type == 'stage' %}selected{% endif %}>
{% trans "Journey Stage" %}
</option>
<option value="complaint_resolution" {% if filters.survey_type == 'complaint_resolution' %}selected{% endif %}>
{% trans "Complaint Resolution" %}
</option>
<option value="general" {% if filters.survey_type == 'general' %}selected{% endif %}>
{% trans "General" %}
</option>
<option value="nps" {% if filters.survey_type == 'nps' %}selected{% endif %}>
{% trans "NPS" %}
</option>
</select>
</div>
<div class="col-md-2">
<label for="hospital" class="form-label">{% trans "Hospital" %}</label>
<select class="form-select" id="hospital" name="hospital">
<option value="">{% trans "All" %}</option>
{% for hospital in hospitals %}
<option value="{{ hospital.id }}"
{% if filters.hospital == hospital.id|stringformat:"s" %}selected{% endif %}>
{{ hospital.name }}
</option>
{% endfor %}
</select>
</div>
<div class="col-md-2">
<label for="patient_type" class="form-label">{% trans "Patient Type" %}</label>
<select class="form-select" id="patient_type" name="patient_type">
<option value="">{% trans "All" %}</option>
<option value="outpatient" {% if filters.patient_type == 'outpatient' %}selected{% endif %}>
{% trans "Outpatient" %}
</option>
<option value="inpatient" {% if filters.patient_type == 'inpatient' %}selected{% endif %}>
{% trans "Inpatient" %}
</option>
<option value="emergency" {% if filters.patient_type == 'emergency' %}selected{% endif %}>
{% trans "Emergency" %}
</option>
</select>
</div>
<div class="col-md-1">
<label for="date_from" class="form-label">{% trans "From" %}</label>
<input type="date" class="form-control" id="date_from" name="date_from"
value="{{ filters.date_from }}">
</div>
<div class="col-md-1">
<label for="date_to" class="form-label">{% trans "To" %}</label>
<input type="date" class="form-control" id="date_to" name="date_to"
value="{{ filters.date_to }}">
</div>
<div class="col-md-1 d-flex align-items-end">
<button type="submit" class="btn btn-primary w-100">
<i class="bi bi-search"></i>
</button>
</div>
<div class="col-12 mt-2">
<a href="{% url 'surveys:survey_comments_list' %}" class="btn btn-outline-secondary btn-sm">
<i class="bi bi-x-circle me-1"></i>{% trans "Clear Filters" %}
</a>
</div>
</form>
</div>
</div>
<!-- Comments List -->
<div class="card">
<div class="card-body">
{% if page_obj.object_list %}
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>{% trans "Patient" %}</th>
<th>{% trans "Type" %}</th>
<th>{% trans "Comment" %}</th>
<th>{% trans "Sentiment" %}</th>
<th>{% trans "Survey" %}</th>
<th>{% trans "Date" %}</th>
<th>{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
{% for survey in page_obj.object_list %}
<tr>
<td>
<strong>{{ survey.patient.get_full_name }}</strong><br>
<small class="text-muted">{{ survey.patient.mrn }}</small>
{% if survey.survey_template.hospital %}
<br><small class="text-muted">{{ survey.survey_template.hospital.name }}</small>
{% endif %}
</td>
<td>
{% if survey.patient_type and survey.patient_type.code %}
<span class="badge {{ survey.patient_type.color }} text-white" title="Patient Type: {{ survey.patient_type.label }}">
<i class="bi {{ survey.patient_type.icon }} me-1"></i>
{{ survey.patient_type.label }}
</span>
{% else %}
<span class="badge bg-secondary">N/A</span>
{% endif %}
</td>
<td>
<div style="max-width: 400px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;"
title="{{ survey.comment }}">
{{ survey.comment }}
</div>
{% if survey.comment_analyzed %}
{% if survey.comment_analysis.emotion %}
<small class="text-muted">
<i class="bi bi-heart-pulse me-1"></i>{{ survey.comment_analysis.emotion|title }}
</small>
{% endif %}
{% else %}
<small class="text-warning">
<i class="bi bi-hourglass-split me-1"></i>{% trans "Analyzing..." %}
</small>
{% endif %}
</td>
<td>
{% if survey.comment_analyzed and survey.comment_analysis.sentiment %}
<span class="badge {% if survey.comment_analysis.sentiment == 'positive' %}bg-success{% elif survey.comment_analysis.sentiment == 'negative' %}bg-danger{% else %}bg-secondary{% endif %}">
{{ survey.comment_analysis.sentiment|title }}
</span>
{% else %}
<span class="badge bg-secondary">{% trans "Pending" %}</span>
{% endif %}
</td>
<td>
<strong>{{ survey.survey_template.name }}</strong><br>
<small class="text-muted">{{ survey.survey_template.get_survey_type_display }}</small><br>
{% if survey.total_score %}
<small class="{% if survey.is_negative %}text-danger{% else %}text-success{% endif %}">
<strong>Score: {{ survey.total_score|floatformat:1 }}/5.0</strong>
</small>
{% endif %}
</td>
<td>
{{ survey.completed_at|date:"M d, Y" }}<br>
<small class="text-muted">{{ survey.completed_at|time:"H:i" }}</small>
</td>
<td>
<a href="{% url 'surveys:instance_detail' survey.id %}"
class="btn btn-sm btn-outline-primary"
title="{% trans 'View Full Survey' %}">
<i class="bi bi-eye"></i>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Pagination -->
{% if page_obj.has_other_pages %}
<nav class="mt-4">
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page=1{% if filters.search %}&search={{ filters.search }}{% endif %}{% if filters.sentiment %}&sentiment={{ filters.sentiment }}{% endif %}{% if filters.survey_type %}&survey_type={{ filters.survey_type }}{% endif %}{% if filters.hospital %}&hospital={{ filters.hospital }}{% endif %}{% if filters.date_from %}&date_from={{ filters.date_from }}{% endif %}{% if filters.date_to %}&date_to={{ filters.date_to }}{% endif %}">
<i class="bi bi-chevron-double-left"></i>
</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}{% if filters.search %}&search={{ filters.search }}{% endif %}{% if filters.sentiment %}&sentiment={{ filters.sentiment }}{% endif %}{% if filters.survey_type %}&survey_type={{ filters.survey_type }}{% endif %}{% if filters.hospital %}&hospital={{ filters.hospital }}{% endif %}{% if filters.date_from %}&date_from={{ filters.date_from }}{% endif %}{% if filters.date_to %}&date_to={{ filters.date_to }}{% endif %}">
<i class="bi bi-chevron-left"></i>
</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link"><i class="bi bi-chevron-double-left"></i></span>
</li>
<li class="page-item disabled">
<span class="page-link"><i class="bi bi-chevron-left"></i></span>
</li>
{% endif %}
<li class="page-item active">
<span class="page-link">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
</span>
</li>
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}{% if filters.search %}&search={{ filters.search }}{% endif %}{% if filters.sentiment %}&sentiment={{ filters.sentiment }}{% endif %}{% if filters.survey_type %}&survey_type={{ filters.survey_type }}{% endif %}{% if filters.hospital %}&hospital={{ filters.hospital }}{% endif %}{% if filters.date_from %}&date_from={{ filters.date_from }}{% endif %}{% if filters.date_to %}&date_to={{ filters.date_to }}{% endif %}">
<i class="bi bi-chevron-right"></i>
</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}{% if filters.search %}&search={{ filters.search }}{% endif %}{% if filters.sentiment %}&sentiment={{ filters.sentiment }}{% endif %}{% if filters.survey_type %}&survey_type={{ filters.survey_type }}{% endif %}{% if filters.hospital %}&hospital={{ filters.hospital }}{% endif %}{% if filters.date_from %}&date_from={{ filters.date_from }}{% endif %}{% if filters.date_to %}&date_to={{ filters.date_to }}{% endif %}">
<i class="bi bi-chevron-double-right"></i>
</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link"><i class="bi bi-chevron-right"></i></span>
</li>
<li class="page-item disabled">
<span class="page-link"><i class="bi bi-chevron-double-right"></i></span>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% else %}
<div class="text-center py-5">
<i class="bi bi-chat-quote" style="font-size: 3rem; color: #ccc;"></i>
<p class="text-muted mt-3 mb-0">
{% trans "No survey comments found" %}
</p>
{% if filters.search or filters.sentiment or filters.survey_type or filters.hospital or filters.date_from or filters.date_to %}
<a href="{% url 'surveys:survey_comments_list' %}" class="btn btn-outline-secondary mt-3">
<i class="bi bi-x-circle me-1"></i>{% trans "Clear Filters" %}
</a>
{% endif %}
</div>
{% endif %}
</div>
</div>
</div>
<!-- Chart Scripts -->
<script>
document.addEventListener('DOMContentLoaded', function() {
// Patient Type Distribution Chart (Donut Chart)
const patientTypeDistributionData = {{ patient_type_distribution_json|safe }};
if (patientTypeDistributionData && patientTypeDistributionData.length > 0) {
const patientTypeLabels = patientTypeDistributionData.map(item => item.label);
const patientTypeCounts = patientTypeDistributionData.map(item => item.count);
const patientTypeColors = ['#008FFB', '#FEB019', '#F55252', '#82868a']; // Blue, Yellow, Red, Gray
const patientTypeChartOptions = {
series: patientTypeCounts,
labels: patientTypeLabels,
colors: patientTypeColors,
chart: {
type: 'donut',
height: 300,
fontFamily: 'inherit'
},
plotOptions: {
pie: {
donut: {
size: '65%',
labels: {
show: true,
total: {
show: true,
showAlways: true,
label: 'Total',
formatter: function (w) {
return w.globals.seriesTotals.reduce((a, b) => a + b, 0)
}
}
}
}
}
},
dataLabels: {
enabled: true,
formatter: function(val, opts) {
return patientTypeDistributionData[opts.seriesIndex].percentage + '%'
}
},
tooltip: {
y: {
formatter: function(value, { series, seriesIndex, dataPointIndex, w }) {
return value + ' comments (' + patientTypeDistributionData[seriesIndex].percentage + '%)'
}
}
},
legend: {
position: 'bottom'
}
};
const patientTypeChart = new ApexCharts(document.querySelector("#patientTypeDistributionChart"), patientTypeChartOptions);
patientTypeChart.render();
}
// Sentiment by Patient Type Chart (Stacked Bar Chart)
const sentimentByPatientTypeData = {{ sentiment_by_patient_type_json|safe }};
if (sentimentByPatientTypeData && sentimentByPatientTypeData.types && sentimentByPatientTypeData.types.length > 0) {
const sentimentChartOptions = {
series: [
{
name: 'Positive',
data: sentimentByPatientTypeData.positive
},
{
name: 'Negative',
data: sentimentByPatientTypeData.negative
},
{
name: 'Neutral',
data: sentimentByPatientTypeData.neutral
}
],
chart: {
type: 'bar',
height: 300,
stacked: true,
fontFamily: 'inherit',
toolbar: {
show: false
}
},
plotOptions: {
bar: {
horizontal: false,
borderRadius: 4,
dataLabels: {
total: {
enabled: true,
style: {
fontSize: '13px',
fontWeight: 900
}
}
}
},
},
xaxis: {
categories: sentimentByPatientTypeData.types,
labels: {
style: {
fontSize: '12px'
}
}
},
yaxis: {
title: {
text: 'Number of Comments'
}
},
legend: {
position: 'bottom'
},
fill: {
opacity: 1
},
colors: ['#00e396', '#F55252', '#82868a'], // Green, Red, Gray
dataLabels: {
enabled: false
},
tooltip: {
y: {
formatter: function (value, { series, seriesIndex, dataPointIndex, w }) {
return value + ' comments'
}
}
}
};
const sentimentChart = new ApexCharts(document.querySelector("#sentimentByPatientTypeChart"), sentimentChartOptions);
sentimentChart.render();
}
});
</script>
{% endblock %}