306 lines
12 KiB
HTML
306 lines
12 KiB
HTML
{% extends 'layouts/base.html' %}
|
|
{% load i18n %}
|
|
|
|
{% block title %}{% trans "Complaints Analytics" %}{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<!-- Page Header -->
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<div>
|
|
<h1 class="h3 mb-0">{% trans "Complaints Analytics" %}</h1>
|
|
<p class="text-muted">{% trans "Comprehensive complaints metrics and insights" %}</p>
|
|
</div>
|
|
<div>
|
|
<form method="get" class="d-inline">
|
|
<select name="date_range" class="form-select d-inline-block w-auto" onchange="this.form.submit()">
|
|
<option value="7" {% if date_range == 7 %}selected{% endif %}>{% trans "Last 7 Days" %}</option>
|
|
<option value="30" {% if date_range == 30 %}selected{% endif %}>{% trans "Last 30 Days" %}</option>
|
|
<option value="90" {% if date_range == 90 %}selected{% endif %}>{% trans "Last 90 Days" %}</option>
|
|
</select>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Summary Cards -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-3">
|
|
<div class="card border-left-primary">
|
|
<div class="card-body">
|
|
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">{% trans "Total Complaints" %}</div>
|
|
<div class="h5 mb-0 font-weight-bold">{{ dashboard_summary.status_counts.total }}</div>
|
|
<small class="text-muted">
|
|
{% if dashboard_summary.trend.percentage_change > 0 %}
|
|
<i class="fas fa-arrow-up text-danger"></i> +{{ dashboard_summary.trend.percentage_change }}%
|
|
{% elif dashboard_summary.trend.percentage_change < 0 %}
|
|
<i class="fas fa-arrow-down text-success"></i> {{ dashboard_summary.trend.percentage_change }}%
|
|
{% else %}
|
|
<i class="fas fa-minus text-muted"></i> 0%
|
|
{% endif %}
|
|
{% trans "vs last period" %}
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card border-left-warning">
|
|
<div class="card-body">
|
|
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">{% trans "Open" %}</div>
|
|
<div class="h5 mb-0 font-weight-bold">{{ dashboard_summary.status_counts.open }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card border-left-danger">
|
|
<div class="card-body">
|
|
<div class="text-xs font-weight-bold text-danger text-uppercase mb-1">{% trans "Overdue" %}</div>
|
|
<div class="h5 mb-0 font-weight-bold">{{ dashboard_summary.status_counts.overdue }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card border-left-success">
|
|
<div class="card-body">
|
|
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">{% trans "Resolved" %}</div>
|
|
<div class="h5 mb-0 font-weight-bold">{{ dashboard_summary.status_counts.resolved }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-4">
|
|
<!-- Complaints Trend -->
|
|
<div class="col-lg-8">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h6 class="m-0 font-weight-bold">{% trans "Complaints Trend" %}</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="trendChart"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Top Categories -->
|
|
<div class="col-lg-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h6 class="m-0 font-weight-bold">{% trans "Top Categories" %}</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="categoryChart"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-4">
|
|
<!-- SLA Compliance -->
|
|
<div class="col-lg-6">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h6 class="m-0 font-weight-bold">{% trans "SLA Compliance" %}</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="text-center mb-3">
|
|
<h2 class="{% if sla_compliance.overall_compliance_rate >= 80 %}text-success{% elif sla_compliance.overall_compliance_rate >= 60 %}text-warning{% else %}text-danger{% endif %}">
|
|
{{ sla_compliance.overall_compliance_rate }}%
|
|
</h2>
|
|
<p class="text-muted">{% trans "Overall Compliance Rate" %}</p>
|
|
</div>
|
|
<div class="row text-center">
|
|
<div class="col-6">
|
|
<h4 class="text-success">{{ sla_compliance.on_time }}</h4>
|
|
<small class="text-muted">{% trans "On Time" %}</small>
|
|
</div>
|
|
<div class="col-6">
|
|
<h4 class="text-danger">{{ sla_compliance.overdue }}</h4>
|
|
<small class="text-muted">{% trans "Overdue" %}</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Resolution Rate -->
|
|
<div class="col-lg-6">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h6 class="m-0 font-weight-bold">{% trans "Resolution Metrics" %}</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<div class="d-flex justify-content-between mb-1">
|
|
<span>{% trans "Resolution Rate" %}</span>
|
|
<strong>{{ resolution_rate.resolution_rate }}%</strong>
|
|
</div>
|
|
<div class="progress">
|
|
<div class="progress-bar bg-success" style="width: {{ resolution_rate.resolution_rate }}%"></div>
|
|
</div>
|
|
</div>
|
|
<div class="row text-center">
|
|
<div class="col-6">
|
|
<h4>{{ resolution_rate.resolved }}</h4>
|
|
<small class="text-muted">{% trans "Resolved" %}</small>
|
|
</div>
|
|
<div class="col-6">
|
|
<h4>{{ resolution_rate.pending }}</h4>
|
|
<small class="text-muted">{% trans "Pending" %}</small>
|
|
</div>
|
|
</div>
|
|
{% if resolution_rate.avg_resolution_time_hours %}
|
|
<div class="mt-3 text-center">
|
|
<p class="mb-0"><strong>{% trans "Avg Resolution Time" %}:</strong></p>
|
|
<h5>{{ resolution_rate.avg_resolution_time_hours }} {% trans "hours" %}</h5>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Overdue Complaints -->
|
|
{% if overdue_complaints %}
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h6 class="m-0 font-weight-bold text-danger">{% trans "Overdue Complaints" %}</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>{% trans "ID" %}</th>
|
|
<th>{% trans "Title" %}</th>
|
|
<th>{% trans "Patient" %}</th>
|
|
<th>{% trans "Severity" %}</th>
|
|
<th>{% trans "Due Date" %}</th>
|
|
<th>{% trans "Assigned To" %}</th>
|
|
<th>{% trans "Actions" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for complaint in overdue_complaints %}
|
|
<tr>
|
|
<td><small class="text-muted">{{ complaint.id|truncatechars:8 }}</small></td>
|
|
<td>{{ complaint.title|truncatechars:50 }}</td>
|
|
<td>{{ complaint.patient.get_full_name }}</td>
|
|
<td>
|
|
<span class="badge bg-{% if complaint.severity == 'critical' %}danger{% elif complaint.severity == 'high' %}warning{% else %}secondary{% endif %}">
|
|
{{ complaint.get_severity_display }}
|
|
</span>
|
|
</td>
|
|
<td class="text-danger">{{ complaint.due_at|date:"Y-m-d H:i" }}</td>
|
|
<td>{{ complaint.assigned_to.get_full_name|default:"Unassigned" }}</td>
|
|
<td>
|
|
<a href="{% url 'complaints:complaint_detail' complaint.id %}" class="btn btn-sm btn-primary">
|
|
<i class="fas fa-eye"></i>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<script>
|
|
// Trend Chart - ApexCharts
|
|
var trendOptions = {
|
|
series: [{
|
|
name: '{% trans "Complaints" %}',
|
|
data: {{ trends.data|safe }}
|
|
}],
|
|
chart: {
|
|
type: 'line',
|
|
height: 320,
|
|
toolbar: {
|
|
show: false
|
|
}
|
|
},
|
|
stroke: {
|
|
curve: 'smooth',
|
|
width: 3
|
|
},
|
|
colors: ['#4bc0c0'],
|
|
{#fill: {#}
|
|
{# type: 'gradient',#}
|
|
{# gradient: {#}
|
|
{# shadeIntensity: 1,#}
|
|
{# opacityFrom: 0.4,#}
|
|
{# opacityTo: 0.1,#}
|
|
{# }#}
|
|
{# },#}
|
|
xaxis: {
|
|
categories: {{ trends.labels|safe }},
|
|
labels: {
|
|
style: {
|
|
fontSize: '12px'
|
|
}
|
|
}
|
|
},
|
|
yaxis: {
|
|
min: 0,
|
|
forceNiceScale: true,
|
|
labels: {
|
|
style: {
|
|
fontSize: '12px'
|
|
}
|
|
}
|
|
},
|
|
grid: {
|
|
borderColor: '#e7e7e7',
|
|
strokeDashArray: 5
|
|
},
|
|
tooltip: {
|
|
theme: 'light'
|
|
}
|
|
};
|
|
var trendChart = new ApexCharts(document.querySelector("#trendChart"), trendOptions);
|
|
trendChart.render();
|
|
|
|
// Category Chart - ApexCharts
|
|
var categoryOptions = {
|
|
series: [{% for cat in top_categories.categories %}{{ cat.count }}{% if not forloop.last %},{% endif %}{% endfor %}],
|
|
chart: {
|
|
type: 'donut',
|
|
height: 360
|
|
},
|
|
labels: [{% for cat in top_categories.categories %}'{{ cat.category }}'{% if not forloop.last %},{% endif %}{% endfor %}],
|
|
colors: ['#ff6384', '#36a2eb', '#ffce56', '#4bc0c0', '#9966ff', '#ff9f40'],
|
|
legend: {
|
|
position: 'bottom',
|
|
fontSize: '12px'
|
|
},
|
|
dataLabels: {
|
|
enabled: true,
|
|
formatter: function (val) {
|
|
return val.toFixed(1) + "%"
|
|
}
|
|
},
|
|
plotOptions: {
|
|
pie: {
|
|
donut: {
|
|
size: '65%'
|
|
}
|
|
}
|
|
},
|
|
tooltip: {
|
|
theme: 'light'
|
|
}
|
|
};
|
|
var categoryChart = new ApexCharts(document.querySelector("#categoryChart"), categoryOptions);
|
|
categoryChart.render();
|
|
</script>
|
|
{% endblock %}
|