667 lines
40 KiB
HTML
667 lines
40 KiB
HTML
{% extends 'layouts/base.html' %}
|
|
{% load i18n %}
|
|
{% load static %}
|
|
|
|
{% block title %}{{ patient.get_full_name }} - {% trans "Patient Details" %}{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.info-card {
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
.info-card:hover {
|
|
transform: translateY(-4px);
|
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
}
|
|
.field-label {
|
|
font-size: 10px;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
color: #64748b;
|
|
}
|
|
.field-value {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: #1e293b;
|
|
line-height: 1.5;
|
|
}
|
|
.avatar-circle {
|
|
background: linear-gradient(135deg, #007bbd 0%, #005696 100%);
|
|
box-shadow: 0 4px 6px -1px rgba(0, 123, 189, 0.4);
|
|
}
|
|
.status-badge {
|
|
box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.1);
|
|
}
|
|
.action-btn {
|
|
transition: all 0.2s ease;
|
|
}
|
|
.action-btn:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 12px -1px rgba(0, 0, 0, 0.15);
|
|
}
|
|
.icon-wrapper {
|
|
box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.1);
|
|
}
|
|
.tab-btn {
|
|
padding: 0.625rem 1.25rem;
|
|
border-radius: 0.75rem;
|
|
font-size: 0.8125rem;
|
|
font-weight: 600;
|
|
transition: all 0.2s;
|
|
border: 2px solid transparent;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
.tab-btn.active {
|
|
background: #005696;
|
|
color: white;
|
|
border-color: #005696;
|
|
}
|
|
.tab-btn:not(.active) {
|
|
color: #64748b;
|
|
border-color: #e2e8f0;
|
|
background: white;
|
|
}
|
|
.tab-btn:not(.active):hover {
|
|
border-color: #005696;
|
|
color: #005696;
|
|
background: #f8fafc;
|
|
}
|
|
.tab-btn .tab-count {
|
|
padding: 0.125rem 0.5rem;
|
|
border-radius: 9999px;
|
|
font-size: 0.6875rem;
|
|
font-weight: 700;
|
|
}
|
|
.tab-btn.active .tab-count {
|
|
background: rgba(255,255,255,0.2);
|
|
color: white;
|
|
}
|
|
.tab-btn:not(.active) .tab-count {
|
|
background: #f1f5f9;
|
|
color: #64748b;
|
|
}
|
|
.data-row {
|
|
transition: all 0.2s ease;
|
|
border-left: 3px solid transparent;
|
|
}
|
|
.data-row:hover {
|
|
background: linear-gradient(135deg, #eef6fb 0%, #f0f9ff 100%);
|
|
border-left-color: #007bbd;
|
|
}
|
|
.type-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 0.25rem 0.625rem;
|
|
border-radius: 0.5rem;
|
|
font-size: 0.6875rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.025em;
|
|
}
|
|
.type-badge.ed { background: #fef3c7; color: #92400e; }
|
|
.type-badge.ip { background: #dbeafe; color: #1e40af; }
|
|
.type-badge.op { background: #d1fae5; color: #065f46; }
|
|
.priority-dot {
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 50%;
|
|
display: inline-block;
|
|
}
|
|
.vip-badge {
|
|
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
|
|
color: white;
|
|
padding: 0.125rem 0.5rem;
|
|
border-radius: 9999px;
|
|
font-size: 0.625rem;
|
|
font-weight: 800;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="p-8">
|
|
<header class="mb-8">
|
|
<nav class="flex items-center gap-2 text-sm text-slate mb-6">
|
|
<a href="{% url 'organizations:patient_list' %}"
|
|
class="hover:text-navy transition flex items-center gap-1 group">
|
|
<i data-lucide="arrow-left" class="w-4 h-4 group-hover:-translate-x-1 transition-transform"></i>
|
|
{% trans "Patients" %}
|
|
</a>
|
|
<i data-lucide="chevron-right" class="w-4 h-4 text-slate-300"></i>
|
|
<span class="font-bold text-navy">{{ patient.get_full_name }}</span>
|
|
<span class="ml-2 px-2.5 py-1 rounded-full text-[10px] font-bold uppercase tracking-wider status-badge
|
|
{% if patient.status == 'active' %}bg-gradient-to-r from-green-100 to-emerald-100 text-green-700 border border-green-200
|
|
{% else %}bg-gradient-to-r from-slate-100 to-gray-100 text-slate-700 border border-slate-200{% endif %}">
|
|
{{ patient.get_status_display }}
|
|
</span>
|
|
</nav>
|
|
|
|
<div class="flex items-start justify-between gap-6">
|
|
<div class="flex items-center gap-5">
|
|
<div class="avatar-circle w-20 h-20 rounded-2xl flex items-center justify-center text-white font-bold text-2xl flex-shrink-0">
|
|
{{ patient.first_name|first }}{{ patient.last_name|first }}
|
|
</div>
|
|
<div>
|
|
<h1 class="text-3xl font-bold text-navy mb-2 flex items-center gap-3">
|
|
{{ patient.get_full_name }}
|
|
{% if patient.first_name_ar or patient.last_name_ar %}
|
|
<span class="text-lg text-slate font-normal" dir="rtl">
|
|
{{ patient.first_name_ar|default:'' }} {{ patient.last_name_ar|default:'' }}
|
|
</span>
|
|
{% endif %}
|
|
</h1>
|
|
<div class="flex flex-wrap items-center gap-3 text-sm">
|
|
<span class="flex items-center gap-2 bg-slate-50 px-3 py-1.5 rounded-lg border border-slate-200">
|
|
<i data-lucide="hash" class="w-4 h-4 text-blue"></i>
|
|
<span class="text-slate text-xs font-semibold uppercase">{% trans "MRN" %}:</span>
|
|
<span class="font-mono font-bold text-navy">{{ patient.mrn }}</span>
|
|
</span>
|
|
{% if patient.national_id %}
|
|
<span class="flex items-center gap-2 bg-slate-50 px-3 py-1.5 rounded-lg border border-slate-200">
|
|
<i data-lucide="credit-card" class="w-4 h-4 text-blue"></i>
|
|
<span class="text-slate text-xs font-semibold uppercase">{% trans "SSN" %}:</span>
|
|
<span class="font-mono font-bold text-navy">{{ patient.national_id }}</span>
|
|
</span>
|
|
{% endif %}
|
|
{% if patient.phone %}
|
|
<span class="flex items-center gap-2 bg-slate-50 px-3 py-1.5 rounded-lg border border-slate-200">
|
|
<i data-lucide="phone" class="w-4 h-4 text-blue"></i>
|
|
<span class="font-mono font-bold text-navy">{{ patient.phone }}</span>
|
|
</span>
|
|
{% endif %}
|
|
{% if patient.nationality %}
|
|
<span class="flex items-center gap-2 bg-slate-50 px-3 py-1.5 rounded-lg border border-slate-200">
|
|
<i data-lucide="globe" class="w-4 h-4 text-blue"></i>
|
|
<span class="font-bold text-navy">{{ patient.nationality }}</span>
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="flex gap-3 flex-shrink-0">
|
|
<button id="sendComplaintLinkBtn"
|
|
{% if not patient.phone %}disabled title="{% trans 'No phone number on file' %}"{% endif %}
|
|
data-url="{% url 'organizations:send_complaint_link' patient.pk %}"
|
|
class="action-btn px-5 py-2.5 border border-amber-200 bg-amber-50 text-amber-700 rounded-xl font-semibold hover:bg-amber-100 transition flex items-center gap-2 hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed">
|
|
<i data-lucide="send" class="w-4 h-4"></i>
|
|
{% trans "Complaint Link" %}
|
|
</button>
|
|
<a href="{% url 'organizations:patient_update' patient.pk %}"
|
|
class="action-btn px-5 py-2.5 border border-slate-200 text-slate rounded-xl font-semibold hover:bg-slate-50 hover:border-slate-300 transition flex items-center gap-2 bg-white">
|
|
<i data-lucide="edit-2" class="w-4 h-4"></i>
|
|
{% trans "Edit" %}
|
|
</a>
|
|
{% if request.user.is_px_admin %}
|
|
<a href="{% url 'organizations:patient_delete' patient.pk %}"
|
|
class="action-btn px-5 py-2.5 border border-red-200 bg-red-50 text-red-600 rounded-xl font-semibold hover:bg-red-100 transition flex items-center gap-2 hover:shadow-md">
|
|
<i data-lucide="trash-2" class="w-4 h-4"></i>
|
|
{% trans "Delete" %}
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
<div class="lg:col-span-2 space-y-6">
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
|
|
<h2 class="text-lg font-bold text-navy mb-5 flex items-center gap-2">
|
|
<div class="icon-wrapper w-9 h-9 bg-gradient-to-br from-blue-100 to-blue-50 rounded-xl flex items-center justify-center">
|
|
<i data-lucide="user-circle" class="w-5 h-5 text-blue"></i>
|
|
</div>
|
|
{% trans "Patient Information" %}
|
|
</h2>
|
|
<div class="grid grid-cols-2 md:grid-cols-3 gap-x-8 gap-y-5">
|
|
<div class="space-y-1">
|
|
<p class="field-label">{% trans "First Name" %}</p>
|
|
<p class="field-value">{{ patient.first_name|default:"-" }}</p>
|
|
{% if patient.first_name_ar %}<p class="text-xs text-slate" dir="rtl">{{ patient.first_name_ar }}</p>{% endif %}
|
|
</div>
|
|
<div class="space-y-1">
|
|
<p class="field-label">{% trans "Last Name" %}</p>
|
|
<p class="field-value">{{ patient.last_name|default:"-" }}</p>
|
|
{% if patient.last_name_ar %}<p class="text-xs text-slate" dir="rtl">{{ patient.last_name_ar }}</p>{% endif %}
|
|
</div>
|
|
<div class="space-y-1">
|
|
<p class="field-label">{% trans "Gender" %}</p>
|
|
<p class="field-value">{{ patient.get_gender_display|default:"-" }}</p>
|
|
</div>
|
|
<div class="space-y-1">
|
|
<p class="field-label">{% trans "Date of Birth" %}</p>
|
|
<p class="field-value">{{ patient.date_of_birth|default:"-" }}</p>
|
|
</div>
|
|
<div class="space-y-1">
|
|
<p class="field-label">{% trans "Nationality" %}</p>
|
|
<p class="field-value">{{ patient.nationality|default:"-" }}</p>
|
|
</div>
|
|
<div class="space-y-1">
|
|
<p class="field-label">{% trans "Phone" %}</p>
|
|
<p class="field-value font-mono">{{ patient.phone|default:"-" }}</p>
|
|
</div>
|
|
<div class="space-y-1">
|
|
<p class="field-label">{% trans "Email" %}</p>
|
|
<p class="field-value">{{ patient.email|default:"-" }}</p>
|
|
</div>
|
|
<div class="space-y-1">
|
|
<p class="field-label">{% trans "City" %}</p>
|
|
<p class="field-value">{{ patient.city|default:"-" }}</p>
|
|
</div>
|
|
<div class="space-y-1">
|
|
<p class="field-label">{% trans "Status" %}</p>
|
|
<span class="inline-flex px-3 py-1.5 rounded-full text-xs font-bold uppercase tracking-wider status-badge
|
|
{% if patient.status == 'active' %}bg-gradient-to-r from-green-100 to-emerald-100 text-green-700 border border-green-200
|
|
{% else %}bg-gradient-to-r from-slate-100 to-gray-100 text-slate-700 border border-slate-200{% endif %}">
|
|
{{ patient.get_status_display }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="bg-white rounded-2xl shadow-sm border border-slate-100 overflow-hidden">
|
|
<div class="p-6 pb-4 border-b border-slate-100">
|
|
<div class="flex flex-wrap gap-2">
|
|
<a href="?tab=visits" class="tab-btn {% if tab == 'visits' %}active{% endif %}">
|
|
<i data-lucide="activity" class="w-4 h-4"></i>
|
|
{% trans "HIS Visits" %}
|
|
<span class="tab-count">{{ stats.visits }}</span>
|
|
</a>
|
|
<a href="?tab=surveys" class="tab-btn {% if tab == 'surveys' %}active{% endif %}">
|
|
<i data-lucide="clipboard-list" class="w-4 h-4"></i>
|
|
{% trans "Surveys" %}
|
|
<span class="tab-count">{{ stats.surveys }}</span>
|
|
</a>
|
|
<a href="?tab=complaints" class="tab-btn {% if tab == 'complaints' %}active{% endif %}">
|
|
<i data-lucide="alert-circle" class="w-4 h-4"></i>
|
|
{% trans "Complaints" %}
|
|
<span class="tab-count">{{ stats.complaints }}</span>
|
|
</a>
|
|
<a href="?tab=inquiries" class="tab-btn {% if tab == 'inquiries' %}active{% endif %}">
|
|
<i data-lucide="message-circle" class="w-4 h-4"></i>
|
|
{% trans "Inquiries" %}
|
|
<span class="tab-count">{{ stats.inquiries }}</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="p-6">
|
|
{% if tab == 'visits' %}
|
|
<div class="overflow-x-auto">
|
|
<table class="w-full">
|
|
<thead class="bg-slate-50 border-b border-slate-200">
|
|
<tr>
|
|
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase">{% trans "Adm ID" %}</th>
|
|
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase">{% trans "Type" %}</th>
|
|
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase">{% trans "Dates" %}</th>
|
|
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase">{% trans "Doctor" %}</th>
|
|
<th class="px-4 py-3 text-left text-xs font-bold text-gray-500 uppercase">{% trans "Insurance" %}</th>
|
|
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">{% trans "Status" %}</th>
|
|
<th class="px-4 py-3 text-center text-xs font-bold text-gray-500 uppercase">{% trans "Journey" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-gray-100">
|
|
{% for visit in his_visits %}
|
|
<tr class="data-row">
|
|
<td class="px-4 py-3">
|
|
<a href="{% url 'organizations:patient_visit_journey' patient.pk visit.pk %}"
|
|
class="font-mono text-sm font-bold text-navy hover:text-blue hover:underline transition">{{ visit.admission_id }}</a>
|
|
</td>
|
|
<td class="px-4 py-3">
|
|
<div class="flex items-center gap-2">
|
|
<span class="type-badge {{ visit.patient_type|lower }}">{{ visit.patient_type }}</span>
|
|
{% if visit.is_vip %}<span class="vip-badge">VIP</span>{% endif %}
|
|
</div>
|
|
</td>
|
|
<td class="px-4 py-3">
|
|
<div class="text-sm">
|
|
<div class="text-slate">{{ visit.admit_date|date:"d M Y H:i"|default:"-" }}</div>
|
|
{% if visit.discharge_date %}
|
|
<div class="text-xs text-green-600 flex items-center gap-1 mt-0.5">
|
|
<i data-lucide="log-out" class="w-3 h-3"></i>
|
|
{{ visit.discharge_date|date:"d M Y H:i" }}
|
|
</div>
|
|
{% else %}
|
|
<span class="text-xs text-amber-600">{% trans "In Progress" %}</span>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
<td class="px-4 py-3">
|
|
{% if visit.primary_doctor_fk %}
|
|
<a href="{% url 'organizations:staff_detail' visit.primary_doctor_fk.pk %}"
|
|
class="text-sm text-navy hover:text-blue hover:underline transition truncate max-w-[180px] block"
|
|
title="{{ visit.primary_doctor_fk }}">
|
|
{{ visit.primary_doctor_fk }}
|
|
</a>
|
|
{% else %}
|
|
<span class="text-sm text-slate truncate max-w-[180px] block">{{ visit.doctor_display }}</span>
|
|
{% endif %}
|
|
</td>
|
|
<td class="px-4 py-3">
|
|
{% if visit.insurance_company_name %}
|
|
<span class="text-sm text-slate truncate max-w-[180px] block" title="{{ visit.insurance_company_name }}">{{ visit.insurance_company_name }}</span>
|
|
{% else %}
|
|
<span class="text-sm text-slate">-</span>
|
|
{% endif %}
|
|
{% if visit.bill_type %}
|
|
<span class="text-[10px] font-bold bg-slate-100 text-slate-500 px-1.5 py-0.5 rounded ml-1">{{ visit.bill_type }}</span>
|
|
{% endif %}
|
|
</td>
|
|
<td class="px-4 py-3 text-center">
|
|
{% if visit.is_visit_complete %}
|
|
<span class="inline-flex px-2.5 py-1 rounded-full text-[10px] font-bold uppercase bg-green-100 text-green-700">{% trans "Complete" %}</span>
|
|
{% elif visit.survey_instance %}
|
|
<span class="inline-flex px-2.5 py-1 rounded-full text-[10px] font-bold uppercase bg-blue-100 text-blue-700">{% trans "Survey Sent" %}</span>
|
|
{% else %}
|
|
<span class="inline-flex px-2.5 py-1 rounded-full text-[10px] font-bold uppercase bg-amber-100 text-amber-700">{% trans "Active" %}</span>
|
|
{% endif %}
|
|
</td>
|
|
<td class="px-4 py-3 text-center">
|
|
<a href="{% url 'organizations:patient_visit_journey' patient.pk visit.pk %}"
|
|
class="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-semibold text-navy bg-navy/5 rounded-lg hover:bg-navy/10 hover:text-blue transition"
|
|
title="{% trans 'View visit journey' %}">
|
|
<i data-lucide="route" class="w-3.5 h-3.5"></i>
|
|
{% trans "View" %}
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="7" class="px-4 py-10 text-center">
|
|
<div class="flex flex-col items-center">
|
|
<div class="w-12 h-12 bg-slate-100 rounded-full flex items-center justify-center mb-3">
|
|
<i data-lucide="activity" class="w-6 h-6 text-slate-300"></i>
|
|
</div>
|
|
<p class="text-slate text-sm">{% trans "No HIS visits found" %}</p>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
{% elif tab == 'surveys' %}
|
|
<div class="space-y-3">
|
|
{% for survey in surveys %}
|
|
<div class="data-row flex justify-between items-start p-4 bg-slate-50 rounded-xl border border-slate-100">
|
|
<div class="flex-1 min-w-0">
|
|
<p class="font-semibold text-navy text-sm mb-1 truncate">{{ survey.survey_template.name }}</p>
|
|
<div class="flex items-center gap-3 text-xs text-slate">
|
|
{% if survey.sent_at %}
|
|
<span class="flex items-center gap-1">
|
|
<i data-lucide="send" class="w-3 h-3"></i>
|
|
{{ survey.sent_at|date:"d M Y H:i" }}
|
|
</span>
|
|
{% endif %}
|
|
{% if survey.completed_at %}
|
|
<span class="flex items-center gap-1 text-green-600">
|
|
<i data-lucide="check-circle" class="w-3 h-3"></i>
|
|
{{ survey.completed_at|date:"d M Y H:i" }}
|
|
</span>
|
|
{% endif %}
|
|
{% if survey.delivery_channel %}
|
|
<span class="flex items-center gap-1">
|
|
<i data-lucide="smartphone" class="w-3 h-3"></i>
|
|
{{ survey.delivery_channel|upper }}
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center gap-2 ml-4 flex-shrink-0">
|
|
{% if survey.total_score %}
|
|
<div class="flex flex-col items-end">
|
|
<span class="px-2.5 py-1 rounded-lg text-xs font-bold
|
|
{% if survey.is_negative %}bg-gradient-to-r from-red-100 to-red-50 text-red-700 border border-red-200
|
|
{% else %}bg-gradient-to-r from-green-100 to-emerald-50 text-green-700 border border-green-200{% endif %}">
|
|
{{ survey.total_score }}
|
|
</span>
|
|
<span class="text-[10px] text-slate mt-0.5">{% trans "Score" %}</span>
|
|
</div>
|
|
{% endif %}
|
|
<span class="px-2.5 py-1 rounded-lg text-[10px] font-bold uppercase tracking-wider
|
|
{% if survey.status == 'completed' %}bg-gradient-to-r from-green-100 to-emerald-100 text-green-700 border border-green-200
|
|
{% elif survey.status == 'pending' or survey.status == 'sent' %}bg-gradient-to-r from-blue-100 to-sky-100 text-blue-700 border border-blue-200
|
|
{% else %}bg-gradient-to-r from-slate-100 to-gray-100 text-slate-700 border border-slate-200{% endif %}">
|
|
{{ survey.get_status_display }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
{% empty %}
|
|
<div class="flex flex-col items-center py-10">
|
|
<div class="w-12 h-12 bg-slate-100 rounded-full flex items-center justify-center mb-3">
|
|
<i data-lucide="clipboard-list" class="w-6 h-6 text-slate-300"></i>
|
|
</div>
|
|
<p class="text-slate text-sm">{% trans "No surveys found" %}</p>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
{% elif tab == 'complaints' %}
|
|
<div class="space-y-3">
|
|
{% for complaint in complaints %}
|
|
<div class="data-row flex justify-between items-start p-4 bg-slate-50 rounded-xl border border-slate-100">
|
|
<div class="flex-1 min-w-0">
|
|
<div class="flex items-center gap-2 mb-1">
|
|
<span class="font-mono text-xs text-slate bg-white px-2 py-0.5 rounded border border-slate-200">{{ complaint.reference_number|default:"-" }}</span>
|
|
{% if complaint.priority %}
|
|
<span class="priority-dot {% if complaint.priority == 'critical' %}bg-red-500 animate-pulse{% elif complaint.priority == 'high' %}bg-red-500{% elif complaint.priority == 'medium' %}bg-amber-500{% else %}bg-green-500{% endif %}"></span>
|
|
{% endif %}
|
|
</div>
|
|
<p class="font-semibold text-navy text-sm mb-1 truncate">{{ complaint.title }}</p>
|
|
<div class="flex items-center gap-3 text-xs text-slate">
|
|
<span class="flex items-center gap-1">
|
|
<i data-lucide="calendar" class="w-3 h-3"></i>
|
|
{{ complaint.created_at|date:"d M Y" }}
|
|
</span>
|
|
{% if complaint.department %}
|
|
<span class="flex items-center gap-1">
|
|
<i data-lucide="building" class="w-3 h-3"></i>
|
|
{{ complaint.department.name }}
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<span class="px-2.5 py-1 rounded-lg text-[10px] font-bold uppercase tracking-wider flex-shrink-0 ml-4
|
|
{% if complaint.status == 'closed' %}bg-gradient-to-r from-green-100 to-emerald-100 text-green-700 border border-green-200
|
|
{% elif complaint.status == 'resolved' %}bg-gradient-to-r from-blue-100 to-sky-100 text-blue-700 border border-blue-200
|
|
{% elif complaint.status == 'open' %}bg-gradient-to-r from-amber-100 to-yellow-100 text-amber-700 border border-amber-200
|
|
{% else %}bg-gradient-to-r from-slate-100 to-gray-100 text-slate-700 border border-slate-200{% endif %}">
|
|
{{ complaint.get_status_display }}
|
|
</span>
|
|
</div>
|
|
{% empty %}
|
|
<div class="flex flex-col items-center py-10">
|
|
<div class="w-12 h-12 bg-slate-100 rounded-full flex items-center justify-center mb-3">
|
|
<i data-lucide="alert-circle" class="w-6 h-6 text-slate-300"></i>
|
|
</div>
|
|
<p class="text-slate text-sm">{% trans "No complaints found" %}</p>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
{% elif tab == 'inquiries' %}
|
|
<div class="space-y-3">
|
|
{% for inquiry in inquiries %}
|
|
<div class="data-row flex justify-between items-start p-4 bg-slate-50 rounded-xl border border-slate-100">
|
|
<div class="flex-1 min-w-0">
|
|
<p class="font-semibold text-navy text-sm mb-1 truncate">{{ inquiry.subject }}</p>
|
|
<div class="flex items-center gap-3 text-xs text-slate">
|
|
<span class="flex items-center gap-1">
|
|
<i data-lucide="calendar" class="w-3 h-3"></i>
|
|
{{ inquiry.created_at|date:"d M Y" }}
|
|
</span>
|
|
<span class="px-2 py-0.5 rounded bg-slate-200 text-slate-600 text-[10px] font-bold uppercase">{{ inquiry.category }}</span>
|
|
{% if inquiry.department %}
|
|
<span class="flex items-center gap-1">
|
|
<i data-lucide="building" class="w-3 h-3"></i>
|
|
{{ inquiry.department.name }}
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<span class="px-2.5 py-1 rounded-lg text-[10px] font-bold uppercase tracking-wider flex-shrink-0 ml-4
|
|
{% if inquiry.status == 'closed' or inquiry.status == 'resolved' %}bg-gradient-to-r from-green-100 to-emerald-100 text-green-700 border border-green-200
|
|
{% elif inquiry.status == 'open' %}bg-gradient-to-r from-amber-100 to-yellow-100 text-amber-700 border border-amber-200
|
|
{% else %}bg-gradient-to-r from-slate-100 to-gray-100 text-slate-700 border border-slate-200{% endif %}">
|
|
{{ inquiry.get_status_display }}
|
|
</span>
|
|
</div>
|
|
{% empty %}
|
|
<div class="flex flex-col items-center py-10">
|
|
<div class="w-12 h-12 bg-slate-100 rounded-full flex items-center justify-center mb-3">
|
|
<i data-lucide="message-circle" class="w-6 h-6 text-slate-300"></i>
|
|
</div>
|
|
<p class="text-slate text-sm">{% trans "No inquiries found" %}</p>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</section>
|
|
</div>
|
|
|
|
<div class="space-y-6">
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100 info-card">
|
|
<h3 class="text-sm font-bold text-navy mb-5 flex items-center gap-2">
|
|
<div class="icon-wrapper w-9 h-9 bg-gradient-to-br from-slate-100 to-slate-50 rounded-xl flex items-center justify-center">
|
|
<i data-lucide="bar-chart-3" class="w-5 h-5 text-slate"></i>
|
|
</div>
|
|
{% trans "Summary" %}
|
|
</h3>
|
|
<div class="space-y-3">
|
|
<a href="?tab=visits" class="flex justify-between items-center p-3 rounded-xl bg-slate-50 border border-slate-100 hover:border-navy hover:bg-blue-50/30 transition group">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-8 h-8 bg-blue/10 rounded-lg flex items-center justify-center">
|
|
<i data-lucide="activity" class="w-4 h-4 text-blue"></i>
|
|
</div>
|
|
<span class="text-sm font-medium text-slate group-hover:text-navy transition">{% trans "HIS Visits" %}</span>
|
|
</div>
|
|
<span class="text-lg font-bold text-navy">{{ stats.visits }}</span>
|
|
</a>
|
|
<a href="?tab=surveys" class="flex justify-between items-center p-3 rounded-xl bg-slate-50 border border-slate-100 hover:border-navy hover:bg-blue-50/30 transition group">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-8 h-8 bg-purple-500/10 rounded-lg flex items-center justify-center">
|
|
<i data-lucide="clipboard-list" class="w-4 h-4 text-purple-600"></i>
|
|
</div>
|
|
<span class="text-sm font-medium text-slate group-hover:text-navy transition">{% trans "Surveys" %}</span>
|
|
</div>
|
|
<span class="text-lg font-bold text-navy">{{ stats.surveys }}</span>
|
|
</a>
|
|
<a href="?tab=complaints" class="flex justify-between items-center p-3 rounded-xl bg-slate-50 border border-slate-100 hover:border-navy hover:bg-blue-50/30 transition group">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-8 h-8 bg-amber-500/10 rounded-lg flex items-center justify-center">
|
|
<i data-lucide="alert-circle" class="w-4 h-4 text-amber-600"></i>
|
|
</div>
|
|
<span class="text-sm font-medium text-slate group-hover:text-navy transition">{% trans "Complaints" %}</span>
|
|
</div>
|
|
<span class="text-lg font-bold text-navy">{{ stats.complaints }}</span>
|
|
</a>
|
|
<a href="?tab=inquiries" class="flex justify-between items-center p-3 rounded-xl bg-slate-50 border border-slate-100 hover:border-navy hover:bg-blue-50/30 transition group">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-8 h-8 bg-green-500/10 rounded-lg flex items-center justify-center">
|
|
<i data-lucide="message-circle" class="w-4 h-4 text-green-600"></i>
|
|
</div>
|
|
<span class="text-sm font-medium text-slate group-hover:text-navy transition">{% trans "Inquiries" %}</span>
|
|
</div>
|
|
<span class="text-lg font-bold text-navy">{{ stats.inquiries }}</span>
|
|
</a>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="rounded-2xl p-6 shadow-lg text-white info-card relative overflow-hidden" style="background: linear-gradient(135deg, #007bbd 0%, #005696 100%);">
|
|
<div class="absolute top-0 right-0 w-32 h-32 bg-white/5 rounded-full -translate-y-1/2 translate-x-1/2"></div>
|
|
<div class="absolute bottom-0 left-0 w-24 h-24 bg-white/5 rounded-full translate-y-1/2 -translate-x-1/2"></div>
|
|
<div class="relative z-10">
|
|
<div class="flex items-center gap-3 mb-4">
|
|
<div class="w-10 h-10 bg-white/10 rounded-xl flex items-center justify-center backdrop-blur-sm">
|
|
<i data-lucide="building-2" class="w-5 h-5 text-blue"></i>
|
|
</div>
|
|
<h3 class="text-sm font-bold">{% trans "Hospital" %}</h3>
|
|
</div>
|
|
<p class="font-bold text-lg mb-1">{{ patient.primary_hospital.name|default:"-" }}</p>
|
|
{% if patient.primary_hospital.name_ar %}
|
|
<p class="text-sm text-blue/80" dir="rtl">{{ patient.primary_hospital.name_ar }}</p>
|
|
{% endif %}
|
|
</div>
|
|
</section>
|
|
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100 info-card">
|
|
<div class="flex items-center gap-3 mb-5">
|
|
<div class="icon-wrapper w-9 h-9 bg-gradient-to-br from-slate-100 to-slate-50 rounded-xl flex items-center justify-center">
|
|
<i data-lucide="info" class="w-5 h-5 text-slate"></i>
|
|
</div>
|
|
<h3 class="text-sm font-bold text-navy">{% trans "Record Info" %}</h3>
|
|
</div>
|
|
<div class="space-y-4">
|
|
<div class="flex justify-between items-center pb-4 border-b border-slate-100">
|
|
<div class="flex items-center gap-2">
|
|
<i data-lucide="plus-circle" class="w-3.5 h-3.5 text-slate"></i>
|
|
<span class="text-xs font-semibold text-slate uppercase">{% trans "Created" %}</span>
|
|
</div>
|
|
<span class="text-sm font-medium text-navy">{{ patient.created_at|date:"d M Y H:i" }}</span>
|
|
</div>
|
|
<div class="flex justify-between items-center">
|
|
<div class="flex items-center gap-2">
|
|
<i data-lucide="refresh-cw" class="w-3.5 h-3.5 text-slate"></i>
|
|
<span class="text-xs font-semibold text-slate uppercase">{% trans "Updated" %}</span>
|
|
</div>
|
|
<span class="text-sm font-medium text-navy">{{ patient.updated_at|date:"d M Y H:i" }}</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
lucide.createIcons();
|
|
|
|
document.getElementById('sendComplaintLinkBtn')?.addEventListener('click', function() {
|
|
const btn = this;
|
|
const url = btn.dataset.url;
|
|
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<svg class="animate-spin w-4 h-4" viewBox="0 0 24 24" fill="none"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg> Sending...';
|
|
|
|
fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]')?.value || '{{ csrf_token }}',
|
|
},
|
|
})
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
showToast('Complaint link sent to ' + data.phone, 'success');
|
|
} else {
|
|
showToast(data.error || 'Failed to send complaint link', 'error');
|
|
}
|
|
})
|
|
.catch(() => showToast('Network error', 'error'))
|
|
.finally(() => {
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i data-lucide="send" class="w-4 h-4"></i> {% trans "Complaint Link" %}';
|
|
lucide.createIcons();
|
|
});
|
|
});
|
|
|
|
function showToast(message, type) {
|
|
const colors = {
|
|
success: 'bg-green-500',
|
|
error: 'bg-red-500',
|
|
};
|
|
const toast = document.createElement('div');
|
|
toast.className = `fixed bottom-6 right-6 ${colors[type] || colors.success} text-white px-5 py-3 rounded-xl shadow-lg text-sm font-medium z-50 flex items-center gap-2 transition-all`;
|
|
toast.innerHTML = `<i data-lucide="${type === 'success' ? 'check-circle' : 'alert-circle'}" class="w-4 h-4"></i> ${message}`;
|
|
document.body.appendChild(toast);
|
|
lucide.createIcons();
|
|
setTimeout(() => { toast.style.opacity = '0'; setTimeout(() => toast.remove(), 300); }, 4000);
|
|
}
|
|
</script>
|
|
{% endblock %}
|