560 lines
25 KiB
HTML
560 lines
25 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}Lab Result - {{ lab_result.test.test_name }} - {{ block.super }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<!-- Page Header -->
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<div>
|
|
<h1 class="h3 mb-1">
|
|
<i class="fas fa-chart-line me-2"></i>Lab Result - {{ lab_result.test.test_name }}
|
|
</h1>
|
|
<nav aria-label="breadcrumb">
|
|
<ol class="breadcrumb mb-0">
|
|
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'laboratory:dashboard' %}">Laboratory</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'laboratory:lab_result_list' %}">Results</a></li>
|
|
<li class="breadcrumb-item active">{{ lab_result.test.test_name }}</li>
|
|
</ol>
|
|
</nav>
|
|
</div>
|
|
<div class="btn-group">
|
|
<button type="button" class="btn btn-outline-secondary" onclick="printResult()">
|
|
<i class="fas fa-print me-2"></i>Print
|
|
</button>
|
|
<div class="btn-group">
|
|
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown">
|
|
<i class="fas fa-cog me-2"></i>Actions
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
{% if lab_result.status == 'PENDING' and perms.laboratory.change_labresult %}
|
|
<li><a class="dropdown-item" href="#" onclick="verifyResult()">
|
|
<i class="fas fa-check me-2"></i>Verify Result
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="reviewResult()">
|
|
<i class="fas fa-eye me-2"></i>Mark as Reviewed
|
|
</a></li>
|
|
{% endif %}
|
|
<li><a class="dropdown-item" href="#" onclick="exportResult()">
|
|
<i class="fas fa-download me-2"></i>Export Result
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="emailResult()">
|
|
<i class="fas fa-envelope me-2"></i>Email Result
|
|
</a></li>
|
|
<li><hr class="dropdown-divider"></li>
|
|
<li><a class="dropdown-item" href="{% url 'laboratory:lab_order_detail' lab_result.order.pk %}">
|
|
<i class="fas fa-clipboard-list me-2"></i>View Order
|
|
</a></li>
|
|
{% if lab_result.is_critical %}
|
|
<li><hr class="dropdown-divider"></li>
|
|
<li><a class="dropdown-item text-danger" href="#" onclick="notifyCritical()">
|
|
<i class="fas fa-exclamation-triangle me-2"></i>Send Critical Alert
|
|
</a></li>
|
|
{% endif %}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Critical Result Alert -->
|
|
{% if lab_result.is_critical %}
|
|
<div class="alert alert-danger">
|
|
<h6 class="alert-heading">
|
|
<i class="fas fa-exclamation-triangle me-2"></i>Critical Result Alert
|
|
</h6>
|
|
<p class="mb-0">
|
|
This result contains critical values that require immediate attention.
|
|
The ordering provider has been notified automatically.
|
|
</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Abnormal Result Alert -->
|
|
{% if lab_result.is_abnormal and not lab_result.is_critical %}
|
|
<div class="alert alert-warning">
|
|
<i class="fas fa-info-circle me-2"></i>
|
|
This result is outside the normal reference range.
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="row">
|
|
<!-- Result Information -->
|
|
<div class="col-lg-8 mb-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-chart-bar me-2"></i>Result Details
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<!-- Main Result Display -->
|
|
<div class="text-center mb-4 p-4 bg-light rounded">
|
|
<h2 class="display-4 mb-2 {% if lab_result.is_critical %}text-danger{% elif lab_result.is_abnormal %}text-warning{% else %}text-success{% endif %}">
|
|
{{ lab_result.result_value }}
|
|
{% if lab_result.unit %}
|
|
<small class="text-muted">{{ lab_result.unit }}</small>
|
|
{% endif %}
|
|
</h2>
|
|
<h5 class="text-muted">{{ lab_result.test.test_name }}</h5>
|
|
{% if lab_result.test_component %}
|
|
<p class="text-muted mb-0">{{ lab_result.test_component }}</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td class="fw-semibold text-muted">Test:</td>
|
|
<td>
|
|
<a href="{% url 'laboratory:lab_test_detail' lab_result.test.pk %}" class="text-decoration-none">
|
|
{{ lab_result.test.test_name }}
|
|
</a>
|
|
<br><small class="text-muted">{{ lab_result.test.test_code }}</small>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-semibold text-muted">Result Value:</td>
|
|
<td class="fw-semibold {% if lab_result.is_critical %}text-danger{% elif lab_result.is_abnormal %}text-warning{% else %}text-success{% endif %}">
|
|
{{ lab_result.result_value }} {{ lab_result.unit|default:"" }}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-semibold text-muted">Reference Range:</td>
|
|
<td>{{ lab_result.reference_range|default:"Not specified" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-semibold text-muted">Status:</td>
|
|
<td>
|
|
<span class="badge bg-{% if lab_result.status == 'VERIFIED' %}success{% elif lab_result.status == 'REVIEWED' %}info{% else %}warning{% endif %} fs-6">
|
|
{{ lab_result.get_status_display }}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-semibold text-muted">Result Date:</td>
|
|
<td>{{ lab_result.result_datetime|date:"M d, Y H:i" }}</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td class="fw-semibold text-muted">Performed By:</td>
|
|
<td>{{ lab_result.performed_by.get_full_name|default:"System" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-semibold text-muted">Verified By:</td>
|
|
<td>{{ lab_result.verified_by.get_full_name|default:"Pending" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-semibold text-muted">Method:</td>
|
|
<td>{{ lab_result.method|default:"Standard" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-semibold text-muted">Instrument:</td>
|
|
<td>{{ lab_result.instrument|default:"N/A" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-semibold text-muted">Created:</td>
|
|
<td>{{ lab_result.created_at|date:"M d, Y H:i" }}</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Result Flags -->
|
|
<div class="mt-3">
|
|
<h6 class="fw-semibold text-muted mb-2">Result Flags:</h6>
|
|
<div class="d-flex flex-wrap gap-2">
|
|
{% if lab_result.is_critical %}
|
|
<span class="badge bg-danger">Critical</span>
|
|
{% endif %}
|
|
{% if lab_result.is_abnormal %}
|
|
<span class="badge bg-warning">Abnormal</span>
|
|
{% endif %}
|
|
{% if lab_result.status == 'VERIFIED' %}
|
|
<span class="badge bg-success">Verified</span>
|
|
{% endif %}
|
|
{% if not lab_result.is_abnormal and not lab_result.is_critical %}
|
|
<span class="badge bg-success">Normal</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
{% if lab_result.comments %}
|
|
<div class="mt-3">
|
|
<h6 class="fw-semibold text-muted">Comments:</h6>
|
|
<div class="alert alert-light">
|
|
{{ lab_result.comments }}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Order and Patient Information -->
|
|
<div class="col-lg-4 mb-4">
|
|
<!-- Order Information -->
|
|
<div class="card mb-3">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-clipboard-list me-2"></i>Order Information
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<table class="table table-borderless table-sm">
|
|
<tr>
|
|
<td class="fw-semibold text-muted">Order #:</td>
|
|
<td>
|
|
<a href="{% url 'laboratory:lab_order_detail' lab_result.order.pk %}" class="text-decoration-none">
|
|
{{ lab_result.order.order_number }}
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-semibold text-muted">Priority:</td>
|
|
<td>
|
|
<span class="badge bg-{% if lab_result.order.priority == 'URGENT' %}danger{% elif lab_result.order.priority == 'STAT' %}warning{% else %}primary{% endif %}">
|
|
{{ lab_result.order.get_priority_display }}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-semibold text-muted">Ordered:</td>
|
|
<td>{{ lab_result.order.order_datetime|date:"M d, Y H:i" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-semibold text-muted">Provider:</td>
|
|
<td>{{ lab_result.order.ordering_provider.get_full_name }}</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Patient Information -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-user me-2"></i>Patient Information
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="text-center mb-3">
|
|
<div class="bg-primary bg-gradient rounded-circle d-flex align-items-center justify-content-center mx-auto mb-2" style="width: 50px; height: 50px;">
|
|
<i class="fas fa-user fa-lg text-white"></i>
|
|
</div>
|
|
<h6 class="mb-1">{{ lab_result.order.patient.get_full_name }}</h6>
|
|
<small class="text-muted">MRN: {{ lab_result.order.patient.mrn }}</small>
|
|
</div>
|
|
|
|
<table class="table table-borderless table-sm">
|
|
<tr>
|
|
<td class="fw-semibold text-muted">Age:</td>
|
|
<td>{{ lab_result.order.patient.age }} years</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-semibold text-muted">Gender:</td>
|
|
<td>{{ lab_result.order.patient.get_gender_display }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="fw-semibold text-muted">DOB:</td>
|
|
<td>{{ lab_result.order.patient.date_of_birth|date:"M d, Y" }}</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<div class="d-grid">
|
|
<a href="{% url 'patients:patient_detail' lab_result.order.patient.pk %}" class="btn btn-outline-primary btn-sm">
|
|
<i class="fas fa-eye me-2"></i>View Patient
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Reference Range Comparison -->
|
|
{% if lab_result.reference_range %}
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-ruler me-2"></i>Reference Range Comparison
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row align-items-center">
|
|
<div class="col-md-8">
|
|
<div class="position-relative">
|
|
<!-- Reference Range Visualization -->
|
|
<div class="bg-light rounded p-3">
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<small class="text-muted">Low</small>
|
|
<small class="text-muted">Normal Range</small>
|
|
<small class="text-muted">High</small>
|
|
</div>
|
|
<div class="progress" style="height: 20px;">
|
|
<div class="progress-bar bg-danger" style="width: 20%"></div>
|
|
<div class="progress-bar bg-success" style="width: 60%"></div>
|
|
<div class="progress-bar bg-danger" style="width: 20%"></div>
|
|
</div>
|
|
<div class="d-flex justify-content-center mt-2">
|
|
<div class="position-relative">
|
|
<i class="fas fa-caret-down fa-lg {% if lab_result.is_critical %}text-danger{% elif lab_result.is_abnormal %}text-warning{% else %}text-success{% endif %}"></i>
|
|
<small class="position-absolute top-100 start-50 translate-middle-x text-nowrap">
|
|
{{ lab_result.result_value }}
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="text-center">
|
|
<h6 class="text-muted">Reference Range</h6>
|
|
<div class="h5 mb-0">{{ lab_result.reference_range }}</div>
|
|
<small class="text-muted">{{ lab_result.unit|default:"" }}</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Historical Results -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-history me-2"></i>Historical Results for {{ lab_result.test.test_name }}
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if historical_results %}
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Date</th>
|
|
<th>Result</th>
|
|
<th>Reference Range</th>
|
|
<th>Status</th>
|
|
<th>Provider</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for result in historical_results %}
|
|
<tr {% if result.pk == lab_result.pk %}class="table-primary"{% endif %}>
|
|
<td>{{ result.result_datetime|date:"M d, Y" }}</td>
|
|
<td class="fw-semibold {% if result.is_critical %}text-danger{% elif result.is_abnormal %}text-warning{% else %}text-success{% endif %}">
|
|
{{ result.result_value }} {{ result.unit|default:"" }}
|
|
{% if result.pk == lab_result.pk %}
|
|
<small class="badge bg-primary ms-1">Current</small>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ result.reference_range|default:"N/A" }}</td>
|
|
<td>
|
|
<span class="badge bg-{% if result.status == 'VERIFIED' %}success{% elif result.status == 'REVIEWED' %}info{% else %}warning{% endif %}">
|
|
{{ result.get_status_display }}
|
|
</span>
|
|
</td>
|
|
<td>{{ result.order.ordering_provider.get_full_name }}</td>
|
|
<td>
|
|
{% if result.pk != lab_result.pk %}
|
|
<a href="{% url 'laboratory:lab_result_detail' result.pk %}" class="btn btn-outline-primary btn-sm">
|
|
<i class="fas fa-eye"></i>
|
|
</a>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="fas fa-history fa-2x text-muted mb-3"></i>
|
|
<h6 class="text-muted">No historical results</h6>
|
|
<p class="text-muted">This is the first result for this test for this patient</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function printResult() {
|
|
window.print();
|
|
}
|
|
|
|
function exportResult() {
|
|
// Export result data
|
|
const resultData = {
|
|
test: '{{ lab_result.test.test_name }}',
|
|
patient: '{{ lab_result.order.patient.get_full_name }}',
|
|
result_value: '{{ lab_result.result_value }}',
|
|
unit: '{{ lab_result.unit|default:"" }}',
|
|
reference_range: '{{ lab_result.reference_range|default:"" }}',
|
|
status: '{{ lab_result.get_status_display }}',
|
|
result_date: '{{ lab_result.result_datetime|date:"Y-m-d H:i" }}',
|
|
is_critical: {{ lab_result.is_critical|yesno:"true,false" }},
|
|
is_abnormal: {{ lab_result.is_abnormal|yesno:"true,false" }}
|
|
};
|
|
|
|
const dataStr = JSON.stringify(resultData, null, 2);
|
|
const dataBlob = new Blob([dataStr], {type: 'application/json'});
|
|
const url = URL.createObjectURL(dataBlob);
|
|
const link = document.createElement('a');
|
|
link.href = url;
|
|
link.download = 'result_{{ lab_result.pk }}.json';
|
|
link.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
function verifyResult() {
|
|
if (confirm('Verify this result? This action cannot be undone.')) {
|
|
fetch(`{% url 'laboratory:verify_result' lab_result.pk %}`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Error: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function reviewResult() {
|
|
if (confirm('Mark this result as reviewed?')) {
|
|
fetch(`{% url 'laboratory:review_result' lab_result.pk %}`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Error: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function emailResult() {
|
|
const email = prompt('Enter email address to send result:');
|
|
if (email) {
|
|
fetch(`{% url 'laboratory:email_result' lab_result.pk %}`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({email: email})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
alert('Result sent successfully!');
|
|
} else {
|
|
alert('Error: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function notifyCritical() {
|
|
if (confirm('Send critical result notification to ordering provider?')) {
|
|
fetch(`{% url 'laboratory:notify_critical' lab_result.pk %}`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
alert('Critical notification sent!');
|
|
} else {
|
|
alert('Error: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.bg-gradient {
|
|
background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
|
}
|
|
|
|
.progress {
|
|
border-radius: 10px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.table-primary {
|
|
--bs-table-bg: rgba(13, 110, 253, 0.1);
|
|
}
|
|
|
|
@media print {
|
|
.btn, .dropdown, .breadcrumb {
|
|
display: none !important;
|
|
}
|
|
|
|
.card {
|
|
border: 1px solid #dee2e6 !important;
|
|
box-shadow: none !important;
|
|
}
|
|
|
|
.alert {
|
|
border: 1px solid #dee2e6 !important;
|
|
box-shadow: none !important;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.d-flex.justify-content-between {
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.btn-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.table-responsive {
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.display-4 {
|
|
font-size: 2rem;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|