575 lines
25 KiB
HTML
575 lines
25 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}{{ ward.name }} - Ward 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">
|
|
<i class="fas fa-hospital me-2"></i>{{ ward.name }}
|
|
</h1>
|
|
<nav aria-label="breadcrumb">
|
|
<ol class="breadcrumb mb-0">
|
|
<li class="breadcrumb-item"><a href="{% url 'inpatients:dashboard' %}">Inpatients</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'inpatients:ward_list' %}">Wards</a></li>
|
|
<li class="breadcrumb-item active">{{ ward.name }}</li>
|
|
</ol>
|
|
</nav>
|
|
</div>
|
|
<div class="btn-group">
|
|
<a href="{% url 'inpatients:ward_list' %}" class="btn btn-outline-secondary">
|
|
<i class="fas fa-arrow-left me-2"></i>Back to Wards
|
|
</a>
|
|
<div class="btn-group">
|
|
<button type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown">
|
|
<i class="fas fa-cog me-2"></i>Actions
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
<li><a class="dropdown-item" href="#">
|
|
<i class="fas fa-edit me-2"></i>Edit Ward
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="{% url 'inpatients:bed_management' %}?ward={{ ward.id }}">
|
|
<i class="fas fa-bed me-2"></i>Manage Beds
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#">
|
|
<i class="fas fa-user-plus me-2"></i>Admit Patient
|
|
</a></li>
|
|
<li><hr class="dropdown-divider"></li>
|
|
<li><a class="dropdown-item" href="#">
|
|
<i class="fas fa-chart-bar me-2"></i>Ward Report
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#">
|
|
<i class="fas fa-print me-2"></i>Print Census
|
|
</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<!-- Main Content -->
|
|
<div class="col-lg-8">
|
|
<!-- Ward Information -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-info-circle me-2"></i>Ward Information
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<dl class="row">
|
|
<dt class="col-sm-4">Ward ID:</dt>
|
|
<dd class="col-sm-8">
|
|
<span class="font-monospace">{{ ward.ward_id }}</span>
|
|
</dd>
|
|
|
|
<dt class="col-sm-4">Ward Type:</dt>
|
|
<dd class="col-sm-8">
|
|
<span class="badge bg-primary">{{ ward.get_ward_type_display }}</span>
|
|
</dd>
|
|
|
|
<dt class="col-sm-4">Specialty:</dt>
|
|
<dd class="col-sm-8">
|
|
<span class="badge bg-info">{{ ward.get_specialty_display }}</span>
|
|
</dd>
|
|
|
|
<dt class="col-sm-4">Total Beds:</dt>
|
|
<dd class="col-sm-8">{{ ward.total_beds }}</dd>
|
|
</dl>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<dl class="row">
|
|
<dt class="col-sm-4">Nurse Manager:</dt>
|
|
<dd class="col-sm-8">
|
|
{% if ward.nurse_manager %}
|
|
{{ ward.nurse_manager.get_full_name }}
|
|
{% else %}
|
|
<span class="text-muted">Not assigned</span>
|
|
{% endif %}
|
|
</dd>
|
|
|
|
<dt class="col-sm-4">Location:</dt>
|
|
<dd class="col-sm-8">
|
|
{% if ward.floor %}Floor {{ ward.floor }}{% endif %}
|
|
{% if ward.building %}, {{ ward.building }}{% endif %}
|
|
{% if not ward.floor and not ward.building %}
|
|
<span class="text-muted">Not specified</span>
|
|
{% endif %}
|
|
</dd>
|
|
|
|
<dt class="col-sm-4">Status:</dt>
|
|
<dd class="col-sm-8">
|
|
<span class="badge bg-{% if ward.is_active %}success{% else %}secondary{% endif %}">
|
|
{% if ward.is_active %}Active{% else %}Inactive{% endif %}
|
|
</span>
|
|
</dd>
|
|
|
|
<dt class="col-sm-4">Capacity:</dt>
|
|
<dd class="col-sm-8">
|
|
<div class="progress" style="height: 20px;">
|
|
<div class="progress-bar bg-{% if ward.occupancy_rate > 90 %}danger{% elif ward.occupancy_rate > 75 %}warning{% else %}success{% endif %}"
|
|
style="width: {{ ward.occupancy_rate }}%">
|
|
{{ ward.occupancy_rate|floatformat:0 }}%
|
|
</div>
|
|
</div>
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
|
|
{% if ward.description %}
|
|
<div class="mt-3">
|
|
<h6>Description</h6>
|
|
<p class="text-muted">{{ ward.description }}</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bed Layout -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-bed me-2"></i>Bed Layout
|
|
</h5>
|
|
<div class="btn-group btn-group-sm">
|
|
<button type="button" class="btn btn-outline-secondary" onclick="refreshBedLayout()">
|
|
<i class="fas fa-sync-alt"></i>
|
|
</button>
|
|
<button type="button" class="btn btn-outline-primary" onclick="toggleBedView()">
|
|
<i class="fas fa-th me-1"></i>Grid View
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="bed-layout" class="bed-grid">
|
|
{% for bed in ward.beds.all %}
|
|
<div class="bed-item bed-{{ bed.status|lower }}" data-bed-id="{{ bed.id }}" onclick="showBedDetails({{ bed.id }})">
|
|
<div class="bed-number">{{ bed.bed_number }}</div>
|
|
<div class="bed-status">
|
|
<span class="badge bg-{% if bed.status == 'AVAILABLE' %}success{% elif bed.status == 'OCCUPIED' %}primary{% elif bed.status == 'MAINTENANCE' %}warning{% elif bed.status == 'RESERVED' %}info{% else %}secondary{% endif %}">
|
|
{{ bed.get_status_display }}
|
|
</span>
|
|
</div>
|
|
{% if bed.current_patient %}
|
|
<div class="patient-info">
|
|
<small>{{ bed.current_patient.get_full_name }}</small>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% empty %}
|
|
<div class="text-center py-4">
|
|
<i class="fas fa-bed fa-3x text-muted mb-3"></i>
|
|
<h6 class="text-muted">No beds configured</h6>
|
|
<p class="text-muted">Add beds to this ward to see the layout</p>
|
|
<a href="{% url 'inpatients:bed_management' %}?ward={{ ward.id }}" class="btn btn-primary">
|
|
<i class="fas fa-plus me-2"></i>Add Beds
|
|
</a>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Current Patients -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-users me-2"></i>Current Patients
|
|
</h5>
|
|
<span class="badge bg-primary">{{ current_patients.count }} patients</span>
|
|
</div>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Bed</th>
|
|
<th>Patient</th>
|
|
<th>Admission Date</th>
|
|
<th>Diagnosis</th>
|
|
<th>Attending Physician</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for admission in current_patients %}
|
|
<tr>
|
|
<td>
|
|
<span class="badge bg-info">{{ admission.bed.bed_number }}</span>
|
|
</td>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<div class="avatar-sm bg-primary bg-gradient rounded-circle d-flex align-items-center justify-content-center me-2">
|
|
<span class="text-white small fw-bold">
|
|
{{ admission.patient.first_name.0 }}{{ admission.patient.last_name.0 }}
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<div class="fw-semibold">{{ admission.patient.get_full_name }}</div>
|
|
<small class="text-muted">{{ admission.patient.patient_id }}</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div>{{ admission.admission_date|date:"M d, Y" }}</div>
|
|
<small class="text-muted">{{ admission.admission_date|timesince }} ago</small>
|
|
</td>
|
|
<td>
|
|
{% if admission.primary_diagnosis %}
|
|
{{ admission.primary_diagnosis|truncatechars:30 }}
|
|
{% else %}
|
|
<span class="text-muted">Not specified</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
{% if admission.attending_physician %}
|
|
{{ admission.attending_physician.get_full_name }}
|
|
{% else %}
|
|
<span class="text-muted">Not assigned</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<div class="btn-group btn-group-sm">
|
|
<a href="{% url 'inpatients:admission_detail' admission.pk %}"
|
|
class="btn btn-outline-primary btn-sm" title="View Details">
|
|
<i class="fas fa-eye"></i>
|
|
</a>
|
|
<button type="button" class="btn btn-outline-secondary btn-sm 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="#">
|
|
<i class="fas fa-exchange-alt me-2"></i>Transfer
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#">
|
|
<i class="fas fa-sign-out-alt me-2"></i>Discharge
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#">
|
|
<i class="fas fa-notes-medical me-2"></i>Add Note
|
|
</a></li>
|
|
</ul>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="6" class="text-center py-4">
|
|
<i class="fas fa-user-slash fa-2x text-muted mb-2"></i>
|
|
<h6 class="text-muted">No patients currently admitted</h6>
|
|
<p class="text-muted">Patient admissions will appear here</p>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sidebar -->
|
|
<div class="col-lg-4">
|
|
<!-- Ward Statistics -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-chart-bar me-2"></i>Ward Statistics
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row text-center">
|
|
<div class="col-6 mb-3">
|
|
<div class="card bg-success bg-gradient text-white">
|
|
<div class="card-body">
|
|
<h4 class="mb-1">{{ ward.available_beds }}</h4>
|
|
<small>Available</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 mb-3">
|
|
<div class="card bg-primary bg-gradient text-white">
|
|
<div class="card-body">
|
|
<h4 class="mb-1">{{ ward.occupied_beds }}</h4>
|
|
<small>Occupied</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 mb-3">
|
|
<div class="card bg-warning bg-gradient text-white">
|
|
<div class="card-body">
|
|
<h4 class="mb-1">{{ ward.maintenance_beds }}</h4>
|
|
<small>Maintenance</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 mb-3">
|
|
<div class="card bg-info bg-gradient text-white">
|
|
<div class="card-body">
|
|
<h4 class="mb-1">{{ ward.reserved_beds }}</h4>
|
|
<small>Reserved</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-3">
|
|
<h6>Occupancy Rate</h6>
|
|
<div class="progress mb-2" style="height: 25px;">
|
|
<div class="progress-bar bg-{% if ward.occupancy_rate > 90 %}danger{% elif ward.occupancy_rate > 75 %}warning{% else %}success{% endif %}"
|
|
style="width: {{ ward.occupancy_rate }}%">
|
|
{{ ward.occupancy_rate|floatformat:1 }}%
|
|
</div>
|
|
</div>
|
|
<small class="text-muted">{{ ward.occupied_beds }} of {{ ward.total_beds }} beds occupied</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 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="d-grid gap-2">
|
|
<a href="#" class="btn btn-primary">
|
|
<i class="fas fa-user-plus me-2"></i>Admit Patient
|
|
</a>
|
|
<a href="{% url 'inpatients:bed_management' %}?ward={{ ward.id }}" class="btn btn-outline-secondary">
|
|
<i class="fas fa-bed me-2"></i>Manage Beds
|
|
</a>
|
|
<a href="#" class="btn btn-outline-info">
|
|
<i class="fas fa-exchange-alt me-2"></i>Transfer Patient
|
|
</a>
|
|
<a href="#" class="btn btn-outline-success">
|
|
<i class="fas fa-sign-out-alt me-2"></i>Discharge Patient
|
|
</a>
|
|
<a href="#" class="btn btn-outline-warning">
|
|
<i class="fas fa-chart-bar me-2"></i>Ward Report
|
|
</a>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="printWardCensus()">
|
|
<i class="fas fa-print me-2"></i>Print Census
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Ward Staff -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-user-nurse me-2"></i>Ward Staff
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="list-group list-group-flush">
|
|
{% if ward.nurse_manager %}
|
|
<div class="list-group-item d-flex justify-content-between align-items-center px-0">
|
|
<div>
|
|
<h6 class="mb-1">{{ ward.nurse_manager.get_full_name }}</h6>
|
|
<small class="text-muted">Nurse Manager</small>
|
|
</div>
|
|
<span class="badge bg-primary">Manager</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% for staff in ward_staff %}
|
|
<div class="list-group-item d-flex justify-content-between align-items-center px-0">
|
|
<div>
|
|
<h6 class="mb-1">{{ staff.get_full_name }}</h6>
|
|
<small class="text-muted">{{ staff.job_title }}</small>
|
|
</div>
|
|
<span class="badge bg-secondary">{{ staff.shift|default:"Day" }}</span>
|
|
</div>
|
|
{% empty %}
|
|
<div class="text-center py-3">
|
|
<i class="fas fa-user-friends fa-2x text-muted mb-2"></i>
|
|
<p class="text-muted mb-0">No staff assigned</p>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bed Details Modal -->
|
|
<div class="modal fade" id="bedDetailsModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Bed Details</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body" id="bedDetailsContent">
|
|
<!-- Content loaded via AJAX -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Ward detail functionality
|
|
function refreshBedLayout() {
|
|
// Refresh bed layout via HTMX or AJAX
|
|
location.reload();
|
|
}
|
|
|
|
function toggleBedView() {
|
|
const bedGrid = document.getElementById('bed-layout');
|
|
bedGrid.classList.toggle('list-view');
|
|
}
|
|
|
|
function showBedDetails(bedId) {
|
|
// Load bed details in modal
|
|
fetch(`/inpatients/beds/${bedId}/details/`)
|
|
.then(response => response.text())
|
|
.then(html => {
|
|
document.getElementById('bedDetailsContent').innerHTML = html;
|
|
new bootstrap.Modal(document.getElementById('bedDetailsModal')).show();
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading bed details:', error);
|
|
});
|
|
}
|
|
|
|
function printWardCensus() {
|
|
window.print();
|
|
}
|
|
|
|
// Auto-refresh every 5 minutes
|
|
setInterval(function() {
|
|
refreshBedLayout();
|
|
}, 300000);
|
|
</script>
|
|
|
|
<style>
|
|
.bed-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
gap: 1rem;
|
|
}
|
|
|
|
.bed-item {
|
|
border: 2px solid #dee2e6;
|
|
border-radius: 8px;
|
|
padding: 1rem;
|
|
text-align: center;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
min-height: 100px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
}
|
|
|
|
.bed-item:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.bed-available {
|
|
border-color: #28a745;
|
|
background-color: #f8fff9;
|
|
}
|
|
|
|
.bed-occupied {
|
|
border-color: #007bff;
|
|
background-color: #f8f9ff;
|
|
}
|
|
|
|
.bed-maintenance {
|
|
border-color: #ffc107;
|
|
background-color: #fffdf8;
|
|
}
|
|
|
|
.bed-reserved {
|
|
border-color: #17a2b8;
|
|
background-color: #f8feff;
|
|
}
|
|
|
|
.bed-number {
|
|
font-weight: bold;
|
|
font-size: 1.1rem;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.bed-status {
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.patient-info {
|
|
font-size: 0.8rem;
|
|
color: #6c757d;
|
|
}
|
|
|
|
.avatar-sm {
|
|
width: 32px;
|
|
height: 32px;
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
.bg-gradient {
|
|
background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
|
}
|
|
|
|
.font-monospace {
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.list-group-item {
|
|
border: none;
|
|
padding: 0.75rem 0;
|
|
}
|
|
|
|
@media print {
|
|
.btn-group,
|
|
.card:last-child {
|
|
display: none !important;
|
|
}
|
|
|
|
.container-fluid {
|
|
padding: 0 !important;
|
|
}
|
|
|
|
.card {
|
|
border: 1px solid #000 !important;
|
|
break-inside: avoid;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.bed-grid {
|
|
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.bed-item {
|
|
padding: 0.5rem;
|
|
min-height: 80px;
|
|
}
|
|
|
|
.d-flex.justify-content-between {
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|