HH/templates/dashboard/command_center.html
2026-02-22 08:35:53 +03:00

513 lines
21 KiB
HTML

{% extends 'layouts/base.html' %}
{% load i18n %}
{% block title %}{% trans "PX Command Center" %} - PX360{% endblock %}
{% block content %}
<div class="space-y-6">
<!-- 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-3">
<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">{% now "j M Y, H:i" %}</p>
</div>
</div>
</header>
<!-- Stat Cards -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<div class="card stat-card">
<div class="flex items-start justify-between">
<div>
<p class="text-xs font-bold text-slate uppercase tracking-wider mb-1">{% trans "Total Complaints" %}</p>
<p class="text-3xl font-bold text-navy">{{ stats.total_complaints }}</p>
<div class="flex items-center gap-1.5 mt-2">
<i data-lucide="trending-up" class="w-4 h-4 text-green-500"></i>
<span class="text-sm font-bold text-green-500">+12.5%</span>
<span class="text-xs text-slate">vs last month</span>
</div>
</div>
<div class="p-3 bg-blue-50 rounded-xl">
<i data-lucide="message-square" class="w-6 h-6 text-blue"></i>
</div>
</div>
</div>
<div class="card stat-card">
<div class="flex items-start justify-between">
<div>
<p class="text-xs font-bold text-slate uppercase tracking-wider mb-1">{% trans "Avg. Resolution" %}</p>
<p class="text-3xl font-bold text-navy">{{ stats.avg_resolution_time }}</p>
<div class="flex items-center gap-1.5 mt-2">
<i data-lucide="trending-down" class="w-4 h-4 text-green-500"></i>
<span class="text-sm font-bold text-green-500">-8.3%</span>
<span class="text-xs text-slate">faster</span>
</div>
</div>
<div class="p-3 bg-green-50 rounded-xl">
<i data-lucide="clock" class="w-6 h-6 text-green-500"></i>
</div>
</div>
</div>
<div class="card stat-card">
<div class="flex items-start justify-between">
<div>
<p class="text-xs font-bold text-slate uppercase tracking-wider mb-1">{% trans "Patient Satisfaction" %}</p>
<p class="text-3xl font-bold text-navy">{{ stats.satisfaction_score }}%</p>
<div class="flex items-center gap-1.5 mt-2">
<i data-lucide="trending-up" class="w-4 h-4 text-green-500"></i>
<span class="text-sm font-bold text-green-500">+5.2%</span>
<span class="text-xs text-slate">improvement</span>
</div>
</div>
<div class="p-3 bg-purple-50 rounded-xl">
<i data-lucide="smile" class="w-6 h-6 text-purple-500"></i>
</div>
</div>
</div>
<div class="card stat-card">
<div class="flex items-start justify-between">
<div>
<p class="text-xs font-bold text-slate uppercase tracking-wider mb-1">{% trans "Active Actions" %}</p>
<p class="text-3xl font-bold text-navy">{{ stats.active_actions }}</p>
<div class="flex items-center gap-1.5 mt-2">
<i data-lucide="activity" class="w-4 h-4 text-blue"></i>
<span class="text-sm font-bold text-navy">{{ stats.new_today }}</span>
<span class="text-xs text-slate">new today</span>
</div>
</div>
<div class="p-3 bg-orange-50 rounded-xl">
<i data-lucide="check-circle" class="w-6 h-6 text-orange-500"></i>
</div>
</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 card">
<div class="card-header">
<h3 class="card-title flex items-center gap-2">
<i data-lucide="trending-up" class="w-4 h-4"></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">Day</button>
<button class="px-3 py-1.5 text-xs font-bold bg-navy text-white rounded-lg">Week</button>
<button class="px-3 py-1.5 text-xs font-bold text-slate hover:text-navy hover:bg-light rounded-lg transition">Month</button>
</div>
</div>
<div id="complaintsTrendChart" class="h-[320px]"></div>
</div>
<!-- Survey Satisfaction -->
<div class="card">
<div class="card-header">
<h3 class="card-title flex items-center gap-2">
<i data-lucide="star" class="w-4 h-4"></i>
{% trans "Survey Satisfaction" %}
</h3>
</div>
<div class="text-center py-8">
<div class="text-6xl font-bold text-navy mb-2">{{ chart_data.survey_satisfaction|floatformat:1 }}</div>
<p class="text-sm text-slate mb-6">{% trans "Average Score (Last 30 Days)" %}</p>
<div class="w-full bg-slate-100 rounded-full h-3 overflow-hidden">
<div class="bg-gradient-to-r from-blue to-navy h-full transition-all duration-500"
style="width: {{ chart_data.survey_satisfaction|floatformat:0 }}%"></div>
</div>
<div class="mt-4 flex justify-between text-xs text-slate">
<span>0</span>
<span>50</span>
<span>100</span>
</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="card">
<div class="card-header">
<h3 class="card-title 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">
{% if latest_complaints %}
{% for complaint in latest_complaints %}
<a href="{% url 'complaints:complaint_detail' complaint.id %}"
class="block p-5 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 mb-2 truncate group-hover:text-blue transition">{{ complaint.title }}</h4>
<div class="flex items-center gap-3 text-sm text-slate mb-2">
<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-2 items-end">
<span class="px-3 py-1 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-3 py-1 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-16 h-16 rounded-full flex items-center justify-center mx-auto mb-4">
<i data-lucide="check-circle" class="w-8 h-8 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="card">
<div class="card-header">
<h3 class="card-title 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">
{% if latest_actions %}
{% for action in latest_actions %}
<a href="{% url 'actions:action_detail' action.id %}"
class="block p-5 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 mb-2 truncate group-hover:text-blue transition">{{ action.title }}</h4>
<div class="flex items-center gap-3 text-sm text-slate mb-2">
<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-3 py-1 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-16 h-16 rounded-full flex items-center justify-center mx-auto mb-4">
<i data-lucide="check-circle" class="w-8 h-8 text-green-500"></i>
</div>
<p class="text-sm text-slate">{% trans "No escalated actions" %}</p>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Top Physicians This Month -->
<div class="card">
<div class="card-header">
<h3 class="card-title 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-6 py-4 text-left text-xs font-bold text-slate uppercase tracking-wider" style="width: 80px;">{% trans "Rank" %}</th>
<th class="px-6 py-4 text-left text-xs font-bold text-slate uppercase tracking-wider">{% trans "Physician" %}</th>
<th class="px-6 py-4 text-left text-xs font-bold text-slate uppercase tracking-wider">{% trans "Specialization" %}</th>
<th class="px-6 py-4 text-left text-xs font-bold text-slate uppercase tracking-wider">{% trans "Department" %}</th>
<th class="px-6 py-4 text-left text-xs font-bold text-slate uppercase tracking-wider">{% trans "Rating" %}</th>
<th class="px-6 py-4 text-left text-xs font-bold text-slate uppercase tracking-wider">{% trans "Surveys" %}</th>
<th class="px-6 py-4 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-6 py-4">
{% if forloop.counter == 1 %}
<div class="w-10 h-10 bg-yellow-100 rounded-full flex items-center justify-center">
<i data-lucide="trophy" class="w-5 h-5 text-yellow-500 fill-current"></i>
</div>
{% elif forloop.counter == 2 %}
<div class="w-10 h-10 bg-slate-200 rounded-full flex items-center justify-center">
<i data-lucide="trophy" class="w-5 h-5 text-slate-500 fill-current"></i>
</div>
{% elif forloop.counter == 3 %}
<div class="w-10 h-10 bg-amber-100 rounded-full flex items-center justify-center">
<i data-lucide="trophy" class="w-5 h-5 text-amber-600 fill-current"></i>
</div>
{% else %}
<span class="text-slate-400 font-bold">#{{ forloop.counter }}</span>
{% endif %}
</td>
<td class="px-6 py-4">
<div class="font-bold text-navy">{{ rating.staff }}</div>
<div class="text-xs text-slate">{{ rating.staff.license_number }}</div>
</td>
<td class="px-6 py-4 text-sm text-slate">{{ rating.staff.specialization }}</td>
<td class="px-6 py-4 text-sm text-slate">
{% if rating.staff.department %}
{{ rating.staff.department.name }}
{% else %}
<span class="text-slate-400">-</span>
{% endif %}
</td>
<td class="px-6 py-4">
<div class="text-2xl font-bold text-green-500">{{ rating.average_rating|floatformat:2 }}</div>
</td>
<td class="px-6 py-4">
<span class="px-3 py-1 rounded-full text-xs font-bold bg-slate-100 text-slate">{{ rating.total_surveys }}</span>
</td>
<td class="px-6 py-4">
<div class="flex gap-1.5">
<span class="px-2 py-1 rounded-full text-xs font-bold bg-green-100 text-green-600" title="{% trans 'Positive' %}">{{ rating.positive_count }}</span>
<span class="px-2 py-1 rounded-full text-xs font-bold bg-yellow-100 text-yellow-600" title="{% trans 'Neutral' %}">{{ rating.neutral_count }}</span>
<span class="px-2 py-1 rounded-full text-xs font-bold bg-red-100 text-red-600" title="{% trans 'Negative' %}">{{ rating.negative_count }}</span>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if physician_stats.total_physicians %}
<div class="bg-gradient-to-r from-light to-blue-50 px-6 py-4 border-t border-slate-100">
<div class="grid grid-cols-3 gap-6 text-center">
<div>
<div class="text-2xl font-bold text-navy">{{ physician_stats.total_physicians }}</div>
<div class="text-xs text-slate uppercase tracking-wider mt-1">{% trans "Physicians Rated" %}</div>
</div>
<div>
<div class="text-2xl font-bold text-navy">{{ physician_stats.avg_rating|floatformat:2 }}</div>
<div class="text-xs text-slate uppercase tracking-wider mt-1">{% trans "Average Rating" %}</div>
</div>
<div>
<div class="text-2xl font-bold text-navy">{{ physician_stats.total_surveys }}</div>
<div class="text-xs text-slate uppercase tracking-wider mt-1">{% trans "Total Surveys" %}</div>
</div>
</div>
</div>
{% endif %}
</div>
<!-- Latest Integration Events -->
<div class="card">
<div class="card-header">
<h3 class="card-title flex items-center gap-2">
<i data-lucide="activity" class="w-4 h-4"></i>
{% trans "Latest Integration Events" %}
</h3>
</div>
<div class="overflow-x-auto">
<table class="w-full">
<thead>
<tr class="bg-light">
<th class="px-6 py-4 text-left text-xs font-bold text-slate uppercase tracking-wider">{% trans "Source" %}</th>
<th class="px-6 py-4 text-left text-xs font-bold text-slate uppercase tracking-wider">{% trans "Event Code" %}</th>
<th class="px-6 py-4 text-left text-xs font-bold text-slate uppercase tracking-wider">{% trans "Encounter ID" %}</th>
<th class="px-6 py-4 text-left text-xs font-bold text-slate uppercase tracking-wider">{% trans "Status" %}</th>
<th class="px-6 py-4 text-left text-xs font-bold text-slate uppercase tracking-wider">{% trans "Processed At" %}</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100">
{% for event in latest_events %}
<tr class="hover:bg-light transition">
<td class="px-6 py-4">
<span class="px-3 py-1 rounded-full text-xs font-bold bg-light text-navy">{{ event.get_source_system_display }}</span>
</td>
<td class="px-6 py-4">
<code class="px-2 py-1 bg-slate-100 rounded text-sm">{{ event.event_code }}</code>
</td>
<td class="px-6 py-4 text-sm text-slate">{{ event.encounter_id }}</td>
<td class="px-6 py-4">
<span class="px-3 py-1 rounded-full text-xs font-bold bg-green-100 text-green-600">{{ event.get_status_display }}</span>
</td>
<td class="px-6 py-4 text-sm text-slate">
<i data-lucide="clock" class="w-3 h-3 inline mr-1"></i>
{{ event.processed_at|timesince }} {% trans "ago" %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if not latest_events %}
<div class="p-8 text-center">
<div class="bg-slate-100 w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-4">
<i data-lucide="inbox" class="w-8 h-8 text-slate-400"></i>
</div>
<p class="text-sm text-slate">{% trans "No recent events" %}</p>
</div>
{% endif %}
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
// Complaints Trend Chart - ApexCharts
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: 320,
toolbar: {
show: false
},
fontFamily: 'Inter, sans-serif'
},
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: '12px',
fontFamily: 'Inter, sans-serif',
colors: ['#64748b']
}
},
axisBorder: {
show: false
},
axisTicks: {
show: false
}
},
yaxis: {
min: 0,
forceNiceScale: true,
labels: {
style: {
fontSize: '12px',
fontFamily: 'Inter, sans-serif',
colors: ['#64748b']
}
}
},
grid: {
borderColor: '#f1f5f9',
strokeDashArray: 5,
yaxis: {
lines: {
show: true
}
}
},
tooltip: {
theme: 'light',
x: {
format: 'dd MMM yyyy'
},
style: {
fontSize: '12px',
fontFamily: 'Inter, sans-serif'
}
},
dataLabels: {
enabled: false
},
markers: {
size: 0,
hover: {
size: 6
}
}
};
var chart = new ApexCharts(chartElement, options);
chart.render();
}
// Initialize Lucide icons
document.addEventListener('DOMContentLoaded', function() {
lucide.createIcons();
});
</script>
{% endblock %}