Marwan Alwali 09932ffe8a update
2025-09-06 16:22:28 +03:00

468 lines
19 KiB
HTML

{% extends 'base.html' %}
{% load static %}
{% block title %}Blood Unit Management{% endblock %}
{% block extra_css %}
<link href="{% static 'plugins/datatables.net-bs5/css/dataTables.bootstrap5.min.css' %}" rel="stylesheet" />
<link href="{% static 'plugins/datatables.net-responsive-bs5/css/responsive.bootstrap5.min.css' %}" rel="stylesheet" />
<link href="{% static 'plugins/select2/dist/css/select2.min.css' %}" rel="stylesheet" />
{% 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 active">Blood Units</li>
</ol>
<!-- END breadcrumb -->
<!-- BEGIN page-header -->
<h1 class="page-header">Blood Unit Management <small>track and manage blood inventory</small></h1>
<!-- END page-header -->
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Blood Unit Inventory</h4>
<div class="panel-heading-btn">
<a href="{% url 'blood_bank:blood_unit_create' %}" class="btn btn-primary btn-sm">
<i class="fa fa-plus"></i> Register Blood Unit
</a>
</div>
</div>
<div class="panel-body">
<!-- BEGIN search and filter form -->
<form method="get" class="mb-4">
<div class="row">
<div class="col-md-3">
<div class="form-group">
<label for="blood_group">Blood Group</label>
{{ form.blood_group }}
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="component">Component</label>
{{ form.component }}
</div>
</div>
<div class="col-md-2">
<div class="form-group">
<label for="status">Status</label>
{{ form.status }}
</div>
</div>
<div class="col-md-2">
<div class="form-group">
<label for="expiry_days">Expiring in (days)</label>
{{ form.expiry_days }}
</div>
</div>
<div class="col-md-2">
<div class="form-group">
<label>&nbsp;</label>
<div class="d-grid">
<button type="submit" class="btn btn-primary">
<i class="fa fa-search"></i> Filter
</button>
</div>
</div>
</div>
</div>
</form>
<!-- END search and filter form -->
<!-- BEGIN summary cards -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card bg-primary text-white">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h6 class="card-title">Available Units</h6>
<h3 class="mb-0">{{ page_obj.paginator.count }}</h3>
</div>
<div class="align-self-center">
<i class="fa fa-tint fa-2x"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-success text-white">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h6 class="card-title">Fresh Units</h6>
<h3 class="mb-0">
{{ page_obj.object_list|length }}
</h3>
</div>
<div class="align-self-center">
<i class="fa fa-check-circle fa-2x"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-warning text-white">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h6 class="card-title">Expiring Soon</h6>
<h3 class="mb-0" id="expiringSoonCount">0</h3>
</div>
<div class="align-self-center">
<i class="fa fa-exclamation-triangle fa-2x"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-danger text-white">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h6 class="card-title">Expired Units</h6>
<h3 class="mb-0" id="expiredCount">0</h3>
</div>
<div class="align-self-center">
<i class="fa fa-times-circle fa-2x"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- END summary cards -->
<!-- BEGIN table -->
<div class="table-responsive">
<table id="bloodUnitTable" class="table table-striped table-bordered align-middle">
<thead>
<tr>
<th>Unit Number</th>
<th>Donor</th>
<th>Blood Group</th>
<th>Component</th>
<th>Collection Date</th>
<th>Expiry Date</th>
<th>Volume (ml)</th>
<th>Status</th>
<th>Location</th>
<th>Days to Expiry</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for unit in page_obj %}
<tr data-unit-id="{{ unit.id }}" data-days-to-expiry="{{ unit.days_to_expiry }}" data-status="{{ unit.status }}">
<td>
<a href="{% url 'blood_bank:blood_unit_detail' unit.id %}" class="text-decoration-none">
<strong>{{ unit.unit_number }}</strong>
</a>
</td>
<td>
<a href="{% url 'blood_bank:donor_detail' unit.donor.id %}" class="text-decoration-none">
{{ unit.donor.full_name }}
</a>
<br>
<small class="text-muted">{{ unit.donor.donor_id }}</small>
</td>
<td>
<span class="badge bg-primary fs-6">{{ unit.blood_group.display_name }}</span>
</td>
<td>{{ unit.component.get_name_display }}</td>
<td>{{ unit.collection_date|date:"M d, Y H:i" }}</td>
<td>
{{ unit.expiry_date|date:"M d, Y" }}
{% if unit.days_to_expiry <= 3 and unit.status == 'available' %}
<br><span class="badge bg-danger">Expiring Soon</span>
{% elif unit.is_expired %}
<br><span class="badge bg-danger">Expired</span>
{% endif %}
</td>
<td>{{ unit.volume_ml }}</td>
<td>
{% if unit.status == 'available' %}
<span class="badge bg-success">{{ unit.get_status_display }}</span>
{% elif unit.status == 'expired' %}
<span class="badge bg-danger">{{ unit.get_status_display }}</span>
{% elif unit.status == 'issued' %}
<span class="badge bg-info">{{ unit.get_status_display }}</span>
{% elif unit.status == 'transfused' %}
<span class="badge bg-primary">{{ unit.get_status_display }}</span>
{% elif unit.status == 'discarded' %}
<span class="badge bg-dark">{{ unit.get_status_display }}</span>
{% else %}
<span class="badge bg-warning">{{ unit.get_status_display }}</span>
{% endif %}
</td>
<td>{{ unit.location }}</td>
<td>
{% if unit.is_expired %}
<span class="text-danger fw-bold">Expired</span>
{% elif unit.days_to_expiry <= 3 %}
<span class="text-warning fw-bold">{{ unit.days_to_expiry }} days</span>
{% else %}
<span class="text-success">{{ unit.days_to_expiry }} days</span>
{% endif %}
</td>
<td>
<div class="btn-group" role="group">
<a href="{% url 'blood_bank:blood_unit_detail' unit.id %}"
class="btn btn-outline-primary btn-sm" title="View Details">
<i class="fa fa-eye"></i>
</a>
{% if unit.status == 'collected' or unit.status == 'testing' %}
<a href="{% url 'blood_bank:blood_test_create' unit.id %}"
class="btn btn-outline-success btn-sm" title="Add Test">
<i class="fa fa-flask"></i>
</a>
{% endif %}
{% if unit.status == 'available' %}
<button type="button" class="btn btn-outline-warning btn-sm"
onclick="moveUnit({{ unit.id }})" title="Move Location">
<i class="fa fa-arrows-alt"></i>
</button>
{% endif %}
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="11" class="text-center">
<div class="py-4">
<i class="fa fa-tint fa-3x text-muted mb-3"></i>
<h5 class="text-muted">No blood units found</h5>
<p class="text-muted">Try adjusting your search criteria or register a new blood unit.</p>
<a href="{% url 'blood_bank:blood_unit_create' %}" class="btn btn-primary">
<i class="fa fa-plus"></i> Register Blood Unit
</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- END table -->
<!-- BEGIN pagination -->
{% if is_paginated %}
{% include 'partial/pagination.html' %}
{% endif %}
<!-- END pagination -->
<!-- BEGIN summary -->
<div class="row mt-3">
<div class="col-md-6 text-end">
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="window.print()">
<i class="fa fa-print"></i> Print
</button>
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="exportToCSV()">
<i class="fa fa-download"></i> Export
</button>
<button type="button" class="btn btn-outline-info btn-sm" onclick="showExpiryReport()">
<i class="fa fa-chart-bar"></i> Expiry Report
</button>
</div>
</div>
</div>
<!-- END summary -->
</div>
</div>
<!-- END panel -->
<!-- BEGIN move unit modal -->
<div class="modal fade" id="moveUnitModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Move Blood Unit</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="moveUnitForm">
<div class="mb-3">
<label for="newLocation" class="form-label">New Location</label>
<select class="form-select" id="newLocation" required>
<option value="">Select Location</option>
<option value="Refrigerator A">Refrigerator A</option>
<option value="Refrigerator B">Refrigerator B</option>
<option value="Freezer 1">Freezer 1</option>
<option value="Platelet Agitator">Platelet Agitator</option>
<option value="Quarantine">Quarantine</option>
</select>
</div>
<div class="mb-3">
<label for="moveReason" class="form-label">Reason for Move</label>
<textarea class="form-control" id="moveReason" rows="3"></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="confirmMoveUnit()">Move Unit</button>
</div>
</div>
</div>
</div>
<!-- END move unit modal -->
{% endblock %}
{% block extra_js %}
<script src="{% static 'assets/plugins/datatables.net/js/jquery.dataTables.min.js' %}"></script>
<script src="{% static 'assets/plugins/datatables.net-bs5/js/dataTables.bootstrap5.min.js' %}"></script>
<script src="{% static 'assets/plugins/datatables.net-responsive/js/dataTables.responsive.min.js' %}"></script>
<script src="{% static 'assets/plugins/datatables.net-responsive-bs5/js/responsive.bootstrap5.min.js' %}"></script>
<script src="{% static 'assets/plugins/select2/dist/js/select2.min.js' %}"></script>
<script>
$(document).ready(function() {
// Initialize Select2 for filters
$('.form-select').select2({
theme: 'bootstrap-5',
width: '100%'
});
// Initialize DataTable
$('#bloodUnitTable').DataTable({
responsive: true,
pageLength: 25,
order: [[4, 'desc']], // Sort by collection date
columnDefs: [
{ orderable: false, targets: [10] } // Actions column
]
});
// Calculate summary statistics
calculateSummaryStats();
});
function calculateSummaryStats() {
var expiringSoon = 0;
var expired = 0;
$('tr[data-unit-id]').each(function() {
var daysToExpiry = parseInt($(this).data('days-to-expiry'));
var status = $(this).data('status');
if (status === 'expired' || daysToExpiry === 0) {
expired++;
} else if (daysToExpiry <= 7 && status === 'available') {
expiringSoon++;
}
});
$('#expiringSoonCount').text(expiringSoon);
$('#expiredCount').text(expired);
}
var currentUnitId = null;
function moveUnit(unitId) {
currentUnitId = unitId;
$('#moveUnitModal').modal('show');
}
function confirmMoveUnit() {
var newLocation = $('#newLocation').val();
var reason = $('#moveReason').val();
if (!newLocation) {
Swal.fire({
icon: 'error',
title: 'Missing Information',
text: 'Please select a new location.',
confirmButtonText: 'OK'
});
return;
}
// Here you would make an AJAX call to update the unit location
// For now, we'll just show a success message
Swal.fire({
icon: 'success',
title: 'Unit Moved',
text: 'Blood unit has been moved to ' + newLocation,
confirmButtonText: 'OK'
}).then(() => {
$('#moveUnitModal').modal('hide');
location.reload(); // Refresh the page
});
}
function exportToCSV() {
var csv = [];
var rows = document.querySelectorAll("#bloodUnitTable tr");
for (var i = 0; i < rows.length; i++) {
var row = [], cols = rows[i].querySelectorAll("td, th");
for (var j = 0; j < cols.length - 1; j++) { // Exclude actions column
row.push(cols[j].innerText.replace(/,/g, ';'));
}
csv.push(row.join(","));
}
var csvFile = new Blob([csv.join("\n")], {type: "text/csv"});
var downloadLink = document.createElement("a");
downloadLink.download = "blood_units.csv";
downloadLink.href = window.URL.createObjectURL(csvFile);
downloadLink.style.display = "none";
document.body.appendChild(downloadLink);
downloadLink.click();
}
function showExpiryReport() {
var expiryData = {};
$('tr[data-unit-id]').each(function() {
var daysToExpiry = parseInt($(this).data('days-to-expiry'));
var status = $(this).data('status');
if (status === 'available') {
if (daysToExpiry <= 0) {
expiryData['Expired'] = (expiryData['Expired'] || 0) + 1;
} else if (daysToExpiry <= 3) {
expiryData['1-3 days'] = (expiryData['1-3 days'] || 0) + 1;
} else if (daysToExpiry <= 7) {
expiryData['4-7 days'] = (expiryData['4-7 days'] || 0) + 1;
} else if (daysToExpiry <= 14) {
expiryData['8-14 days'] = (expiryData['8-14 days'] || 0) + 1;
} else {
expiryData['15+ days'] = (expiryData['15+ days'] || 0) + 1;
}
}
});
var reportHtml = '<table class="table table-sm"><thead><tr><th>Expiry Range</th><th>Count</th></tr></thead><tbody>';
Object.keys(expiryData).forEach(function(range) {
reportHtml += '<tr><td>' + range + '</td><td>' + expiryData[range] + '</td></tr>';
});
reportHtml += '</tbody></table>';
Swal.fire({
title: 'Blood Unit Expiry Report',
html: reportHtml,
width: '500px',
confirmButtonText: 'Close'
});
}
</script>
{% endblock %}