742 lines
35 KiB
HTML
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 %}
|
|
|