517 lines
23 KiB
HTML
517 lines
23 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}{{ patient.get_full_name }} - Patient Details - {{ block.super }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<!-- Page Header -->
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<div>
|
|
<h1 class="h3 mb-1">{{ patient.get_full_name }}</h1>
|
|
<nav aria-label="breadcrumb">
|
|
<ol class="breadcrumb mb-0">
|
|
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'patients:patient_list' %}">Patients</a></li>
|
|
<li class="breadcrumb-item active">{{ patient.get_full_name }}</li>
|
|
</ol>
|
|
</nav>
|
|
<div class="mt-2">
|
|
<span class="badge bg-primary me-2">MRN: {{ patient.mrn }}</span>
|
|
<span class="badge bg-info me-2">{{ patient.get_gender_display }}</span>
|
|
<span class="badge bg-secondary me-2">Age: {{ patient.age }}</span>
|
|
{% if patient.is_vip %}
|
|
<span class="badge bg-warning text-dark me-2">VIP</span>
|
|
{% endif %}
|
|
{% if patient.status == 'ACTIVE' %}
|
|
<span class="badge bg-success">Active</span>
|
|
{% elif patient.status == 'INACTIVE' %}
|
|
<span class="badge bg-secondary">Inactive</span>
|
|
{% elif patient.status == 'DECEASED' %}
|
|
<span class="badge bg-dark">Deceased</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="btn-group">
|
|
<a href="{% url 'patients:patient_update' patient.pk %}" class="btn btn-primary">
|
|
<i class="fas fa-edit me-2"></i>Edit Patient
|
|
</a>
|
|
<button type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown">
|
|
<span class="visually-hidden">Toggle Dropdown</span>
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
<li><a class="dropdown-item" href="#" onclick="printPatient()">
|
|
<i class="fas fa-print me-2"></i>Print Summary
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="?export=pdf">
|
|
<i class="fas fa-file-pdf me-2"></i>Export PDF
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="{% url 'appointments:appointment_create' %}?patient={{ patient.pk }}">
|
|
<i class="fas fa-calendar-plus me-2"></i>Schedule Appointment
|
|
</a></li>
|
|
<li><hr class="dropdown-divider"></li>
|
|
{# <li><a class="dropdown-item" href="{% url 'patients:patient_deactivate' patient.pk %}">#}
|
|
{# <i class="fas fa-user-slash me-2"></i>Deactivate Patient#}
|
|
{# </a></li>#}
|
|
{# <li><a class="dropdown-item text-danger" href="{% url 'patients:patient_delete' patient.pk %}">#}
|
|
{# <i class="fas fa-trash me-2"></i>Delete Patient#}
|
|
{# </a></li>#}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<!-- Left Column - Patient Information -->
|
|
<div class="col-lg-4">
|
|
<!-- Patient Photo & Basic Info -->
|
|
<div class="card mb-4">
|
|
<div class="card-body text-center">
|
|
{% if patient.photo %}
|
|
<img src="{{ patient.photo.url }}" alt="Patient Photo" class="img-fluid rounded-circle mb-3" style="width: 150px; height: 150px; object-fit: cover;">
|
|
{% else %}
|
|
<div class="bg-light rounded-circle d-inline-flex align-items-center justify-content-center mb-3" style="width: 150px; height: 150px;">
|
|
<i class="fas fa-user fa-4x text-muted"></i>
|
|
</div>
|
|
{% endif %}
|
|
<h5 class="mb-1">{{ patient.get_full_name }}</h5>
|
|
<p class="text-muted mb-3">{{ patient.get_gender_display }} • {{ patient.age }} years old</p>
|
|
|
|
<div class="row text-center">
|
|
<div class="col-4">
|
|
<div class="border-end">
|
|
<h6 class="mb-0">{{ patient.appointments.count }}</h6>
|
|
<small class="text-muted">Appointments</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-4">
|
|
<div class="border-end">
|
|
<h6 class="mb-0">{{ patient.admissions.count }}</h6>
|
|
<small class="text-muted">Admissions</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-4">
|
|
<h6 class="mb-0">{{ patient.bills.count }}</h6>
|
|
<small class="text-muted">Bills</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Personal Information -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-user me-2"></i>Personal Information
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row mb-3">
|
|
<div class="col-sm-5"><strong>Date of Birth:</strong></div>
|
|
<div class="col-sm-7">{{ patient.date_of_birth|date:"F d, Y" }}</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-sm-5"><strong>Gender:</strong></div>
|
|
<div class="col-sm-7">{{ patient.get_gender_display }}</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-sm-5"><strong>Phone:</strong></div>
|
|
<div class="col-sm-7">
|
|
{% if patient.phone_number %}
|
|
<a href="tel:{{ patient.phone_number }}">{{ patient.phone_number }}</a>
|
|
{% else %}
|
|
<span class="text-muted">Not provided</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-sm-5"><strong>Email:</strong></div>
|
|
<div class="col-sm-7">
|
|
{% if patient.email %}
|
|
<a href="mailto:{{ patient.email }}">{{ patient.email }}</a>
|
|
{% else %}
|
|
<span class="text-muted">Not provided</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-sm-5"><strong>Address:</strong></div>
|
|
<div class="col-sm-7">
|
|
{% if patient.address %}
|
|
{{ patient.address }}
|
|
{% else %}
|
|
<span class="text-muted">Not provided</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-sm-5"><strong>SSN:</strong></div>
|
|
<div class="col-sm-7">
|
|
{% if patient.ssn %}
|
|
***-**-{{ patient.ssn|slice:"-4:" }}
|
|
{% else %}
|
|
<span class="text-muted">Not provided</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-sm-5"><strong>Registered:</strong></div>
|
|
<div class="col-sm-7">{{ patient.registration_date|date:"F d, Y" }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Emergency Contacts -->
|
|
<div class="card mb-4">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-phone me-2"></i>Emergency Contacts
|
|
</h5>
|
|
<a href="{% url 'patients:emergency_contact_create' patient.pk %}" class="btn btn-sm btn-outline-primary">
|
|
<i class="fas fa-plus me-1"></i>Add
|
|
</a>
|
|
</div>
|
|
<div class="card-body" id="emergency-contacts"
|
|
hx-get="{% url 'patients:emergency_contacts_list' patient.id %}"
|
|
hx-trigger="load">
|
|
<div class="text-center">
|
|
<div class="spinner-border spinner-border-sm text-primary" role="status">
|
|
<span class="visually-hidden">Loading...</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Insurance Information -->
|
|
<div class="card mb-4">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-shield-alt me-2"></i>Insurance Information
|
|
</h5>
|
|
<a href="{% url 'patients:insurance_create' patient.pk %}" class="btn btn-sm btn-outline-primary">
|
|
<i class="fas fa-plus me-1"></i>Add
|
|
</a>
|
|
</div>
|
|
<div class="card-body" id="insurance-info"
|
|
hx-get="{% url 'patients:insurance_info_list' patient.id %}"
|
|
hx-trigger="load">
|
|
<div class="text-center">
|
|
<div class="spinner-border spinner-border-sm text-primary" role="status">
|
|
<span class="visually-hidden">Loading...</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right Column - Medical Information & Activities -->
|
|
<div class="col-lg-8">
|
|
<!-- Quick Actions -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-bolt me-2"></i>Quick Actions
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-3">
|
|
<a href="{% url 'appointments:appointment_create' %}?patient={{ patient.pk }}" class="btn btn-outline-primary w-100">
|
|
<i class="fas fa-calendar-plus d-block mb-2"></i>
|
|
Schedule Appointment
|
|
</a>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<a href="{% url 'emr:encounter_create' %}?patient={{ patient.pk }}" class="btn btn-outline-success w-100">
|
|
<i class="fas fa-notes-medical d-block mb-2"></i>
|
|
New Encounter
|
|
</a>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<a href="{% url 'laboratory:lab_order_create' %}?patient={{ patient.pk }}" class="btn btn-outline-info w-100">
|
|
<i class="fas fa-vial d-block mb-2"></i>
|
|
Lab Order
|
|
</a>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<a href="{% url 'billing:bill_create' %}?patient={{ patient.pk }}" class="btn btn-outline-warning w-100">
|
|
<i class="fas fa-file-invoice-dollar d-block mb-2"></i>
|
|
Create Bill
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tabs for Different Sections -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<ul class="nav nav-tabs card-header-tabs" id="patientTabs" role="tablist">
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link active" id="appointments-tab" data-bs-toggle="tab" data-bs-target="#appointments" type="button" role="tab">
|
|
<i class="fas fa-calendar me-2"></i>Appointments
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="medical-history-tab" data-bs-toggle="tab" data-bs-target="#medical-history" type="button" role="tab">
|
|
<i class="fas fa-notes-medical me-2"></i>Medical History
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="lab-results-tab" data-bs-toggle="tab" data-bs-target="#lab-results" type="button" role="tab">
|
|
<i class="fas fa-vial me-2"></i>Lab Results
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="billing-tab" data-bs-toggle="tab" data-bs-target="#billing" type="button" role="tab">
|
|
<i class="fas fa-file-invoice me-2"></i>Billing
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="notes-tab" data-bs-toggle="tab" data-bs-target="#notes" type="button" role="tab">
|
|
<i class="fas fa-sticky-note me-2"></i>Notes
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="tab-content" id="patientTabsContent">
|
|
<!-- Appointments Tab -->
|
|
<div class="tab-pane fade show active" id="appointments" role="tabpanel">
|
|
<div hx-get="{% url 'patients:patient_appointments' patient.id %}"
|
|
hx-trigger="load">
|
|
<div class="text-center">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden">Loading appointments...</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Medical History Tab -->
|
|
{# <div class="tab-pane fade" id="medical-history" role="tabpanel">#}
|
|
{# <div hx-get="{% url 'patients:patient_medical_history' patient.id %}"#}
|
|
{# hx-trigger="intersection once">#}
|
|
{# <div class="text-center">#}
|
|
{# <div class="spinner-border text-primary" role="status">#}
|
|
{# <span class="visually-hidden">Loading medical history...</span>#}
|
|
{# </div>#}
|
|
{# </div>#}
|
|
{# </div>#}
|
|
{# </div>#}
|
|
|
|
<!-- Lab Results Tab -->
|
|
{# <div class="tab-pane fade" id="lab-results" role="tabpanel">#}
|
|
{# <div hx-get="{% url 'patients:patient_lab_results' patient.id %}"#}
|
|
{# hx-trigger="intersection once">#}
|
|
{# <div class="text-center">#}
|
|
{# <div class="spinner-border text-primary" role="status">#}
|
|
{# <span class="visually-hidden">Loading lab results...</span>#}
|
|
{# </div>#}
|
|
{# </div>#}
|
|
{# </div>#}
|
|
{# </div>#}
|
|
|
|
<!-- Billing Tab -->
|
|
{# <div class="tab-pane fade" id="billing" role="tabpanel">#}
|
|
{# <div hx-get="{% url 'patients:patient_billing' patient.id %}"#}
|
|
{# hx-trigger="intersection once">#}
|
|
{# <div class="text-center">#}
|
|
{# <div class="spinner-border text-primary" role="status">#}
|
|
{# <span class="visually-hidden">Loading billing information...</span>#}
|
|
{# </div>#}
|
|
{# </div>#}
|
|
{# </div>#}
|
|
{# </div>#}
|
|
|
|
<!-- Notes Tab -->
|
|
<div class="tab-pane fade" id="notes" role="tabpanel">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h6 class="mb-0">Patient Notes</h6>
|
|
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#addNoteModal">
|
|
<i class="fas fa-plus me-1"></i>Add Note
|
|
</button>
|
|
</div>
|
|
<div hx-get="{% url 'patients:patient_notes_list' patient.id %}"
|
|
hx-trigger="intersection once">
|
|
<div class="text-center">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden">Loading notes...</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Add Note Modal -->
|
|
<div class="modal fade" id="addNoteModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Add Patient Note</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<form hx-post="{% url 'patients:patient_note_create' patient.id %}"
|
|
hx-target="#notes .tab-pane"
|
|
hx-swap="innerHTML">
|
|
<div class="modal-body">
|
|
{% csrf_token %}
|
|
<div class="mb-3">
|
|
<label for="noteType" class="form-label">Note Type</label>
|
|
<select class="form-select" id="noteType" name="note_type" required>
|
|
<option value="">Select type...</option>
|
|
<option value="GENERAL">General</option>
|
|
<option value="MEDICAL">Medical</option>
|
|
<option value="ADMINISTRATIVE">Administrative</option>
|
|
<option value="BILLING">Billing</option>
|
|
<option value="INSURANCE">Insurance</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="noteContent" class="form-label">Note Content</label>
|
|
<textarea class="form-control" id="noteContent" name="content" rows="4" required></textarea>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="isConfidential" name="is_confidential">
|
|
<label class="form-check-label" for="isConfidential">
|
|
Confidential Note
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">Add Note</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Print patient summary
|
|
function printPatient() {
|
|
window.print();
|
|
}
|
|
|
|
// Auto-refresh emergency contacts and insurance info
|
|
setInterval(function() {
|
|
htmx.trigger('#emergency-contacts', 'refresh');
|
|
htmx.trigger('#insurance-info', 'refresh');
|
|
}, 300000); // Refresh every 5 minutes
|
|
|
|
// Tab switching with HTMX loading
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const tabTriggers = document.querySelectorAll('#patientTabs button[data-bs-toggle="tab"]');
|
|
|
|
tabTriggers.forEach(function(tab) {
|
|
tab.addEventListener('shown.bs.tab', function(event) {
|
|
const targetPane = document.querySelector(event.target.getAttribute('data-bs-target'));
|
|
const htmxElement = targetPane.querySelector('[hx-get]');
|
|
|
|
if (htmxElement && !htmxElement.hasAttribute('data-loaded')) {
|
|
htmx.trigger(htmxElement, 'load');
|
|
htmxElement.setAttribute('data-loaded', 'true');
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
// Handle note modal submission
|
|
document.getElementById('addNoteModal').addEventListener('hidden.bs.modal', function() {
|
|
this.querySelector('form').reset();
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
.card {
|
|
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
|
border: 1px solid rgba(0, 0, 0, 0.125);
|
|
}
|
|
|
|
.card-header {
|
|
background-color: rgba(13, 110, 253, 0.1);
|
|
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
|
|
}
|
|
|
|
.btn {
|
|
border-radius: 0.375rem;
|
|
transition: all 0.15s ease-in-out;
|
|
}
|
|
|
|
.btn:hover:not(:disabled) {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.nav-tabs .nav-link {
|
|
border-radius: 0.375rem 0.375rem 0 0;
|
|
}
|
|
|
|
.nav-tabs .nav-link.active {
|
|
background-color: #fff;
|
|
border-color: #dee2e6 #dee2e6 #fff;
|
|
}
|
|
|
|
.badge {
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
.border-end {
|
|
border-right: 1px solid #dee2e6 !important;
|
|
}
|
|
|
|
.htmx-indicator {
|
|
opacity: 0;
|
|
transition: opacity 0.3s ease-in-out;
|
|
}
|
|
|
|
.htmx-request .htmx-indicator {
|
|
opacity: 1;
|
|
}
|
|
|
|
/* Print styles */
|
|
@media print {
|
|
.btn, .dropdown, .nav-tabs, #addNoteModal {
|
|
display: none !important;
|
|
}
|
|
|
|
.card {
|
|
border: 1px solid #000 !important;
|
|
box-shadow: none !important;
|
|
}
|
|
|
|
.tab-content .tab-pane {
|
|
display: block !important;
|
|
opacity: 1 !important;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.btn-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.d-flex.justify-content-between {
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.nav-tabs {
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.nav-tabs .nav-link {
|
|
font-size: 0.875rem;
|
|
padding: 0.5rem 0.75rem;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|