Marwan Alwali 610e165e17 update
2025-09-04 19:19:52 +03:00

567 lines
27 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "base.html" %}
{% load static %}
{% block title %}{% if object %}Edit{% else %}Add{% endif %} Operating Room - {{ 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-procedures me-2"></i>{% if object %}Edit{% else %}Add{% endif %} Operating Room
</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 'operating_theatre:dashboard' %}">Operating Theatre</a></li>
<li class="breadcrumb-item"><a href="{% url 'operating_theatre:operating_room_list' %}">Operating Rooms</a></li>
{% if object %}
<li class="breadcrumb-item"><a href="{% url 'operating_theatre:operating_room_detail' object.pk %}">{{ object.room_number }}</a></li>
<li class="breadcrumb-item active">Edit</li>
{% else %}
<li class="breadcrumb-item active">Add</li>
{% endif %}
</ol>
</nav>
</div>
<div class="btn-group">
<a href="{% if object %}{% url 'operating_theatre:operating_room_detail' object.pk %}{% else %}{% url 'operating_theatre:operating_room_list' %}{% endif %}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-2"></i>Back
</a>
{% if object %}
<a href="{% url 'operating_theatre:operating_room_list' %}" class="btn btn-outline-primary">
<i class="fas fa-list me-2"></i>All Rooms
</a>
{% endif %}
</div>
</div>
<form method="post" id="roomForm" novalidate>
{% csrf_token %}
<div class="row">
<!-- Main Form -->
<div class="col-lg-8">
<!-- Basic Information -->
<div class="panel panel-inverse mb-4">
<div class="panel-heading">
<h4 class="panel-title"><i class="fas fa-info-circle me-2"></i>Basic Information</h4>
<div class="panel-heading-btn">
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
</div>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-6 mb-3">
<label for="{{ form.room_number.id_for_label }}" class="form-label">Room Number <span class="text-danger">*</span></label>
{{ form.room_number }}
{% if form.room_number.errors %}<div class="invalid-feedback d-block">{{ form.room_number.errors.0 }}</div>{% endif %}
<div class="form-text">Unique identifier for the operating room</div>
</div>
<div class="col-md-6 mb-3">
<label for="{{ form.room_name.id_for_label }}" class="form-label">Room Name <span class="text-danger">*</span></label>
{{ form.room_name }}
{% if form.room_name.errors %}<div class="invalid-feedback d-block">{{ form.room_name.errors.0 }}</div>{% endif %}
<div class="form-text">Descriptive name for the operating room</div>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="{{ form.room_type.id_for_label }}" class="form-label">Room Type <span class="text-danger">*</span></label>
{{ form.room_type }}
{% if form.room_type.errors %}<div class="invalid-feedback d-block">{{ form.room_type.errors.0 }}</div>{% endif %}
<div class="form-text">Primary surgical specialty for this room</div>
</div>
<div class="col-md-6 mb-3">
<label for="{{ form.status.id_for_label }}" class="form-label">Status <span class="text-danger">*</span></label>
{{ form.status }}
{% if form.status.errors %}<div class="invalid-feedback d-block">{{ form.status.errors.0 }}</div>{% endif %}
<div class="form-text">Current operational status</div>
</div>
</div>
</div>
</div>
<!-- Physical Characteristics -->
<div class="panel panel-inverse mb-4">
<div class="panel-heading">
<h4 class="panel-title"><i class="fas fa-ruler-combined me-2"></i>Physical Characteristics</h4>
<div class="panel-heading-btn">
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
</div>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-4 mb-3">
<label for="{{ form.floor_number.id_for_label }}" class="form-label">Floor Number <span class="text-danger">*</span></label>
{{ form.floor_number }}
{% if form.floor_number.errors %}<div class="invalid-feedback d-block">{{ form.floor_number.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-4 mb-3">
<label for="{{ form.building.id_for_label }}" class="form-label">Building</label>
{{ form.building }}
{% if form.building.errors %}<div class="invalid-feedback d-block">{{ form.building.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-4 mb-3">
<label for="{{ form.wing.id_for_label }}" class="form-label">Wing/Section</label>
{{ form.wing }}
{% if form.wing.errors %}<div class="invalid-feedback d-block">{{ form.wing.errors.0 }}</div>{% endif %}
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="{{ form.room_size.id_for_label }}" class="form-label">Room Size (m²)</label>
{{ form.room_size }}
{% if form.room_size.errors %}<div class="invalid-feedback d-block">{{ form.room_size.errors.0 }}</div>{% endif %}
<div class="form-text">Room area in square meters</div>
</div>
<div class="col-md-6 mb-3">
<label for="{{ form.ceiling_height.id_for_label }}" class="form-label">Ceiling Height (m)</label>
{{ form.ceiling_height }}
{% if form.ceiling_height.errors %}<div class="invalid-feedback d-block">{{ form.ceiling_height.errors.0 }}</div>{% endif %}
<div class="form-text">Ceiling height in meters</div>
</div>
</div>
</div>
</div>
<!-- Environmental Controls -->
<div class="panel panel-inverse mb-4">
<div class="panel-heading">
<h4 class="panel-title"><i class="fas fa-thermometer-half me-2"></i>Environmental Controls</h4>
<div class="panel-heading-btn">
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
</div>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">Temperature Range (°C)</label>
<div class="row">
<div class="col-6">
<label for="{{ form.temperature_min.id_for_label }}" class="form-label small">Minimum</label>
{{ form.temperature_min }}
{% if form.temperature_min.errors %}<div class="invalid-feedback d-block">{{ form.temperature_min.errors.0 }}</div>{% endif %}
</div>
<div class="col-6">
<label for="{{ form.temperature_max.id_for_label }}" class="form-label small">Maximum</label>
{{ form.temperature_max }}
{% if form.temperature_max.errors %}<div class="invalid-feedback d-block">{{ form.temperature_max.errors.0 }}</div>{% endif %}
</div>
</div>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Humidity Range (%)</label>
<div class="row">
<div class="col-6">
<label for="{{ form.humidity_min.id_for_label }}" class="form-label small">Minimum</label>
{{ form.humidity_min }}
{% if form.humidity_min.errors %}<div class="invalid-feedback d-block">{{ form.humidity_min.errors.0 }}</div>{% endif %}
</div>
<div class="col-6">
<label for="{{ form.humidity_max.id_for_label }}" class="form-label small">Maximum</label>
{{ form.humidity_max }}
{% if form.humidity_max.errors %}<div class="invalid-feedback d-block">{{ form.humidity_max.errors.0 }}</div>{% endif %}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="{{ form.air_changes_per_hour.id_for_label }}" class="form-label">Air Changes per Hour</label>
{{ form.air_changes_per_hour }}
{% if form.air_changes_per_hour.errors %}<div class="invalid-feedback d-block">{{ form.air_changes_per_hour.errors.0 }}</div>{% endif %}
<div class="form-text">Recommended: 20+ for operating rooms</div>
</div>
<div class="col-md-6 mb-3">
<label class="form-label">Room Pressure</label>
<div class="form-check">
{{ form.positive_pressure }}
<label for="{{ form.positive_pressure.id_for_label }}" class="form-check-label">Positive Pressure Room</label>
</div>
<div class="form-text">Most ORs should maintain positive pressure</div>
</div>
</div>
</div>
</div>
<!-- Capabilities -->
<div class="panel panel-inverse mb-4">
<div class="panel-heading">
<h4 class="panel-title"><i class="fas fa-cogs me-2"></i>Surgical Capabilities</h4>
<div class="panel-heading-btn">
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
</div>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-6">
<h6 class="fw-semibold mb-3">Surgical Approaches</h6>
<div class="form-check mb-2">
{{ form.supports_robotic }}
<label for="{{ form.supports_robotic.id_for_label }}" class="form-check-label">Robotic Surgery</label>
</div>
<div class="form-check mb-2">
{{ form.supports_laparoscopic }}
<label for="{{ form.supports_laparoscopic.id_for_label }}" class="form-check-label">Laparoscopic Surgery</label>
</div>
<div class="form-check mb-2">
{{ form.supports_microscopy }}
<label for="{{ form.supports_microscopy.id_for_label }}" class="form-check-label">Surgical Microscopy</label>
</div>
<div class="form-check mb-3">
{{ form.supports_laser }}
<label for="{{ form.supports_laser.id_for_label }}" class="form-check-label">Laser Surgery</label>
</div>
</div>
<div class="col-md-6">
<h6 class="fw-semibold mb-3">Imaging Capabilities</h6>
<div class="form-check mb-2">
{{ form.has_c_arm }}
<label for="{{ form.has_c_arm.id_for_label }}" class="form-check-label">C-Arm Fluoroscopy</label>
</div>
<div class="form-check mb-2">
{{ form.has_ct }}
<label for="{{ form.has_ct.id_for_label }}" class="form-check-label">Intraoperative CT</label>
</div>
<div class="form-check mb-2">
{{ form.has_mri }}
<label for="{{ form.has_mri.id_for_label }}" class="form-check-label">Intraoperative MRI</label>
</div>
<div class="form-check mb-2">
{{ form.has_ultrasound }}
<label for="{{ form.has_ultrasound.id_for_label }}" class="form-check-label">Ultrasound</label>
</div>
<div class="form-check mb-3">
{{ form.has_neuromonitoring }}
<label for="{{ form.has_neuromonitoring.id_for_label }}" class="form-check-label">Neuromonitoring</label>
</div>
</div>
</div>
</div>
</div>
<!-- Scheduling Configuration -->
<div class="panel panel-inverse mb-4">
<div class="panel-heading">
<h4 class="panel-title"><i class="fas fa-clock me-2"></i>Scheduling Configuration</h4>
<div class="panel-heading-btn">
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
</div>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-4 mb-3">
<label for="{{ form.max_case_duration.id_for_label }}" class="form-label">Max Case Duration (minutes)</label>
{{ form.max_case_duration }}
{% if form.max_case_duration.errors %}<div class="invalid-feedback d-block">{{ form.max_case_duration.errors.0 }}</div>{% endif %}
<div class="form-text">Maximum allowed case duration</div>
</div>
<div class="col-md-4 mb-3">
<label for="{{ form.turnover_time.id_for_label }}" class="form-label">Turnover Time (minutes)</label>
{{ form.turnover_time }}
{% if form.turnover_time.errors %}<div class="invalid-feedback d-block">{{ form.turnover_time.errors.0 }}</div>{% endif %}
<div class="form-text">Standard time between cases</div>
</div>
<div class="col-md-4 mb-3">
<label for="{{ form.cleaning_time.id_for_label }}" class="form-label">Deep Cleaning Time (minutes)</label>
{{ form.cleaning_time }}
{% if form.cleaning_time.errors %}<div class="invalid-feedback d-block">{{ form.cleaning_time.errors.0 }}</div>{% endif %}
<div class="form-text">Time required for deep cleaning</div>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="{{ form.required_nurses.id_for_label }}" class="form-label">Required Nurses</label>
{{ form.required_nurses }}
{% if form.required_nurses.errors %}<div class="invalid-feedback d-block">{{ form.required_nurses.errors.0 }}</div>{% endif %}
<div class="form-text">Minimum nursing staff required</div>
</div>
<div class="col-md-6 mb-3">
<label for="{{ form.required_techs.id_for_label }}" class="form-label">Required Technicians</label>
{{ form.required_techs }}
{% if form.required_techs.errors %}<div class="invalid-feedback d-block">{{ form.required_techs.errors.0 }}</div>{% endif %}
<div class="form-text">Minimum technical staff required</div>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<div class="form-check">
{{ form.is_active }}
<label for="{{ form.is_active.id_for_label }}" class="form-check-label">Room is Active</label>
</div>
<div class="form-text">Available for scheduling</div>
</div>
<div class="col-md-6 mb-3">
<div class="form-check">
{{ form.accepts_emergency }}
<label for="{{ form.accepts_emergency.id_for_label }}" class="form-check-label">Accepts Emergency Cases</label>
</div>
<div class="form-text">Can be used for emergency procedures</div>
</div>
</div>
</div>
</div>
</div>
<!-- Sidebar -->
<div class="col-lg-4">
<!-- Actions -->
<div class="panel panel-inverse mb-4">
<div class="panel-heading">
<h4 class="panel-title"><i class="fas fa-save me-2"></i>Actions</h4>
<div class="panel-heading-btn">
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
</div>
</div>
<div class="panel-body">
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-2"></i>{% if object %}Update{% else %}Create{% endif %} Operating Room
</button>
<button type="button" class="btn btn-outline-secondary" onclick="saveDraft()">
<i class="fas fa-file-alt me-2"></i>Save as Draft
</button>
<a href="{% if object %}{% url 'operating_theatre:operating_room_detail' object.pk %}{% else %}{% url 'operating_theatre:operating_room_list' %}{% endif %}" class="btn btn-outline-danger">
<i class="fas fa-times me-2"></i>Cancel
</a>
</div>
</div>
</div>
<!-- Help & Guidelines -->
<div class="panel panel-inverse mb-4">
<div class="panel-heading">
<h4 class="panel-title"><i class="fas fa-question-circle me-2"></i>Help & Guidelines</h4>
<div class="panel-heading-btn">
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
</div>
</div>
<div class="panel-body">
<div class="accordion" id="helpAccordion">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#roomTypes">Room Types</button>
</h2>
<div id="roomTypes" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
<div class="accordion-body small">
<ul class="list-unstyled">
<li><strong>General:</strong> Multi-purpose surgical procedures</li>
<li><strong>Cardiac:</strong> Heart and vascular surgery</li>
<li><strong>Neuro:</strong> Brain and spine surgery</li>
<li><strong>Trauma:</strong> Emergency trauma cases</li>
<li><strong>Robotic:</strong> Robot-assisted procedures</li>
</ul>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#environmental">Environmental Standards</button>
</h2>
<div id="environmental" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
<div class="accordion-body small">
<ul class="list-unstyled">
<li><strong>Temperature:</strong> 1826°C typical range</li>
<li><strong>Humidity:</strong> 3060% recommended</li>
<li><strong>Air Changes:</strong> Minimum 20 per hour</li>
<li><strong>Pressure:</strong> Positive pressure preferred</li>
</ul>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#scheduling">Scheduling Guidelines</button>
</h2>
<div id="scheduling" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
<div class="accordion-body small">
<ul class="list-unstyled">
<li><strong>Turnover:</strong> 1545 minutes typical</li>
<li><strong>Cleaning:</strong> 3060 minutes for deep clean</li>
<li><strong>Staffing:</strong> Minimum 2 nurses, 1 tech</li>
<li><strong>Max Duration:</strong> 8 hours typical limit</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Validation Summary -->
<div class="panel panel-inverse" id="validationSummary" style="display: none;">
<div class="panel-heading bg-danger text-white">
<h4 class="panel-title"><i class="fas fa-exclamation-triangle me-2"></i>Validation Errors</h4>
</div>
<div class="panel-body">
<ul id="errorList" class="list-unstyled mb-0"></ul>
</div>
</div>
</div>
</div>
</form>
</div>
<script>
// CSRF from cookie (aligns with detail template pattern)
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
// Form validation
document.getElementById('roomForm').addEventListener('submit', function(e) {
const errors = [];
// Required field validation
const requiredFields = ['room_number', 'room_name', 'room_type', 'floor_number'];
requiredFields.forEach(field => {
const input = document.getElementById(`id_${field}`);
if (input && !String(input.value || '').trim()) {
errors.push(`${field.replace('_', ' ')} is required`);
}
});
// Temperature range validation
const tmin = parseFloat(document.getElementById('id_temperature_min').value);
const tmax = parseFloat(document.getElementById('id_temperature_max').value);
if (!isNaN(tmin) && !isNaN(tmax) && tmin >= tmax) {
errors.push('Maximum temperature must be greater than minimum temperature');
}
// Humidity range validation
const hmin = parseFloat(document.getElementById('id_humidity_min').value);
const hmax = parseFloat(document.getElementById('id_humidity_max').value);
if (!isNaN(hmin) && !isNaN(hmax) && hmin >= hmax) {
errors.push('Maximum humidity must be greater than minimum humidity');
}
if (errors.length > 0) {
e.preventDefault();
showValidationErrors(errors);
}
});
function showValidationErrors(errors) {
const errorList = document.getElementById('errorList');
const validationSummary = document.getElementById('validationSummary');
errorList.innerHTML = '';
errors.forEach(err => {
const li = document.createElement('li');
li.innerHTML = `<i class="fas fa-times text-danger me-2"></i>${err}`;
errorList.appendChild(li);
});
validationSummary.style.display = 'block';
validationSummary.scrollIntoView({ behavior: 'smooth' });
}
// Save draft (uses cookie CSRF, no reliance on hidden input)
function saveDraft() {
const form = document.getElementById('roomForm');
const formData = new FormData(form);
formData.append('save_draft', 'true');
fetch(window.location.href, {
method: 'POST',
body: formData,
headers: { 'X-CSRFToken': getCookie('csrftoken') }
})
.then(r => r.json())
.then(data => alert(data && data.success ? 'Draft saved successfully' : 'Error saving draft'))
.catch(() => alert('Error saving draft'));
}
// Real-time validation feedback
document.querySelectorAll('input, select').forEach(el => {
el.addEventListener('blur', () => validateField(el));
});
function validateField(field) {
const value = String(field.value || '').trim();
field.classList.remove('is-valid', 'is-invalid');
if (field.hasAttribute('required') && !value) {
field.classList.add('is-invalid');
return false;
}
if (field.name === 'room_number' && value && !/^[A-Z0-9\-]+$/i.test(value)) {
field.classList.add('is-invalid');
return false;
}
if (value) field.classList.add('is-valid');
return true;
}
// Dynamic form updates by room type
const roomTypeEl = document.getElementById('id_room_type');
if (roomTypeEl) {
roomTypeEl.addEventListener('change', function() {
const roomType = this.value;
const caps = {
'CARDIAC': { 'has_c_arm': true, 'supports_microscopy': true, 'required_nurses': 3, 'required_techs': 2 },
'NEURO': { 'supports_microscopy': true, 'has_neuromonitoring': true, 'required_nurses': 3, 'required_techs': 2 },
'ROBOTIC': { 'supports_robotic': true, 'supports_laparoscopic': true, 'required_nurses': 3, 'required_techs': 3 },
'TRAUMA': { 'has_c_arm': true, 'accepts_emergency': true, 'required_nurses': 4, 'required_techs': 2 }
};
if (caps[roomType]) {
Object.keys(caps[roomType]).forEach(field => {
const input = document.getElementById(`id_${field}`);
if (!input) return;
if (input.type === 'checkbox') input.checked = !!caps[roomType][field];
else input.value = caps[roomType][field];
});
}
});
}
// Keyboard shortcuts
document.addEventListener('keydown', function(e) {
if (e.ctrlKey || e.metaKey) {
if (e.key === 's') { e.preventDefault(); document.getElementById('roomForm').submit(); }
if (e.key === 'd') { e.preventDefault(); saveDraft(); }
}
});
</script>
<style>
/* Inputs */
.form-control, .form-select {
border-radius: 0.375rem;
border: 1px solid #ced4da;
transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
}
.form-control:focus, .form-select:focus {
border-color: #86b7fe;
box-shadow: 0 0 0 .25rem rgba(13,110,253,.25);
}
.is-valid { border-color: #198754; }
.is-invalid { border-color: #dc3545; }
.form-check-input:checked { background-color: #0d6efd; border-color: #0d6efd; }
.accordion-button:not(.collapsed) { background-color: #e7f1ff; color: #0c63e4; }
/* Panel theme tweaks to mirror detail page */
.panel-title { color: #495057; }
.progress { background-color: #e9ecef; }
.badge { font-size: .75rem; }
.table-borderless td { padding: .5rem 0; }
/* Print: target panels, not cards */
@media print {
.btn, .dropdown, nav, .panel-heading .fa-cog { display: none !important; }
.panel { border: 1px solid #dee2e6 !important; box-shadow: none !important; }
}
/* Responsive */
@media (max-width: 768px) {
.col-md-6, .col-md-4 { margin-bottom: 1rem; }
.btn-group { display: flex; flex-direction: column; gap: .5rem; }
}
</style>
{% endblock %}