981 lines
41 KiB
HTML
981 lines
41 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}Transfusion Details - {{ transfusion.transfusion_id }}{% endblock %}
|
|
|
|
{% block css %}
|
|
<link href="{% static 'plugins/chart.js/dist/Chart.min.css' %}" rel="stylesheet" />
|
|
<style>
|
|
.transfusion-header {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
color: white;
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.status-active {
|
|
background-color: #d4edda;
|
|
border-left: 4px solid #28a745;
|
|
}
|
|
|
|
.status-completed {
|
|
background-color: #d1ecf1;
|
|
border-left: 4px solid #17a2b8;
|
|
}
|
|
|
|
.status-stopped {
|
|
background-color: #fff3cd;
|
|
border-left: 4px solid #ffc107;
|
|
}
|
|
|
|
.status-cancelled {
|
|
background-color: #f8d7da;
|
|
border-left: 4px solid #dc3545;
|
|
}
|
|
|
|
.vital-signs-chart {
|
|
height: 300px;
|
|
}
|
|
|
|
.timeline {
|
|
position: relative;
|
|
padding-left: 30px;
|
|
}
|
|
|
|
.timeline::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: 15px;
|
|
top: 0;
|
|
bottom: 0;
|
|
width: 2px;
|
|
background: #e9ecef;
|
|
}
|
|
|
|
.timeline-item {
|
|
position: relative;
|
|
margin-bottom: 20px;
|
|
background: white;
|
|
border-radius: 8px;
|
|
padding: 15px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.timeline-item::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: -22px;
|
|
top: 20px;
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 50%;
|
|
background: #007bff;
|
|
border: 3px solid white;
|
|
box-shadow: 0 0 0 2px #007bff;
|
|
}
|
|
|
|
.timeline-time {
|
|
font-size: 0.9em;
|
|
color: #6c757d;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.vital-normal { color: #28a745; }
|
|
.vital-warning { color: #ffc107; }
|
|
.vital-critical { color: #dc3545; }
|
|
|
|
.reaction-alert {
|
|
animation: pulse 2s infinite;
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0% { opacity: 1; }
|
|
50% { opacity: 0.7; }
|
|
100% { opacity: 1; }
|
|
}
|
|
|
|
.progress-ring {
|
|
transform: rotate(-90deg);
|
|
}
|
|
|
|
.progress-ring-circle {
|
|
transition: stroke-dasharray 0.35s;
|
|
transform-origin: 50% 50%;
|
|
}
|
|
</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:transfusion_list' %}">Transfusions</a></li>
|
|
<li class="breadcrumb-item active">{{ transfusion.transfusion_id }}</li>
|
|
</ol>
|
|
<!-- END breadcrumb -->
|
|
|
|
<!-- BEGIN page-header -->
|
|
<h1 class="page-header">Transfusion Details <small>{{ transfusion.transfusion_id }}</small></h1>
|
|
<!-- END page-header -->
|
|
|
|
<!-- BEGIN transfusion header -->
|
|
<div class="transfusion-header">
|
|
<div class="row align-items-center">
|
|
<div class="col-md-8">
|
|
<h3 class="mb-2">
|
|
<i class="fa fa-heartbeat"></i> {{ transfusion.patient.full_name }}
|
|
<span class="badge bg-light text-dark ms-2">{{ transfusion.patient.patient_id }}</span>
|
|
</h3>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<p class="mb-1"><strong>Blood Unit:</strong> {{ transfusion.blood_unit.unit_number }}</p>
|
|
<p class="mb-1"><strong>Component:</strong> {{ transfusion.blood_unit.component.get_name_display }}</p>
|
|
<p class="mb-0"><strong>Blood Group:</strong> {{ transfusion.blood_unit.blood_group.display_name }}</p>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<p class="mb-1"><strong>Started:</strong> {{ transfusion.start_time|date:"M d, Y H:i" }}</p>
|
|
{% if transfusion.end_time %}
|
|
<p class="mb-1"><strong>Completed:</strong> {{ transfusion.end_time|date:"M d, Y H:i" }}</p>
|
|
<p class="mb-0"><strong>Duration:</strong> {{ transfusion.duration_minutes }} minutes</p>
|
|
{% else %}
|
|
<p class="mb-1"><strong>Elapsed:</strong> {{ transfusion.elapsed_minutes }} minutes</p>
|
|
<p class="mb-0"><strong>Status:</strong> {{ transfusion.get_status_display }}</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4 text-end">
|
|
{% if transfusion.status == 'in_progress' %}
|
|
<div class="mb-3">
|
|
<svg class="progress-ring" width="120" height="120">
|
|
<circle class="progress-ring-circle" stroke="#ffffff" stroke-width="4" fill="transparent" r="52" cx="60" cy="60"
|
|
style="stroke-dasharray: {{ transfusion.progress_circumference }}; stroke-dashoffset: {{ transfusion.progress_offset }};"/>
|
|
</svg>
|
|
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">
|
|
<div class="text-center">
|
|
<h4 class="mb-0">{{ transfusion.progress_percentage }}%</h4>
|
|
<small>Complete</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
<div>
|
|
{% if transfusion.status == 'in_progress' %}
|
|
<span class="badge bg-success fs-6">Active Transfusion</span>
|
|
{% elif transfusion.status == 'completed' %}
|
|
<span class="badge bg-primary fs-6">Completed</span>
|
|
{% elif transfusion.status == 'stopped' %}
|
|
<span class="badge bg-warning fs-6">Stopped</span>
|
|
{% elif transfusion.status == 'cancelled' %}
|
|
<span class="badge bg-danger fs-6">Cancelled</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END transfusion header -->
|
|
|
|
<!-- BEGIN main content -->
|
|
<div class="row">
|
|
<!-- BEGIN left column -->
|
|
<div class="col-xl-8">
|
|
<!-- BEGIN vital signs monitoring -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Vital Signs Monitoring</h4>
|
|
<div class="panel-heading-btn">
|
|
{% if transfusion.status == 'in_progress' %}
|
|
<button type="button" class="btn btn-primary btn-sm" onclick="recordVitals()">
|
|
<i class="fa fa-plus"></i> Record Vitals
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="panel-body">
|
|
{% if transfusion.vital_signs.exists %}
|
|
<div class="vital-signs-chart">
|
|
<canvas id="vitalSignsChart"></canvas>
|
|
</div>
|
|
|
|
<!-- Latest vitals summary -->
|
|
{% with latest_vitals=transfusion.vital_signs.first %}
|
|
{% if latest_vitals %}
|
|
<div class="row mt-3">
|
|
<div class="col-md-3">
|
|
<div class="text-center">
|
|
<h5 class="{% if latest_vitals.blood_pressure_normal %}vital-normal{% else %}vital-critical{% endif %}">
|
|
{{ latest_vitals.blood_pressure }}
|
|
</h5>
|
|
<small class="text-muted">Blood Pressure</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="text-center">
|
|
<h5 class="{% if latest_vitals.heart_rate_normal %}vital-normal{% else %}vital-warning{% endif %}">
|
|
{{ latest_vitals.heart_rate }}
|
|
</h5>
|
|
<small class="text-muted">Heart Rate</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="text-center">
|
|
<h5 class="{% if latest_vitals.temperature_normal %}vital-normal{% else %}vital-warning{% endif %}">
|
|
{{ latest_vitals.temperature }}°C
|
|
</h5>
|
|
<small class="text-muted">Temperature</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="text-center">
|
|
<h5 class="vital-normal">{{ latest_vitals.oxygen_saturation }}%</h5>
|
|
<small class="text-muted">O2 Saturation</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% endwith %}
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="fa fa-heartbeat fa-3x text-muted mb-3"></i>
|
|
<h5 class="text-muted">No vital signs recorded</h5>
|
|
{% if transfusion.status == 'in_progress' %}
|
|
<button type="button" class="btn btn-primary" onclick="recordVitals()">
|
|
<i class="fa fa-plus"></i> Record First Vitals
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<!-- END vital signs monitoring -->
|
|
|
|
<!-- BEGIN adverse reactions -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Adverse Reactions</h4>
|
|
<div class="panel-heading-btn">
|
|
{% if transfusion.status == 'in_progress' %}
|
|
<button type="button" class="btn btn-warning btn-sm" onclick="reportReaction()">
|
|
<i class="fa fa-exclamation-triangle"></i> Report Reaction
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="panel-body">
|
|
{% if transfusion.adverse_reactions.exists %}
|
|
{% for reaction in transfusion.adverse_reactions.all %}
|
|
<div class="alert alert-danger reaction-alert">
|
|
<div class="d-flex justify-content-between align-items-start">
|
|
<div>
|
|
<h6><i class="fa fa-exclamation-triangle"></i> {{ reaction.get_reaction_type_display }}</h6>
|
|
<p class="mb-1"><strong>Severity:</strong> {{ reaction.get_severity_display }}</p>
|
|
<p class="mb-1"><strong>Symptoms:</strong> {{ reaction.symptoms }}</p>
|
|
<p class="mb-0"><strong>Time:</strong> {{ reaction.onset_time|date:"H:i" }}</p>
|
|
</div>
|
|
<div class="text-end">
|
|
<span class="badge bg-danger">{{ reaction.severity|upper }}</span>
|
|
</div>
|
|
</div>
|
|
{% if reaction.treatment_given %}
|
|
<hr>
|
|
<p class="mb-0"><strong>Treatment:</strong> {{ reaction.treatment_given }}</p>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="fa fa-check-circle fa-3x text-success mb-3"></i>
|
|
<h5 class="text-success">No adverse reactions reported</h5>
|
|
<p class="text-muted">Transfusion proceeding without complications</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<!-- END adverse reactions -->
|
|
|
|
<!-- BEGIN transfusion timeline -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Transfusion Timeline</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="timeline">
|
|
<!-- Transfusion started -->
|
|
<div class="timeline-item">
|
|
<div class="timeline-time">{{ transfusion.start_time|date:"M d, Y H:i" }}</div>
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h6><i class="fa fa-play text-success"></i> Transfusion Started</h6>
|
|
<p class="mb-0">Started by {{ transfusion.started_by.get_full_name }}</p>
|
|
</div>
|
|
<span class="badge bg-success">Started</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Vital signs entries -->
|
|
{% for vitals in transfusion.vital_signs.all %}
|
|
<div class="timeline-item">
|
|
<div class="timeline-time">{{ vitals.recorded_at|date:"H:i" }}</div>
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h6><i class="fa fa-heartbeat text-info"></i> Vital Signs Recorded</h6>
|
|
<p class="mb-0">BP: {{ vitals.blood_pressure }}, HR: {{ vitals.heart_rate }}, Temp: {{ vitals.temperature }}°C</p>
|
|
</div>
|
|
<span class="badge bg-{% if vitals.is_normal %}success{% else %}warning{% endif %}">
|
|
{% if vitals.is_normal %}Normal{% else %}Abnormal{% endif %}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
|
|
<!-- Adverse reactions -->
|
|
{% for reaction in transfusion.adverse_reactions.all %}
|
|
<div class="timeline-item">
|
|
<div class="timeline-time">{{ reaction.onset_time|date:"H:i" }}</div>
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h6><i class="fa fa-exclamation-triangle text-danger"></i> Adverse Reaction</h6>
|
|
<p class="mb-0">{{ reaction.get_reaction_type_display }} - {{ reaction.get_severity_display }}</p>
|
|
</div>
|
|
<span class="badge bg-danger">{{ reaction.severity|upper }}</span>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
|
|
<!-- Transfusion completed/stopped -->
|
|
{% if transfusion.end_time %}
|
|
<div class="timeline-item">
|
|
<div class="timeline-time">{{ transfusion.end_time|date:"M d, Y H:i" }}</div>
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h6>
|
|
{% if transfusion.status == 'completed' %}
|
|
<i class="fa fa-check text-primary"></i> Transfusion Completed
|
|
{% else %}
|
|
<i class="fa fa-stop text-warning"></i> Transfusion Stopped
|
|
{% endif %}
|
|
</h6>
|
|
<p class="mb-0">
|
|
Volume transfused: {{ transfusion.volume_transfused }} ml
|
|
{% if transfusion.completion_notes %}
|
|
<br>Notes: {{ transfusion.completion_notes }}
|
|
{% endif %}
|
|
</p>
|
|
</div>
|
|
<span class="badge bg-{% if transfusion.status == 'completed' %}primary{% else %}warning{% endif %}">
|
|
{{ transfusion.get_status_display }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END transfusion timeline -->
|
|
</div>
|
|
<!-- END left column -->
|
|
|
|
<!-- BEGIN right column -->
|
|
<div class="col-xl-4">
|
|
<!-- BEGIN transfusion details -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Transfusion Details</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td class="fw-bold">Transfusion ID:</td>
|
|
<td>{{ transfusion.transfusion_id }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Blood Unit:</td>
|
|
<td>
|
|
<a href="{% url 'blood_bank:blood_unit_detail' transfusion.blood_unit.id %}">
|
|
{{ transfusion.blood_unit.unit_number }}
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Component:</td>
|
|
<td>{{ transfusion.blood_unit.component.get_name_display }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Volume:</td>
|
|
<td>{{ transfusion.blood_unit.volume_ml }} ml</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Rate:</td>
|
|
<td>{{ transfusion.transfusion_rate|default:"Standard" }} ml/hr</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Started By:</td>
|
|
<td>{{ transfusion.started_by.get_full_name }}</td>
|
|
</tr>
|
|
{% if transfusion.completed_by %}
|
|
<tr>
|
|
<td class="fw-bold">Completed By:</td>
|
|
<td>{{ transfusion.completed_by.get_full_name }}</td>
|
|
</tr>
|
|
{% endif %}
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<!-- END transfusion details -->
|
|
|
|
<!-- BEGIN patient information -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Patient Information</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td class="fw-bold">Name:</td>
|
|
<td>
|
|
<a href="{% url 'patients:patient_detail' transfusion.patient.id %}">
|
|
{{ transfusion.patient.full_name }}
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Patient ID:</td>
|
|
<td>{{ transfusion.patient.patient_id }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Blood Group:</td>
|
|
<td>
|
|
<span class="badge bg-primary">{{ transfusion.patient.blood_group.display_name|default:"Unknown" }}</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Age:</td>
|
|
<td>{{ transfusion.patient.age }} years</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-bold">Weight:</td>
|
|
<td>{{ transfusion.patient.weight|default:"Not recorded" }} kg</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<!-- END patient information -->
|
|
|
|
<!-- BEGIN quick actions -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Quick Actions</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="d-grid gap-2">
|
|
{% if transfusion.status == 'in_progress' %}
|
|
<button type="button" class="btn btn-primary" onclick="recordVitals()">
|
|
<i class="fa fa-heartbeat"></i> Record Vital Signs
|
|
</button>
|
|
<button type="button" class="btn btn-warning" onclick="reportReaction()">
|
|
<i class="fa fa-exclamation-triangle"></i> Report Adverse Reaction
|
|
</button>
|
|
<button type="button" class="btn btn-success" onclick="completeTransfusion()">
|
|
<i class="fa fa-check"></i> Complete Transfusion
|
|
</button>
|
|
<button type="button" class="btn btn-danger" onclick="stopTransfusion()">
|
|
<i class="fa fa-stop"></i> Stop Transfusion
|
|
</button>
|
|
{% endif %}
|
|
|
|
<button type="button" class="btn btn-outline-secondary" onclick="window.print()">
|
|
<i class="fa fa-print"></i> Print Report
|
|
</button>
|
|
<button type="button" class="btn btn-outline-info" onclick="generateReport()">
|
|
<i class="fa fa-file-pdf"></i> Generate PDF
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END quick actions -->
|
|
|
|
<!-- BEGIN related information -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Related Information</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="mb-3">
|
|
<h6>Blood Request</h6>
|
|
{% if transfusion.blood_issue.blood_request %}
|
|
<a href="{% url 'blood_bank:blood_request_detail' transfusion.blood_issue.blood_request.id %}" class="btn btn-outline-primary btn-sm">
|
|
View Original Request
|
|
</a>
|
|
{% else %}
|
|
<span class="text-muted">No associated request</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<h6>Crossmatch Results</h6>
|
|
{% if transfusion.blood_unit.crossmatch_results.exists %}
|
|
<span class="badge bg-success">Compatible</span>
|
|
{% else %}
|
|
<span class="badge bg-warning">Pending</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div>
|
|
<h6>Blood Tests</h6>
|
|
{% if transfusion.blood_unit.blood_tests.exists %}
|
|
<span class="badge bg-success">All Tests Passed</span>
|
|
{% else %}
|
|
<span class="badge bg-warning">Tests Pending</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END related information -->
|
|
</div>
|
|
<!-- END right column -->
|
|
</div>
|
|
<!-- END main content -->
|
|
|
|
<!-- BEGIN vital signs modal -->
|
|
<div class="modal fade" id="vitalsModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Record Vital Signs</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="vitalsForm">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="bloodPressure" class="form-label">Blood Pressure <span class="text-danger">*</span></label>
|
|
<input type="text" class="form-control" id="bloodPressure" placeholder="120/80" required>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="heartRate" class="form-label">Heart Rate (bpm) <span class="text-danger">*</span></label>
|
|
<input type="number" class="form-control" id="heartRate" placeholder="72" min="40" max="200" required>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="temperature" class="form-label">Temperature (°C) <span class="text-danger">*</span></label>
|
|
<input type="number" step="0.1" class="form-control" id="temperature" placeholder="36.5" min="35" max="42" required>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="respiratoryRate" class="form-label">Respiratory Rate</label>
|
|
<input type="number" class="form-control" id="respiratoryRate" placeholder="16" min="8" max="40">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="oxygenSaturation" class="form-label">Oxygen Saturation (%)</label>
|
|
<input type="number" class="form-control" id="oxygenSaturation" placeholder="98" min="70" max="100">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="painScore" class="form-label">Pain Score (0-10)</label>
|
|
<input type="number" class="form-control" id="painScore" placeholder="0" min="0" max="10">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="vitalsNotes" class="form-label">Notes</label>
|
|
<textarea class="form-control" id="vitalsNotes" rows="3" placeholder="Any observations or concerns..."></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="saveVitals()">Save Vital Signs</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END vital signs modal -->
|
|
|
|
<!-- BEGIN adverse reaction modal -->
|
|
<div class="modal fade" id="reactionModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header bg-danger text-white">
|
|
<h5 class="modal-title"><i class="fa fa-exclamation-triangle"></i> Report Adverse Reaction</h5>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="alert alert-danger">
|
|
<strong>Important:</strong> Stop the transfusion immediately if a severe reaction is suspected.
|
|
</div>
|
|
<form id="reactionForm">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="reactionType" class="form-label">Reaction Type <span class="text-danger">*</span></label>
|
|
<select class="form-select" id="reactionType" required>
|
|
<option value="">Select reaction type...</option>
|
|
<option value="allergic">Allergic Reaction</option>
|
|
<option value="febrile">Febrile Non-Hemolytic</option>
|
|
<option value="hemolytic">Hemolytic Reaction</option>
|
|
<option value="circulatory">Circulatory Overload</option>
|
|
<option value="bacterial">Bacterial Contamination</option>
|
|
<option value="other">Other</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="severity" class="form-label">Severity <span class="text-danger">*</span></label>
|
|
<select class="form-select" id="severity" required>
|
|
<option value="">Select severity...</option>
|
|
<option value="mild">Mild</option>
|
|
<option value="moderate">Moderate</option>
|
|
<option value="severe">Severe</option>
|
|
<option value="life_threatening">Life Threatening</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="symptoms" class="form-label">Symptoms <span class="text-danger">*</span></label>
|
|
<textarea class="form-control" id="symptoms" rows="3" placeholder="Describe the symptoms observed..." required></textarea>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="treatmentGiven" class="form-label">Treatment Given</label>
|
|
<textarea class="form-control" id="treatmentGiven" rows="3" placeholder="Describe any treatment administered..."></textarea>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="transfusionStopped">
|
|
<label class="form-check-label" for="transfusionStopped">
|
|
Transfusion was stopped due to this reaction
|
|
</label>
|
|
</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-danger" onclick="saveReaction()">Report Reaction</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END adverse reaction modal -->
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script src="{% static 'plugins/chart.js/dist/chart.umd.js' %}"></script>
|
|
|
|
<script>
|
|
$(document).ready(function() {
|
|
{% if transfusion.vital_signs.exists %}
|
|
initializeVitalSignsChart();
|
|
{% endif %}
|
|
|
|
// Auto-refresh if transfusion is active
|
|
{% if transfusion.status == 'in_progress' %}
|
|
setInterval(function() {
|
|
if (document.visibilityState === 'visible') {
|
|
location.reload();
|
|
}
|
|
}, 60000); // Refresh every minute
|
|
{% endif %}
|
|
});
|
|
|
|
function initializeVitalSignsChart() {
|
|
var ctx = document.getElementById('vitalSignsChart').getContext('2d');
|
|
|
|
var vitalSignsChart = new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: {{ vital_signs_times|safe }},
|
|
datasets: [{
|
|
label: 'Heart Rate',
|
|
data: {{ heart_rate_data|safe }},
|
|
borderColor: '#dc3545',
|
|
backgroundColor: 'rgba(220, 53, 69, 0.1)',
|
|
yAxisID: 'y'
|
|
}, {
|
|
label: 'Systolic BP',
|
|
data: {{ systolic_bp_data|safe }},
|
|
borderColor: '#007bff',
|
|
backgroundColor: 'rgba(0, 123, 255, 0.1)',
|
|
yAxisID: 'y'
|
|
}, {
|
|
label: 'Temperature',
|
|
data: {{ temperature_data|safe }},
|
|
borderColor: '#28a745',
|
|
backgroundColor: 'rgba(40, 167, 69, 0.1)',
|
|
yAxisID: 'y1'
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: {
|
|
y: {
|
|
type: 'linear',
|
|
display: true,
|
|
position: 'left',
|
|
title: {
|
|
display: true,
|
|
text: 'HR / BP'
|
|
}
|
|
},
|
|
y1: {
|
|
type: 'linear',
|
|
display: true,
|
|
position: 'right',
|
|
title: {
|
|
display: true,
|
|
text: 'Temperature (°C)'
|
|
},
|
|
grid: {
|
|
drawOnChartArea: false,
|
|
},
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function recordVitals() {
|
|
$('#vitalsModal').modal('show');
|
|
}
|
|
|
|
function saveVitals() {
|
|
var vitalsData = {
|
|
blood_pressure: $('#bloodPressure').val(),
|
|
heart_rate: $('#heartRate').val(),
|
|
temperature: $('#temperature').val(),
|
|
respiratory_rate: $('#respiratoryRate').val(),
|
|
oxygen_saturation: $('#oxygenSaturation').val(),
|
|
pain_score: $('#painScore').val(),
|
|
notes: $('#vitalsNotes').val()
|
|
};
|
|
|
|
// Validate required fields
|
|
if (!vitalsData.blood_pressure || !vitalsData.heart_rate || !vitalsData.temperature) {
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: 'Missing Information',
|
|
text: 'Please fill in blood pressure, heart rate, and temperature.',
|
|
confirmButtonText: 'OK'
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Here you would make an AJAX call to save the vitals
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: 'Vital Signs Recorded',
|
|
text: 'Vital signs have been recorded successfully.',
|
|
confirmButtonText: 'OK'
|
|
}).then(() => {
|
|
$('#vitalsModal').modal('hide');
|
|
location.reload();
|
|
});
|
|
}
|
|
|
|
function reportReaction() {
|
|
$('#reactionModal').modal('show');
|
|
}
|
|
|
|
function saveReaction() {
|
|
var reactionData = {
|
|
reaction_type: $('#reactionType').val(),
|
|
severity: $('#severity').val(),
|
|
symptoms: $('#symptoms').val(),
|
|
treatment_given: $('#treatmentGiven').val(),
|
|
transfusion_stopped: $('#transfusionStopped').is(':checked')
|
|
};
|
|
|
|
// Validate required fields
|
|
if (!reactionData.reaction_type || !reactionData.severity || !reactionData.symptoms) {
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: 'Missing Information',
|
|
text: 'Please fill in all required fields.',
|
|
confirmButtonText: 'OK'
|
|
});
|
|
return;
|
|
}
|
|
|
|
Swal.fire({
|
|
title: 'Report Adverse Reaction?',
|
|
text: 'This will create an adverse reaction report and may trigger additional protocols.',
|
|
icon: 'warning',
|
|
showCancelButton: true,
|
|
confirmButtonColor: '#d33',
|
|
cancelButtonColor: '#3085d6',
|
|
confirmButtonText: 'Yes, Report Reaction',
|
|
cancelButtonText: 'Cancel'
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
// Here you would make an AJAX call to save the reaction
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: 'Reaction Reported',
|
|
text: 'Adverse reaction has been reported and documented.',
|
|
confirmButtonText: 'OK'
|
|
}).then(() => {
|
|
$('#reactionModal').modal('hide');
|
|
location.reload();
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function completeTransfusion() {
|
|
Swal.fire({
|
|
title: 'Complete Transfusion?',
|
|
html: `
|
|
<div class="text-start">
|
|
<p>Please confirm the transfusion completion details:</p>
|
|
<div class="mb-3">
|
|
<label class="form-label">Volume Transfused (ml)</label>
|
|
<input type="number" class="form-control" id="volumeTransfused" value="{{ transfusion.blood_unit.volume_ml }}" min="1">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Completion Notes</label>
|
|
<textarea class="form-control" id="completionNotes" rows="3" placeholder="Any final observations..."></textarea>
|
|
</div>
|
|
</div>
|
|
`,
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Complete Transfusion',
|
|
cancelButtonText: 'Cancel',
|
|
preConfirm: () => {
|
|
const volume = document.getElementById('volumeTransfused').value;
|
|
const notes = document.getElementById('completionNotes').value;
|
|
|
|
if (!volume || volume <= 0) {
|
|
Swal.showValidationMessage('Please enter a valid volume');
|
|
return false;
|
|
}
|
|
|
|
return { volume, notes };
|
|
}
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: 'Transfusion Completed',
|
|
text: 'The transfusion has been marked as completed.',
|
|
confirmButtonText: 'OK'
|
|
}).then(() => {
|
|
location.reload();
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function stopTransfusion() {
|
|
Swal.fire({
|
|
title: 'Stop Transfusion?',
|
|
html: `
|
|
<div class="text-start">
|
|
<div class="alert alert-warning">
|
|
<strong>Warning:</strong> This will stop the transfusion before completion.
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Reason for Stopping <span class="text-danger">*</span></label>
|
|
<select class="form-select" id="stopReason">
|
|
<option value="">Select reason...</option>
|
|
<option value="adverse_reaction">Adverse Reaction</option>
|
|
<option value="patient_request">Patient Request</option>
|
|
<option value="physician_order">Physician Order</option>
|
|
<option value="technical_issue">Technical Issue</option>
|
|
<option value="other">Other</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Volume Transfused (ml)</label>
|
|
<input type="number" class="form-control" id="volumeTransfusedStop" min="0">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Notes</label>
|
|
<textarea class="form-control" id="stopNotes" rows="3" placeholder="Explain why the transfusion was stopped..."></textarea>
|
|
</div>
|
|
</div>
|
|
`,
|
|
showCancelButton: true,
|
|
confirmButtonColor: '#d33',
|
|
confirmButtonText: 'Stop Transfusion',
|
|
cancelButtonText: 'Continue Transfusion',
|
|
preConfirm: () => {
|
|
const reason = document.getElementById('stopReason').value;
|
|
const volume = document.getElementById('volumeTransfusedStop').value;
|
|
const notes = document.getElementById('stopNotes').value;
|
|
|
|
if (!reason) {
|
|
Swal.showValidationMessage('Please select a reason for stopping');
|
|
return false;
|
|
}
|
|
|
|
return { reason, volume, notes };
|
|
}
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: 'Transfusion Stopped',
|
|
text: 'The transfusion has been stopped and documented.',
|
|
confirmButtonText: 'OK'
|
|
}).then(() => {
|
|
location.reload();
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function generateReport() {
|
|
Swal.fire({
|
|
title: 'Generate Transfusion Report',
|
|
html: `
|
|
<div class="text-start">
|
|
<div class="mb-3">
|
|
<label class="form-label">Report Type</label>
|
|
<select class="form-select" id="reportType">
|
|
<option value="summary">Transfusion Summary</option>
|
|
<option value="detailed">Detailed Report</option>
|
|
<option value="adverse_events">Adverse Events Report</option>
|
|
<option value="vital_signs">Vital Signs Report</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Format</label>
|
|
<select class="form-select" id="reportFormat">
|
|
<option value="pdf">PDF</option>
|
|
<option value="excel">Excel</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
`,
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Generate Report',
|
|
cancelButtonText: 'Cancel'
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: 'Report Generated',
|
|
text: 'Transfusion report is being prepared for download.',
|
|
confirmButtonText: 'OK'
|
|
});
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|