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

557 lines
19 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "layouts/base.html" %}
{% load i18n %}
{% load static %}
{% block title %}{{ physician.get_full_name }} - {% trans "Physicians" %} - PX360{% endblock %}
{% block extra_css %}
<style>
:root {
--hh-navy: #005696;
--hh-blue: #007bbd;
--hh-light: #eef6fb;
--hh-slate: #64748b;
--hh-success: #10b981;
--hh-warning: #f59e0b;
--hh-danger: #ef4444;
--hh-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--hh-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--hh-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
/* Page Header */
.page-header {
background: linear-gradient(135deg, var(--hh-navy) 0%, #0069a8 50%, var(--hh-blue) 100%);
color: white;
padding: 2rem 2.5rem;
border-radius: 1rem;
margin-bottom: 2rem;
box-shadow: var(--hh-shadow-lg);
position: relative;
overflow: hidden;
}
.page-header::before {
content: '';
position: absolute;
top: 0;
right: 0;
width: 200px;
height: 200px;
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
pointer-events: none;
}
.breadcrumb {
background: transparent;
padding: 0;
margin: 0 0 1rem 0;
font-size: 0.9rem;
}
.breadcrumb-item a {
color: rgba(255,255,255,0.85);
text-decoration: none;
transition: color 0.2s ease;
}
.breadcrumb-item a:hover {
color: white;
}
.breadcrumb-item.active {
color: rgba(255,255,255,0.7);
}
.breadcrumb-item + .breadcrumb-item::before {
color: rgba(255,255,255,0.5);
content: "";
padding: 0 0.5rem;
}
/* Stat Cards */
.stat-card {
background: white;
border-radius: 1rem;
padding: 1.5rem;
box-shadow: var(--hh-shadow-md);
border: 1px solid #e2e8f0;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
height: 100%;
}
.stat-card:hover {
box-shadow: var(--hh-shadow-lg);
transform: translateY(-4px);
}
.stat-card.primary {
border-left: 4px solid var(--hh-navy);
}
.stat-card.success {
border-left: 4px solid var(--hh-success);
}
.stat-card.warning {
border-left: 4px solid var(--hh-warning);
}
.stat-card.info {
border-left: 4px solid var(--hh-blue);
}
.stat-value {
font-size: 2rem;
font-weight: 800;
color: #1e293b;
margin-bottom: 0.25rem;
}
.stat-label {
color: #64748b;
font-size: 0.875rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.025em;
}
/* Info Card */
.info-card {
background: white;
border-radius: 1rem;
border: 1px solid #e2e8f0;
overflow: hidden;
box-shadow: var(--hh-shadow-md);
transition: all 0.3s ease;
}
.info-card:hover {
box-shadow: var(--hh-shadow-lg);
}
.info-card .card-header {
background: linear-gradient(135deg, var(--hh-light), #e0f2fe);
padding: 1.25rem 1.75rem;
border-bottom: 1px solid #bae6fd;
}
.info-card .card-header h5 {
color: var(--hh-navy);
font-weight: 700;
margin: 0;
font-size: 1.1rem;
}
.info-card .card-body {
padding: 1.5rem;
}
.info-item {
margin-bottom: 1.25rem;
}
.info-item:last-child {
margin-bottom: 0;
}
.info-label {
color: #64748b;
font-size: 0.875rem;
font-weight: 600;
margin-bottom: 0.375rem;
}
.info-value {
color: #1e293b;
font-size: 1rem;
font-weight: 500;
}
/* Status Badge */
.status-badge {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
border-radius: 9999px;
font-size: 0.875rem;
font-weight: 600;
}
.status-badge.active {
background: linear-gradient(135deg, #dcfce7, #bbf7d0);
color: #166534;
}
.status-badge.inactive {
background: linear-gradient(135deg, #f1f5f9, #e2e8f0);
color: #475569;
}
/* Table */
.ratings-table {
width: 100%;
border-collapse: collapse;
}
.ratings-table th {
background: linear-gradient(135deg, var(--hh-light), #e0f2fe);
padding: 0.875rem 1rem;
text-align: left;
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--hh-navy);
border-bottom: 2px solid #bae6fd;
}
.ratings-table td {
padding: 1rem;
border-bottom: 1px solid #f1f5f9;
color: #475569;
font-size: 0.875rem;
}
.ratings-table tbody tr {
transition: background-color 0.2s ease;
}
.ratings-table tbody tr:hover {
background-color: var(--hh-light);
}
.ratings-table tbody tr:last-child td {
border-bottom: none;
}
/* Rating Badge */
.rating-badge {
display: inline-block;
padding: 0.25rem 0.75rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 700;
}
.rating-badge.positive {
background: linear-gradient(135deg, #dcfce7, #bbf7d0);
color: #166534;
}
.rating-badge.neutral {
background: linear-gradient(135deg, #fef3c7, #fde68a);
color: #92400e;
}
.rating-badge.negative {
background: linear-gradient(135deg, #fee2e2, #fecaca);
color: #991b1b;
}
/* Trend Badge */
.trend-badge {
display: inline-flex;
align-items: center;
gap: 0.375rem;
padding: 0.375rem 0.75rem;
border-radius: 0.5rem;
font-size: 0.875rem;
font-weight: 600;
}
.trend-badge.improving {
background: linear-gradient(135deg, #dcfce7, #bbf7d0);
color: #166534;
}
.trend-badge.declining {
background: linear-gradient(135deg, #fee2e2, #fecaca);
color: #991b1b;
}
/* Empty State */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 3rem 1.5rem;
text-align: center;
}
.empty-state-icon {
width: 80px;
height: 80px;
background: linear-gradient(135deg, var(--hh-light), #e0f2fe);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 1.5rem;
color: var(--hh-blue);
}
/* Animations */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-in {
animation: fadeIn 0.5s ease-out forwards;
}
</style>
{% endblock %}
{% block content %}
<div class="px-4 py-6">
<!-- Page Header -->
<div class="page-header animate-in">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'physicians:physician_list' %}">{% trans "Physicians" %}</a></li>
<li class="breadcrumb-item active">{{ physician.get_full_name }}</li>
</ol>
</nav>
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold mb-2">
<i data-lucide="user" class="w-7 h-7 inline-block me-2"></i>
{{ physician.get_full_name }}
</h1>
<p class="text-white/90 text-base">
{% if physician.specialization %}{{ physician.specialization }}{% endif %}
</p>
</div>
<div>
{% if physician.status == 'active' %}
<span class="status-badge active">
<i data-lucide="check-circle" class="w-4 h-4"></i>
{% trans "Active" %}
</span>
{% else %}
<span class="status-badge inactive">
<i data-lucide="x-circle" class="w-4 h-4"></i>
{% trans "Inactive" %}
</span>
{% endif %}
</div>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Left Column: Physician Info -->
<div class="lg:col-span-1 space-y-6">
<!-- Basic Information -->
<div class="info-card animate-in">
<div class="card-header">
<h5>
<i data-lucide="user" class="w-5 h-5 inline-block me-2"></i>
{% trans "Basic Information" %}
</h5>
</div>
<div class="card-body">
<div class="info-item">
<p class="info-label">{% trans "License Number" %}</p>
<p class="info-value">{{ physician.license_number }}</p>
</div>
<div class="info-item">
<p class="info-label">{% trans "Specialization" %}</p>
<p class="info-value">{{ physician.specialization|default:"-" }}</p>
</div>
<div class="info-item">
<p class="info-label">{% trans "Hospital" %}</p>
<p class="info-value">{{ physician.hospital.name }}</p>
</div>
{% if physician.department %}
<div class="info-item">
<p class="info-label">{% trans "Department" %}</p>
<p class="info-value">{{ physician.department.name }}</p>
</div>
{% endif %}
{% if physician.email %}
<div class="info-item">
<p class="info-label">{% trans "Email" %}</p>
<p class="info-value">{{ physician.email }}</p>
</div>
{% endif %}
{% if physician.phone %}
<div class="info-item">
<p class="info-label">{% trans "Phone" %}</p>
<p class="info-value">{{ physician.phone }}</p>
</div>
{% endif %}
</div>
</div>
<!-- Current Month Performance -->
{% if current_month_rating %}
<div class="info-card animate-in">
<div class="card-header">
<h5>
<i data-lucide="star" class="w-5 h-5 inline-block me-2"></i>
{% trans "Current Month" %}
</h5>
</div>
<div class="card-body text-center">
<div class="mb-4">
<p class="text-6xl font-bold text-navy mb-2">{{ current_month_rating.average_rating|floatformat:2 }}</p>
<p class="text-slate text-sm">{% trans "Average Rating" %}</p>
</div>
<div class="grid grid-cols-2 gap-4 mb-4">
<div class="bg-light rounded-xl p-4">
<p class="text-2xl font-bold text-navy">{{ current_month_rating.total_surveys }}</p>
<p class="text-slate text-xs">{% trans "Surveys" %}</p>
</div>
<div class="bg-light rounded-xl p-4">
{% if current_month_rating.hospital_rank %}
<p class="text-2xl font-bold text-navy">#{{ current_month_rating.hospital_rank }}</p>
<p class="text-slate text-xs">{% trans "Rank" %}</p>
{% else %}
<p class="text-2xl font-bold text-slate-400">-</p>
<p class="text-slate text-xs">{% trans "No Rank" %}</p>
{% endif %}
</div>
</div>
<!-- Trend -->
{% if trend != 'stable' %}
<div>
{% if trend == 'improving' %}
<span class="trend-badge improving">
<i data-lucide="trending-up" class="w-4 h-4"></i>
{% trans "Improving" %} {{ trend_percentage|floatformat:1 }}%
</span>
{% else %}
<span class="trend-badge declining">
<i data-lucide="trending-down" class="w-4 h-4"></i>
{% trans "Declining" %} {{ trend_percentage|floatformat:1 }}%
</span>
{% endif %}
</div>
{% endif %}
</div>
</div>
{% endif %}
</div>
<!-- Right Column: Performance Metrics -->
<div class="lg:col-span-2 space-y-6">
<!-- Year-to-Date Performance -->
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 animate-in">
<div class="stat-card primary">
<p class="stat-label">{% trans "YTD Average Rating" %}</p>
{% if ytd_average %}
<p class="stat-value text-green-600">{{ ytd_average|floatformat:2 }}</p>
{% else %}
<p class="stat-value text-slate-400">-</p>
{% endif %}
</div>
<div class="stat-card info">
<p class="stat-label">{% trans "YTD Total Surveys" %}</p>
<p class="stat-value">{{ ytd_surveys|default:0 }}</p>
</div>
</div>
<!-- Best & Worst Months -->
{% if best_month or worst_month %}
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 animate-in">
{% if best_month %}
<div class="stat-card success">
<p class="stat-label">{% trans "Best Month" %}</p>
<p class="stat-value">{{ best_month.average_rating|floatformat:2 }}</p>
<p class="text-slate text-sm">{{ best_month.year }}-{{ best_month.month|stringformat:"02d" }}</p>
</div>
{% endif %}
{% if worst_month %}
<div class="stat-card warning">
<p class="stat-label">{% trans "Lowest Month" %}</p>
<p class="stat-value">{{ worst_month.average_rating|floatformat:2 }}</p>
<p class="text-slate text-sm">{{ worst_month.year }}-{{ worst_month.month|stringformat:"02d" }}</p>
</div>
{% endif %}
</div>
{% endif %}
<!-- Ratings History -->
<div class="info-card animate-in">
<div class="card-header">
<h5>
<i data-lucide="circle-help" class="w-5 h-5 inline-block me-2"></i>
{% trans "Ratings History" %} ({% trans "Last 12 Months" %})
</h5>
</div>
<div class="card-body p-0">
{% if ratings_history %}
<div class="overflow-x-auto">
<table class="ratings-table">
<thead>
<tr>
<th>{% trans "Month" %}</th>
<th>{% trans "Rating" %}</th>
<th>{% trans "Surveys" %}</th>
<th>{% trans "Positive" %}</th>
<th>{% trans "Neutral" %}</th>
<th>{% trans "Negative" %}</th>
<th>{% trans "Rank" %}</th>
</tr>
</thead>
<tbody>
{% for rating in ratings_history %}
<tr>
<td class="font-semibold">{{ rating.year }}-{{ rating.month|stringformat:"02d" }}</td>
<td>
<strong class="text-navy">{{ rating.average_rating|floatformat:2 }}</strong>
</td>
<td>{{ rating.total_surveys }}</td>
<td>
<span class="rating-badge positive">{{ rating.positive_count }}</span>
</td>
<td>
<span class="rating-badge neutral">{{ rating.neutral_count }}</span>
</td>
<td>
<span class="rating-badge negative">{{ rating.negative_count }}</span>
</td>
<td>
{% if rating.hospital_rank %}
<span class="font-semibold text-navy">#{{ rating.hospital_rank }}</span>
{% else %}
<span class="text-slate-400">-</span>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="empty-state">
<div class="empty-state-icon">
<i data-lucide="table" class="w-10 h-10"></i>
</div>
<p class="text-slate font-medium mb-2">{% trans "No rating history available" %}</p>
<p class="text-slate text-sm">{% trans "Ratings will appear here once the physician has received survey responses." %}</p>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
lucide.createIcons();
});
</script>
{% endblock %}