Marwan Alwali b9b8c69129 update
2025-08-31 10:47:23 +03:00

590 lines
22 KiB
HTML

{% extends 'base.html' %}
{% load static %}
{% block title %}OR Block Schedule{% endblock %}
{% block css %}
<style>
.schedule-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 0.5rem;
padding: 2rem;
margin-bottom: 2rem;
}
.block-card {
border: 1px solid #dee2e6;
border-radius: 0.375rem;
margin-bottom: 1rem;
background: white;
transition: all 0.3s ease;
}
.block-card:hover {
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
transform: translateY(-2px);
}
.block-header {
padding: 1rem 1.5rem;
border-bottom: 1px solid #dee2e6;
display: flex;
justify-content: space-between;
align-items: center;
}
.block-content {
padding: 1.5rem;
}
.block-info {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 1rem;
}
.info-item {
display: flex;
flex-direction: column;
}
.info-label {
font-size: 0.875rem;
color: #6c757d;
font-weight: 600;
margin-bottom: 0.25rem;
}
.info-value {
color: #495057;
font-weight: 500;
}
.utilization-bar {
height: 20px;
background: #e9ecef;
border-radius: 10px;
overflow: hidden;
position: relative;
}
.utilization-fill {
height: 100%;
border-radius: 10px;
transition: width 0.3s ease;
}
.utilization-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 0.75rem;
font-weight: bold;
color: #495057;
}
.case-list {
margin-top: 1rem;
}
.case-item {
display: flex;
align-items: center;
padding: 0.75rem;
margin-bottom: 0.5rem;
background: #f8f9fa;
border-radius: 0.25rem;
border-left: 4px solid #007bff;
}
.case-time {
font-weight: bold;
color: #007bff;
margin-right: 1rem;
min-width: 80px;
}
.case-details {
flex-grow: 1;
}
.case-patient {
font-weight: 600;
margin-bottom: 0.25rem;
}
.case-procedure {
font-size: 0.875rem;
color: #6c757d;
}
.case-status {
margin-left: auto;
}
.filter-section {
background: #f8f9fa;
border-radius: 0.375rem;
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.calendar-nav {
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
margin-bottom: 2rem;
}
.date-picker {
display: flex;
align-items: center;
gap: 0.5rem;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.stat-card {
background: white;
border: 1px solid #dee2e6;
border-radius: 0.375rem;
padding: 1.5rem;
text-align: center;
}
.stat-number {
font-size: 2rem;
font-weight: bold;
color: #007bff;
margin-bottom: 0.5rem;
}
.stat-label {
color: #6c757d;
font-size: 0.875rem;
}
@media (max-width: 768px) {
.schedule-header {
padding: 1.5rem;
}
.block-info {
grid-template-columns: 1fr;
}
.case-item {
flex-direction: column;
align-items: flex-start;
}
.case-time {
margin-bottom: 0.5rem;
}
.calendar-nav {
flex-direction: column;
}
}
</style>
{% endblock %}
{% block content %}
<div id="content" class="app-content">
<!-- Page Header -->
<div class="d-flex align-items-center mb-3">
<div>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{% url 'operating_theatre:dashboard' %}">Operating Theatre</a></li>
<li class="breadcrumb-item active">Block Schedule</li>
</ol>
<h1 class="page-header mb-0">
<i class="fas fa-calendar-alt me-2"></i>OR Block Schedule
</h1>
</div>
<div class="ms-auto">
<div class="btn-group">
<a href="{% url 'operating_theatre:or_block_create' %}" class="btn btn-primary">
<i class="fas fa-plus me-1"></i>New Block
</a>
<button class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown">
<i class="fas fa-download me-1"></i>Export
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" onclick="exportSchedule('pdf')">
<i class="fas fa-file-pdf me-2"></i>PDF
</a></li>
<li><a class="dropdown-item" href="#" onclick="exportSchedule('excel')">
<i class="fas fa-file-excel me-2"></i>Excel
</a></li>
</ul>
</div>
</div>
</div>
<!-- Schedule Header -->
<div class="schedule-header">
<div class="row">
<div class="col-md-8">
<h2 class="mb-3">{{ current_date|date:"l, F d, Y" }}</h2>
<div class="row">
<div class="col-md-4">
<div class="mb-2">
<strong>Total Blocks:</strong> {{ stats.total_blocks }}
</div>
<div class="mb-2">
<strong>Active Blocks:</strong> {{ stats.active_blocks }}
</div>
</div>
<div class="col-md-4">
<div class="mb-2">
<strong>Scheduled Cases:</strong> {{ stats.scheduled_cases }}
</div>
<div class="mb-2">
<strong>Available Slots:</strong> {{ stats.available_slots }}
</div>
</div>
<div class="col-md-4">
<div class="mb-2">
<strong>Overall Utilization:</strong> {{ stats.utilization_percentage }}%
</div>
<div class="mb-2">
<strong>Emergency Slots:</strong> {{ stats.emergency_slots }}
</div>
</div>
</div>
</div>
<div class="col-md-4 text-end">
<div class="calendar-nav">
<a href="?date={{ previous_date|date:'Y-m-d' }}" class="btn btn-outline-light">
<i class="fas fa-chevron-left"></i>
</a>
<div class="date-picker">
<input type="date" class="form-control" id="datePicker"
value="{{ current_date|date:'Y-m-d' }}" onchange="changeDate()">
</div>
<a href="?date={{ next_date|date:'Y-m-d' }}" class="btn btn-outline-light">
<i class="fas fa-chevron-right"></i>
</a>
</div>
</div>
</div>
</div>
<!-- Filters -->
<div class="filter-section">
<form method="get" id="filterForm">
<input type="hidden" name="date" value="{{ current_date|date:'Y-m-d' }}">
<div class="row">
<div class="col-md-3">
<div class="form-group">
<label class="form-label">Operating Room</label>
<select class="form-select" name="room">
<option value="">All Rooms</option>
{% for room in operating_rooms %}
<option value="{{ room.id }}" {% if request.GET.room == room.id|stringformat:"s" %}selected{% endif %}>
{{ room.name }}
</option>
{% endfor %}
</select>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label class="form-label">Surgeon</label>
<select class="form-select" name="surgeon">
<option value="">All Surgeons</option>
{% for surgeon in surgeons %}
<option value="{{ surgeon.id }}" {% if request.GET.surgeon == surgeon.id|stringformat:"s" %}selected{% endif %}>
{{ surgeon.get_full_name }}
</option>
{% endfor %}
</select>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label class="form-label">Block Type</label>
<select class="form-select" name="block_type">
<option value="">All Types</option>
<option value="regular" {% if request.GET.block_type == 'regular' %}selected{% endif %}>Regular</option>
<option value="emergency" {% if request.GET.block_type == 'emergency' %}selected{% endif %}>Emergency</option>
<option value="maintenance" {% if request.GET.block_type == 'maintenance' %}selected{% endif %}>Maintenance</option>
</select>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label class="form-label">Status</label>
<select class="form-select" name="status">
<option value="">All Status</option>
<option value="available" {% if request.GET.status == 'available' %}selected{% endif %}>Available</option>
<option value="booked" {% if request.GET.status == 'booked' %}selected{% endif %}>Booked</option>
<option value="in_progress" {% if request.GET.status == 'in_progress' %}selected{% endif %}>In Progress</option>
<option value="completed" {% if request.GET.status == 'completed' %}selected{% endif %}>Completed</option>
</select>
</div>
</div>
</div>
<div class="row mt-3">
<div class="col-12">
<button type="submit" class="btn btn-primary me-2">
<i class="fas fa-filter me-1"></i>Apply Filters
</button>
<a href="?date={{ current_date|date:'Y-m-d' }}" class="btn btn-outline-secondary">
<i class="fas fa-times me-1"></i>Clear
</a>
</div>
</div>
</form>
</div>
<!-- Block Schedule -->
<div class="row">
{% for block in blocks %}
<div class="col-lg-6 col-xl-4">
<div class="block-card">
<div class="block-header">
<div>
<h5 class="mb-1">{{ block.operating_room.name }}</h5>
<small class="text-muted">{{ block.start_time|date:"H:i" }} - {{ block.end_time|date:"H:i" }}</small>
</div>
<div>
{% if block.status == 'available' %}
<span class="badge bg-success">Available</span>
{% elif block.status == 'booked' %}
<span class="badge bg-primary">Booked</span>
{% elif block.status == 'in_progress' %}
<span class="badge bg-warning">In Progress</span>
{% elif block.status == 'completed' %}
<span class="badge bg-secondary">Completed</span>
{% endif %}
</div>
</div>
<div class="block-content">
<div class="block-info">
<div class="info-item">
<div class="info-label">Assigned Surgeon</div>
<div class="info-value">
{% if block.assigned_surgeon %}
{{ block.assigned_surgeon.get_full_name }}
{% else %}
<span class="text-muted">Unassigned</span>
{% endif %}
</div>
</div>
<div class="info-item">
<div class="info-label">Block Type</div>
<div class="info-value">{{ block.get_block_type_display }}</div>
</div>
<div class="info-item">
<div class="info-label">Duration</div>
<div class="info-value">{{ block.duration_hours }} hours</div>
</div>
<div class="info-item">
<div class="info-label">Utilization</div>
<div class="info-value">
<div class="utilization-bar">
<div class="utilization-fill bg-{% if block.utilization_percentage >= 80 %}success{% elif block.utilization_percentage >= 60 %}warning{% else %}danger{% endif %}"
style="width: {{ block.utilization_percentage }}%"></div>
<div class="utilization-text">{{ block.utilization_percentage }}%</div>
</div>
</div>
</div>
</div>
{% if block.scheduled_cases.exists %}
<div class="case-list">
<h6 class="mb-2">
<i class="fas fa-procedures me-1"></i>Scheduled Cases ({{ block.scheduled_cases.count }})
</h6>
{% for case in block.scheduled_cases.all %}
<div class="case-item">
<div class="case-time">{{ case.scheduled_start_time|date:"H:i" }}</div>
<div class="case-details">
<div class="case-patient">{{ case.patient.get_full_name }}</div>
<div class="case-procedure">{{ case.procedure_name }}</div>
</div>
<div class="case-status">
{% if case.status == 'scheduled' %}
<span class="badge bg-primary">Scheduled</span>
{% elif case.status == 'in_progress' %}
<span class="badge bg-warning">In Progress</span>
{% elif case.status == 'completed' %}
<span class="badge bg-success">Completed</span>
{% elif case.status == 'cancelled' %}
<span class="badge bg-danger">Cancelled</span>
{% endif %}
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center py-3">
<i class="fas fa-calendar-plus fa-2x text-muted mb-2"></i>
<p class="text-muted mb-0">No cases scheduled</p>
</div>
{% endif %}
<div class="d-flex justify-content-between mt-3">
<div class="btn-group">
<a href="{% url 'operating_theatre:block_detail' block.pk %}"
class="btn btn-outline-primary btn-sm">
<i class="fas fa-eye me-1"></i>View
</a>
{% if block.status == 'available' %}
<a href="{% url 'operating_theatre:block_edit' block.pk %}"
class="btn btn-outline-secondary btn-sm">
<i class="fas fa-edit me-1"></i>Edit
</a>
{% endif %}
</div>
<div class="btn-group">
{% if block.status == 'available' %}
<button class="btn btn-success btn-sm" onclick="addCase('{{ block.pk }}')">
<i class="fas fa-plus me-1"></i>Add Case
</button>
{% endif %}
<div class="dropdown">
<button class="btn btn-outline-secondary btn-sm dropdown-toggle"
data-bs-toggle="dropdown">
<i class="fas fa-ellipsis-v"></i>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item" href="#" onclick="duplicateBlock('{{ block.pk }}')">
<i class="fas fa-copy me-2"></i>Duplicate
</a>
</li>
<li>
<a class="dropdown-item" href="#" onclick="printBlock('{{ block.pk }}')">
<i class="fas fa-print me-2"></i>Print Schedule
</a>
</li>
<li><hr class="dropdown-divider"></li>
<li>
<a class="dropdown-item text-danger" href="#" onclick="cancelBlock('{{ block.pk }}')">
<i class="fas fa-times me-2"></i>Cancel Block
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
{% empty %}
<div class="col-12">
<div class="text-center py-5">
<i class="fas fa-calendar-times fa-3x text-muted mb-3"></i>
<h5 class="text-muted">No blocks scheduled</h5>
<p class="text-muted">No OR blocks are scheduled for the selected date and filters.</p>
<a href="{% url 'operating_theatre:or_block_create' %}" class="btn btn-primary">
<i class="fas fa-plus me-1"></i>Create First Block
</a>
</div>
</div>
{% endfor %}
</div>
<!-- Pagination -->
{% if is_paginated %}
<div class="d-flex justify-content-center mt-4">
<nav>
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page=1{% if request.GET.urlencode %}&{{ request.GET.urlencode }}{% endif %}">First</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}{% if request.GET.urlencode %}&{{ request.GET.urlencode }}{% endif %}">Previous</a>
</li>
{% endif %}
<li class="page-item active">
<span class="page-link">{{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span>
</li>
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}{% if request.GET.urlencode %}&{{ request.GET.urlencode }}{% endif %}">Next</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}{% if request.GET.urlencode %}&{{ request.GET.urlencode }}{% endif %}">Last</a>
</li>
{% endif %}
</ul>
</nav>
</div>
{% endif %}
</div>
{% endblock %}
{% block js %}
<script>
function changeDate() {
const selectedDate = document.getElementById('datePicker').value;
window.location.href = `?date=${selectedDate}`;
}
function addCase(blockId) {
window.location.href = `/operating-theatre/cases/create/?block=${blockId}`;
}
function duplicateBlock(blockId) {
if (confirm('Duplicate this block for another date?')) {
window.location.href = `/operating-theatre/blocks/${blockId}/duplicate/`;
}
}
function printBlock(blockId) {
window.open(`/operating-theatre/blocks/${blockId}/print/`, '_blank');
}
function cancelBlock(blockId) {
if (confirm('Cancel this OR block? All scheduled cases will need to be rescheduled.')) {
$.ajax({
url: `/operating-theatre/blocks/${blockId}/cancel/`,
method: 'POST',
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
success: function(response) {
if (response.success) {
location.reload();
} else {
alert('Error cancelling block: ' + response.error);
}
},
error: function() {
alert('Error cancelling block');
}
});
}
}
function exportSchedule(format) {
const currentDate = '{{ current_date|date:"Y-m-d" }}';
window.location.href = `/operating-theatre/blocks/export/?date=${currentDate}&format=${format}`;
}
// Auto-submit form on filter change
$('#filterForm select').on('change', function() {
$('#filterForm').submit();
});
</script>
{% endblock %}