2025-08-12 13:33:25 +03:00

742 lines
35 KiB
HTML

{% 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="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-info-circle me-2"></i>Basic Information
</h5>
</div>
<div class="card-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="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-ruler-combined me-2"></i>Physical Characteristics
</h5>
</div>
<div class="card-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="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-thermometer-half me-2"></i>Environmental Controls
</h5>
</div>
<div class="card-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="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-cogs me-2"></i>Surgical Capabilities
</h5>
</div>
<div class="card-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="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-clock me-2"></i>Scheduling Configuration
</h5>
</div>
<div class="card-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">
<!-- Form Actions -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-save me-2"></i>Actions
</h5>
</div>
<div class="card-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 Panel -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-question-circle me-2"></i>Help & Guidelines
</h5>
</div>
<div class="card-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> 18-26°C typical range</li>
<li><strong>Humidity:</strong> 30-60% 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> 15-45 minutes typical</li>
<li><strong>Cleaning:</strong> 30-60 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="card" id="validationSummary" style="display: none;">
<div class="card-header bg-danger text-white">
<h5 class="mb-0">
<i class="fas fa-exclamation-triangle me-2"></i>Validation Errors
</h5>
</div>
<div class="card-body">
<ul id="errorList" class="list-unstyled mb-0"></ul>
</div>
</div>
</div>
</div>
</form>
</div>
<script>
// 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.value.trim()) {
errors.push(`${field.replace('_', ' ')} is required`);
}
});
// Temperature range validation
const tempMin = parseFloat(document.getElementById('id_temperature_min').value);
const tempMax = parseFloat(document.getElementById('id_temperature_max').value);
if (tempMin >= tempMax) {
errors.push('Maximum temperature must be greater than minimum temperature');
}
// Humidity range validation
const humidityMin = parseFloat(document.getElementById('id_humidity_min').value);
const humidityMax = parseFloat(document.getElementById('id_humidity_max').value);
if (humidityMin >= humidityMax) {
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(error => {
const li = document.createElement('li');
li.innerHTML = `<i class="fas fa-times text-danger me-2"></i>${error}`;
errorList.appendChild(li);
});
validationSummary.style.display = 'block';
validationSummary.scrollIntoView({ behavior: 'smooth' });
}
// Auto-save functionality
function saveDraft() {
const formData = new FormData(document.getElementById('roomForm'));
formData.append('save_draft', 'true');
fetch(window.location.href, {
method: 'POST',
body: formData,
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Draft saved successfully');
} else {
alert('Error saving draft');
}
})
.catch(error => {
console.error('Error:', error);
alert('Error saving draft');
});
}
// Real-time validation feedback
document.querySelectorAll('input, select').forEach(input => {
input.addEventListener('blur', function() {
validateField(this);
});
});
function validateField(field) {
const value = field.value.trim();
const fieldName = field.name;
// Remove existing validation classes
field.classList.remove('is-valid', 'is-invalid');
// Basic validation
if (field.hasAttribute('required') && !value) {
field.classList.add('is-invalid');
return false;
}
// Specific validations
if (fieldName === 'room_number' && value) {
// Check for valid room number format
if (!/^[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
document.getElementById('id_room_type').addEventListener('change', function() {
const roomType = this.value;
updateCapabilitiesForRoomType(roomType);
});
function updateCapabilitiesForRoomType(roomType) {
// Set default capabilities based on room type
const capabilities = {
'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 (capabilities[roomType]) {
Object.keys(capabilities[roomType]).forEach(field => {
const input = document.getElementById(`id_${field}`);
if (input) {
if (input.type === 'checkbox') {
input.checked = capabilities[roomType][field];
} else {
input.value = capabilities[roomType][field];
}
}
});
}
}
// Keyboard shortcuts
document.addEventListener('keydown', function(e) {
if (e.ctrlKey || e.metaKey) {
switch(e.key) {
case 's':
e.preventDefault();
document.getElementById('roomForm').submit();
break;
case 'd':
e.preventDefault();
saveDraft();
break;
}
}
});
</script>
<style>
.form-control, .form-select {
border-radius: 0.375rem;
border: 1px solid #ced4da;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
.form-control:focus, .form-select:focus {
border-color: #86b7fe;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.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;
}
.card-header h5 {
color: #495057;
}
@media (max-width: 768px) {
.col-md-6, .col-md-4 {
margin-bottom: 1rem;
}
.btn-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
}
</style>
{% endblock %}