465 lines
22 KiB
HTML
465 lines
22 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}Laboratory Dashboard - {{ 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-microscope me-2"></i>Laboratory Dashboard
|
|
</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 active">Laboratory</li>
|
|
</ol>
|
|
</nav>
|
|
</div>
|
|
<div class="btn-group">
|
|
<a href="{% url 'laboratory:lab_order_create' %}" class="btn btn-primary">
|
|
<i class="fas fa-plus me-2"></i>New Order
|
|
</a>
|
|
<a href="{% url 'laboratory:lab_result_create' %}" class="btn btn-success">
|
|
<i class="fas fa-flask me-2"></i>Enter Result
|
|
</a>
|
|
<div class="btn-group">
|
|
<button type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown">
|
|
<i class="fas fa-cog me-2"></i>Actions
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
<li><a class="dropdown-item" href="{% url 'laboratory:lab_test_list' %}">
|
|
<i class="fas fa-vial me-2"></i>Manage Tests
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="{% url 'laboratory:specimen_list' %}">
|
|
<i class="fas fa-microscope me-2"></i>View Specimens
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="{% url 'laboratory:quality_control_list' %}">
|
|
<i class="fas fa-check-circle me-2"></i>Quality Control
|
|
</a></li>
|
|
<li><hr class="dropdown-divider"></li>
|
|
<li><a class="dropdown-item" href="{% url 'laboratory:reference_range_list' %}">
|
|
<i class="fas fa-ruler me-2"></i>Reference Ranges
|
|
</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Statistics Cards -->
|
|
<div class="row mb-4" hx-get="{% url 'laboratory:laboratory_stats' %}" hx-trigger="load, every 30s">
|
|
<div class="col-xl-3 col-md-6 mb-3">
|
|
<div class="card bg-gradient-warning text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<div class="h2 mb-1">{{ pending_orders|default:0 }}</div>
|
|
<div class="small">Pending Orders</div>
|
|
</div>
|
|
<div class="fa-3x opacity-50">
|
|
<i class="fas fa-clock"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-footer bg-white bg-opacity-25">
|
|
<a href="{% url 'laboratory:lab_order_list' %}?status=PENDING" class="text-white text-decoration-none small">
|
|
View Details <i class="fas fa-arrow-right ms-1"></i>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-xl-3 col-md-6 mb-3">
|
|
<div class="card bg-gradient-info text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<div class="h2 mb-1">{{ in_progress_orders|default:0 }}</div>
|
|
<div class="small">In Progress</div>
|
|
</div>
|
|
<div class="fa-3x opacity-50">
|
|
<i class="fas fa-spinner"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-footer bg-white bg-opacity-25">
|
|
<a href="{% url 'laboratory:lab_order_list' %}?status=IN_PROGRESS" class="text-white text-decoration-none small">
|
|
View Details <i class="fas fa-arrow-right ms-1"></i>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-xl-3 col-md-6 mb-3">
|
|
<div class="card bg-gradient-success text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<div class="h2 mb-1">{{ specimens_collected|default:0 }}</div>
|
|
<div class="small">Specimens Today</div>
|
|
</div>
|
|
<div class="fa-3x opacity-50">
|
|
<i class="fas fa-vial"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-footer bg-white bg-opacity-25">
|
|
<a href="{% url 'laboratory:specimen_list' %}?date=today" class="text-white text-decoration-none small">
|
|
View Details <i class="fas fa-arrow-right ms-1"></i>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-xl-3 col-md-6 mb-3">
|
|
<div class="card bg-gradient-primary text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<div class="h2 mb-1">{{ results_completed_today|default:0 }}</div>
|
|
<div class="small">Results Today</div>
|
|
</div>
|
|
<div class="fa-3x opacity-50">
|
|
<i class="fas fa-chart-line"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-footer bg-white bg-opacity-25">
|
|
<a href="{% url 'laboratory:lab_result_list' %}?date=today" class="text-white text-decoration-none small">
|
|
View Details <i class="fas fa-arrow-right ms-1"></i>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Secondary Statistics -->
|
|
<div class="row mb-4">
|
|
<div class="col-xl-3 col-md-6 mb-3">
|
|
<div class="card border-warning">
|
|
<div class="card-body text-center">
|
|
<div class="h3 text-warning mb-1">{{ results_pending|default:0 }}</div>
|
|
<div class="text-muted">Results Pending</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-xl-3 col-md-6 mb-3">
|
|
<div class="card border-info">
|
|
<div class="card-body text-center">
|
|
<div class="h3 text-info mb-1">{{ qc_tests_today|default:0 }}</div>
|
|
<div class="text-muted">QC Tests Today</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-xl-3 col-md-6 mb-3">
|
|
<div class="card border-success">
|
|
<div class="card-body text-center">
|
|
<div class="h3 text-success mb-1">{{ total_tests_available|default:0 }}</div>
|
|
<div class="text-muted">Available Tests</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-xl-3 col-md-6 mb-3">
|
|
<div class="card border-danger">
|
|
<div class="card-body text-center">
|
|
<div class="h3 text-danger mb-1">{{ critical_results|default:0 }}</div>
|
|
<div class="text-muted">Critical Results</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Activity -->
|
|
<div class="row mb-4">
|
|
<!-- Recent Orders -->
|
|
<div class="col-lg-6 mb-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-clipboard-list me-2"></i>Recent Orders
|
|
</h5>
|
|
<a href="{% url 'laboratory:lab_order_list' %}" class="btn btn-outline-primary btn-sm">
|
|
<i class="fas fa-list me-1"></i>View All
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
{% if recent_orders %}
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Patient</th>
|
|
<th>Test</th>
|
|
<th>Status</th>
|
|
<th>Ordered</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for order in recent_orders %}
|
|
<tr>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<div class="bg-primary bg-gradient rounded-circle d-flex align-items-center justify-content-center me-2" style="width: 32px; height: 32px;">
|
|
<i class="fas fa-user text-white small"></i>
|
|
</div>
|
|
<div>
|
|
<div class="fw-semibold">{{ order.patient.get_full_name }}</div>
|
|
<small class="text-muted">{{ order.patient.mrn }}</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div class="fw-semibold">{{ order.test.test_name }}</div>
|
|
<small class="text-muted">{{ order.test.test_code }}</small>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-{% if order.status == 'PENDING' %}warning{% elif order.status == 'IN_PROGRESS' %}info{% elif order.status == 'COMPLETED' %}success{% else %}secondary{% endif %}">
|
|
{{ order.get_status_display }}
|
|
</span>
|
|
</td>
|
|
<td>{{ order.order_datetime|date:"M d, H:i" }}</td>
|
|
<td>
|
|
<a href="{% url 'laboratory:lab_order_detail' order.pk %}" class="btn btn-outline-primary btn-sm">
|
|
<i class="fas fa-eye"></i>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="fas fa-clipboard-list fa-2x text-muted mb-3"></i>
|
|
<h6 class="text-muted">No recent orders</h6>
|
|
<p class="text-muted">Recent laboratory orders will appear here</p>
|
|
<a href="{% url 'laboratory:lab_order_create' %}" class="btn btn-primary">
|
|
<i class="fas fa-plus me-2"></i>Create First Order
|
|
</a>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Results -->
|
|
<div class="col-lg-6 mb-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-chart-line me-2"></i>Recent Results
|
|
</h5>
|
|
<a href="{% url 'laboratory:lab_result_list' %}" class="btn btn-outline-success btn-sm">
|
|
<i class="fas fa-list me-1"></i>View All
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
{% if recent_results %}
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Patient</th>
|
|
<th>Test</th>
|
|
<th>Result</th>
|
|
<th>Status</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for result in recent_results %}
|
|
<tr>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<div class="bg-success bg-gradient rounded-circle d-flex align-items-center justify-content-center me-2" style="width: 32px; height: 32px;">
|
|
<i class="fas fa-user text-white small"></i>
|
|
</div>
|
|
<div>
|
|
<div class="fw-semibold">{{ result.order.patient.get_full_name }}</div>
|
|
<small class="text-muted">{{ result.order.patient.mrn }}</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div class="fw-semibold">{{ result.test.test_name }}</div>
|
|
<small class="text-muted">{{ result.test.test_code }}</small>
|
|
</td>
|
|
<td>
|
|
<div class="fw-semibold {% if result.is_critical %}text-danger{% elif result.is_abnormal %}text-warning{% else %}text-success{% endif %}">
|
|
{% if result.result_value %}
|
|
{{ result.result_value }} {{ result.unit|default:"" }}
|
|
{% else %}
|
|
{{ result.result_text|truncatechars:20 }}
|
|
{% endif %}
|
|
</div>
|
|
{% if result.is_critical %}
|
|
<small class="badge bg-danger">Critical</small>
|
|
{% elif result.is_abnormal %}
|
|
<small class="badge bg-warning">Abnormal</small>
|
|
{% endif %}
|
|
</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>
|
|
<a href="{% url 'laboratory:lab_result_detail' result.pk %}" class="btn btn-outline-success btn-sm">
|
|
<i class="fas fa-eye"></i>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="fas fa-chart-line fa-2x text-muted mb-3"></i>
|
|
<h6 class="text-muted">No recent results</h6>
|
|
<p class="text-muted">Recent laboratory results will appear here</p>
|
|
<a href="{% url 'laboratory:lab_result_create' %}" class="btn btn-success">
|
|
<i class="fas fa-flask me-2"></i>Enter First Result
|
|
</a>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-bolt me-2"></i>Quick Actions
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-lg-3 col-md-6">
|
|
<a href="{% url 'laboratory:lab_test_list' %}" class="btn btn-outline-primary w-100 h-100 d-flex flex-column align-items-center justify-content-center p-4">
|
|
<i class="fas fa-vial fa-2x mb-2"></i>
|
|
<span class="fw-semibold">Manage Tests</span>
|
|
<small class="text-muted">Configure lab tests</small>
|
|
</a>
|
|
</div>
|
|
<div class="col-lg-3 col-md-6">
|
|
<a href="{% url 'laboratory:specimen_list' %}" class="btn btn-outline-info w-100 h-100 d-flex flex-column align-items-center justify-content-center p-4">
|
|
<i class="fas fa-microscope fa-2x mb-2"></i>
|
|
<span class="fw-semibold">View Specimens</span>
|
|
<small class="text-muted">Track specimens</small>
|
|
</a>
|
|
</div>
|
|
<div class="col-lg-3 col-md-6">
|
|
<a href="{% url 'laboratory:quality_control_list' %}" class="btn btn-outline-success w-100 h-100 d-flex flex-column align-items-center justify-content-center p-4">
|
|
<i class="fas fa-check-circle fa-2x mb-2"></i>
|
|
<span class="fw-semibold">Quality Control</span>
|
|
<small class="text-muted">QC management</small>
|
|
</a>
|
|
</div>
|
|
<div class="col-lg-3 col-md-6">
|
|
<a href="{% url 'laboratory:reference_range_list' %}" class="btn btn-outline-warning w-100 h-100 d-flex flex-column align-items-center justify-content-center p-4">
|
|
<i class="fas fa-ruler fa-2x mb-2"></i>
|
|
<span class="fw-semibold">Reference Ranges</span>
|
|
<small class="text-muted">Manage ranges</small>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Auto-refresh functionality
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Refresh statistics every 30 seconds
|
|
setInterval(function() {
|
|
htmx.trigger('[hx-get]', 'refresh');
|
|
}, 30000);
|
|
});
|
|
|
|
// Real-time notifications for critical results
|
|
{#function checkCriticalResults() {#}
|
|
{# fetch('{% url "laboratory:check_critical_results" %}')#}
|
|
{# .then(response => response.json())#}
|
|
{# .then(data => {#}
|
|
{# if (data.critical_count > 0) {#}
|
|
{# showCriticalAlert(data.critical_count);#}
|
|
{# }#}
|
|
{# })#}
|
|
{# .catch(error => console.error('Error checking critical results:', error));#}
|
|
{# }#}
|
|
|
|
function showCriticalAlert(count) {
|
|
const alertHtml = `
|
|
<div class="alert alert-danger alert-dismissible fade show position-fixed top-0 end-0 m-3" style="z-index: 1050;" role="alert">
|
|
<strong><i class="fas fa-exclamation-triangle me-2"></i>Critical Results Alert!</strong>
|
|
${count} critical result(s) require immediate attention.
|
|
<a href="{% url 'laboratory:lab_result_list' %}?critical=true" class="alert-link">View Now</a>
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
</div>
|
|
`;
|
|
document.body.insertAdjacentHTML('beforeend', alertHtml);
|
|
}
|
|
|
|
// Check for critical results every 2 minutes
|
|
setInterval(checkCriticalResults, 120000);
|
|
</script>
|
|
|
|
<style>
|
|
.bg-gradient {
|
|
background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
|
}
|
|
|
|
.card-footer {
|
|
border-top: 1px solid rgba(255, 255, 255, 0.125);
|
|
}
|
|
|
|
.opacity-50 {
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.btn:hover {
|
|
transform: translateY(-1px);
|
|
transition: transform 0.2s ease-in-out;
|
|
}
|
|
|
|
@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;
|
|
}
|
|
|
|
.fa-2x {
|
|
font-size: 1.5em;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|