Marwan Alwali 35be20ae4c update
2025-09-06 19:07:14 +03:00

654 lines
28 KiB
HTML

{% extends 'base.html' %}
{% load static %}
{% block title %}{% if form.instance.pk %}Edit Blood Request{% else %}New Blood Request{% endif %}{% endblock %}
{% block css %}
<link href="{% static 'plugins/select2/dist/css/select2.min.css' %}" rel="stylesheet" />
<link href="{% static 'plugins/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css' %}" rel="stylesheet" />
<style>
.form-section {
background: #f8f9fa;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
border-left: 4px solid #007bff;
}
.form-section h5 {
color: #007bff;
margin-bottom: 15px;
}
.required-field {
color: #dc3545;
}
.patient-info {
background: #d4edda;
border-left: 4px solid #28a745;
}
.clinical-info {
background: #d1ecf1;
border-left: 4px solid #17a2b8;
}
.urgency-info {
background: #fff3cd;
border-left: 4px solid #ffc107;
}
.emergency-alert {
background: #f8d7da;
border-left: 4px solid #dc3545;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.8; }
100% { opacity: 1; }
}
.compatibility-check {
background: #e2e3e5;
border-radius: 8px;
padding: 15px;
margin-top: 15px;
}
.availability-indicator {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 5px;
}
.available { background-color: #28a745; }
.limited { background-color: #ffc107; }
.unavailable { background-color: #dc3545; }
</style>
{% endblock %}
{% block content %}
<!-- BEGIN breadcrumb -->
<ol class="breadcrumb float-xl-end">
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Home</a></li>
<li class="breadcrumb-item"><a href="{% url 'blood_bank:dashboard' %}">Blood Bank</a></li>
<li class="breadcrumb-item"><a href="{% url 'blood_bank:blood_request_list' %}">Blood Requests</a></li>
{% if form.instance.pk %}
<li class="breadcrumb-item"><a href="{% url 'blood_bank:blood_request_detail' form.instance.id %}">{{ form.instance.request_number }}</a></li>
<li class="breadcrumb-item active">Edit</li>
{% else %}
<li class="breadcrumb-item active">New Request</li>
{% endif %}
</ol>
<!-- END breadcrumb -->
<!-- BEGIN page-header -->
<h1 class="page-header">
{% if form.instance.pk %}
Edit Blood Request <small>{{ form.instance.request_number }}</small>
{% else %}
New Blood Request <small>patient transfusion request</small>
{% endif %}
</h1>
<!-- END page-header -->
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">
<i class="fa fa-clipboard-list"></i> Blood Request Information
</h4>
<div class="panel-heading-btn">
<span class="badge bg-info">Request ID: {{ form.instance.request_number|default:'Auto-generated' }}</span>
</div>
</div>
<div class="panel-body">
<form method="post" id="bloodRequestForm">
{% csrf_token %}
<!-- BEGIN patient information -->
<div class="form-section patient-info">
<h5><i class="fa fa-user-injured"></i> Patient Information</h5>
<div class="row">
<div class="col-md-8">
<div class="form-group">
<label for="{{ form.patient.id_for_label }}" class="form-label">
Patient <span class="required-field">*</span>
</label>
<select class="form-select" name="{{ form.patient.name }}" id="{{ form.patient.id_for_label }}" required>
<option value="">Select patient...</option>
{% for patient in form.patient.field.queryset %}
<option value="{{ patient.id }}"
data-blood-group="{{ patient.blood_group.display_name|default:'Unknown' }}"
data-age="{{ patient.age }}"
data-gender="{{ patient.gender }}"
{% if form.patient.value == patient.id %}selected{% endif %}>
{{ patient.full_name }} ({{ patient.patient_id }})
</option>
{% endfor %}
</select>
{% if form.patient.errors %}
<div class="text-danger">{{ form.patient.errors.0 }}</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label class="form-label">Patient Details</label>
<div id="patientDetails" class="alert alert-info">
<i class="fa fa-info-circle"></i> Select a patient to view details
</div>
</div>
</div>
</div>
</div>
<!-- END patient information -->
<!-- BEGIN clinical information -->
<div class="form-section clinical-info">
<h5><i class="fa fa-stethoscope"></i> Clinical Information</h5>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="{{ form.requesting_physician.id_for_label }}" class="form-label">
Requesting Physician <span class="required-field">*</span>
</label>
<select class="form-select" name="{{ form.requesting_physician.name }}" id="{{ form.requesting_physician.id_for_label }}" required>
<option value="">Select physician...</option>
{% for physician in form.requesting_physician.field.queryset %}
<option value="{{ physician.id }}" {% if form.requesting_physician.value == physician.id %}selected{% endif %}>
Dr. {{ physician.get_full_name }}
</option>
{% endfor %}
</select>
{% if form.requesting_physician.errors %}
<div class="text-danger">{{ form.requesting_physician.errors.0 }}</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="{{ form.department.id_for_label }}" class="form-label">
Department <span class="required-field">*</span>
</label>
<select class="form-select" name="{{ form.department.name }}" id="{{ form.department.id_for_label }}" required>
<option value="">Select department...</option>
{% for dept in form.department.field.queryset %}
<option value="{{ dept.id }}" {% if form.department.value == dept.id %}selected{% endif %}>
{{ dept.name }}
</option>
{% endfor %}
</select>
{% if form.department.errors %}
<div class="text-danger">{{ form.department.errors.0 }}</div>
{% endif %}
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label for="{{ form.clinical_indication.id_for_label }}" class="form-label">
Clinical Indication <span class="required-field">*</span>
</label>
<textarea class="form-control" name="{{ form.clinical_indication.name }}"
id="{{ form.clinical_indication.id_for_label }}" rows="3"
placeholder="Describe the clinical reason for blood transfusion..." required>{{ form.clinical_indication.value|default:'' }}</textarea>
{% if form.clinical_indication.errors %}
<div class="text-danger">{{ form.clinical_indication.errors.0 }}</div>
{% endif %}
</div>
</div>
</div>
</div>
<!-- END clinical information -->
<!-- BEGIN blood component request -->
<div class="form-section">
<h5><i class="fa fa-tint"></i> Blood Component Request</h5>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label for="{{ form.component_requested.id_for_label }}" class="form-label">
Component Requested <span class="required-field">*</span>
</label>
<select class="form-select" name="{{ form.component_requested.name }}" id="{{ form.component_requested.id_for_label }}" required>
<option value="">Select component...</option>
{% for component in form.component_requested.field.queryset %}
<option value="{{ component.id }}" {% if form.component_requested.value == component.id %}selected{% endif %}>
{{ component.get_name_display }}
</option>
{% endfor %}
</select>
{% if form.component_requested.errors %}
<div class="text-danger">{{ form.component_requested.errors.0 }}</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="{{ form.units_requested.id_for_label }}" class="form-label">
Units Requested <span class="required-field">*</span>
</label>
<input type="number" class="form-control" name="{{ form.units_requested.name }}"
id="{{ form.units_requested.id_for_label }}"
value="{{ form.units_requested.value }}" min="1" max="10" required>
{% if form.units_requested.errors %}
<div class="text-danger">{{ form.units_requested.errors.0 }}</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label class="form-label">Availability Check</label>
<div id="availabilityCheck" class="alert alert-info">
<i class="fa fa-info-circle"></i> Select patient and component to check availability
</div>
</div>
</div>
</div>
</div>
<!-- END blood component request -->
<!-- BEGIN urgency and timing -->
<div class="form-section urgency-info" id="urgencySection">
<h5><i class="fa fa-clock"></i> Urgency & Timing</h5>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label for="{{ form.urgency.id_for_label }}" class="form-label">
Urgency Level <span class="required-field">*</span>
</label>
<select class="form-select" name="{{ form.urgency.name }}" id="{{ form.urgency.id_for_label }}" required>
<option value="">Select urgency...</option>
{% for value, label in form.urgency.field.choices %}
<option value="{{ value }}" {% if form.urgency.value == value %}selected{% endif %}>
{{ label }}
</option>
{% endfor %}
</select>
{% if form.urgency.errors %}
<div class="text-danger">{{ form.urgency.errors.0 }}</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="{{ form.required_by.id_for_label }}" class="form-label">
Required By <span class="required-field">*</span>
</label>
<input type="datetime-local" class="form-control" name="{{ form.required_by.name }}"
id="{{ form.required_by.id_for_label }}"
value="{{ form.required_by.value|date:'Y-m-d\TH:i' }}" required>
{% if form.required_by.errors %}
<div class="text-danger">{{ form.required_by.errors.0 }}</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label class="form-label">Time to Required</label>
<div id="timeToRequired" class="alert alert-info">
<i class="fa fa-clock"></i> Set required by time
</div>
</div>
</div>
</div>
</div>
<!-- END urgency and timing -->
<!-- BEGIN special requirements -->
<div class="form-section">
<h5><i class="fa fa-exclamation-circle"></i> Special Requirements</h5>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label class="form-label">Special Requirements</label>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="irradiated" id="irradiated"
{% if form.instance.special_requirements.irradiated %}checked{% endif %}>
<label class="form-check-label" for="irradiated">
Irradiated Blood Products
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="cmv_negative" id="cmvNegative"
{% if form.instance.special_requirements.cmv_negative %}checked{% endif %}>
<label class="form-check-label" for="cmvNegative">
CMV Negative
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="leukoreduced" id="leukoreduced"
{% if form.instance.special_requirements.leukoreduced %}checked{% endif %}>
<label class="form-check-label" for="leukoreduced">
Leukoreduced
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="washed" id="washed"
{% if form.instance.special_requirements.washed %}checked{% endif %}>
<label class="form-check-label" for="washed">
Washed Red Cells
</label>
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="{{ form.special_instructions.id_for_label }}" class="form-label">
Special Instructions
</label>
<textarea class="form-control" name="{{ form.special_instructions.name }}"
id="{{ form.special_instructions.id_for_label }}" rows="4"
placeholder="Any special handling or administration instructions...">{{ form.special_instructions.value|default:'' }}</textarea>
{% if form.special_instructions.errors %}
<div class="text-danger">{{ form.special_instructions.errors.0 }}</div>
{% endif %}
</div>
</div>
</div>
</div>
<!-- END special requirements -->
<!-- BEGIN compatibility check -->
<div class="compatibility-check">
<h5><i class="fa fa-shield-alt"></i> Compatibility Check</h5>
<div id="compatibilityResults">
<p class="text-muted">Select patient and component to perform compatibility check</p>
</div>
</div>
<!-- END compatibility check -->
<!-- BEGIN form actions -->
<div class="d-flex justify-content-between mt-4">
<a href="{% if form.instance.pk %}{% url 'blood_bank:blood_request_detail' form.instance.id %}{% else %}{% url 'blood_bank:blood_request_list' %}{% endif %}"
class="btn btn-secondary">
<i class="fa fa-arrow-left"></i> Cancel
</a>
<div>
<button type="button" class="btn btn-info" onclick="validateRequest()">
<i class="fa fa-check"></i> Validate Request
</button>
<button type="submit" class="btn btn-primary">
<i class="fa fa-save"></i>
{% if form.instance.pk %}Update Request{% else %}Submit Request{% endif %}
</button>
</div>
</div>
<!-- END form actions -->
</form>
</div>
</div>
<!-- END panel -->
{% endblock %}
{% block js %}
<script src="{% static 'plugins/select2/dist/js/select2.min.js' %}"></script>
<script>
$(document).ready(function() {
// Initialize Select2
$('#{{ form.patient.id_for_label }}, #{{ form.requesting_physician.id_for_label }}, #{{ form.department.id_for_label }}, #{{ form.component_requested.id_for_label }}').select2({
theme: 'bootstrap-5'
});
// Set default required by time (2 hours from now)
if (!$('#{{ form.required_by.id_for_label }}').val()) {
var defaultTime = new Date();
defaultTime.setHours(defaultTime.getHours() + 2);
$('#{{ form.required_by.id_for_label }}').val(defaultTime.toISOString().slice(0, 16));
}
// Update displays when form changes
updatePatientDetails();
updateTimeToRequired();
updateUrgencySection();
checkAvailability();
// Event listeners
$('#{{ form.patient.id_for_label }}').on('change', function() {
updatePatientDetails();
checkAvailability();
performCompatibilityCheck();
});
$('#{{ form.component_requested.id_for_label }}, #{{ form.units_requested.id_for_label }}').on('change', function() {
checkAvailability();
performCompatibilityCheck();
});
$('#{{ form.urgency.id_for_label }}').on('change', function() {
updateUrgencySection();
updateRequiredByTime();
});
$('#{{ form.required_by.id_for_label }}').on('change', function() {
updateTimeToRequired();
});
// Form validation
$('#bloodRequestForm').on('submit', function(e) {
if (!validateRequest()) {
e.preventDefault();
}
});
});
function updatePatientDetails() {
var patientId = $('#{{ form.patient.id_for_label }}').val();
if (!patientId) {
$('#patientDetails').html('<i class="fa fa-info-circle"></i> Select a patient to view details').removeClass().addClass('alert alert-info');
return;
}
var selectedOption = $('#{{ form.patient.id_for_label }} option:selected');
var bloodGroup = selectedOption.data('blood-group');
var age = selectedOption.data('age');
var gender = selectedOption.data('gender');
var detailsHtml = '<strong>Blood Group:</strong> ' + (bloodGroup || 'Unknown') + '<br>';
detailsHtml += '<strong>Age:</strong> ' + (age || 'Unknown') + '<br>';
detailsHtml += '<strong>Gender:</strong> ' + (gender || 'Unknown');
$('#patientDetails').html(detailsHtml).removeClass().addClass('alert alert-success');
}
function updateUrgencySection() {
var urgency = $('#{{ form.urgency.id_for_label }}').val();
var section = $('#urgencySection');
section.removeClass('urgency-info emergency-alert');
if (urgency === 'emergency') {
section.addClass('emergency-alert');
} else {
section.addClass('urgency-info');
}
}
function updateRequiredByTime() {
var urgency = $('#{{ form.urgency.id_for_label }}').val();
var now = new Date();
var requiredBy;
switch(urgency) {
case 'emergency':
requiredBy = new Date(now.getTime() + 30 * 60000); // 30 minutes
break;
case 'urgent':
requiredBy = new Date(now.getTime() + 2 * 60 * 60000); // 2 hours
break;
case 'routine':
requiredBy = new Date(now.getTime() + 24 * 60 * 60000); // 24 hours
break;
default:
return;
}
$('#{{ form.required_by.id_for_label }}').val(requiredBy.toISOString().slice(0, 16));
updateTimeToRequired();
}
function updateTimeToRequired() {
var requiredBy = $('#{{ form.required_by.id_for_label }}').val();
if (!requiredBy) {
$('#timeToRequired').html('<i class="fa fa-clock"></i> Set required by time').removeClass().addClass('alert alert-info');
return;
}
var now = new Date();
var required = new Date(requiredBy);
var diffMs = required - now;
var diffHours = Math.floor(diffMs / (1000 * 60 * 60));
var diffMinutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
var alertClass = 'alert-info';
var icon = 'fa-clock';
if (diffMs < 0) {
$('#timeToRequired').html('<i class="fa fa-exclamation-triangle"></i> Overdue!').removeClass().addClass('alert alert-danger');
} else if (diffHours < 1) {
alertClass = 'alert-danger';
icon = 'fa-exclamation-triangle';
$('#timeToRequired').html('<i class="fa ' + icon + '"></i> ' + diffMinutes + ' minutes remaining').removeClass().addClass('alert ' + alertClass);
} else if (diffHours < 4) {
alertClass = 'alert-warning';
$('#timeToRequired').html('<i class="fa ' + icon + '"></i> ' + diffHours + ' hours, ' + diffMinutes + ' minutes').removeClass().addClass('alert ' + alertClass);
} else {
$('#timeToRequired').html('<i class="fa ' + icon + '"></i> ' + diffHours + ' hours, ' + diffMinutes + ' minutes').removeClass().addClass('alert ' + alertClass);
}
}
function checkAvailability() {
var patientId = $('#{{ form.patient.id_for_label }}').val();
var componentId = $('#{{ form.component_requested.id_for_label }}').val();
var unitsRequested = $('#{{ form.units_requested.id_for_label }}').val();
if (!patientId || !componentId) {
$('#availabilityCheck').html('<i class="fa fa-info-circle"></i> Select patient and component to check availability').removeClass().addClass('alert alert-info');
return;
}
// Simulate availability check (in real implementation, this would be an AJAX call)
var availableUnits = Math.floor(Math.random() * 10) + 1;
var requested = parseInt(unitsRequested) || 1;
var indicator, message, alertClass;
if (availableUnits >= requested) {
indicator = '<span class="availability-indicator available"></span>';
message = availableUnits + ' units available';
alertClass = 'alert-success';
} else if (availableUnits > 0) {
indicator = '<span class="availability-indicator limited"></span>';
message = 'Only ' + availableUnits + ' units available (need ' + requested + ')';
alertClass = 'alert-warning';
} else {
indicator = '<span class="availability-indicator unavailable"></span>';
message = 'No units available';
alertClass = 'alert-danger';
}
$('#availabilityCheck').html(indicator + message).removeClass().addClass('alert ' + alertClass);
}
function performCompatibilityCheck() {
var patientId = $('#{{ form.patient.id_for_label }}').val();
var componentId = $('#{{ form.component_requested.id_for_label }}').val();
if (!patientId || !componentId) {
$('#compatibilityResults').html('<p class="text-muted">Select patient and component to perform compatibility check</p>');
return;
}
var selectedPatient = $('#{{ form.patient.id_for_label }} option:selected');
var patientBloodGroup = selectedPatient.data('blood-group');
if (!patientBloodGroup || patientBloodGroup === 'Unknown') {
$('#compatibilityResults').html('<div class="alert alert-warning"><i class="fa fa-exclamation-triangle"></i> Patient blood group unknown - type and screen required</div>');
return;
}
// Simulate compatibility check
var compatible = Math.random() > 0.1; // 90% compatibility rate
if (compatible) {
$('#compatibilityResults').html('<div class="alert alert-success"><i class="fa fa-check-circle"></i> Compatible blood group (' + patientBloodGroup + ') - crossmatch required before transfusion</div>');
} else {
$('#compatibilityResults').html('<div class="alert alert-danger"><i class="fa fa-times-circle"></i> Potential compatibility issues detected - additional testing required</div>');
}
}
function validateRequest() {
var errors = [];
// Check required fields
if (!$('#{{ form.patient.id_for_label }}').val()) {
errors.push('Please select a patient');
}
if (!$('#{{ form.requesting_physician.id_for_label }}').val()) {
errors.push('Please select requesting physician');
}
if (!$('#{{ form.component_requested.id_for_label }}').val()) {
errors.push('Please select blood component');
}
if (!$('#{{ form.units_requested.id_for_label }}').val()) {
errors.push('Please specify number of units');
}
if (!$('#{{ form.urgency.id_for_label }}').val()) {
errors.push('Please select urgency level');
}
if (!$('#{{ form.clinical_indication.id_for_label }}').val().trim()) {
errors.push('Please provide clinical indication');
}
// Check timing
var requiredBy = $('#{{ form.required_by.id_for_label }}').val();
if (requiredBy) {
var required = new Date(requiredBy);
var now = new Date();
if (required <= now) {
errors.push('Required by time must be in the future');
}
}
if (errors.length > 0) {
Swal.fire({
icon: 'error',
title: 'Validation Errors',
html: '<ul class="text-start"><li>' + errors.join('</li><li>') + '</li></ul>',
confirmButtonText: 'OK'
});
return false;
}
Swal.fire({
icon: 'success',
title: 'Validation Passed',
text: 'Blood request is ready for submission.',
timer: 1500,
showConfirmButton: false
});
return true;
}
// Update time display every minute
setInterval(updateTimeToRequired, 60000);
</script>
{% endblock %}