618 lines
26 KiB
HTML
618 lines
26 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}Blood Unit Details - {{ blood_unit.unit_number }}{% endblock %}
|
|
|
|
{% block 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" />
|
|
|
|
<style>
|
|
.status-timeline {
|
|
position: relative;
|
|
padding-left: 30px;
|
|
}
|
|
|
|
.status-timeline::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: 15px;
|
|
top: 0;
|
|
bottom: 0;
|
|
width: 2px;
|
|
background: #e9ecef;
|
|
}
|
|
|
|
.timeline-item {
|
|
position: relative;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.timeline-item::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: -23px;
|
|
top: 5px;
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 50%;
|
|
background: #007bff;
|
|
border: 2px solid #fff;
|
|
box-shadow: 0 0 0 2px #e9ecef;
|
|
}
|
|
|
|
.timeline-item.current::before {
|
|
background: #28a745;
|
|
box-shadow: 0 0 0 2px #28a745;
|
|
}
|
|
|
|
.test-result-positive {
|
|
background-color: #f8d7da;
|
|
border-color: #f5c6cb;
|
|
}
|
|
|
|
.test-result-negative {
|
|
background-color: #d4edda;
|
|
border-color: #c3e6cb;
|
|
}
|
|
|
|
.test-result-pending {
|
|
background-color: #fff3cd;
|
|
border-color: #ffeaa7;
|
|
}
|
|
</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>
|
|
<li class="breadcrumb-item active">{{ blood_unit.unit_number }}</li>
|
|
</ol>
|
|
<!-- END breadcrumb -->
|
|
|
|
<!-- BEGIN page-header -->
|
|
<h1 class="page-header">
|
|
Blood Unit Details
|
|
<small>{{ blood_unit.unit_number }}</small>
|
|
</h1>
|
|
<!-- END page-header -->
|
|
|
|
<!-- BEGIN row -->
|
|
<div class="row">
|
|
<!-- BEGIN col-4 -->
|
|
<div class="col-xl-4">
|
|
<!-- BEGIN unit info panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Unit Information</h4>
|
|
<div class="panel-heading-btn">
|
|
<span class="badge {% if blood_unit.status == 'available' %}bg-success{% elif blood_unit.status == 'expired' %}bg-danger{% else %}bg-warning{% endif %}">
|
|
{{ blood_unit.get_status_display }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="text-center mb-4">
|
|
<div class="bg-light rounded p-4">
|
|
<i class="fa fa-tint fa-4x text-primary mb-2"></i>
|
|
<h4>{{ blood_unit.unit_number }}</h4>
|
|
<p class="text-muted mb-0">{{ blood_unit.component.get_name_display }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td class="fw-bold">Blood Group:</td>
|
|
<td>
|
|
<span class="badge bg-primary fs-6">{{ blood_unit.blood_group.display_name }}</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Component:</td>
|
|
<td>{{ blood_unit.component.get_name_display }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Volume:</td>
|
|
<td>{{ blood_unit.volume_ml }} ml</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Collection Date:</td>
|
|
<td>{{ blood_unit.collection_date|date:"M d, Y H:i" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Expiry Date:</td>
|
|
<td>
|
|
{{ blood_unit.expiry_date|date:"M d, Y" }}
|
|
{% if blood_unit.days_to_expiry <= 3 and blood_unit.status == 'available' %}
|
|
<br><span class="badge bg-warning">{{ blood_unit.days_to_expiry }} days left</span>
|
|
{% elif blood_unit.is_expired %}
|
|
<br><span class="badge bg-danger">Expired</span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Location:</td>
|
|
<td>{{ blood_unit.location }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Bag Type:</td>
|
|
<td>{{ blood_unit.bag_type }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Anticoagulant:</td>
|
|
<td>{{ blood_unit.anticoagulant }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Collection Site:</td>
|
|
<td>{{ blood_unit.collection_site }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Collected By:</td>
|
|
<td>{{ blood_unit.collected_by.get_full_name }}</td>
|
|
</tr>
|
|
</table>
|
|
|
|
{% if blood_unit.notes %}
|
|
<hr>
|
|
<h6 class="fw-bold">Notes</h6>
|
|
<p class="text-muted">{{ blood_unit.notes }}</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<!-- END unit info panel -->
|
|
|
|
<!-- BEGIN donor info panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Donor Information</h4>
|
|
<div class="panel-heading-btn">
|
|
<a href="{% url 'blood_bank:donor_detail' blood_unit.donor.id %}" class="btn btn-primary btn-sm">
|
|
<i class="fa fa-user"></i> View Donor
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="panel-body">
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td class="fw-bold">Donor ID:</td>
|
|
<td>{{ blood_unit.donor.donor_id }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Name:</td>
|
|
<td>{{ blood_unit.donor.full_name }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Age:</td>
|
|
<td>{{ blood_unit.donor.age }} years</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Gender:</td>
|
|
<td>{{ blood_unit.donor.get_gender_display }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Phone:</td>
|
|
<td>{{ blood_unit.donor.phone }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Total Donations:</td>
|
|
<td>{{ blood_unit.donor.total_donations }}</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<!-- END donor info panel -->
|
|
</div>
|
|
<!-- END col-4 -->
|
|
|
|
<!-- BEGIN col-8 -->
|
|
<div class="col-xl-8">
|
|
<!-- BEGIN status timeline -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Unit Status Timeline</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="status-timeline">
|
|
<div class="timeline-item {% if blood_unit.status == 'collected' %}current{% endif %}">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h6 class="card-title">
|
|
<i class="fa fa-tint text-primary"></i> Blood Collected
|
|
</h6>
|
|
<p class="card-text">
|
|
<small class="text-muted">{{ blood_unit.collection_date|date:"M d, Y H:i" }}</small><br>
|
|
Blood unit collected from donor {{ blood_unit.donor.full_name }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if tests %}
|
|
<div class="timeline-item {% if blood_unit.status == 'testing' %}current{% endif %}">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h6 class="card-title">
|
|
<i class="fa fa-flask text-info"></i> Testing Phase
|
|
</h6>
|
|
<p class="card-text">
|
|
<small class="text-muted">{{ tests.first.test_date|date:"M d, Y H:i" }}</small><br>
|
|
Laboratory testing initiated
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if blood_unit.status == 'quarantine' %}
|
|
<div class="timeline-item current">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h6 class="card-title">
|
|
<i class="fa fa-pause text-warning"></i> Quarantine
|
|
</h6>
|
|
<p class="card-text">
|
|
Unit placed in quarantine pending test results
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if blood_unit.status == 'available' %}
|
|
<div class="timeline-item current">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h6 class="card-title">
|
|
<i class="fa fa-check text-success"></i> Available for Use
|
|
</h6>
|
|
<p class="card-text">
|
|
Unit cleared for transfusion and available in inventory
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if blood_unit.status == 'issued' %}
|
|
<div class="timeline-item current">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h6 class="card-title">
|
|
<i class="fa fa-share text-primary"></i> Issued
|
|
</h6>
|
|
<p class="card-text">
|
|
Unit issued for patient transfusion
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if blood_unit.status == 'transfused' %}
|
|
<div class="timeline-item current">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h6 class="card-title">
|
|
<i class="fa fa-heartbeat text-success"></i> Transfused
|
|
</h6>
|
|
<p class="card-text">
|
|
Unit successfully transfused to patient
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if blood_unit.status == 'expired' or blood_unit.status == 'discarded' %}
|
|
<div class="timeline-item current">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h6 class="card-title">
|
|
<i class="fa fa-times text-danger"></i> {{ blood_unit.get_status_display }}
|
|
</h6>
|
|
<p class="card-text">
|
|
Unit removed from inventory
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END status timeline -->
|
|
|
|
<!-- BEGIN test results -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Test Results</h4>
|
|
<div class="panel-heading-btn">
|
|
{% if blood_unit.status in 'collected,testing,quarantine' %}
|
|
<a href="{% url 'blood_bank:blood_test_create' blood_unit.id %}" class="btn btn-success btn-sm">
|
|
<i class="fa fa-plus"></i> Add Test Result
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="panel-body">
|
|
{% if tests %}
|
|
<div class="table-responsive">
|
|
<table class="table table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Test Type</th>
|
|
<th>Result</th>
|
|
<th>Test Date</th>
|
|
<th>Tested By</th>
|
|
<th>Equipment</th>
|
|
<th>Verified</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for test in tests %}
|
|
<tr class="{% if test.result == 'positive' %}test-result-positive{% elif test.result == 'negative' %}test-result-negative{% else %}test-result-pending{% endif %}">
|
|
<td>{{ test.get_test_type_display }}</td>
|
|
<td>
|
|
{% if test.result == 'positive' %}
|
|
<span class="badge bg-danger">{{ test.get_result_display }}</span>
|
|
{% elif test.result == 'negative' %}
|
|
<span class="badge bg-success">{{ test.get_result_display }}</span>
|
|
{% elif test.result == 'indeterminate' %}
|
|
<span class="badge bg-warning">{{ test.get_result_display }}</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary">{{ test.get_result_display }}</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ test.test_date|date:"M d, Y H:i" }}</td>
|
|
<td>{{ test.tested_by.get_full_name }}</td>
|
|
<td>{{ test.equipment_used|default:"-" }}</td>
|
|
<td>
|
|
{% if test.verified_by %}
|
|
<span class="badge bg-success">
|
|
<i class="fa fa-check"></i> {{ test.verified_by.get_full_name }}
|
|
</span>
|
|
<br><small class="text-muted">{{ test.verified_at|date:"M d, Y H:i" }}</small>
|
|
{% else %}
|
|
<span class="badge bg-warning">Pending</span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="fa fa-flask fa-3x text-muted mb-3"></i>
|
|
<h5 class="text-muted">No Test Results</h5>
|
|
<p class="text-muted">No laboratory tests have been performed on this unit yet.</p>
|
|
{% if blood_unit.status in 'collected,testing,quarantine' %}
|
|
<a href="{% url 'blood_bank:blood_test_create' blood_unit.id %}" class="btn btn-primary">
|
|
<i class="fa fa-plus"></i> Add First Test
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<!-- END test results -->
|
|
|
|
<!-- BEGIN crossmatch results -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Crossmatch Results</h4>
|
|
<div class="panel-heading-btn">
|
|
{% if blood_unit.status == 'available' %}
|
|
<button type="button" class="btn btn-info btn-sm" data-bs-toggle="modal" data-bs-target="#crossmatchModal">
|
|
<i class="fa fa-plus"></i> Add Crossmatch
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="panel-body">
|
|
{% if crossmatches %}
|
|
<div class="table-responsive">
|
|
<table class="table table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Patient</th>
|
|
<th>Test Type</th>
|
|
<th>Compatibility</th>
|
|
<th>Test Date</th>
|
|
<th>Tested By</th>
|
|
<th>Verified</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for crossmatch in crossmatches %}
|
|
<tr>
|
|
<td>
|
|
{{ crossmatch.recipient.full_name }}
|
|
<br><small class="text-muted">{{ crossmatch.recipient.patient_id }}</small>
|
|
</td>
|
|
<td>{{ crossmatch.get_test_type_display }}</td>
|
|
<td>
|
|
{% if crossmatch.compatibility == 'compatible' %}
|
|
<span class="badge bg-success">{{ crossmatch.get_compatibility_display }}</span>
|
|
{% elif crossmatch.compatibility == 'incompatible' %}
|
|
<span class="badge bg-danger">{{ crossmatch.get_compatibility_display }}</span>
|
|
{% else %}
|
|
<span class="badge bg-warning">{{ crossmatch.get_compatibility_display }}</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ crossmatch.test_date|date:"M d, Y H:i" }}</td>
|
|
<td>{{ crossmatch.tested_by.get_full_name }}</td>
|
|
<td>
|
|
{% if crossmatch.verified_by %}
|
|
<span class="badge bg-success">
|
|
<i class="fa fa-check"></i> Verified
|
|
</span>
|
|
{% else %}
|
|
<span class="badge bg-warning">Pending</span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="fa fa-exchange-alt fa-3x text-muted mb-3"></i>
|
|
<h5 class="text-muted">No Crossmatch Results</h5>
|
|
<p class="text-muted">No crossmatch tests have been performed for this unit.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<!-- END crossmatch results -->
|
|
</div>
|
|
<!-- END col-8 -->
|
|
</div>
|
|
<!-- END row -->
|
|
|
|
<!-- BEGIN action buttons -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="d-flex justify-content-between">
|
|
<a href="{% url 'blood_bank:blood_unit_list' %}" class="btn btn-default">
|
|
<i class="fa fa-arrow-left"></i> Back to Units
|
|
</a>
|
|
<div>
|
|
{% if blood_unit.status in 'collected,testing,quarantine' %}
|
|
<a href="{% url 'blood_bank:blood_test_create' blood_unit.id %}" class="btn btn-success">
|
|
<i class="fa fa-flask"></i> Add Test
|
|
</a>
|
|
{% endif %}
|
|
{% if blood_unit.status == 'available' %}
|
|
<button type="button" class="btn btn-warning" data-bs-toggle="modal" data-bs-target="#moveUnitModal">
|
|
<i class="fa fa-arrows-alt"></i> Move Location
|
|
</button>
|
|
{% endif %}
|
|
<button type="button" class="btn btn-info" onclick="window.print()">
|
|
<i class="fa fa-print"></i> Print Label
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Crossmatch Modal -->
|
|
<div class="modal fade" id="crossmatchModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Add Crossmatch Test</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="crossmatchForm">
|
|
<div class="mb-3">
|
|
<label class="form-label">Patient</label>
|
|
<select class="form-select" id="patientSelect">
|
|
<option value="">Select Patient</option>
|
|
<!-- Load patients via AJAX if needed -->
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Test Type</label>
|
|
<select class="form-select" id="testType">
|
|
<option value="major">Major Crossmatch</option>
|
|
<option value="minor">Minor Crossmatch</option>
|
|
<option value="immediate_spin">Immediate Spin</option>
|
|
<option value="antiglobulin">Antiglobulin Test</option>
|
|
</select>
|
|
</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="submitCrossmatch()">Create Crossmatch</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Move Unit Modal -->
|
|
<div class="modal fade" id="moveUnitModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg modal-dialog-centered">
|
|
<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" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="moveUnitForm">
|
|
<div class="mb-3">
|
|
<label class="form-label">New Location</label>
|
|
<select class="form-select" id="newLocation">
|
|
<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 class="form-label">Reason</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-warning" onclick="submitMoveUnit()">Move Unit</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END action buttons -->
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script src="{% static 'plugins/datatables.net/js/dataTables.min.js' %}"></script>
|
|
<script src="{% static 'plugins/datatables.net-bs5/js/dataTables.bootstrap5.min.js' %}"></script>
|
|
<script src="{% static 'plugins/sweetalert/dist/sweetalert.min.js' %}"></script>
|
|
|
|
<script>
|
|
|
|
function submitCrossmatch() {
|
|
const patient = document.getElementById("patientSelect").value;
|
|
const testType = document.getElementById("testType").value;
|
|
|
|
if (!patient) {
|
|
alert("Please select a patient");
|
|
return;
|
|
}
|
|
|
|
// Redirect to your creation URL
|
|
window.location.href = `en/blood-bank/units/{{ blood_unit.id }}/crossmatch/${patient}/?test_type=${testType}`;
|
|
}
|
|
|
|
function submitMoveUnit() {
|
|
const location = document.getElementById("newLocation").value;
|
|
const reason = document.getElementById("moveReason").value;
|
|
|
|
if (!location) {
|
|
alert("Please select a new location");
|
|
return;
|
|
}
|
|
|
|
// For now reload page, later replace with AJAX
|
|
alert(`Blood unit moved to ${location} for reason: ${reason}`);
|
|
location.reload();
|
|
}
|
|
|
|
// Auto-refresh page every 5 minutes to update status
|
|
setInterval(function() {
|
|
if (document.visibilityState === 'visible') {
|
|
location.reload();
|
|
}
|
|
}, 300000);
|
|
</script>
|
|
{% endblock %}
|
|
|