HH/templates/dashboard/complaint_quarterly_report.html
ismail c5f76b3855
Some checks are pending
Build and Push Docker Image / build (push) Waiting to run
updates
2026-05-11 14:45:30 +03:00

892 lines
50 KiB
HTML

{% extends "layouts/base.html" %}
{% load i18n %}
{% block title %}Complaints Yearly Report - PX360{% endblock %}
{% block content %}
<div class="max-w-[1400px] mx-auto">
<div class="flex justify-between items-center mb-6">
<div>
<h1 class="text-2xl font-bold text-gray-800">{% trans "Complaints Yearly Report Card" %}</h1>
<p class="text-sm text-gray-500 mt-1">{% trans "Step 2/Yearly — KPI tables, source breakdowns, escalated analysis, response rates" %}</p>
</div>
<a href="{% url 'dashboard:complaint_quarterly_export' %}?hospital={{ selected_hospital }}&start_date={{ start_date }}&end_date={{ end_date }}"
class="inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition"
style="background-color: #059669; color: white;">
<i data-lucide="download" class="w-4 h-4" style="color: white;"></i>
{% trans "Export Excel" %}
</a>
</div>
{% if no_data %}
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-12 text-center">
<div class="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-4">
<i data-lucide="bar-chart-3" class="w-8 h-8 text-gray-400"></i>
</div>
<h3 class="text-lg font-semibold text-gray-700 mb-2">{% trans "No Data" %}</h3>
<p class="text-gray-500">{% trans "No complaint records for the selected hospital." %}</p>
</div>
{% else %}
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 mb-6">
<form method="get" class="flex flex-wrap gap-4 items-end">
<div class="flex-1 min-w-[200px]">
<label class="block text-xs font-semibold text-gray-600 mb-1">{% trans "Hospital" %}</label>
<select name="hospital" class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm" data-tomselect>
{% for h in hospitals %}
<option value="{{ h.id }}" {% if selected_hospital == h.id|stringformat:'s' %}selected{% endif %}>{{ h.name }}</option>
{% endfor %}
</select>
</div>
<div class="w-40">
<label class="block text-xs font-semibold text-gray-600 mb-1">{% trans "Start Date" %}</label>
<input type="date" name="start_date" value="{{ start_date }}" class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm">
</div>
<div class="w-40">
<label class="block text-xs font-semibold text-gray-600 mb-1">{% trans "End Date" %}</label>
<input type="date" name="end_date" value="{{ end_date }}" class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm">
</div>
<button type="submit" class="px-4 py-2 bg-navy text-white text-sm font-medium rounded-lg hover:bg-navy/90 transition">
{% trans "Apply" %}
</button>
</form>
</div>
<div class="mb-6 border-b border-gray-200">
<nav class="flex gap-1 -mb-px" id="report-tabs">
<button onclick="switchTab('dashboard')" data-tab="dashboard"
class="tab-btn px-4 py-2 text-sm font-medium border-b-2 border-blue-600 text-blue-600">
{% trans "Dashboard" %}
</button>
<button onclick="switchTab('kpi')" data-tab="kpi"
class="tab-btn px-4 py-2 text-sm font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700">
1. KPI
</button>
<button onclick="switchTab('first-table')" data-tab="first-table"
class="tab-btn px-4 py-2 text-sm font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700">
2. 1st Table
</button>
<button onclick="switchTab('escalated')" data-tab="escalated"
class="tab-btn px-4 py-2 text-sm font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700">
3. Escalated
</button>
<button onclick="switchTab('each-dep')" data-tab="each-dep"
class="tab-btn px-4 py-2 text-sm font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700">
4. Each Dep.
</button>
<button onclick="switchTab('response-rates')" data-tab="response-rates"
class="tab-btn px-4 py-2 text-sm font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700">
5-7. Response Rates
</button>
</nav>
</div>
<div id="tab-dashboard">
<div class="grid grid-cols-1 md:grid-cols-5 gap-4 mb-6">
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<p class="text-xs text-gray-500 font-medium">{% trans "Total Complaints" %}</p>
<p class="text-2xl font-bold text-gray-800">{{ kpi_data.totals.total }}</p>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<p class="text-xs text-gray-500 font-medium">{% trans "Resolution Rate" %}</p>
<p class="text-2xl font-bold {% if kpi_data.totals.resolution_rate >= 0.95 %}text-emerald-600{% elif kpi_data.totals.resolution_rate >= 0.9 %}text-amber-600{% else %}text-red-600{% endif %}">
{{ kpi_data.totals.resolution_rate|floatformat:1 }}%
</p>
<p class="text-[10px] text-gray-400">{% trans "Target: 95%" %}</p>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<p class="text-xs text-gray-500 font-medium">{% trans "72h Resolution Rate" %}</p>
<p class="text-2xl font-bold {% if kpi_data.totals.resolved_72h_rate >= 0.95 %}text-emerald-600{% elif kpi_data.totals.resolved_72h_rate >= 0.9 %}text-amber-600{% else %}text-red-600{% endif %}">
{{ kpi_data.totals.resolved_72h_rate|floatformat:1 }}%
</p>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<p class="text-xs text-gray-500 font-medium">{% trans "Satisfaction Rate" %}</p>
<p class="text-2xl font-bold {% if satisfaction_data.totals.rate >= 0.95 %}text-emerald-600{% elif satisfaction_data.totals.rate >= 0.7 %}text-amber-600{% else %}text-red-600{% endif %}">
{{ satisfaction_data.totals.rate|floatformat:1 }}%
</p>
<p class="text-[10px] text-gray-400">{{ satisfaction_data.totals.surveyed }} {% trans "surveyed" %}</p>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<p class="text-xs text-gray-500 font-medium">{% trans "Escalated" %}</p>
<p class="text-2xl font-bold text-gray-800">{{ escalated.total_escalated }}</p>
<p class="text-[10px] text-gray-400">{{ escalated.escalation_rate|floatformat:1 }}% {% trans "rate" %}</p>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<h3 class="text-sm font-semibold text-gray-700 mb-4">{% trans "Resolution Rate Trend" %}</h3>
<div id="resolution-trend-chart"></div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<h3 class="text-sm font-semibold text-gray-700 mb-4">{% trans "Monthly Complaints" %}</h3>
<div id="monthly-totals-chart"></div>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6">
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<h3 class="text-sm font-semibold text-gray-700 mb-4">{% trans "External vs Internal" %}</h3>
<div id="source-chart"></div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<h3 class="text-sm font-semibold text-gray-700 mb-4">{% trans "By Location" %}</h3>
<div id="location-chart"></div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<h3 class="text-sm font-semibold text-gray-700 mb-4">{% trans "By Department Type" %}</h3>
<div id="dept-type-chart"></div>
</div>
</div>
{% if response_rates.internal.total > 0 %}
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6">
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<h3 class="text-sm font-semibold text-gray-700 mb-4">{% trans "Internal Response Rate" %}</h3>
<p class="text-xs text-gray-400 mb-2">{% trans "Total:" %} {{ response_rates.internal.total }}</p>
<div id="rr-internal-chart"></div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<h3 class="text-sm font-semibold text-gray-700 mb-4">{% trans "MOH Response Rate" %}</h3>
<p class="text-xs text-gray-400 mb-2">{% trans "Total:" %} {{ response_rates.moh.total }}</p>
<div id="rr-moh-chart"></div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<h3 class="text-sm font-semibold text-gray-700 mb-4">{% trans "CHI Response Rate" %}</h3>
<p class="text-xs text-gray-400 mb-2">{% trans "Total:" %} {{ response_rates.chi.total }}</p>
<div id="rr-chi-chart"></div>
</div>
</div>
{% endif %}
</div>
<div id="tab-kpi" class="hidden">
{% for block in kpi_blocks %}
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 mb-6 overflow-x-auto">
<h3 class="text-sm font-semibold text-gray-700 mb-3">{{ block.label }}</h3>
<table class="w-full text-xs border-collapse">
<thead>
<tr class="bg-gray-800 text-white">
<th class="border border-gray-300 px-2 py-1.5 text-left min-w-[280px]">{{ block.numerator_label }}</th>
{% for m in month_labels %}<th class="border border-gray-300 px-2 py-1.5 text-center min-w-[50px]">{{ m }}</th>{% endfor %}
<th class="border border-gray-300 px-2 py-1.5 text-center min-w-[50px] font-bold">Total</th>
</tr>
</thead>
<tbody>
<tr>
<td class="border border-gray-300 px-2 py-1">{{ block.numerator_label }}</td>
{% for m in block.numerator_values %}<td class="border border-gray-300 px-2 py-1 text-center">{{ m }}</td>{% endfor %}
<td class="border border-gray-300 px-2 py-1 text-center font-semibold">{{ block.total_num }}</td>
</tr>
<tr>
<td class="border border-gray-300 px-2 py-1">{{ block.denominator_label }}</td>
{% for m in block.denominator_values %}<td class="border border-gray-300 px-2 py-1 text-center">{{ m }}</td>{% endfor %}
<td class="border border-gray-300 px-2 py-1 text-center font-semibold">{{ block.total_den }}</td>
</tr>
<tr class="bg-gray-50">
<td class="border border-gray-300 px-2 py-1 font-semibold">{{ block.result_label }}</td>
{% for m in block.result_values %}<td class="border border-gray-300 px-2 py-1 text-center font-semibold">{{ m|floatformat:1 }}%</td>{% endfor %}
<td class="border border-gray-300 px-2 py-1 text-center font-bold">{{ block.total_res|floatformat:1 }}%</td>
</tr>
</tbody>
</table>
</div>
{% endfor %}
</div>
<div id="tab-first-table" class="hidden">
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 mb-6 overflow-x-auto">
<h3 class="text-sm font-semibold text-gray-700 mb-3">{% trans "Source Breakdown" %}</h3>
<table class="w-full text-xs border-collapse">
<thead>
<tr class="bg-gray-800 text-white">
<th class="border border-gray-300 px-2 py-1.5 text-center">%</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">Number</th>
<th class="border border-gray-300 px-2 py-1.5 text-left">Type</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">Total</th>
{% for m in month_labels %}<th class="border border-gray-300 px-2 py-1.5 text-center">{{ m }}</th>{% endfor %}
</tr>
</thead>
<tbody>
<tr class="bg-blue-50">
<td class="border border-gray-300 px-2 py-1 text-center">{{ source_totals.external.pct|floatformat:1 }}%</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ source_totals.external.count }}</td>
<td class="border border-gray-300 px-2 py-1 font-semibold">External</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ source_totals.external.count }}</td>
{% for m in month_labels %}<td class="border border-gray-300 px-2 py-1 text-center">{{ source_breakdown.forloop.counter0|default:'' }}</td>{% endfor %}
</tr>
{% for sub in source_sub_external %}
<tr>
<td class="border border-gray-300 px-2 py-1"></td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ sub.total }}</td>
<td class="border border-gray-300 px-2 py-1 pl-6">{{ sub.label }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ sub.total }}</td>
{% for v in sub.monthly %}<td class="border border-gray-300 px-2 py-1 text-center">{{ v.count }}</td>{% endfor %}
</tr>
{% endfor %}
<tr class="bg-green-50">
<td class="border border-gray-300 px-2 py-1 text-center">{{ source_totals.internal.pct|floatformat:1 }}%</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ source_totals.internal.count }}</td>
<td class="border border-gray-300 px-2 py-1 font-semibold">Internal</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ source_totals.internal.count }}</td>
{% for v in source_internal_monthly %}<td class="border border-gray-300 px-2 py-1 text-center">{{ v }}</td>{% endfor %}
</tr>
{% for sub in source_sub_internal %}
<tr>
<td class="border border-gray-300 px-2 py-1"></td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ sub.total }}</td>
<td class="border border-gray-300 px-2 py-1 pl-6">{{ sub.label }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ sub.total }}</td>
{% for v in sub.monthly %}<td class="border border-gray-300 px-2 py-1 text-center">{{ v.count }}</td>{% endfor %}
</tr>
{% endfor %}
<tr class="bg-gray-100 font-semibold">
<td class="border border-gray-300 px-2 py-1"></td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ source_totals.total }}</td>
<td class="border border-gray-300 px-2 py-1">Total</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ source_totals.total }}</td>
{% for v in source_total_monthly %}<td class="border border-gray-300 px-2 py-1 text-center">{{ v }}</td>{% endfor %}
</tr>
</tbody>
</table>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 mb-6 overflow-x-auto">
<h3 class="text-sm font-semibold text-gray-700 mb-3">{% trans "Location Breakdown" %}</h3>
<table class="w-full text-xs border-collapse">
<thead>
<tr class="bg-gray-800 text-white">
<th class="border border-gray-300 px-2 py-1.5 text-center">%</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">Number</th>
<th class="border border-gray-300 px-2 py-1.5 text-left">Location</th>
{% for m in month_labels %}<th class="border border-gray-300 px-2 py-1.5 text-center">{{ m }}</th>{% endfor %}
</tr>
</thead>
<tbody>
{% for row in location_rows %}
<tr{% if row.is_total %} class="bg-gray-100 font-semibold"{% endif %}>
<td class="border border-gray-300 px-2 py-1 text-center">{{ row.pct|floatformat:1 }}%</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ row.total }}</td>
<td class="border border-gray-300 px-2 py-1">{{ row.label }}</td>
{% for v in row.monthly %}<td class="border border-gray-300 px-2 py-1 text-center">{{ v }}</td>{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 mb-6 overflow-x-auto">
<h3 class="text-sm font-semibold text-gray-700 mb-3">{% trans "Department Type Breakdown" %}</h3>
<table class="w-full text-xs border-collapse">
<thead>
<tr class="bg-gray-800 text-white">
<th class="border border-gray-300 px-2 py-1.5 text-center">%</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">Number</th>
<th class="border border-gray-300 px-2 py-1.5 text-left">Department</th>
{% for m in month_labels %}<th class="border border-gray-300 px-2 py-1.5 text-center">{{ m }}</th>{% endfor %}
</tr>
</thead>
<tbody>
{% for row in dept_type_rows %}
<tr{% if row.is_total %} class="bg-gray-100 font-semibold"{% endif %}>
<td class="border border-gray-300 px-2 py-1 text-center">{{ row.pct|floatformat:1 }}%</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ row.total }}</td>
<td class="border border-gray-300 px-2 py-1">{{ row.label }}</td>
{% for v in row.monthly %}<td class="border border-gray-300 px-2 py-1 text-center">{{ v }}</td>{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 mb-6 overflow-x-auto">
<h3 class="text-sm font-semibold text-gray-700 mb-3">{% trans "Monthly Summary" %}</h3>
<table class="w-full text-xs border-collapse">
<thead>
<tr class="bg-gray-800 text-white">
<th class="border border-gray-300 px-2 py-1.5 text-left">Month</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">MOH</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">CHI</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">Insurance</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">Internal</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">Total</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">MOH %</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">CHI %</th>
</tr>
</thead>
<tbody>
{% for ms in monthly_summary %}
<tr{% if ms.month == 'TOTAL' %} class="bg-gray-100 font-semibold"{% endif %}>
<td class="border border-gray-300 px-2 py-1">{{ ms.month }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ ms.moh }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ ms.chi }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ ms.insurance }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ ms.internal }}</td>
<td class="border border-gray-300 px-2 py-1 text-center font-semibold">{{ ms.total }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ ms.moh_pct|floatformat:1 }}%</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ ms.chi_pct|floatformat:1 }}%</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5 mb-6">
<h3 class="text-sm font-semibold text-gray-700 mb-4">{% trans "Total Complaints - External & Internal" %}</h3>
<div id="source-bar-chart"></div>
</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 mb-6 overflow-x-auto">
<h3 class="text-sm font-semibold text-gray-700 mb-3">{% trans "Source Totals" %}</h3>
<table class="w-full text-xs border-collapse max-w-lg">
<thead>
<tr class="bg-gray-800 text-white">
<th class="border border-gray-300 px-2 py-1.5 text-left">Source</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">Count</th>
<th class="border border-gray-300 px-2 py-1.5 text-left"></th>
<th class="border border-gray-300 px-2 py-1.5 text-center">Percentage</th>
</tr>
</thead>
<tbody>
{% for row in source_total_rows %}
<tr>
<td class="border border-gray-300 px-2 py-1">{{ row.label }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ row.count }}</td>
<td class="border border-gray-300 px-2 py-1">{{ row.pct_label }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ row.pct|floatformat:1 }}%</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% for lr in location_ratio_tables %}
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 mb-6 overflow-x-auto">
<h3 class="text-sm font-semibold text-gray-700 mb-3">{{ lr.title }}</h3>
<table class="w-full text-xs border-collapse">
<thead>
<tr class="bg-gray-800 text-white">
<th class="border border-gray-300 px-2 py-1.5 text-left">Month</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">{{ lr.complaint_col }}</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">{{ lr.patient_col }}</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">Percentage</th>
</tr>
</thead>
<tbody>
{% for row in lr.rows %}
<tr{% if row.is_total %} class="bg-gray-100 font-semibold"{% endif %}>
<td class="border border-gray-300 px-2 py-1">{{ row.month }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ row.complaints }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ row.patients }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ row.ratio|floatformat:4 }}%</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endfor %}
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 mb-6 overflow-x-auto">
<h3 class="text-sm font-semibold text-gray-700 mb-3">{% trans "Department Type Monthly" %}</h3>
<table class="w-full text-xs border-collapse">
<thead>
<tr class="bg-gray-800 text-white">
<th class="border border-gray-300 px-2 py-1.5 text-left">Month</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">Medical</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">Admin</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">Nursing</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">Support Services</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">Total</th>
</tr>
</thead>
<tbody>
{% for row in dept_monthly_rows %}
<tr{% if row.is_total %} class="bg-gray-100 font-semibold"{% elif row.is_pct %} class="bg-gray-50"{% endif %}>
<td class="border border-gray-300 px-2 py-1{% if row.is_total or row.is_pct %} font-semibold{% endif %}">{{ row.month }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ row.medical }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ row.admin }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ row.nursing }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ row.support }}</td>
<td class="border border-gray-300 px-2 py-1 text-center font-semibold">{{ row.total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5 mb-6">
<h3 class="text-sm font-semibold text-gray-700 mb-4">{% trans "Total Complaints - Main Department" %}</h3>
<div id="dept-pie-chart"></div>
</div>
</div>
<div id="tab-escalated" class="hidden">
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 mb-6 overflow-x-auto">
<h3 class="text-sm font-semibold text-gray-700 mb-3">{% trans "Escalated Complaints by Category" %}</h3>
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4">
{% for cat in escalated_table %}
<div>
<table class="w-full text-xs border-collapse">
<thead>
<tr class="bg-gray-800 text-white">
<th class="border border-gray-300 px-2 py-1.5 text-left">{{ cat.label }}</th>
<th class="border border-gray-300 px-2 py-1.5 text-center w-16">Escalated</th>
</tr>
</thead>
<tbody>
{% for sub in cat.sub_depts %}
<tr>
<td class="border border-gray-300 px-2 py-1">{{ sub.name }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ sub.count }}</td>
</tr>
{% endfor %}
{% if cat.sub_depts %}
<tr class="bg-gray-100 font-semibold">
<td class="border border-gray-300 px-2 py-1">Total</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ cat.total_escalated }}</td>
</tr>
{% endif %}
<tr class="bg-gray-50">
<td class="border border-gray-300 px-2 py-1">Total Complaints</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ cat.total_complaints }}</td>
</tr>
<tr class="bg-amber-50 font-semibold">
<td class="border border-gray-300 px-2 py-1">% Escalated</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ cat.escalation_rate|floatformat:1 }}%</td>
</tr>
</tbody>
</table>
</div>
{% endfor %}
</div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 mb-6">
<h3 class="text-sm font-semibold text-gray-700 mb-3">{% trans "Escalated Complaints Summary" %}</h3>
<table class="text-xs border-collapse">
<tbody>
<tr class="bg-gray-100">
<td class="border border-gray-300 px-3 py-1.5 font-semibold">{% trans "Total Number of Escalated Complaints" %}</td>
<td class="border border-gray-300 px-3 py-1.5 text-center font-bold">{{ escalated.total_escalated }}</td>
</tr>
<tr>
<td class="border border-gray-300 px-3 py-1.5 pl-6">{% trans "Internal Complaints" %}</td>
<td class="border border-gray-300 px-3 py-1.5 text-center">{{ escalated.by_source.internal|default:0 }}</td>
</tr>
<tr>
<td class="border border-gray-300 px-3 py-1.5 pl-6">{% trans "MOH" %}</td>
<td class="border border-gray-300 px-3 py-1.5 text-center">{{ escalated.by_source.moh|default:0 }}</td>
</tr>
<tr>
<td class="border border-gray-300 px-3 py-1.5 pl-6">{% trans "CHI" %}</td>
<td class="border border-gray-300 px-3 py-1.5 text-center">{{ escalated.by_source.chi|default:0 }}</td>
</tr>
<tr class="bg-gray-100 font-semibold">
<td class="border border-gray-300 px-3 py-1.5">{% trans "Total" %}</td>
<td class="border border-gray-300 px-3 py-1.5 text-center">{{ escalated.total_escalated }}</td>
</tr>
</tbody>
</table>
<div class="mt-3 text-xs text-gray-500">
{% trans "Total Escalated Comp." %}: {{ escalated.total_escalated }} &nbsp;|&nbsp;
{% trans "Total Number of Comp." %}: {{ escalated.total_complaints }} &nbsp;|&nbsp;
{% trans "%" %}: {{ escalated.escalation_rate|floatformat:1 }}%
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6">
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<h3 class="text-sm font-semibold text-gray-700 mb-4">{% trans "Escalated by Source (External & Internal)" %}</h3>
<div id="escalated-source-chart"></div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<h3 class="text-sm font-semibold text-gray-700 mb-4">{% trans "Escalated Rate by Department" %}</h3>
<div id="escalated-rate-chart"></div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<h3 class="text-sm font-semibold text-gray-700 mb-4">{% trans "Escalated Count by Department" %}</h3>
<div id="escalated-count-chart"></div>
</div>
</div>
</div>
<div id="tab-each-dep" class="hidden">
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 mb-6 overflow-x-auto">
<h3 class="text-sm font-semibold text-gray-700 mb-3">{% trans "Complaints by Sub-Department" %}</h3>
<div class="flex border-b border-gray-200 mb-4">
{% for cat in dept_category_tables %}
<button onclick="switchDeptTab({{ forloop.counter0 }})"
class="dept-tab-btn px-4 py-2 text-xs font-semibold transition-colors whitespace-nowrap
{% if forloop.first %}text-navy border-b-2 border-navy{% else %}text-gray-500 hover:text-gray-700{% endif %}"
data-idx="{{ forloop.counter0 }}">
{{ cat.label }}
</button>
{% endfor %}
</div>
{% for cat in dept_category_tables %}
<div class="dept-tab-panel {% if not forloop.first %}hidden{% endif %}" data-idx="{{ forloop.counter0 }}">
<table class="w-full text-xs border-collapse">
<thead>
<tr class="bg-gray-800 text-white">
<th class="border border-gray-300 px-2 py-1 text-left">{{ cat.label }}</th>
<th class="border border-gray-300 px-1 py-1 text-center w-10">MOH</th>
<th class="border border-gray-300 px-1 py-1 text-center w-10">CHI</th>
<th class="border border-gray-300 px-1 py-1 text-center w-10">Ins.</th>
<th class="border border-gray-300 px-1 py-1 text-center w-10">Int.</th>
<th class="border border-gray-300 px-1 py-1 text-center w-10">Total</th>
<th class="border border-gray-300 px-1 py-1 text-center w-10">Esc.</th>
<th class="border border-gray-300 px-1 py-1 text-center w-10">Days</th>
</tr>
</thead>
<tbody>
{% for d in cat.depts %}
<tr>
<td class="border border-gray-300 px-2 py-0.5">{{ d.name }}</td>
<td class="border border-gray-300 px-1 py-0.5 text-center">{{ d.moh }}</td>
<td class="border border-gray-300 px-1 py-0.5 text-center">{{ d.chi }}</td>
<td class="border border-gray-300 px-1 py-0.5 text-center">{{ d.insurance }}</td>
<td class="border border-gray-300 px-1 py-0.5 text-center">{{ d.internal }}</td>
<td class="border border-gray-300 px-1 py-0.5 text-center font-semibold">{{ d.total }}</td>
<td class="border border-gray-300 px-1 py-0.5 text-center">{{ d.escalated }}</td>
<td class="border border-gray-300 px-1 py-0.5 text-center">{{ d.avg_response_days|floatformat:1 }}</td>
</tr>
{% endfor %}
{% if cat.depts %}
<tr class="bg-gray-100 font-semibold">
<td class="border border-gray-300 px-2 py-0.5">Total</td>
<td class="border border-gray-300 px-1 py-0.5 text-center">{{ cat.totals.moh }}</td>
<td class="border border-gray-300 px-1 py-0.5 text-center">{{ cat.totals.chi }}</td>
<td class="border border-gray-300 px-1 py-0.5 text-center">{{ cat.totals.insurance }}</td>
<td class="border border-gray-300 px-1 py-0.5 text-center">{{ cat.totals.internal }}</td>
<td class="border border-gray-300 px-1 py-0.5 text-center">{{ cat.totals.total }}</td>
<td class="border border-gray-300 px-1 py-0.5 text-center">{{ cat.totals.escalated }}</td>
<td class="border border-gray-300 px-1 py-0.5 text-center"></td>
</tr>
{% endif %}
</tbody>
</table>
</div>
{% endfor %}
</div>
<script>
function switchDeptTab(idx) {
document.querySelectorAll('.dept-tab-panel').forEach(function(p) { p.classList.add('hidden'); });
document.querySelectorAll('.dept-tab-btn').forEach(function(b) {
b.classList.remove('text-navy', 'border-b-2', 'border-navy');
b.classList.add('text-gray-500');
});
var panel = document.querySelector('.dept-tab-panel[data-idx="' + idx + '"]');
if (panel) panel.classList.remove('hidden');
var btn = document.querySelector('.dept-tab-btn[data-idx="' + idx + '"]');
if (btn) { btn.classList.remove('text-gray-500'); btn.classList.add('text-navy', 'border-b-2', 'border-navy'); }
}
</script>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 overflow-x-auto">
<h3 class="text-sm font-semibold text-gray-700 mb-3">{% trans "Source Totals" %}</h3>
<table class="text-xs border-collapse">
<thead>
<tr class="bg-gray-800 text-white">
<th class="border border-gray-300 px-3 py-1.5 text-left">{% trans "Source" %}</th>
<th class="border border-gray-300 px-3 py-1.5 text-center">{% trans "Count" %}</th>
</tr>
</thead>
<tbody>
{% for row in per_dept_source_rows %}
<tr{% if row.is_total %} class="bg-gray-100 font-semibold"{% endif %}>
<td class="border border-gray-300 px-3 py-1.5">{{ row.label }}</td>
<td class="border border-gray-300 px-3 py-1.5 text-center">{{ row.count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<h3 class="text-sm font-semibold text-gray-700 mb-4">{% trans "Average Response Rate in Days" %}</h3>
<div id="avg-response-chart"></div>
</div>
</div>
</div>
<div id="tab-response-rates" class="hidden">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6">
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<h3 class="text-sm font-semibold text-gray-700 mb-2">{% trans "Internal Complaints Response Rate" %}</h3>
<p class="text-xs text-gray-400 mb-3">{% trans "Total:" %} {{ response_rates.internal.total }}</p>
<div id="rr-internal-detail-chart"></div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<h3 class="text-sm font-semibold text-gray-700 mb-2">{% trans "MOH Complaints Response Rate" %}</h3>
<p class="text-xs text-gray-400 mb-3">{% trans "Total:" %} {{ response_rates.moh.total }}</p>
<div id="rr-moh-detail-chart"></div>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-5">
<h3 class="text-sm font-semibold text-gray-700 mb-2">{% trans "CHI Complaints Response Rate" %}</h3>
<p class="text-xs text-gray-400 mb-3">{% trans "Total:" %} {{ response_rates.chi.total }}</p>
<div id="rr-chi-detail-chart"></div>
</div>
</div>
{% for src in response_rate_source_list %}
{% if src.total > 0 %}
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 mb-6 overflow-x-auto">
<h3 class="text-sm font-semibold text-gray-700 mb-3">{{ src.label }} — {% trans "Individual Complaints" %}</h3>
<table class="w-full text-xs border-collapse">
<thead>
<tr class="bg-gray-800 text-white">
<th class="border border-gray-300 px-2 py-1.5 text-left">{% trans "Reference" %}</th>
<th class="border border-gray-300 px-2 py-1.5 text-left">{% trans "Department" %}</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">{% trans "Hours" %}</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">{% trans "Bucket" %}</th>
<th class="border border-gray-300 px-2 py-1.5 text-center">{% trans "Status" %}</th>
</tr>
</thead>
<tbody>
{% for item in src.individual %}
<tr>
<td class="border border-gray-300 px-2 py-1">{{ item.ref }}</td>
<td class="border border-gray-300 px-2 py-1">{{ item.dept }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ item.hours|floatformat:1 }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ item.bucket }}</td>
<td class="border border-gray-300 px-2 py-1 text-center">{{ item.status }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
</div>
{% endblock %}
{% block extra_js %}
{% if not no_data and chart_data_json %}
<script>
function switchTab(tabId) {
document.querySelectorAll('[id^="tab-"]').forEach(function(el) { el.classList.add('hidden'); });
document.getElementById('tab-' + tabId).classList.remove('hidden');
document.querySelectorAll('.tab-btn').forEach(function(btn) {
btn.classList.remove('border-blue-600', 'text-blue-600');
btn.classList.add('border-transparent', 'text-gray-500');
});
var activeBtn = document.querySelector('[data-tab="' + tabId + '"]');
activeBtn.classList.add('border-blue-600', 'text-blue-600');
activeBtn.classList.remove('border-transparent', 'text-gray-500');
}
(function() {
var cd = JSON.parse('{{ chart_data_json|escapejs }}');
new ApexCharts(document.getElementById('resolution-trend-chart'), {
chart: { type: 'line', height: 280, toolbar: { show: true } },
series: [
{ name: 'Resolution Rate', data: cd.kpi_resolution.values },
{ name: '72h Rate', data: cd.kpi_72h.values },
],
xaxis: { categories: cd.kpi_resolution.months },
yaxis: { min: 0, max: 1, labels: { formatter: function(v) { return (v * 100).toFixed(0) + '%'; } } },
stroke: { curve: 'smooth', width: 3 },
colors: ['#3B82F6', '#10B981'],
annotations: {
yaxis: [
{ y: cd.kpi_resolution.target, borderColor: '#10B981', label: { text: 'Target 95%' } },
{ y: cd.kpi_resolution.threshold, borderColor: '#F59E0B', label: { text: 'Threshold 90%' } },
]
},
tooltip: { y: { formatter: function(v) { return (v * 100).toFixed(1) + '%'; } } },
}).render();
new ApexCharts(document.getElementById('monthly-totals-chart'), {
chart: { type: 'bar', height: 280, toolbar: { show: true } },
plotOptions: { bar: { borderRadius: 3 } },
dataLabels: { enabled: false },
series: [{ name: 'Complaints', data: cd.monthly_totals.values, color: '#6366F1' }],
xaxis: { categories: cd.monthly_totals.months },
}).render();
new ApexCharts(document.getElementById('source-chart'), {
chart: { type: 'pie', height: 250 },
series: [cd.source_distribution.external, cd.source_distribution.internal],
labels: ['External', 'Internal'],
colors: ['#3B82F6', '#10B981'],
legend: { position: 'bottom' },
}).render();
new ApexCharts(document.getElementById('location-chart'), {
chart: { type: 'pie', height: 250 },
series: [cd.location_distribution.IP, cd.location_distribution.OP, cd.location_distribution.ER],
labels: ['Inpatient', 'Outpatient', 'Emergency'],
colors: ['#8B5CF6', '#3B82F6', '#EF4444'],
legend: { position: 'bottom' },
}).render();
new ApexCharts(document.getElementById('dept-type-chart'), {
chart: { type: 'pie', height: 250 },
series: [cd.dept_type_distribution.Medical, cd.dept_type_distribution.Admin, cd.dept_type_distribution.Nursing, cd.dept_type_distribution.Support],
labels: ['Medical', 'Admin', 'Nursing', 'Support'],
colors: ['#3B82F6', '#F59E0B', '#8B5CF6', '#6B7280'],
legend: { position: 'bottom' },
}).render();
function makeRRChart(elId, data) {
if (!data || data.total === 0) return;
var buckets = ['24h', '48h', '72h', '72h+'];
var counts = buckets.map(function(b) { return data.counts[b] || 0; });
new ApexCharts(document.getElementById(elId), {
chart: { type: 'donut', height: 200 },
series: counts,
labels: buckets,
colors: ['#10B981', '#3B82F6', '#F59E0B', '#EF4444'],
legend: { position: 'bottom' },
}).render();
}
{% if response_rates.internal.total > 0 %}
var rr = JSON.parse('{{ response_rates|escapejs }}');
makeRRChart('rr-internal-chart', rr.internal);
if (rr.moh && rr.moh.total > 0) makeRRChart('rr-moh-chart', rr.moh);
if (rr.chi && rr.chi.total > 0) makeRRChart('rr-chi-chart', rr.chi);
{% endif %}
var msData = JSON.parse('{{ monthly_summary_json|escapejs }}');
var msMonths = msData.filter(function(r) { return r.month !== 'TOTAL'; }).map(function(r) { return r.month; });
var msMoh = msData.filter(function(r) { return r.month !== 'TOTAL'; }).map(function(r) { return r.moh; });
var msChi = msData.filter(function(r) { return r.month !== 'TOTAL'; }).map(function(r) { return r.chi; });
var msIns = msData.filter(function(r) { return r.month !== 'TOTAL'; }).map(function(r) { return r.insurance; });
var msInt = msData.filter(function(r) { return r.month !== 'TOTAL'; }).map(function(r) { return r.internal; });
var srcSeries = [];
var srcColors = [];
if (msMoh.some(function(v) { return v > 0; })) { srcSeries.push({ name: 'MOH', data: msMoh }); srcColors.push('#3B82F6'); }
if (msChi.some(function(v) { return v > 0; })) { srcSeries.push({ name: 'CHI', data: msChi }); srcColors.push('#F59E0B'); }
if (msIns.some(function(v) { return v > 0; })) { srcSeries.push({ name: 'Insurance', data: msIns }); srcColors.push('#EF4444'); }
srcSeries.push({ name: 'Internal', data: msInt }); srcColors.push('#10B981');
new ApexCharts(document.getElementById('source-bar-chart'), {
chart: { type: 'bar', height: 350, stacked: true, toolbar: { show: true } },
plotOptions: { bar: { borderRadius: 2 } },
dataLabels: { enabled: false },
series: srcSeries,
xaxis: { categories: msMonths },
colors: srcColors,
legend: { position: 'top' },
tooltip: { shared: true, intersect: false },
}).render();
var dtRows = JSON.parse('{{ dept_type_rows_json|escapejs }}');
var dtNonTotal = dtRows.filter(function(r) { return !r.is_total; });
var dtLabels = dtNonTotal.map(function(r) { return r.label; });
var dtValues = dtNonTotal.map(function(r) { return r.total; });
new ApexCharts(document.getElementById('dept-pie-chart'), {
chart: { type: 'pie', height: 350 },
series: dtValues,
labels: dtLabels,
colors: ['#3B82F6', '#F59E0B', '#8B5CF6', '#6B7280'],
legend: { position: 'bottom' },
}).render();
var escData = JSON.parse('{{ escalated_json|escapejs }}');
var escSource = escData.by_source;
var escSourceLabels = ['Internal Complaints'];
var escSourceValues = [escSource.internal || 0];
var escSourceColors = ['#3B82F6'];
if ((escSource.moh || 0) > 0) {
escSourceLabels.push('MOH');
escSourceValues.push(escSource.moh || 0);
escSourceColors.push('#10B981');
}
if ((escSource.chi || 0) > 0) {
escSourceLabels.push('CHI');
escSourceValues.push(escSource.chi || 0);
escSourceColors.push('#F59E0B');
}
new ApexCharts(document.getElementById('escalated-source-chart'), {
chart: { type: 'pie', height: 300 },
series: escSourceValues,
labels: escSourceLabels,
colors: escSourceColors,
legend: { position: 'bottom' },
}).render();
var escCatLabels = Object.keys(escData.by_category_rates);
var escRateValues = escCatLabels.map(function(k) { return parseFloat((escData.by_category_rates[k] * 100).toFixed(1)); });
new ApexCharts(document.getElementById('escalated-rate-chart'), {
chart: { type: 'bar', height: 300, toolbar: { show: true } },
plotOptions: { bar: { borderRadius: 3, horizontal: true } },
dataLabels: { enabled: true, formatter: function(v) { return v + '%'; } },
series: [{ name: 'Escalated Rate', data: escRateValues }],
xaxis: { categories: escCatLabels, labels: { formatter: function(v) { return v + '%'; } } },
colors: ['#EF4444'],
}).render();
var escCountValues = escCatLabels.map(function(k) { return escData.by_category_counts[k]; });
new ApexCharts(document.getElementById('escalated-count-chart'), {
chart: { type: 'bar', height: 300, toolbar: { show: true } },
plotOptions: { bar: { borderRadius: 3, horizontal: true } },
dataLabels: { enabled: true },
series: [{ name: 'Escalated Count', data: escCountValues }],
xaxis: { categories: escCatLabels },
colors: ['#8B5CF6'],
}).render();
var pdData = JSON.parse('{{ per_dept_json|escapejs }}');
var avgResp = pdData.avg_response_days;
var avgLabels = Object.keys(avgResp).map(function(k) {
var map = {medical: 'Medical', non_medical: 'Non-Medical', nursing: 'Nursing', support: 'Support Services'};
return map[k] || k;
});
var avgValues = Object.keys(avgResp).map(function(k) { return parseFloat((avgResp[k]).toFixed(1)); });
new ApexCharts(document.getElementById('avg-response-chart'), {
chart: { type: 'bar', height: 280, toolbar: { show: true } },
plotOptions: { bar: { borderRadius: 3, horizontal: true } },
dataLabels: { enabled: true, formatter: function(v) { return v + ' days'; } },
series: [{ name: 'Avg Response (Days)', data: avgValues }],
xaxis: { categories: avgLabels },
colors: ['#3B82F6'],
}).render();
var rrDetail = JSON.parse('{{ response_rates_json|escapejs }}');
var rrBucketColors = ['#10B981', '#3B82F6', '#F59E0B', '#EF4444'];
var rrBuckets = ['24h', '48h', '72h', '72h+'];
var rrChartConfigs = [
{key: 'internal', elId: 'rr-internal-detail-chart'},
{key: 'moh', elId: 'rr-moh-detail-chart'},
{key: 'chi', elId: 'rr-chi-detail-chart'},
];
rrChartConfigs.forEach(function(cfg) {
var data = rrDetail[cfg.key];
if (!data || data.total === 0) return;
var el = document.getElementById(cfg.elId);
if (!el) return;
var counts = rrBuckets.map(function(b) { return data.counts[b] || 0; });
new ApexCharts(el, {
chart: { type: 'donut', height: 280 },
series: counts,
labels: rrBuckets,
colors: rrBucketColors,
legend: { position: 'bottom' },
}).render();
});
})();
</script>
{% endif %}
{% endblock %}