HH/templates/dashboard/command_center.html
2026-03-09 16:10:24 +03:00

894 lines
37 KiB
HTML

{% extends 'layouts/base.html' %}
{% load i18n %}
{% block title %}{% trans "PX Command Center" %} - PX360{% endblock %}
{% block extra_css %}
<style>
/* Card Styling with Borders */
.card {
background: white;
border-radius: 1rem;
border: 2px solid #e2e8f0;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
overflow: hidden;
transition: all 0.3s ease;
}
.card:hover {
border-color: #005696;
box-shadow: 0 10px 25px -5px rgba(0, 86, 150, 0.15);
}
.card-header {
padding: 1rem 1.25rem;
border-bottom: 2px solid #e2e8f0;
background: linear-gradient(to right, #f8fafc, #f1f5f9);
display: flex;
justify-content: space-between;
align-items: center;
}
.card-title {
font-weight: 700;
color: #1e293b;
font-size: 1rem;
}
/* Module Card Specific Styles */
.module-card {
border-left: 4px solid;
}
.module-card.complaints {
border-left-color: #ef4444;
}
.module-card.surveys {
border-left-color: #a855f7;
}
.module-card.actions {
border-left-color: #f97316;
}
.module-card.inquiries {
border-left-color: #3b82f6;
}
.module-card.observations {
border-left-color: #22c55e;
}
/* Section Containers */
.section-card {
background: white;
border-radius: 1rem;
border: 2px solid #e2e8f0;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.section-header {
padding: 1rem 1.5rem;
border-bottom: 2px solid #e2e8f0;
background: linear-gradient(to right, #f8fafc, #f1f5f9);
}
.section-content {
padding: 1.5rem;
}
/* Table Styling */
.data-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
}
.data-table th {
background: #f8fafc;
border-bottom: 2px solid #e2e8f0;
padding: 0.75rem 1rem;
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
color: #64748b;
}
.data-table td {
border-bottom: 1px solid #e2e8f0;
padding: 0.75rem 1rem;
}
.data-table tr:hover td {
background: #f8fafc;
}
/* Chart Containers */
.chart-container {
border: 1px solid #e2e8f0;
border-radius: 0.75rem;
padding: 1rem;
background: #fafafa;
}
</style>
{% endblock %}
{% block content %}
<div class="space-y-6" id="command-center-container">
<!-- Page Header -->
<header class="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-2">
<div>
<h1 class="text-2xl font-bold text-navy flex items-center gap-3">
<i data-lucide="layout-dashboard" class="w-7 h-7"></i>
{% trans "PX Command Center" %}
</h1>
<p class="text-sm text-slate mt-1">{% trans "Real-time overview of your patient experience operations" %}</p>
</div>
<div class="flex items-center gap-4">
<!-- Auto-refresh indicator -->
<div class="flex items-center gap-2 text-sm">
<div class="flex items-center gap-2 px-3 py-1.5 bg-green-50 rounded-full">
<span class="relative flex h-2 w-2">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
<span class="relative inline-flex rounded-full h-2 w-2 bg-green-500"></span>
</span>
<span class="text-green-700 font-medium">{% trans "Live" %}</span>
</div>
<span class="text-slate text-xs" id="refresh-timer">{% trans "Refreshes in 60s" %}</span>
</div>
<div class="text-right">
<p class="text-xs text-slate uppercase tracking-wider">{% trans "Last Updated" %}</p>
<p class="text-sm font-bold text-navy" id="last-updated">{{ last_updated }}</p>
</div>
<button onclick="refreshCommandCenter()" class="p-2 bg-light rounded-lg hover:bg-slate-200 transition" title="{% trans 'Refresh Now' %}">
<i data-lucide="refresh-cw" class="w-5 h-5 text-navy" id="refresh-icon"></i>
</button>
</div>
</header>
<!-- RED ALERT BANNER (5-Second Rule) -->
{% if has_red_alerts %}
<div class="bg-gradient-to-r from-red-500 to-red-600 rounded-xl p-4 shadow-lg animate-pulse-subtle" id="red-alert-banner">
<div class="flex items-center gap-3 mb-3">
<div class="p-2 bg-white/20 rounded-lg">
<i data-lucide="alert-octagon" class="w-6 h-6 text-white"></i>
</div>
<div>
<h2 class="text-lg font-bold text-white">{% trans "⚠️ ATTENTION REQUIRED" %}</h2>
<p class="text-sm text-white/80">{% trans "Items requiring immediate action" %}</p>
</div>
</div>
<div class="grid grid-cols-2 sm:grid-cols-4 gap-3">
{% for alert in red_alerts %}
<a href="{{ alert.url }}" class="bg-white/10 hover:bg-white/20 rounded-lg p-3 transition group">
<div class="flex items-center gap-2 mb-1">
<i data-lucide="{{ alert.icon }}" class="w-4 h-4 text-white"></i>
<span class="text-xs font-medium text-white/80">{{ alert.label }}</span>
</div>
<div class="text-2xl font-bold text-white">{{ alert.value }}</div>
</a>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Core Module Cards Row -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-4">
<!-- Complaints Module Card -->
<div class="card module-card complaints hover:shadow-lg transition cursor-pointer" onclick="window.location.href='{% url 'complaints:complaint_list' %}'">
<div class="p-4">
<div class="flex items-center justify-between mb-3">
<div class="p-2 bg-red-50 rounded-lg">
<i data-lucide="message-square-warning" class="w-5 h-5 text-red-500"></i>
</div>
<span class="text-xs font-bold px-2 py-1 rounded-full {{ complaints_module.variance_direction|yesno:'bg-green-100 text-green-600,bg-red-100 text-red-600,bg-slate-100 text-slate' }}">
{% if complaints_module.variance > 0 %}+{% endif %}{{ complaints_module.variance }}%
</span>
</div>
<h3 class="text-sm font-medium text-slate mb-1">{% trans "Complaints" %}</h3>
<p class="text-3xl font-bold text-navy">{{ complaints_module.total_active }}</p>
<div class="mt-2 flex items-center gap-2 text-xs text-slate">
<span class="flex items-center gap-1">
<span class="w-2 h-2 rounded-full bg-red-500"></span>
{{ complaints_module.by_severity.critical }} {% trans "critical" %}
</span>
</div>
{% if complaints_module.overdue > 0 %}
<div class="mt-2 text-xs font-bold text-red-500 flex items-center gap-1">
<i data-lucide="clock" class="w-3 h-3"></i>
{{ complaints_module.overdue }} {% trans "overdue" %}
</div>
{% endif %}
</div>
<div class="px-4 py-2 bg-light border-t border-slate-100 flex justify-between items-center">
<span class="text-xs text-slate">{{ complaints_module.avg_resolution_hours }}h {% trans "avg resolution" %}</span>
<i data-lucide="chevron-right" class="w-4 h-4 text-slate"></i>
</div>
</div>
<!-- Survey Insights Module Card -->
<div class="card module-card surveys hover:shadow-lg transition cursor-pointer" onclick="window.location.href='{% url 'surveys:instance_list' %}'">
<div class="p-4">
<div class="flex items-center justify-between mb-3">
<div class="p-2 bg-purple-50 rounded-lg">
<i data-lucide="clipboard-check" class="w-5 h-5 text-purple-500"></i>
</div>
<span class="text-xs font-bold px-2 py-1 rounded-full bg-purple-100 text-purple-600">
NPS: {{ survey_module.nps_score }}
</span>
</div>
<h3 class="text-sm font-medium text-slate mb-1">{% trans "Survey Insights" %}</h3>
<p class="text-3xl font-bold text-navy">{{ survey_module.avg_satisfaction }}%</p>
<div class="mt-2 flex items-center gap-2 text-xs text-slate">
<span class="flex items-center gap-1">
<i data-lucide="send" class="w-3 h-3"></i>
{{ survey_module.response_rate }}% {% trans "response" %}
</span>
</div>
{% if survey_module.negative_24h > 0 %}
<div class="mt-2 text-xs font-bold text-orange-500 flex items-center gap-1">
<i data-lucide="frown" class="w-3 h-3"></i>
{{ survey_module.negative_24h }} {% trans "negative (24h)" %}
</div>
{% endif %}
</div>
<div class="px-4 py-2 bg-light border-t border-slate-100 flex justify-between items-center">
<span class="text-xs text-slate">{{ survey_module.total_completed }} {% trans "completed" %}</span>
<i data-lucide="chevron-right" class="w-4 h-4 text-slate"></i>
</div>
</div>
<!-- PX Actions Module Card -->
<div class="card module-card actions hover:shadow-lg transition cursor-pointer" onclick="window.location.href='{% url 'actions:action_list' %}'">
<div class="p-4">
<div class="flex items-center justify-between mb-3">
<div class="p-2 bg-orange-50 rounded-lg">
<i data-lucide="check-circle-2" class="w-5 h-5 text-orange-500"></i>
</div>
{% if actions_module.escalated > 0 %}
<span class="text-xs font-bold px-2 py-1 rounded-full bg-red-100 text-red-600 animate-pulse">
{{ actions_module.escalated }} {% trans "escalated" %}
</span>
{% endif %}
</div>
<h3 class="text-sm font-medium text-slate mb-1">{% trans "PX Actions" %}</h3>
<p class="text-3xl font-bold text-navy">{{ actions_module.open|add:actions_module.in_progress }}</p>
<div class="mt-2 flex items-center gap-3 text-xs text-slate">
<span>{{ actions_module.open }} {% trans "open" %}</span>
<span>{{ actions_module.in_progress }} {% trans "in progress" %}</span>
</div>
{% if actions_module.overdue > 0 %}
<div class="mt-2 text-xs font-bold text-red-500 flex items-center gap-1">
<i data-lucide="clock" class="w-3 h-3"></i>
{{ actions_module.overdue }} {% trans "overdue" %}
</div>
{% endif %}
</div>
<div class="px-4 py-2 bg-light border-t border-slate-100 flex justify-between items-center">
<span class="text-xs text-slate">{{ actions_module.closed_30d }} {% trans "closed (30d)" %}</span>
<i data-lucide="chevron-right" class="w-4 h-4 text-slate"></i>
</div>
</div>
<!-- Inquiries Module Card -->
<div class="card module-card inquiries hover:shadow-lg transition cursor-pointer" onclick="window.location.href='{% url 'complaints:complaint_list' %}?type=inquiry'">
<div class="p-4">
<div class="flex items-center justify-between mb-3">
<div class="p-2 bg-blue-50 rounded-lg">
<i data-lucide="help-circle" class="w-5 h-5 text-blue"></i>
</div>
{% if inquiries_module.new_24h > 0 %}
<span class="text-xs font-bold px-2 py-1 rounded-full bg-blue-100 text-blue-600">
{{ inquiries_module.new_24h }} {% trans "new" %}
</span>
{% endif %}
</div>
<h3 class="text-sm font-medium text-slate mb-1">{% trans "Inquiries" %}</h3>
<p class="text-3xl font-bold text-navy">{{ inquiries_module.total_active }}</p>
<div class="mt-2 flex items-center gap-3 text-xs text-slate">
<span>{{ inquiries_module.open }} {% trans "open" %}</span>
<span>{{ inquiries_module.in_progress }} {% trans "pending" %}</span>
</div>
</div>
<div class="px-4 py-2 bg-light border-t border-slate-100 flex justify-between items-center">
<span class="text-xs text-slate">{{ inquiries_module.resolved_30d }} {% trans "resolved (30d)" %}</span>
<i data-lucide="chevron-right" class="w-4 h-4 text-slate"></i>
</div>
</div>
<!-- Observations Module Card -->
<div class="card module-card observations hover:shadow-lg transition cursor-pointer" onclick="window.location.href='{% url 'observations:observation_list' %}'">
<div class="p-4">
<div class="flex items-center justify-between mb-3">
<div class="p-2 bg-green-50 rounded-lg">
<i data-lucide="eye" class="w-5 h-5 text-green-500"></i>
</div>
{% if observations_module.critical > 0 %}
<span class="text-xs font-bold px-2 py-1 rounded-full bg-red-100 text-red-600">
{{ observations_module.critical }} {% trans "critical" %}
</span>
{% endif %}
</div>
<h3 class="text-sm font-medium text-slate mb-1">{% trans "Observations" %}</h3>
<p class="text-3xl font-bold text-navy">{{ observations_module.total_active }}</p>
<div class="mt-2 flex items-center gap-3 text-xs text-slate">
<span>{{ observations_module.new }} {% trans "new" %}</span>
<span>{{ observations_module.in_progress }} {% trans "in progress" %}</span>
</div>
</div>
<div class="px-4 py-2 bg-light border-t border-slate-100 flex justify-between items-center">
<span class="text-xs text-slate">{{ observations_module.resolved_30d }} {% trans "resolved (30d)" %}</span>
<i data-lucide="chevron-right" class="w-4 h-4 text-slate"></i>
</div>
</div>
</div>
<!-- Charts Row -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Complaints Trend -->
<div class="lg:col-span-2 section-card">
<div class="section-header flex justify-between items-center">
<h3 class="font-bold text-navy flex items-center gap-2">
<i data-lucide="trending-up" class="w-5 h-5 text-blue"></i>
{% trans "Complaints Trend" %}
</h3>
<div class="flex gap-2">
<button class="px-3 py-1.5 text-xs font-bold text-slate hover:text-navy hover:bg-light rounded-lg transition border border-slate-200">Day</button>
<button class="px-3 py-1.5 text-xs font-bold bg-navy text-white rounded-lg border border-navy">Week</button>
<button class="px-3 py-1.5 text-xs font-bold text-slate hover:text-navy hover:bg-light rounded-lg transition border border-slate-200">Month</button>
</div>
</div>
<div class="section-content">
<div id="complaintsTrendChart" class="h-[280px] chart-container"></div>
</div>
</div>
<!-- NPS Trend -->
<div class="section-card">
<div class="section-header">
<h3 class="font-bold text-navy flex items-center gap-2">
<i data-lucide="smile" class="w-5 h-5 text-purple-500"></i>
{% trans "NPS Score Trend" %}
</h3>
</div>
<div class="section-content">
<div id="npsTrendChart" class="h-[200px] chart-container"></div>
<div class="mt-4 pt-4 border-t-2 border-slate-100 text-center">
<div class="text-3xl font-bold text-navy">{{ survey_module.nps_score }}</div>
<p class="text-sm text-slate">{% trans "Current NPS Score" %}</p>
</div>
</div>
</div>
</div>
<!-- Severity Distribution & Actions Funnel -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- Complaints by Severity -->
<div class="section-card">
<div class="section-header flex justify-between items-center">
<h3 class="font-bold text-navy flex items-center gap-2">
<i data-lucide="pie-chart" class="w-5 h-5 text-red-500"></i>
{% trans "Active Complaints by Severity" %}
</h3>
<a href="{% url 'complaints:complaint_list' %}" class="text-xs font-bold text-navy hover:text-blue border border-navy px-3 py-1 rounded-lg transition">
{% trans "View All" %} →
</a>
</div>
<div class="section-content">
<div id="severityDonutChart" class="h-[250px] chart-container"></div>
</div>
</div>
<!-- Actions Funnel -->
<div class="section-card">
<div class="section-header">
<h3 class="font-bold text-navy flex items-center gap-2">
<i data-lucide="git-branch" class="w-5 h-5 text-orange-500"></i>
{% trans "PX Actions Pipeline" %}
</h3>
</div>
<div class="p-4">
<div class="space-y-4">
<div class="relative">
<div class="flex items-center justify-between mb-1">
<span class="text-sm font-medium text-slate">{% trans "Open" %}</span>
<span class="text-sm font-bold text-navy">{{ actions_module.open }}</span>
</div>
<div class="h-8 bg-blue-100 rounded-lg overflow-hidden">
<div class="h-full bg-blue-500 flex items-center justify-end pr-2" style="width: {% widthratio actions_module.open actions_module.open|add:actions_module.in_progress|add:actions_module.pending_approval|add:actions_module.closed_30d 100 %}%"></div>
</div>
</div>
<div class="relative">
<div class="flex items-center justify-between mb-1">
<span class="text-sm font-medium text-slate">{% trans "In Progress" %}</span>
<span class="text-sm font-bold text-navy">{{ actions_module.in_progress }}</span>
</div>
<div class="h-8 bg-orange-100 rounded-lg overflow-hidden">
<div class="h-full bg-orange-500 flex items-center justify-end pr-2" style="width: {% widthratio actions_module.in_progress actions_module.open|add:actions_module.in_progress|add:actions_module.pending_approval|add:actions_module.closed_30d 100 %}%"></div>
</div>
</div>
<div class="relative">
<div class="flex items-center justify-between mb-1">
<span class="text-sm font-medium text-slate">{% trans "Pending Approval" %}</span>
<span class="text-sm font-bold text-navy">{{ actions_module.pending_approval }}</span>
</div>
<div class="h-8 bg-yellow-100 rounded-lg overflow-hidden">
<div class="h-full bg-yellow-500 flex items-center justify-end pr-2" style="width: {% widthratio actions_module.pending_approval actions_module.open|add:actions_module.in_progress|add:actions_module.pending_approval|add:actions_module.closed_30d 100 %}%"></div>
</div>
</div>
<div class="relative">
<div class="flex items-center justify-between mb-1">
<span class="text-sm font-medium text-slate">{% trans "Closed (30d)" %}</span>
<span class="text-sm font-bold text-green-500">{{ actions_module.closed_30d }}</span>
</div>
<div class="h-8 bg-green-100 rounded-lg overflow-hidden">
<div class="h-full bg-green-500 flex items-center justify-end pr-2" style="width: {% widthratio actions_module.closed_30d actions_module.open|add:actions_module.in_progress|add:actions_module.pending_approval|add:actions_module.closed_30d 100 %}%"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Live Feed Row -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- Latest High Severity Complaints -->
<div class="section-card">
<div class="section-header">
<h3 class="font-bold text-navy flex items-center gap-2">
<i data-lucide="alert-triangle" class="w-4 h-4"></i>
{% trans "Latest High Severity Complaints" %}
</h3>
<a href="{% url 'complaints:complaint_list' %}?severity=high,critical"
class="text-xs font-bold text-navy hover:text-blue flex items-center gap-1 transition">
{% trans "View All" %}
<i data-lucide="arrow-right" class="w-3 h-3"></i>
</a>
</div>
<div class="divide-y divide-slate-100 max-h-[400px] overflow-y-auto">
{% if latest_complaints %}
{% for complaint in latest_complaints %}
<a href="{% url 'complaints:complaint_detail' complaint.id %}"
class="block p-4 hover:bg-light transition group">
<div class="flex justify-between items-start gap-4">
<div class="flex-1 min-w-0">
<h4 class="font-bold text-navy text-sm mb-1 truncate group-hover:text-blue transition">{{ complaint.title }}</h4>
<div class="flex items-center gap-2 text-xs text-slate mb-1">
<span class="flex items-center gap-1">
<i data-lucide="user" class="w-3 h-3"></i>
{{ complaint.patient.get_full_name }}
</span>
<span class="flex items-center gap-1">
<i data-lucide="building" class="w-3 h-3"></i>
{{ complaint.hospital.name }}
</span>
</div>
<div class="text-xs text-slate-500">
<i data-lucide="clock" class="w-3 h-3 inline mr-1"></i>
{{ complaint.created_at|timesince }} {% trans "ago" %}
</div>
</div>
<div class="flex flex-col gap-1 items-end">
<span class="px-2 py-0.5 rounded-full text-xs font-bold
{% if complaint.severity == 'critical' %}bg-red-100 text-red-600
{% elif complaint.severity == 'high' %}bg-orange-100 text-orange-600
{% elif complaint.severity == 'medium' %}bg-yellow-100 text-yellow-600
{% else %}bg-blue-100 text-blue-600{% endif %}">
{{ complaint.get_severity_display }}
</span>
{% if complaint.is_overdue %}
<span class="px-2 py-0.5 rounded-full text-xs font-bold bg-red-500 text-white">
{% trans "OVERDUE" %}
</span>
{% endif %}
</div>
</div>
</a>
{% endfor %}
{% else %}
<div class="p-8 text-center">
<div class="bg-green-50 w-12 h-12 rounded-full flex items-center justify-center mx-auto mb-3">
<i data-lucide="check-circle" class="w-6 h-6 text-green-500"></i>
</div>
<p class="text-sm text-slate">{% trans "No high severity complaints" %}</p>
</div>
{% endif %}
</div>
</div>
<!-- Latest Escalated Actions -->
<div class="section-card">
<div class="section-header">
<h3 class="font-bold text-navy flex items-center gap-2">
<i data-lucide="arrow-up-circle" class="w-4 h-4"></i>
{% trans "Latest Escalated Actions" %}
</h3>
<a href="{% url 'actions:action_list' %}"
class="text-xs font-bold text-navy hover:text-blue flex items-center gap-1 transition">
{% trans "View All" %}
<i data-lucide="arrow-right" class="w-3 h-3"></i>
</a>
</div>
<div class="divide-y divide-slate-100 max-h-[400px] overflow-y-auto">
{% if latest_actions %}
{% for action in latest_actions %}
<a href="{% url 'actions:action_detail' action.id %}"
class="block p-4 hover:bg-light transition group">
<div class="flex justify-between items-start gap-4">
<div class="flex-1 min-w-0">
<h4 class="font-bold text-navy text-sm mb-1 truncate group-hover:text-blue transition">{{ action.title }}</h4>
<div class="flex items-center gap-2 text-xs text-slate mb-1">
<span class="flex items-center gap-1">
<i data-lucide="building" class="w-3 h-3"></i>
{{ action.hospital.name }}
</span>
{% if action.assigned_to %}
<span class="flex items-center gap-1">
<i data-lucide="user" class="w-3 h-3"></i>
{{ action.assigned_to.get_full_name }}
</span>
{% endif %}
</div>
<div class="text-xs text-slate-500">
<i data-lucide="clock" class="w-3 h-3 inline mr-1"></i>
{% trans "Escalated" %} {{ action.escalated_at|timesince }} {% trans "ago" %}
</div>
</div>
<span class="px-2 py-0.5 rounded-full text-xs font-bold bg-red-100 text-red-600">
{% trans "Level" %} {{ action.escalation_level }}
</span>
</div>
</a>
{% endfor %}
{% else %}
<div class="p-8 text-center">
<div class="bg-green-50 w-12 h-12 rounded-full flex items-center justify-center mx-auto mb-3">
<i data-lucide="check-circle" class="w-6 h-6 text-green-500"></i>
</div>
<p class="text-sm text-slate">{% trans "No escalated actions" %}</p>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Inquiries & Observations Row -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- Latest Inquiries -->
<div class="section-card">
<div class="section-header">
<h3 class="font-bold text-navy flex items-center gap-2">
<i data-lucide="help-circle" class="w-4 h-4"></i>
{% trans "Recent Inquiries" %}
</h3>
<a href="{% url 'complaints:complaint_list' %}?type=inquiry"
class="text-xs font-bold text-navy hover:text-blue flex items-center gap-1 transition">
{% trans "View All" %}
<i data-lucide="arrow-right" class="w-3 h-3"></i>
</a>
</div>
<div class="divide-y divide-slate-100 max-h-[300px] overflow-y-auto">
{% if latest_inquiries %}
{% for inquiry in latest_inquiries %}
<a href="{% url 'complaints:inquiry_detail' inquiry.id %}"
class="block p-4 hover:bg-light transition group">
<div class="flex justify-between items-start gap-4">
<div class="flex-1 min-w-0">
<h4 class="font-bold text-navy text-sm mb-1 truncate group-hover:text-blue transition">{{ inquiry.subject }}</h4>
<div class="flex items-center gap-2 text-xs text-slate">
<span class="flex items-center gap-1">
<i data-lucide="user" class="w-3 h-3"></i>
{{ inquiry.patient.get_full_name }}
</span>
<span>{{ inquiry.created_at|timesince }} {% trans "ago" %}</span>
</div>
</div>
<span class="px-2 py-0.5 rounded-full text-xs font-bold
{% if inquiry.status == 'open' %}bg-blue-100 text-blue-600
{% elif inquiry.status == 'in_progress' %}bg-yellow-100 text-yellow-600
{% else %}bg-green-100 text-green-600{% endif %}">
{{ inquiry.get_status_display }}
</span>
</div>
</a>
{% endfor %}
{% else %}
<div class="p-6 text-center">
<p class="text-sm text-slate">{% trans "No active inquiries" %}</p>
</div>
{% endif %}
</div>
</div>
<!-- Latest Observations -->
<div class="section-card">
<div class="section-header">
<h3 class="font-bold text-navy flex items-center gap-2">
<i data-lucide="eye" class="w-4 h-4"></i>
{% trans "Recent Observations" %}
</h3>
<a href="{% url 'observations:observation_list' %}"
class="text-xs font-bold text-navy hover:text-blue flex items-center gap-1 transition">
{% trans "View All" %}
<i data-lucide="arrow-right" class="w-3 h-3"></i>
</a>
</div>
<div class="divide-y divide-slate-100 max-h-[300px] overflow-y-auto">
{% if latest_observations %}
{% for observation in latest_observations %}
<a href="{% url 'observations:observation_detail' observation.id %}"
class="block p-4 hover:bg-light transition group">
<div class="flex justify-between items-start gap-4">
<div class="flex-1 min-w-0">
<h4 class="font-bold text-navy text-sm mb-1 truncate group-hover:text-blue transition">{{ observation.title }}</h4>
<div class="flex items-center gap-2 text-xs text-slate">
{% if observation.category %}
<span class="px-2 py-0.5 bg-slate-100 rounded text-slate">{{ observation.category.name_en }}</span>
{% endif %}
<span>{{ observation.created_at|timesince }} {% trans "ago" %}</span>
</div>
</div>
<span class="px-2 py-0.5 rounded-full text-xs font-bold
{% if observation.status == 'new' %}bg-blue-100 text-blue-600
{% elif observation.status == 'in_progress' %}bg-yellow-100 text-yellow-600
{% else %}bg-slate-100 text-slate{% endif %}">
{{ observation.get_status_display }}
</span>
</div>
</a>
{% endfor %}
{% else %}
<div class="p-6 text-center">
<p class="text-sm text-slate">{% trans "No active observations" %}</p>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Top Physicians This Month -->
<div class="section-card">
<div class="section-header">
<h3 class="font-bold text-navy flex items-center gap-2">
<i data-lucide="trophy" class="w-4 h-4"></i>
{% trans "Top Physicians This Month" %}
</h3>
<a href="{% url 'physicians:leaderboard' %}"
class="text-xs font-bold text-navy hover:text-blue flex items-center gap-1 transition">
{% trans "View Leaderboard" %}
<i data-lucide="arrow-right" class="w-3 h-3"></i>
</a>
</div>
<div class="overflow-x-auto">
<table class="w-full">
<thead>
<tr class="bg-light">
<th class="px-4 py-3 text-left text-xs font-bold text-slate uppercase tracking-wider" style="width: 60px;">{% trans "Rank" %}</th>
<th class="px-4 py-3 text-left text-xs font-bold text-slate uppercase tracking-wider">{% trans "Physician" %}</th>
<th class="px-4 py-3 text-left text-xs font-bold text-slate uppercase tracking-wider">{% trans "Department" %}</th>
<th class="px-4 py-3 text-left text-xs font-bold text-slate uppercase tracking-wider">{% trans "Rating" %}</th>
<th class="px-4 py-3 text-left text-xs font-bold text-slate uppercase tracking-wider">{% trans "Surveys" %}</th>
<th class="px-4 py-3 text-left text-xs font-bold text-slate uppercase tracking-wider">{% trans "Sentiment" %}</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100">
{% for rating in top_physicians %}
<tr class="hover:bg-light transition cursor-pointer group">
<td class="px-4 py-3">
{% if forloop.counter == 1 %}
<div class="w-8 h-8 bg-yellow-100 rounded-full flex items-center justify-center">
<i data-lucide="trophy" class="w-4 h-4 text-yellow-500 fill-current"></i>
</div>
{% elif forloop.counter == 2 %}
<div class="w-8 h-8 bg-slate-200 rounded-full flex items-center justify-center">
<i data-lucide="trophy" class="w-4 h-4 text-slate-500 fill-current"></i>
</div>
{% elif forloop.counter == 3 %}
<div class="w-8 h-8 bg-amber-100 rounded-full flex items-center justify-center">
<i data-lucide="trophy" class="w-4 h-4 text-amber-600 fill-current"></i>
</div>
{% else %}
<span class="text-slate-400 font-bold text-sm">#{{ forloop.counter }}</span>
{% endif %}
</td>
<td class="px-4 py-3">
<div class="font-bold text-navy text-sm">{{ rating.staff }}</div>
</td>
<td class="px-4 py-3 text-sm text-slate">
{% if rating.staff.department %}{{ rating.staff.department.name }}{% else %}-{% endif %}
</td>
<td class="px-4 py-3">
<div class="text-lg font-bold text-green-500">{{ rating.average_rating|floatformat:2 }}</div>
</td>
<td class="px-4 py-3">
<span class="px-2 py-0.5 rounded-full text-xs font-bold bg-slate-100 text-slate">{{ rating.total_surveys }}</span>
</td>
<td class="px-4 py-3">
<div class="flex gap-1">
<span class="px-2 py-0.5 rounded-full text-xs font-bold bg-green-100 text-green-600">{{ rating.positive_count }}</span>
<span class="px-2 py-0.5 rounded-full text-xs font-bold bg-red-100 text-red-600">{{ rating.negative_count }}</span>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="6" class="px-4 py-8 text-center text-slate">{% trans "No physician ratings this month" %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
// Auto-refresh configuration
let refreshInterval = 60; // seconds
let countdownTimer = refreshInterval;
// Countdown timer
function updateCountdown() {
countdownTimer--;
document.getElementById('refresh-timer').textContent =
`{% trans "Refreshes in" %} ${countdownTimer}s`;
if (countdownTimer <= 0) {
refreshCommandCenter();
countdownTimer = refreshInterval;
}
}
// Start countdown
setInterval(updateCountdown, 1000);
// Manual refresh function
function refreshCommandCenter() {
const refreshIcon = document.getElementById('refresh-icon');
refreshIcon.classList.add('animate-spin');
// Reload page to refresh data
window.location.reload();
}
// Complaints Trend Chart
const chartElement = document.getElementById('complaintsTrendChart');
if (chartElement) {
const trendData = {{ chart_data.complaints_trend|safe }};
var options = {
series: [{
name: '{% trans "Complaints" %}',
data: trendData.map(d => d.count)
}],
chart: {
type: 'area',
height: 280,
toolbar: { show: false },
fontFamily: 'Inter, sans-serif',
animations: {
enabled: true,
dynamicAnimation: { speed: 1000 }
}
},
stroke: { curve: 'smooth', width: 3 },
colors: ['#005696'],
fill: {
type: 'gradient',
gradient: {
shadeIntensity: 1,
opacityFrom: 0.4,
opacityTo: 0.1,
stops: [0, 90, 100]
}
},
xaxis: {
categories: trendData.map(d => d.date),
labels: {
style: { fontSize: '11px', colors: ['#64748b'] }
},
axisBorder: { show: false },
axisTicks: { show: false }
},
yaxis: {
min: 0,
forceNiceScale: true,
labels: {
style: { fontSize: '11px', colors: ['#64748b'] }
}
},
grid: {
borderColor: '#f1f5f9',
strokeDashArray: 5
},
tooltip: {
theme: 'light',
x: { format: 'dd MMM yyyy' }
},
dataLabels: { enabled: false },
markers: {
size: 0,
hover: { size: 6 }
}
};
new ApexCharts(chartElement, options).render();
}
// NPS Trend Chart
const npsElement = document.getElementById('npsTrendChart');
if (npsElement) {
const npsData = {{ chart_data.nps_trend|safe }};
var npsOptions = {
series: [{
name: 'NPS',
data: npsData.map(d => d.nps)
}],
chart: {
type: 'line',
height: 200,
toolbar: { show: false },
fontFamily: 'Inter, sans-serif'
},
stroke: { curve: 'smooth', width: 2 },
colors: ['#10b981'],
xaxis: {
categories: npsData.map(d => d.date),
labels: { show: false }
},
yaxis: {
min: -100,
max: 100,
labels: {
style: { fontSize: '10px', colors: ['#64748b'] }
}
},
grid: {
borderColor: '#f1f5f9',
strokeDashArray: 3
},
tooltip: { theme: 'light' },
dataLabels: { enabled: false }
};
new ApexCharts(npsElement, npsOptions).render();
}
// Severity Donut Chart
const severityElement = document.getElementById('severityDonutChart');
if (severityElement) {
const severityData = {{ chart_data.complaints_by_severity|safe }};
var severityOptions = {
series: [severityData.critical, severityData.high, severityData.medium, severityData.low],
chart: {
type: 'donut',
height: 250,
fontFamily: 'Inter, sans-serif'
},
colors: ['#ef4444', '#f97316', '#eab308', '#3b82f6'],
labels: ['{% trans "Critical" %}', '{% trans "High" %}', '{% trans "Medium" %}', '{% trans "Low" %}'],
plotOptions: {
pie: {
donut: {
size: '65%',
labels: {
show: true,
total: {
show: true,
label: '{% trans "Total" %}',
fontSize: '14px',
fontWeight: 'bold'
}
}
}
}
},
legend: {
position: 'bottom',
fontSize: '12px'
},
dataLabels: { enabled: false }
};
new ApexCharts(severityElement, severityOptions).render();
}
// Initialize Lucide icons
document.addEventListener('DOMContentLoaded', function() {
lucide.createIcons();
});
</script>
{% endblock %}