983 lines
42 KiB
HTML
983 lines
42 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}Bed Management{% endblock %}
|
|
{% block css %}
|
|
<style>
|
|
.bed-card {
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
border: 2px solid transparent;
|
|
}
|
|
|
|
.bed-card:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.bed-card.selected {
|
|
border-color: #0d6efd;
|
|
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
|
|
}
|
|
|
|
.bed-available {
|
|
background-color: #d1e7dd;
|
|
border-color: #badbcc;
|
|
}
|
|
|
|
.bed-occupied {
|
|
background-color: #f8d7da;
|
|
border-color: #f5c2c7;
|
|
}
|
|
|
|
.bed-maintenance {
|
|
background-color: #fff3cd;
|
|
border-color: #ffecb5;
|
|
}
|
|
|
|
.bed-blocked {
|
|
background-color: #f8d7da;
|
|
border-color: #f5c2c7;
|
|
}
|
|
|
|
.bed-cleaning {
|
|
background-color: #cff4fc;
|
|
border-color: #b6effb;
|
|
}
|
|
|
|
.bed-reserved {
|
|
background-color: #fff3cd;
|
|
color: #856404;
|
|
}
|
|
.bed-out_of_order {
|
|
background-color: #c5c5c5;
|
|
color: #434242;
|
|
}
|
|
|
|
.bed-status-available {
|
|
color: #198754;
|
|
}
|
|
|
|
.bed-status-occupied {
|
|
color: #dc3545;
|
|
}
|
|
|
|
.bed-status-maintenance {
|
|
color: #fd7e14;
|
|
}
|
|
|
|
.bed-status-blocked {
|
|
color: #6c757d;
|
|
}
|
|
|
|
.bed-status-cleaning {
|
|
color: #0dcaf0;
|
|
}
|
|
|
|
.bed-status-reserved {
|
|
color: #ffc107;
|
|
}
|
|
|
|
.bed-status-out_of_order {
|
|
color: #6c757d;
|
|
}
|
|
|
|
.bg-available {
|
|
background-color: #198754;
|
|
}
|
|
|
|
.bg-occupied {
|
|
background-color: #dc3545;
|
|
}
|
|
|
|
.bg-maintenance {
|
|
background-color: #fd7e14;
|
|
}
|
|
|
|
.bg-blocked {
|
|
background-color: #6c757d;
|
|
}
|
|
|
|
.bg-cleaning {
|
|
background-color: #0dcaf0;
|
|
}
|
|
.bg-reserved {
|
|
background-color: #ffc107;
|
|
}
|
|
.bg-out_of_order {
|
|
background-color: #6c757d;
|
|
}
|
|
|
|
.bed-icon {
|
|
position: relative;
|
|
}
|
|
|
|
.bed-status-badge {
|
|
position: absolute;
|
|
top: 5px;
|
|
right: 5px;
|
|
}
|
|
|
|
.ward-section {
|
|
border-left: 4px solid #dee2e6;
|
|
padding-left: 1rem;
|
|
margin-left: 0.5rem;
|
|
}
|
|
|
|
.progress {
|
|
background-color: #e9ecef;
|
|
}
|
|
|
|
.progress-bar {
|
|
transition: width 0.3s ease;
|
|
}
|
|
|
|
.table-active {
|
|
background-color: rgba(13, 110, 253, 0.075);
|
|
}
|
|
|
|
.badge-sm {
|
|
font-size: 0.7em;
|
|
padding: 0.25em 0.4em;
|
|
}
|
|
|
|
.btn-group-sm .btn {
|
|
padding: 0.25rem 0.5rem;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.dropdown-menu {
|
|
border: 1px solid #dee2e6;
|
|
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
|
}
|
|
|
|
.dropdown-item:hover {
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.bed-card {
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.btn-group {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.btn-group .btn {
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.table-responsive {
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.ward-section {
|
|
border-left: none;
|
|
border-top: 4px solid #dee2e6;
|
|
padding-left: 0;
|
|
padding-top: 1rem;
|
|
margin-left: 0;
|
|
margin-top: 1rem;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 576px) {
|
|
.col-4 {
|
|
flex: 0 0 50%;
|
|
max-width: 50%;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
{% block content %}
|
|
|
|
<div class="container-fluid">
|
|
<ul class="breadcrumb">
|
|
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'inpatients:dashboard' %}">Inpatients</a></li>
|
|
<li class="breadcrumb-item active">Bed Management</li>
|
|
</ul>
|
|
|
|
<div class="row align-items-center mb-3">
|
|
<div class="col">
|
|
<h1 class="page-header">Bed Management</h1>
|
|
<p class="text-muted">Real-time bed occupancy and availability management</p>
|
|
</div>
|
|
<div class="col-auto">
|
|
<div class="btn-group">
|
|
<button class="btn btn-primary" onclick="addBed()">
|
|
<i class="fa fa-plus me-2"></i>Add Bed
|
|
</button>
|
|
<button class="btn btn-outline-secondary" onclick="bulkUpdate()">
|
|
<i class="fa fa-edit me-2"></i>Bulk Update
|
|
</button>
|
|
<button class="btn btn-outline-info" onclick="viewFloorPlan()">
|
|
<i class="fa fa-map me-2"></i>Floor Plan
|
|
</button>
|
|
<button class="btn btn-outline-success" onclick="generateReport()">
|
|
<i class="fa fa-chart-bar me-2"></i>Reports
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bed Statistics -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-3">
|
|
<div class="card bg-primary text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<h4 class="mb-0">{{ total_beds }}</h4>
|
|
<p class="mb-0">Total Beds</p>
|
|
</div>
|
|
<div class="ms-3">
|
|
<i class="fa fa-bed fa-2x"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card bg-success text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<h4 class="mb-0">{{ available_beds }}</h4>
|
|
<p class="mb-0">Available</p>
|
|
</div>
|
|
<div class="ms-3">
|
|
<i class="fa fa-check-circle fa-2x"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card bg-danger text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<h4 class="mb-0">{{ occupied_beds }}</h4>
|
|
<p class="mb-0">Occupied</p>
|
|
</div>
|
|
<div class="ms-3">
|
|
<i class="fa fa-user fa-2x"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card bg-warning text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<h4 class="mb-0">{{ maintenance_beds }}</h4>
|
|
<p class="mb-0">Maintenance</p>
|
|
</div>
|
|
<div class="ms-3">
|
|
<i class="fa fa-tools fa-2x"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Occupancy Rate -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-6">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">Occupancy Rate</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<div class="progress" style="height: 20px;">
|
|
<div class="progress-bar bg-info"
|
|
role="progressbar"
|
|
style="width: {{ occupancy_rate }}%"
|
|
title="{{ occupancy_rate|floatformat:0 }}% occupancy">
|
|
{{ occupancy_rate|floatformat:0}}%
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="ms-3">
|
|
<h4 class="mb-0">{{ occupancy_rate|floatformat:0 }}%</h4>
|
|
</div>
|
|
</div>
|
|
<div class="mt-2">
|
|
<small class="text-muted">{{ occupied_beds }} of {{ total_beds }} beds occupied</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title mb-0">Ward Distribution</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% for ward in wards %}
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<span>{{ ward.name }}</span>
|
|
<div class="d-flex align-items-center">
|
|
<div class="progress me-2" style="width: 100px; height: 8px;">
|
|
<div class="progress-bar
|
|
bg-{%if ward.occupancy_rate >= 90%}red{% elif ward.occupancy_rate >= 75 %}orange{% elif ward.occupancy_rate >= 50 %}blue{% else %}green{% endif %}"
|
|
role="progressbar"
|
|
style="width: {{ ward.occupancy_rate }}%">
|
|
</div>
|
|
</div>
|
|
<span class="text-muted">{{ ward.occupancy_rate|floatformat:0 }}%</span>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bed Management Interface -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="row align-items-center">
|
|
<div class="col">
|
|
<h5 class="card-title mb-0">Bed Status Overview</h5>
|
|
</div>
|
|
<div class="col-auto">
|
|
<div class="btn-group btn-group-sm">
|
|
<button class="btn btn-outline-secondary active" onclick="viewMode('grid')" id="gridView">
|
|
<i class="fa fa-th"></i> Grid
|
|
</button>
|
|
<button class="btn btn-outline-secondary" onclick="viewMode('list')" id="listView">
|
|
<i class="fa fa-list"></i> List
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<!-- Filters -->
|
|
<div class="row mb-3">
|
|
<div class="col-md-3">
|
|
<select class="form-select" id="wardFilter">
|
|
<option value="">All Wards</option>
|
|
{% for ward in wards %}
|
|
<option value="{{ ward.id }}">{{ ward.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<select class="form-select" id="statusFilter">
|
|
<option value="">All Status</option>
|
|
<option value="AVAILABLE">Available</option>
|
|
<option value="OCCUPIED">Occupied</option>
|
|
<option value="MAINTENANCE">Maintenance</option>
|
|
<option value="BLOCKED">Blocked</option>
|
|
<option value="CLEANING">Cleaning</option>
|
|
</select>
|
|
</div>
|
|
{# <div class="col-md-3">#}
|
|
{# <select class="form-select" id="bedTypeFilter">#}
|
|
{# <option value="">All Types</option>#}
|
|
{# <option value="standard">Standard</option>#}
|
|
{# <option value="icu">ICU</option>#}
|
|
{# <option value="isolation">Isolation</option>#}
|
|
{# <option value="pediatric">Pediatric</option>#}
|
|
{# <option value="maternity">Maternity</option>#}
|
|
{# </select>#}
|
|
{# </div>#}
|
|
<div class="col-md-3">
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" placeholder="Search beds..." id="bedSearch">
|
|
<button class="btn btn-outline-secondary" type="button">
|
|
<i class="fa fa-search"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Grid View -->
|
|
<div id="bedGridView">
|
|
{% for ward in wards %}
|
|
<div class="ward-section mb-4" data-ward="{{ ward.id }}">
|
|
<h6 class="text-muted mb-3">
|
|
<i class="fa fa-hospital me-2"></i>{{ ward.name }}
|
|
<span class="badge bg-secondary ms-2">{{ ward.bed_count }} beds</span>
|
|
</h6>
|
|
<div class="row">
|
|
{% for bed in ward.beds.all %}
|
|
<div class="col-md-2 col-sm-3 col-4 mb-3">
|
|
<div class="bed-card card h-100 bed-{{ bed.status|lower }}"
|
|
data-bed-id="{{ bed.id }}"
|
|
data-status="{{ bed.status|lower }}"
|
|
data-type="{{ bed.bed_type }}"
|
|
onclick="selectBed('{{ bed.id }}')">
|
|
<div class="card-body p-2 text-center">
|
|
<div class="bed-icon mb-2">
|
|
<i class="fa fa-bed fa-2x bed-status-{{ bed.status|lower }}"></i>
|
|
</div>
|
|
<h6 class="card-title mb-1">{{ bed.bed_number }}</h6>
|
|
<small class="text-muted">{{ bed.room_number }}</small>
|
|
{% if bed.current_admission %}
|
|
<div class="mt-2">
|
|
<small class="text-truncate d-block">{{ bed.current_patient.get_full_name }}</small>
|
|
<small class="text-muted">{{ bed.occupied_since|timesince }} ago</small>
|
|
</div>
|
|
{% endif %}
|
|
<div class="bed-status-badge">
|
|
<span class="badge bg-{{ bed.status|lower }} badge-sm">
|
|
{{ bed.get_status_display }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="card-footer">
|
|
<div class="btn-group btn-group-sm">
|
|
<a class="btn btn-sm btn-outline-primary" href="{% url 'inpatients:bed_detail' bed.id %}" title="View Details">
|
|
<i class="fa fa-eye"></i>
|
|
</a>
|
|
{# <button class="btn btn-outline-primary" onclick="viewBedDetails('{{ bed.id }}')" title="View Details">#}
|
|
{# <i class="fa fa-eye"></i>#}
|
|
{# </button>#}
|
|
<a class="btn btn-sm btn-outline-secondary" href="{% url 'inpatients:bed_update' bed.id %}" title="Edit">
|
|
<i class="fa fa-edit"></i>
|
|
</a>
|
|
{% if bed.status == 'AVAILABLE' %}
|
|
<a class="btn btn-sm btn-outline-success" href="{% url 'inpatients:admission_create' %}" title="Assign Patient">
|
|
<i class="fa fa-user-plus"></i>
|
|
</a>
|
|
{% elif bed.status == 'OCCUPIED' %}
|
|
{% if bed.current_admission %}
|
|
<a class="btn btn-sm btn-outline-warning" href="{% url 'inpatients:discharge_patient' bed.current_admission.id %}" title="Discharge">
|
|
<i class="fa fa-sign-out-alt"></i>
|
|
</a>
|
|
{% endif %}
|
|
{# <button class="btn btn-outline-warning" onclick="dischargePatient('{{ bed.current_admission.id }}')" title="Discharge">#}
|
|
{# <i class="fa fa-sign-out-alt"></i>#}
|
|
{# </button>#}
|
|
{% endif %}
|
|
<div class="btn-group btn-group-sm">
|
|
<button class="btn btn-sm btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown" title="More Actions">
|
|
<i class="fa fa-ellipsis-v"></i>
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
<li><a class="dropdown-item" href="{% url 'inpatients:maintenance_bed' bed.id %}">
|
|
<i class="fa fa-tools me-2"></i>Schedule Maintenance
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="scheduleCleaning('{{ bed.id }}')">
|
|
<i class="fa fa-broom me-2"></i>Schedule Cleaning
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="blockBed('{{ bed.id }}')">
|
|
<i class="fa fa-ban me-2"></i>Block Bed
|
|
</a></li>
|
|
<li><hr class="dropdown-divider"></li>
|
|
<li><a class="dropdown-item" href="#" onclick="viewHistory('{{ bed.id }}')">
|
|
<i class="fa fa-history me-2"></i>View History
|
|
</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<!-- List View -->
|
|
<div id="bedListView" style="display: none;">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>Bed</th>
|
|
<th>Ward</th>
|
|
<th>Room</th>
|
|
<th>Type</th>
|
|
<th>Status</th>
|
|
<th>Patient</th>
|
|
<th>Occupied Since</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for bed in beds %}
|
|
<tr data-bed-id="{{ bed.id }}"
|
|
data-status="{{ bed.status|lower }}"
|
|
data-type="{{ bed.bed_type }}"
|
|
data-ward="{{ bed.ward.id }}">
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<i class="fa fa-bed me-2 bed-{{ bed.status|lower }}"></i>
|
|
<strong>{{ bed.bed_number }}</strong>
|
|
</div>
|
|
</td>
|
|
<td>{{ bed.ward.name }}</td>
|
|
<td>{{ bed.room_number }}</td>
|
|
<td>
|
|
<span class="badge bg-secondary">{{ bed.get_bed_type_display }}</span>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-{{ bed.status|lower }}">
|
|
{{ bed.get_status_display }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
{% if bed.current_admission %}
|
|
<div>
|
|
<strong>{{ bed.current_patient.get_full_name }}</strong>
|
|
<br><small class="text-muted">{{ bed.current_patient.mrn }}</small>
|
|
</div>
|
|
{% else %}
|
|
<span class="text-muted">-</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
{% if bed.occupied_since %}
|
|
<div>
|
|
<strong>{{ bed.occupied_since|date:"M d, Y" }}</strong>
|
|
<br><small class="text-muted">{{ bed.occupied_since|timesince }} ago</small>
|
|
</div>
|
|
{% else %}
|
|
<span class="text-muted">-</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<div class="btn-group btn-group-sm">
|
|
<a class="btn btn-outline-primary" href="{% url 'inpatients:bed_detail' bed.id %}" title="View Details">
|
|
<i class="fa fa-eye"></i>
|
|
</a>
|
|
{# <button class="btn btn-outline-primary" onclick="viewBedDetails('{{ bed.id }}')" title="View Details">#}
|
|
{# <i class="fa fa-eye"></i>#}
|
|
{# </button>#}
|
|
<button class="btn btn-outline-secondary" onclick="editBed('{{ bed.id }}')" title="Edit">
|
|
<i class="fa fa-edit"></i>
|
|
</button>
|
|
{% if bed.status == 'AVAILABLE' %}
|
|
<button class="btn btn-outline-success" onclick="assignPatient('{{ bed.id }}')" title="Assign Patient">
|
|
<i class="fa fa-user-plus"></i>
|
|
</button>
|
|
{% elif bed.status == 'OCCUPIED' %}
|
|
{% if bed.current_admission %}
|
|
<a class="btn btn-outline-warning" href="{% url 'inpatients:discharge_patient' bed.current_admission.id %}" title="Discharge">
|
|
<i class="fa fa-sign-out-alt"></i>
|
|
</a>
|
|
{% endif %}
|
|
{# <button class="btn btn-outline-warning" onclick="dischargePatient('{{ bed.current_admission.id }}')" title="Discharge">#}
|
|
{# <i class="fa fa-sign-out-alt"></i>#}
|
|
{# </button>#}
|
|
{% endif %}
|
|
<div class="btn-group btn-group-sm">
|
|
<button class="btn btn-outline-info dropdown-toggle" data-bs-toggle="dropdown" title="More Actions">
|
|
<i class="fa fa-ellipsis-v"></i>
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
<li><a class="dropdown-item" href="#" onclick="scheduleMaintenance('{{ bed.id }}')">
|
|
<i class="fa fa-tools me-2"></i>Schedule Maintenance
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="scheduleCleaning('{{ bed.id }}')">
|
|
<i class="fa fa-broom me-2"></i>Schedule Cleaning
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="blockBed('{{ bed.id }}')">
|
|
<i class="fa fa-ban me-2"></i>Block Bed
|
|
</a></li>
|
|
<li><hr class="dropdown-divider"></li>
|
|
<li><a class="dropdown-item" href="#" onclick="viewHistory('{{ bed.id }}')">
|
|
<i class="fa fa-history me-2"></i>View History
|
|
</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
<div class="text-center">
|
|
{% if is_paginated %}
|
|
{% include 'partial/pagination.html' %}
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<!-- Bed Details Modal -->
|
|
<div class="modal fade" id="bedDetailsModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<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 dynamically -->
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
<button type="button" class="btn btn-primary" onclick="editCurrentBed()">Edit Bed</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Patient Assignment Modal -->
|
|
<div class="modal fade" id="patientAssignmentModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Assign Patient to Bed</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<form id="patientAssignmentForm">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="bed_id" id="assignmentBedId">
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">Patient <span class="text-danger">*</span></label>
|
|
<select class="form-select" name="patient_id" id="patientSelect" required>
|
|
<option value="">Select patient...</option>
|
|
{% for patient in available_patients %}
|
|
<option value="{{ patient.id }}">{{ patient.get_full_name }} ({{ patient.patient_id }})</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Admission Date <span class="text-danger">*</span></label>
|
|
<input type="datetime-local" class="form-control" name="admission_date" id="admissionDate" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Admission Type</label>
|
|
<select class="form-select" name="admission_type" id="admissionType">
|
|
<option value="emergency">Emergency</option>
|
|
<option value="elective">Elective</option>
|
|
<option value="transfer">Transfer</option>
|
|
<option value="observation">Observation</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Notes</label>
|
|
<textarea class="form-control" name="notes" id="assignmentNotes" rows="3"></textarea>
|
|
</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">
|
|
<i class="fa fa-user-plus me-2"></i>Assign Patient
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script src="{% static 'plugins/dropzone/dist/min/dropzone.min.js' %}"></script>
|
|
<script>
|
|
$(document).ready(function() {
|
|
setupEventHandlers();
|
|
setupFilters();
|
|
updateBedStatuses();
|
|
|
|
// Auto-refresh every 30 seconds
|
|
setInterval(updateBedStatuses, 30000);
|
|
});
|
|
|
|
function setupEventHandlers() {
|
|
// Patient assignment form
|
|
$('#patientAssignmentForm').on('submit', function(e) {
|
|
e.preventDefault();
|
|
assignPatientToBed();
|
|
});
|
|
|
|
// Filter handlers
|
|
$('#wardFilter, #statusFilter, #bedTypeFilter').on('change', function() {
|
|
filterBeds();
|
|
});
|
|
|
|
$('#bedSearch').on('input', function() {
|
|
filterBeds();
|
|
});
|
|
|
|
// Set default admission date to now
|
|
$('#admissionDate').val(new Date().toISOString().slice(0, 16));
|
|
}
|
|
|
|
function setupFilters() {
|
|
// Initialize filter functionality
|
|
$('.bed-card, tbody tr').each(function() {
|
|
$(this).data('original-display', $(this).css('display'));
|
|
});
|
|
}
|
|
|
|
function viewMode(mode) {
|
|
if (mode === 'grid') {
|
|
$('#bedGridView').show();
|
|
$('#bedListView').hide();
|
|
$('#gridView').addClass('active');
|
|
$('#listView').removeClass('active');
|
|
} else {
|
|
$('#bedGridView').hide();
|
|
$('#bedListView').show();
|
|
$('#gridView').removeClass('active');
|
|
$('#listView').addClass('active');
|
|
}
|
|
}
|
|
|
|
function selectBed(bedId) {
|
|
try {
|
|
// Remove previous selection
|
|
$('.bed-card').removeClass('selected');
|
|
$('tbody tr').removeClass('table-active');
|
|
|
|
// Add selection
|
|
$(`.bed-card[data-bed-id="${bedId}"]`).addClass('selected');
|
|
$(`tbody tr[data-bed-id="${bedId}"]`).addClass('table-active');
|
|
|
|
// Store selected bed
|
|
selectedBedId = bedId;
|
|
window.selectedBedId = bedId;
|
|
} catch (error) {
|
|
console.error('Error selecting bed:', error);
|
|
}
|
|
}
|
|
|
|
function addBed() {
|
|
window.location.href = '{% url "inpatients:bed_create" %}';
|
|
}
|
|
|
|
function editBed(bedId) {
|
|
window.location.href = '{% url "inpatients:bed_update" 0 %}'.replace('0', bedId);
|
|
}
|
|
|
|
function editCurrentBed() {
|
|
if (window.selectedBedId) {
|
|
editBed(window.selectedBedId);
|
|
}
|
|
}
|
|
|
|
function viewBedDetails(bedId) {
|
|
$.get('', {bed_id: bedId}, function(data) {
|
|
if (data.success) {
|
|
$('#bedDetailsContent').html(data.html);
|
|
$('#bedDetailsModal').modal('show');
|
|
window.selectedBedId = bedId;
|
|
} else {
|
|
toastr.error('Failed to load bed details');
|
|
}
|
|
});
|
|
}
|
|
|
|
function assignPatient(bedId) {
|
|
$('#assignmentBedId').val(bedId);
|
|
$('#patientAssignmentModal').modal('show');
|
|
}
|
|
|
|
function assignPatientToBed() {
|
|
var formData = $('#patientAssignmentForm').serialize();
|
|
|
|
$.post('', formData, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Patient assigned to bed successfully');
|
|
$('#patientAssignmentModal').modal('hide');
|
|
updateBedStatuses();
|
|
} else {
|
|
toastr.error('Failed to assign patient: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
{#function dischargePatient(admissionId) {#}
|
|
{# if (confirm('Are you sure you want to discharge the patient from this bed?')) {#}
|
|
{# $.post('{% url 'inpatients:discharge_patient' 0 %}'.replace('0', admissionId), {#}
|
|
{#bed_id: bedId,#}
|
|
{# csrfmiddlewaretoken: '{{ csrf_token }}'#}
|
|
{# }, function(data) {#}
|
|
{# if (data.success) {#}
|
|
{# toastr.success('Patient discharged successfully');#}
|
|
{# updateBedStatuses();#}
|
|
{# } else {#}
|
|
{# toastr.error('Failed to discharge patient: ' + data.error);#}
|
|
{# }#}
|
|
{# });#}
|
|
{# }#}
|
|
{# }#}
|
|
|
|
function scheduleMaintenance(bedId) {
|
|
$.post('', {
|
|
bed_id: bedId,
|
|
csrfmiddlewaretoken: '{{ csrf_token }}'
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Maintenance scheduled');
|
|
updateBedStatuses();
|
|
} else {
|
|
toastr.error('Failed to schedule maintenance: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function scheduleCleaning(bedId) {
|
|
$.post('', {
|
|
bed_id: bedId,
|
|
csrfmiddlewaretoken: '{{ csrf_token }}'
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Cleaning scheduled');
|
|
updateBedStatuses();
|
|
} else {
|
|
toastr.error('Failed to schedule cleaning: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function blockBed(bedId) {
|
|
var reason = prompt('Reason for blocking this bed:');
|
|
if (reason) {
|
|
$.post('', {
|
|
bed_id: bedId,
|
|
reason: reason,
|
|
csrfmiddlewaretoken: '{{ csrf_token }}'
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Bed blocked');
|
|
updateBedStatuses();
|
|
} else {
|
|
toastr.error('Failed to block bed: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
|
|
{#function viewHistory(bedId) {#}
|
|
{# // Pass the URL pattern from Django to JavaScript#}
|
|
{# const baseUrl = "{% url 'inpatients:bed_history' 'PLACEHOLDER' %}";#}
|
|
{# window.location.href = baseUrl.replace('PLACEHOLDER', bedId);#}
|
|
{# }#}
|
|
|
|
|
|
function bulkUpdate() {
|
|
window.location.href = '';
|
|
}
|
|
|
|
function viewFloorPlan() {
|
|
window.location.href = '';
|
|
}
|
|
|
|
function generateReport() {
|
|
window.location.href = '';
|
|
}
|
|
|
|
function updateBedStatuses() {
|
|
$.get('', function(data) {
|
|
if (data.success) {
|
|
// Update bed cards
|
|
data.beds.forEach(function(bed) {
|
|
var bedCard = $(`.bed-card[data-bed-id="${bed.id}"]`);
|
|
var bedRow = $(`tbody tr[data-bed-id="${bed.id}"]`);
|
|
|
|
// Update card
|
|
bedCard.removeClass('bed-available bed-occupied bed-maintenance bed-blocked bed-cleaning')
|
|
.addClass('bed-' + bed.status);
|
|
bedCard.find('.bed-status-badge .badge')
|
|
.removeClass('bg-success bg-danger bg-warning bg-secondary bg-info')
|
|
.addClass('bg-' + bed.status)
|
|
.text(bed.status);
|
|
|
|
// Update row
|
|
bedRow.find('.badge').first()
|
|
.removeClass('bg-success bg-danger bg-warning bg-secondary bg-info')
|
|
.addClass('bg-' + bed.status_color)
|
|
.text(bed.status_display);
|
|
});
|
|
|
|
// Update statistics
|
|
$('#totalBeds').text(data.stats.total_beds);
|
|
$('#availableBeds').text(data.stats.available_beds);
|
|
$('#occupiedBeds').text(data.stats.occupied_beds);
|
|
$('#maintenanceBeds').text(data.stats.maintenance_beds);
|
|
}
|
|
});
|
|
}
|
|
|
|
function filterBeds() {
|
|
var wardFilter = $('#wardFilter').val();
|
|
var statusFilter = $('#statusFilter').val();
|
|
var typeFilter = $('#bedTypeFilter').val();
|
|
var searchTerm = $('#bedSearch').val().toLowerCase();
|
|
|
|
// Filter grid view
|
|
$('.bed-card').each(function() {
|
|
var card = $(this);
|
|
var bedId = card.data('bed-id');
|
|
var status = card.data('status');
|
|
var type = card.data('type');
|
|
var text = card.text().toLowerCase();
|
|
var wardId = card.closest('.ward-section').data('ward');
|
|
|
|
var matchesWard = !wardFilter || wardId == wardFilter;
|
|
var matchesStatus = !statusFilter || status === statusFilter;
|
|
var matchesType = !typeFilter || type === typeFilter;
|
|
var matchesSearch = !searchTerm || text.includes(searchTerm);
|
|
|
|
if (matchesWard && matchesStatus && matchesType && matchesSearch) {
|
|
card.parent().show();
|
|
} else {
|
|
card.parent().hide();
|
|
}
|
|
});
|
|
|
|
// Filter list view
|
|
$('tbody tr').each(function() {
|
|
var row = $(this);
|
|
var status = row.data('status');
|
|
var type = row.data('type');
|
|
var wardId = row.data('ward');
|
|
var text = row.text().toLowerCase();
|
|
|
|
var matchesWard = !wardFilter || wardId == wardFilter;
|
|
var matchesStatus = !statusFilter || status === statusFilter;
|
|
var matchesType = !typeFilter || type === typeFilter;
|
|
var matchesSearch = !searchTerm || text.includes(searchTerm);
|
|
|
|
if (matchesWard && matchesStatus && matchesType && matchesSearch) {
|
|
row.show();
|
|
} else {
|
|
row.hide();
|
|
}
|
|
});
|
|
|
|
// Hide empty ward sections
|
|
$('.ward-section').each(function() {
|
|
var section = $(this);
|
|
var visibleBeds = section.find('.bed-card:visible').length;
|
|
if (visibleBeds === 0) {
|
|
section.hide();
|
|
} else {
|
|
section.show();
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
|
|
|
|
{% endblock %}
|
|
|