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

572 lines
25 KiB
HTML

{% extends 'base.html' %}
{% load static %}
{% block title %}{% if form.instance.pk %}Edit Blood Unit{% else %}Register Blood Unit{% 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;
}
.unit-preview {
background: #e9ecef;
border-radius: 8px;
padding: 15px;
margin-top: 15px;
}
.barcode-preview {
font-family: 'Courier New', monospace;
font-size: 1.2em;
font-weight: bold;
text-align: center;
padding: 10px;
background: white;
border: 2px dashed #6c757d;
border-radius: 4px;
}
.collection-info {
background: #d4edda;
border-left: 4px solid #28a745;
}
.component-info {
background: #d1ecf1;
border-left: 4px solid #17a2b8;
}
.storage-info {
background: #fff3cd;
border-left: 4px solid #ffc107;
}
</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_unit_list' %}">Blood Units</a></li>
{% if form.instance.pk %}
<li class="breadcrumb-item"><a href="{% url 'blood_bank:blood_unit_detail' form.instance.id %}">{{ form.instance.unit_number }}</a></li>
<li class="breadcrumb-item active">Edit</li>
{% else %}
<li class="breadcrumb-item active">Register New Unit</li>
{% endif %}
</ol>
<!-- END breadcrumb -->
<!-- BEGIN page-header -->
<h1 class="page-header">
{% if form.instance.pk %}
Edit Blood Unit <small>{{ form.instance.unit_number }}</small>
{% else %}
Register Blood Unit <small>new collection</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-tint"></i> Blood Unit Information
</h4>
<div class="panel-heading-btn">
{% if donor %}
<span class="badge bg-info">Donor: {{ donor.full_name }}</span>
{% endif %}
</div>
</div>
<div class="panel-body">
<form method="post" id="bloodUnitForm">
{% csrf_token %}
<!-- BEGIN donor selection -->
{% if not donor %}
<div class="form-section collection-info">
<h5><i class="fa fa-user"></i> Donor Selection</h5>
<div class="row">
<div class="col-md-8">
<div class="form-group">
<label for="{{ form.donor.id_for_label }}" class="form-label">
Donor <span class="required-field">*</span>
</label>
<select class="form-select" name="{{ form.donor.name }}" id="{{ form.donor.id_for_label }}" required>
<option value="">Select donor...</option>
{% for donor_option in form.donor.field.queryset %}
<option value="{{ donor_option.id }}"
data-blood-group="{{ donor_option.blood_group.display_name }}"
data-last-donation="{{ donor_option.last_donation_date|date:'Y-m-d' }}"
{% if form.donor.value == donor_option.id %}selected{% endif %}>
{{ donor_option.full_name }} ({{ donor_option.donor_id }}) - {{ donor_option.blood_group.display_name }}
</option>
{% endfor %}
</select>
{% if form.donor.errors %}
<div class="text-danger">{{ form.donor.errors.0 }}</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label class="form-label">Donor Eligibility</label>
<div id="donorEligibility" class="alert alert-info">
<i class="fa fa-info-circle"></i> Select a donor to check eligibility
</div>
</div>
</div>
</div>
</div>
{% else %}
<input type="hidden" name="{{ form.donor.name }}" value="{{ donor.id }}">
<div class="form-section collection-info">
<h5><i class="fa fa-user"></i> Donor Information</h5>
<div class="row">
<div class="col-md-6">
<p><strong>Name:</strong> {{ donor.full_name }}</p>
<p><strong>Donor ID:</strong> {{ donor.donor_id }}</p>
<p><strong>Blood Group:</strong> <span class="badge bg-primary">{{ donor.blood_group.display_name }}</span></p>
</div>
<div class="col-md-6">
<p><strong>Last Donation:</strong> {{ donor.last_donation_date|date:"M d, Y"|default:"Never" }}</p>
<p><strong>Total Donations:</strong> {{ donor.total_donations }}</p>
<p><strong>Status:</strong>
<span class="badge bg-{% if donor.is_eligible %}success{% else %}warning{% endif %}">
{% if donor.is_eligible %}Eligible{% else %}Check Required{% endif %}
</span>
</p>
</div>
</div>
</div>
{% endif %}
<!-- END donor selection -->
<!-- BEGIN collection details -->
<div class="form-section collection-info">
<h5><i class="fa fa-calendar"></i> Collection Details</h5>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label for="{{ form.collection_date.id_for_label }}" class="form-label">
Collection Date <span class="required-field">*</span>
</label>
<input type="date" class="form-control" name="{{ form.collection_date.name }}"
id="{{ form.collection_date.id_for_label }}"
value="{{ form.collection_date.value|date:'Y-m-d' }}" required>
{% if form.collection_date.errors %}
<div class="text-danger">{{ form.collection_date.errors.0 }}</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="{{ form.collection_time.id_for_label }}" class="form-label">
Collection Time <span class="required-field">*</span>
</label>
<input type="time" class="form-control" name="{{ form.collection_time.name }}"
id="{{ form.collection_time.id_for_label }}"
value="{{ form.collection_time.value|time:'H:i' }}" required>
{% if form.collection_time.errors %}
<div class="text-danger">{{ form.collection_time.errors.0 }}</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="{{ form.collected_by.id_for_label }}" class="form-label">
Collected By <span class="required-field">*</span>
</label>
<select class="form-select" name="{{ form.collected_by.name }}" id="{{ form.collected_by.id_for_label }}" required>
<option value="">Select staff member...</option>
{% for staff in form.collected_by.field.queryset %}
<option value="{{ staff.id }}" {% if form.collected_by.value == staff.id %}selected{% endif %}>
{{ staff.get_full_name }}
</option>
{% endfor %}
</select>
{% if form.collected_by.errors %}
<div class="text-danger">{{ form.collected_by.errors.0 }}</div>
{% endif %}
</div>
</div>
</div>
</div>
<!-- END collection details -->
<!-- BEGIN component and volume -->
<div class="form-section component-info">
<h5><i class="fa fa-flask"></i> Component & Volume</h5>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label for="{{ form.component.id_for_label }}" class="form-label">
Component <span class="required-field">*</span>
</label>
<select class="form-select" name="{{ form.component.name }}" id="{{ form.component.id_for_label }}" required>
<option value="">Select component...</option>
{% for component in form.component.field.queryset %}
<option value="{{ component.id }}"
data-shelf-life="{{ component.shelf_life_days }}"
data-storage-temp="{{ component.storage_temperature }}"
{% if form.component.value == component.id %}selected{% endif %}>
{{ component.get_name_display }}
</option>
{% endfor %}
</select>
{% if form.component.errors %}
<div class="text-danger">{{ form.component.errors.0 }}</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="{{ form.volume_ml.id_for_label }}" class="form-label">
Volume (ml) <span class="required-field">*</span>
</label>
<input type="number" class="form-control" name="{{ form.volume_ml.name }}"
id="{{ form.volume_ml.id_for_label }}"
value="{{ form.volume_ml.value }}" min="1" max="1000" required>
{% if form.volume_ml.errors %}
<div class="text-danger">{{ form.volume_ml.errors.0 }}</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label class="form-label">Calculated Expiry Date</label>
<input type="text" class="form-control" id="calculatedExpiry" readonly
placeholder="Select component to calculate">
</div>
</div>
</div>
</div>
<!-- END component and volume -->
<!-- BEGIN storage information -->
<div class="form-section storage-info">
<h5><i class="fa fa-warehouse"></i> Storage Information</h5>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="{{ form.location.id_for_label }}" class="form-label">
Storage Location <span class="required-field">*</span>
</label>
<select class="form-select" name="{{ form.location.name }}" id="{{ form.location.id_for_label }}" required>
<option value="">Select location...</option>
{% for location in form.location.field.queryset %}
<option value="{{ location.id }}"
data-capacity="{{ location.current_capacity }}"
data-max-capacity="{{ location.max_capacity }}"
{% if form.location.value == location.id %}selected{% endif %}>
{{ location.name }} ({{ location.location_type }})
</option>
{% endfor %}
</select>
{% if form.location.errors %}
<div class="text-danger">{{ form.location.errors.0 }}</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label class="form-label">Location Capacity</label>
<div id="locationCapacity" class="alert alert-info">
<i class="fa fa-info-circle"></i> Select a location to view capacity
</div>
</div>
</div>
</div>
</div>
<!-- END storage information -->
<!-- BEGIN additional information -->
<div class="form-section">
<h5><i class="fa fa-clipboard"></i> Additional Information</h5>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label for="{{ form.notes.id_for_label }}" class="form-label">Notes</label>
<textarea class="form-control" name="{{ form.notes.name }}"
id="{{ form.notes.id_for_label }}" rows="3"
placeholder="Any additional notes about the collection...">{{ form.notes.value|default:'' }}</textarea>
{% if form.notes.errors %}
<div class="text-danger">{{ form.notes.errors.0 }}</div>
{% endif %}
</div>
</div>
</div>
</div>
<!-- END additional information -->
<!-- BEGIN unit preview -->
<div class="unit-preview">
<h5><i class="fa fa-eye"></i> Unit Preview</h5>
<div class="row">
<div class="col-md-8">
<div id="unitPreview">
<p><strong>Unit Number:</strong> <span id="previewUnitNumber">Will be auto-generated</span></p>
<p><strong>Donor:</strong> <span id="previewDonor">{{ donor.full_name|default:'Not selected' }}</span></p>
<p><strong>Blood Group:</strong> <span id="previewBloodGroup">{{ donor.blood_group.display_name|default:'Unknown' }}</span></p>
<p><strong>Component:</strong> <span id="previewComponent">Not selected</span></p>
<p><strong>Volume:</strong> <span id="previewVolume">0</span> ml</p>
<p><strong>Collection Date:</strong> <span id="previewDate">Not set</span></p>
<p><strong>Expiry Date:</strong> <span id="previewExpiry">Not calculated</span></p>
</div>
</div>
<div class="col-md-4">
<div class="barcode-preview">
<div>Blood Unit Barcode</div>
<div id="barcodePreview">||||| |||| |||||</div>
</div>
</div>
</div>
</div>
<!-- END unit preview -->
<!-- BEGIN form actions -->
<div class="d-flex justify-content-between mt-4">
<a href="{% if form.instance.pk %}{% url 'blood_bank:blood_unit_detail' form.instance.id %}{% else %}{% url 'blood_bank:blood_unit_list' %}{% endif %}"
class="btn btn-secondary">
<i class="fa fa-arrow-left"></i> Cancel
</a>
<div>
<button type="button" class="btn btn-info" onclick="validateForm()">
<i class="fa fa-check"></i> Validate
</button>
<button type="submit" class="btn btn-primary">
<i class="fa fa-save"></i>
{% if form.instance.pk %}Update Blood Unit{% else %}Register Blood Unit{% 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 src="{% static 'plugins/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js' %}"></script>
<script>
$(document).ready(function() {
// Initialize Select2
$('#{{ form.donor.id_for_label }}, #{{ form.collected_by.id_for_label }}, #{{ form.component.id_for_label }}, #{{ form.location.id_for_label }}').select2({
theme: 'bootstrap-5'
});
// Set default collection date to today
if (!$('#{{ form.collection_date.id_for_label }}').val()) {
$('#{{ form.collection_date.id_for_label }}').val(new Date().toISOString().split('T')[0]);
}
// Set default collection time to current time
if (!$('#{{ form.collection_time.id_for_label }}').val()) {
var now = new Date();
var time = now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0');
$('#{{ form.collection_time.id_for_label }}').val(time);
}
// Update preview when form changes
updatePreview();
// Event listeners
$('#{{ form.donor.id_for_label }}').on('change', function() {
checkDonorEligibility();
updatePreview();
});
$('#{{ form.component.id_for_label }}').on('change', function() {
calculateExpiryDate();
updatePreview();
});
$('#{{ form.location.id_for_label }}').on('change', function() {
checkLocationCapacity();
updatePreview();
});
$('#{{ form.collection_date.id_for_label }}, #{{ form.volume_ml.id_for_label }}').on('change', function() {
calculateExpiryDate();
updatePreview();
});
// Form validation
$('#bloodUnitForm').on('submit', function(e) {
if (!validateForm()) {
e.preventDefault();
}
});
});
function checkDonorEligibility() {
var donorId = $('#{{ form.donor.id_for_label }}').val();
if (!donorId) {
$('#donorEligibility').html('<i class="fa fa-info-circle"></i> Select a donor to check eligibility').removeClass().addClass('alert alert-info');
return;
}
var selectedOption = $('#{{ form.donor.id_for_label }} option:selected');
var lastDonation = selectedOption.data('last-donation');
if (lastDonation) {
var daysSinceLastDonation = Math.floor((new Date() - new Date(lastDonation)) / (1000 * 60 * 60 * 24));
if (daysSinceLastDonation < 56) { // 8 weeks minimum interval
$('#donorEligibility').html('<i class="fa fa-exclamation-triangle"></i> Last donation was ' + daysSinceLastDonation + ' days ago. Minimum 56 days required.').removeClass().addClass('alert alert-warning');
} else {
$('#donorEligibility').html('<i class="fa fa-check-circle"></i> Donor is eligible for donation').removeClass().addClass('alert alert-success');
}
} else {
$('#donorEligibility').html('<i class="fa fa-check-circle"></i> First-time donor - eligible').removeClass().addClass('alert alert-success');
}
}
function calculateExpiryDate() {
var collectionDate = $('#{{ form.collection_date.id_for_label }}').val();
var componentId = $('#{{ form.component.id_for_label }}').val();
if (collectionDate && componentId) {
var selectedComponent = $('#{{ form.component.id_for_label }} option:selected');
var shelfLifeDays = selectedComponent.data('shelf-life');
if (shelfLifeDays) {
var expiryDate = new Date(collectionDate);
expiryDate.setDate(expiryDate.getDate() + parseInt(shelfLifeDays));
$('#calculatedExpiry').val(expiryDate.toISOString().split('T')[0]);
}
}
}
function checkLocationCapacity() {
var locationId = $('#{{ form.location.id_for_label }}').val();
if (!locationId) {
$('#locationCapacity').html('<i class="fa fa-info-circle"></i> Select a location to view capacity').removeClass().addClass('alert alert-info');
return;
}
var selectedLocation = $('#{{ form.location.id_for_label }} option:selected');
var currentCapacity = selectedLocation.data('capacity');
var maxCapacity = selectedLocation.data('max-capacity');
var percentage = (currentCapacity / maxCapacity) * 100;
var alertClass = percentage > 90 ? 'alert-danger' : percentage > 75 ? 'alert-warning' : 'alert-success';
$('#locationCapacity').html('<i class="fa fa-warehouse"></i> ' + currentCapacity + ' / ' + maxCapacity + ' units (' + percentage.toFixed(1) + '% full)').removeClass().addClass('alert ' + alertClass);
}
function updatePreview() {
// Update donor info
var donorSelect = $('#{{ form.donor.id_for_label }}');
if (donorSelect.val()) {
var selectedDonor = donorSelect.find('option:selected');
$('#previewDonor').text(selectedDonor.text().split(' (')[0]);
$('#previewBloodGroup').text(selectedDonor.data('blood-group'));
}
// Update component
var componentSelect = $('#{{ form.component.id_for_label }}');
if (componentSelect.val()) {
$('#previewComponent').text(componentSelect.find('option:selected').text());
}
// Update volume
var volume = $('#{{ form.volume_ml.id_for_label }}').val();
$('#previewVolume').text(volume || '0');
// Update collection date
var collectionDate = $('#{{ form.collection_date.id_for_label }}').val();
if (collectionDate) {
$('#previewDate').text(new Date(collectionDate).toLocaleDateString());
}
// Update expiry date
var expiryDate = $('#calculatedExpiry').val();
if (expiryDate) {
$('#previewExpiry').text(new Date(expiryDate).toLocaleDateString());
}
// Generate barcode preview
if (donorSelect.val() && componentSelect.val()) {
var barcode = 'BB' + new Date().getFullYear() + Math.random().toString(36).substr(2, 6).toUpperCase();
$('#previewUnitNumber').text(barcode);
$('#barcodePreview').text('||||| ' + barcode + ' |||||');
}
}
function validateForm() {
var errors = [];
// Check required fields
if (!$('#{{ form.donor.id_for_label }}').val()) {
errors.push('Please select a donor');
}
if (!$('#{{ form.component.id_for_label }}').val()) {
errors.push('Please select a component');
}
if (!$('#{{ form.volume_ml.id_for_label }}').val()) {
errors.push('Please enter volume');
}
if (!$('#{{ form.location.id_for_label }}').val()) {
errors.push('Please select storage location');
}
if (!$('#{{ form.collected_by.id_for_label }}').val()) {
errors.push('Please select who collected the unit');
}
// Check donor eligibility
var eligibilityAlert = $('#donorEligibility');
if (eligibilityAlert.hasClass('alert-warning')) {
errors.push('Selected donor may not be eligible for donation');
}
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: 'All required information is complete.',
timer: 1500,
showConfirmButton: false
});
return true;
}
</script>
{% endblock %}