342 lines
15 KiB
HTML
342 lines
15 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;
|
|
}
|
|
.field-value-secondary {
|
|
font-size: 13px;
|
|
color: #64748b;
|
|
}
|
|
.survey-item {
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
border-left: 3px solid transparent;
|
|
}
|
|
.survey-item:hover {
|
|
border-left-color: #007bbd;
|
|
background: linear-gradient(135deg, #eef6fb 0%, #f0f9ff 100%);
|
|
transform: translateX(4px);
|
|
}
|
|
.metric-card {
|
|
background: linear-gradient(135deg, #007bbd 0%, #005696 100%);
|
|
}
|
|
.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);
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="p-8">
|
|
<!-- Header -->
|
|
<header class="mb-8">
|
|
<!-- Breadcrumb -->
|
|
<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>
|
|
|
|
<!-- Patient Info -->
|
|
<div class="flex items-start justify-between gap-6">
|
|
<div class="flex items-center gap-5">
|
|
<!-- Avatar -->
|
|
<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>
|
|
|
|
<!-- Name & Identifiers -->
|
|
<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 items-center gap-5 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 "National ID" %}:</span>
|
|
<span class="font-mono font-bold text-navy">{{ patient.national_id }}</span>
|
|
</span>
|
|
{% endif %}
|
|
{% if patient.date_of_birth %}
|
|
<span class="flex items-center gap-2 bg-slate-50 px-3 py-1.5 rounded-lg border border-slate-200">
|
|
<i data-lucide="calendar" class="w-4 h-4 text-blue"></i>
|
|
<span class="text-slate text-xs font-semibold uppercase">{% trans "DOB" %}:</span>
|
|
<span class="font-bold text-navy">{{ patient.date_of_birth }}</span>
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Action Buttons -->
|
|
<div class="flex gap-3 flex-shrink-0">
|
|
<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 Patient" %}
|
|
</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">
|
|
<!-- Main Content -->
|
|
<div class="lg:col-span-2 space-y-6">
|
|
|
|
<!-- Basic Information Card -->
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100 info-card">
|
|
<h2 class="text-lg font-bold text-navy mb-6 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 "Basic Information" %}
|
|
</h2>
|
|
|
|
<div class="grid grid-cols-2 gap-x-8 gap-y-6">
|
|
<div class="space-y-2">
|
|
<p class="field-label">{% trans "First Name" %}</p>
|
|
<p class="field-value">{{ patient.first_name|default:"-" }}</p>
|
|
{% if patient.first_name_ar %}
|
|
<p class="field-value-secondary" dir="rtl">{{ patient.first_name_ar }}</p>
|
|
{% endif %}
|
|
</div>
|
|
<div class="space-y-2">
|
|
<p class="field-label">{% trans "Last Name" %}</p>
|
|
<p class="field-value">{{ patient.last_name|default:"-" }}</p>
|
|
{% if patient.last_name_ar %}
|
|
<p class="field-value-secondary" dir="rtl">{{ patient.last_name_ar }}</p>
|
|
{% endif %}
|
|
</div>
|
|
<div class="space-y-2">
|
|
<p class="field-label">{% trans "Date of Birth" %}</p>
|
|
<p class="field-value">{{ patient.date_of_birth|default:"-" }}</p>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<p class="field-label">{% trans "Gender" %}</p>
|
|
<p class="field-value">{{ patient.get_gender_display|default:"-" }}</p>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<p class="field-label">{% trans "National ID" %}</p>
|
|
<p class="field-value font-mono">{{ patient.national_id|default:"-" }}</p>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<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>
|
|
|
|
<!-- Contact Information Card -->
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100 info-card">
|
|
<h2 class="text-lg font-bold text-navy mb-6 flex items-center gap-2">
|
|
<div class="icon-wrapper w-9 h-9 bg-gradient-to-br from-green-100 to-green-50 rounded-xl flex items-center justify-center">
|
|
<i data-lucide="phone" class="w-5 h-5 text-green-600"></i>
|
|
</div>
|
|
{% trans "Contact Information" %}
|
|
</h2>
|
|
|
|
<div class="grid grid-cols-2 gap-x-8 gap-y-6">
|
|
<div class="space-y-2">
|
|
<p class="field-label">{% trans "Phone" %}</p>
|
|
<p class="field-value font-mono">{{ patient.phone|default:"-" }}</p>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<p class="field-label">{% trans "Email" %}</p>
|
|
<p class="field-value">{{ patient.email|default:"-" }}</p>
|
|
</div>
|
|
<div class="col-span-2 space-y-2">
|
|
<p class="field-label">{% trans "Address" %}</p>
|
|
<p class="field-value">{{ patient.address|default:"-" }}</p>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<p class="field-label">{% trans "City" %}</p>
|
|
<p class="field-value">{{ patient.city|default:"-" }}</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Survey History Card -->
|
|
{% if surveys %}
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100 info-card">
|
|
<h2 class="text-lg font-bold text-navy mb-6 flex items-center gap-2">
|
|
<div class="icon-wrapper w-9 h-9 bg-gradient-to-br from-purple-100 to-purple-50 rounded-xl flex items-center justify-center">
|
|
<i data-lucide="clipboard-list" class="w-5 h-5 text-purple-600"></i>
|
|
</div>
|
|
{% trans "Recent Surveys" %}
|
|
<span class="ml-auto text-xs font-bold text-purple-600 bg-purple-50 px-2.5 py-1 rounded-lg">{{ surveys.count }}</span>
|
|
</h2>
|
|
|
|
<div class="space-y-3">
|
|
{% for survey in surveys %}
|
|
<div class="survey-item 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">
|
|
<span class="flex items-center gap-1">
|
|
<i data-lucide="calendar" class="w-3 h-3"></i>
|
|
{{ survey.created_at|date:"d M Y" }}
|
|
</span>
|
|
<span class="flex items-center gap-1">
|
|
<i data-lucide="clock" class="w-3 h-3"></i>
|
|
{{ survey.created_at|date:"H:i" }}
|
|
</span>
|
|
</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 == '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>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<div class="mt-6 text-center pt-4 border-t border-slate-100">
|
|
<a href="{% url 'surveys:instance_list' %}?search={{ patient.mrn }}"
|
|
class="inline-flex items-center gap-2 text-blue font-semibold hover:text-navy text-sm transition group">
|
|
{% trans "View all surveys" %}
|
|
<i data-lucide="arrow-right" class="w-4 h-4 group-hover:translate-x-1 transition-transform"></i>
|
|
</a>
|
|
</div>
|
|
</section>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Sidebar -->
|
|
<div class="space-y-6">
|
|
|
|
<!-- Hospital Information Card -->
|
|
<section class="metric-card rounded-2xl p-6 shadow-lg text-white info-card relative overflow-hidden">
|
|
<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>
|
|
|
|
<!-- Metadata Card -->
|
|
<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 "Metadata" %}</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" }}</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" }}</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
lucide.createIcons();
|
|
</script>
|
|
{% endblock %}
|