Marwan Alwali be70e47e22 update
2025-08-30 09:45:26 +03:00

505 lines
24 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}Specimens{% endblock %}
{% block content %}
<div class="d-flex align-items-center mb-3">
<div>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'laboratory:dashboard' %}">Laboratory</a></li>
<li class="breadcrumb-item active">Specimens</li>
</ol>
<h1 class="page-header mb-0">Specimens</h1>
</div>
<div class="ms-auto">
<div class="btn-group">
<a href="{% url 'laboratory:specimen_create' %}" class="btn btn-primary">
<i class="fas fa-plus me-2"></i>New Specimen
</a>
<button type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown">
<span class="visually-hidden">Toggle Dropdown</span>
<i class="fas fa-ellipsis-v"></i>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" onclick="exportSpecimens()">
<i class="fas fa-download me-2"></i>Export Specimens
</a></li>
<li><a class="dropdown-item" href="#" onclick="printLabels()">
<i class="fas fa-print me-2"></i>Print Labels
</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="bulkUpdate()">
<i class="fas fa-edit me-2"></i>Bulk Update
</a></li>
<li><a class="dropdown-item" href="#" onclick="generateReport()">
<i class="fas fa-chart-bar me-2"></i>Generate Report
</a></li>
</ul>
</div>
</div>
</div>
<!-- Statistics Cards -->
<div class="row mb-4">
<div class="col-xl-3 col-md-6">
<div class="card bg-primary text-white">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<div class="fs-4 fw-bold">{{ total_specimens|default:0 }}</div>
<div>Total Specimens</div>
</div>
<div class="flex-shrink-0">
<i class="fas fa-vial fa-2x opacity-75"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6">
<div class="card bg-success text-white">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<div class="fs-4 fw-bold">{{ collected_today|default:0 }}</div>
<div>Collected Today</div>
</div>
<div class="flex-shrink-0">
<i class="fas fa-calendar-day fa-2x opacity-75"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6">
<div class="card bg-warning text-white">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<div class="fs-4 fw-bold">{{ processing|default:0 }}</div>
<div>Processing</div>
</div>
<div class="flex-shrink-0">
<i class="fas fa-cog fa-2x opacity-75"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6">
<div class="card bg-danger text-white">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<div class="fs-4 fw-bold">{{ rejected|default:0 }}</div>
<div>Rejected</div>
</div>
<div class="flex-shrink-0">
<i class="fas fa-times-circle fa-2x opacity-75"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Filters and Search -->
<div class="card mb-4">
<div class="card-body">
<form method="get" class="row g-3">
<div class="col-md-3">
<label for="search" class="form-label">Search</label>
<input type="text"
class="form-control"
id="search"
name="search"
value="{{ request.GET.search }}"
placeholder="Search specimens...">
</div>
<div class="col-md-2">
<label for="specimen_type" class="form-label">Type</label>
<select class="form-select" id="specimen_type" name="specimen_type">
<option value="">All Types</option>
<option value="blood" {% if request.GET.specimen_type == 'blood' %}selected{% endif %}>Blood</option>
<option value="urine" {% if request.GET.specimen_type == 'urine' %}selected{% endif %}>Urine</option>
<option value="sputum" {% if request.GET.specimen_type == 'sputum' %}selected{% endif %}>Sputum</option>
<option value="swab" {% if request.GET.specimen_type == 'swab' %}selected{% endif %}>Swab</option>
<option value="tissue" {% if request.GET.specimen_type == 'tissue' %}selected{% endif %}>Tissue</option>
</select>
</div>
<div class="col-md-2">
<label for="status" class="form-label">Status</label>
<select class="form-select" id="status" name="status">
<option value="">All Status</option>
<option value="collected" {% if request.GET.status == 'collected' %}selected{% endif %}>Collected</option>
<option value="processing" {% if request.GET.status == 'processing' %}selected{% endif %}>Processing</option>
<option value="completed" {% if request.GET.status == 'completed' %}selected{% endif %}>Completed</option>
<option value="rejected" {% if request.GET.status == 'rejected' %}selected{% endif %}>Rejected</option>
</select>
</div>
<div class="col-md-2">
<label for="priority" class="form-label">Priority</label>
<select class="form-select" id="priority" name="priority">
<option value="">All Priorities</option>
<option value="routine" {% if request.GET.priority == 'routine' %}selected{% endif %}>Routine</option>
<option value="urgent" {% if request.GET.priority == 'urgent' %}selected{% endif %}>Urgent</option>
<option value="stat" {% if request.GET.priority == 'stat' %}selected{% endif %}>STAT</option>
</select>
</div>
<div class="col-md-2">
<label for="date_range" class="form-label">Date Range</label>
<select class="form-select" id="date_range" name="date_range">
<option value="">All Dates</option>
<option value="today" {% if request.GET.date_range == 'today' %}selected{% endif %}>Today</option>
<option value="yesterday" {% if request.GET.date_range == 'yesterday' %}selected{% endif %}>Yesterday</option>
<option value="week" {% if request.GET.date_range == 'week' %}selected{% endif %}>This Week</option>
<option value="month" {% if request.GET.date_range == 'month' %}selected{% endif %}>This Month</option>
</select>
</div>
<div class="col-md-1">
<label class="form-label">&nbsp;</label>
<div class="d-grid">
<button type="submit" class="btn btn-primary">
<i class="fas fa-search"></i>
</button>
</div>
</div>
</form>
</div>
</div>
<!-- Specimens Table -->
<div class="card">
<div class="card-header">
<div class="d-flex align-items-center">
<h4 class="card-title mb-0">
<i class="fas fa-list me-2"></i>
Specimens
{% if object_list %}
<span class="badge bg-secondary ms-2">{{ object_list|length }}</span>
{% endif %}
</h4>
<div class="ms-auto">
<div class="btn-group btn-group-sm">
<button type="button" class="btn btn-outline-secondary" onclick="selectAll()">
<i class="fas fa-check-square me-1"></i>Select All
</button>
<button type="button" class="btn btn-outline-secondary" onclick="clearSelection()">
<i class="fas fa-square me-1"></i>Clear
</button>
<button type="button" class="btn btn-outline-primary" onclick="refreshTable()">
<i class="fas fa-sync me-1"></i>Refresh
</button>
</div>
</div>
</div>
</div>
<div class="card-body p-0">
{% if object_list %}
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th width="40">
<input type="checkbox" id="selectAllCheckbox" onchange="toggleAll(this)">
</th>
<th>Specimen ID</th>
<th>Patient</th>
<th>Type</th>
<th>Collection Date</th>
<th>Collected By</th>
<th>Status</th>
<th>Priority</th>
<th>Tests</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for specimen in specimens %}
<tr>
<td>
<input type="checkbox" class="specimen-checkbox" value="{{ specimen.pk }}">
</td>
<td>
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<div class="fw-bold">
<a href="{% url 'laboratory:specimen_detail' specimen.pk %}" class="text-decoration-none">
{{ specimen.specimen_id }}
</a>
</div>
{% if specimen.barcode %}
<div class="small text-muted">{{ specimen.barcode }}</div>
{% endif %}
</div>
</div>
</td>
<td>
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<div class="fw-bold">{{ specimen.patient.get_full_name }}</div>
<div class="small text-muted">MRN: {{ specimen.patient.medical_record_number }}</div>
</div>
</div>
</td>
<td>
<div class="d-flex align-items-center">
<i class="fas
fa-{% if specimen.specimen_type == 'BLOOD' %}tint text-danger
{% elif specimen.specimen_type == 'URINE' %}flask text-warning
{% elif specimen.specimen_type == 'SPUTUM' %}lungs text-info
{% elif specimen.specimen_type == 'PLASMA' %}tint text-yellow
{% elif specimen.specimen_type == 'SERUM' %}bottle-droplet text-info
{% elif specimen.specimen_type == 'STOOL' %}poop text-cyan
{% elif specimen.specimen_type == 'CSF' %}eye text-primary
{% elif specimen.specimen_type == 'SWAB' %}hand text-secondary
{% elif specimen.specimen_type == 'TISSUE' %}microscope text-green
{% elif specimen.specimen_type == 'FLUID' %}droplet text-info
{% elif specimen.specimen_type == 'SALIVA' %}lungs text-info
{% else %}vial-vertical text-secondary{% endif %} me-1"></i>
<span class="fw-bold">{{ specimen.get_specimen_type_display }}</span>
</div>
{% if specimen.volume %}
<div class="small text-muted">{{ specimen.volume }}</div>
{% endif %}
</td>
<td>
<div class="fw-bold">{{ specimen.collected_datetime|date:"M d, Y" }}</div>
<div class="small text-muted">{{ specimen.collected_datetime|date:"g:i A" }}</div>
</td>
<td>{{ specimen.collected_by.get_full_name }}</td>
<td>
<span class="badge bg-{% if specimen.status == 'COLLECTED' %}success{% elif specimen.status == 'PROCESSING' %}warning{% elif specimen.status == 'COMPLETED' %}info{% elif specimen.status == 'REJECTED' %}danger{% else %}secondary{% endif %}">
{{ specimen.get_status_display }}
</span>
{% if specimen.condition and specimen.condition != 'good' %}
<div class="small text-warning mt-1">
<i class="fas fa-exclamation-triangle me-1"></i>{{ specimen.condition|title }}
</div>
{% endif %}
</td>
<td>
<span class="badge bg-{% if specimen.order.priority == 'STAT' %}danger{% elif specimen.order.priority == 'URGENT' %}warning{% else %}primary{% endif %}">
{{ specimen.order.get_priority_display }}
</span>
{% if specimen.fasting_status %}
<div class="small text-info mt-1">
<i class="fas fa-utensils me-1"></i>Fasting
</div>
{% endif %}
</td>
<td>
{% if specimen.associated_tests_count %}
<span class="badge bg-info">{{ specimen.associated_tests_count }} test{{ specimen.associated_tests_count|pluralize }}</span>
{% else %}
<span class="text-muted">No tests</span>
{% endif %}
</td>
<td>
<div class="btn-group btn-group-sm">
<a href="{% url 'laboratory:specimen_detail' specimen.pk %}"
class="btn btn-outline-primary"
title="View Details">
<i class="fas fa-eye"></i>
</a>
<a href="{% url 'laboratory:specimen_update' specimen.pk %}"
class="btn btn-outline-secondary"
title="Edit">
<i class="fas fa-edit"></i>
</a>
<button type="button"
class="btn btn-outline-info"
onclick="printLabel({{ specimen.pk }})"
title="Print Label">
<i class="fas fa-print"></i>
</button>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Pagination -->
{% if is_paginated %}
{% include 'partial/pagination.html' %}
{% endif %}
<!-- Bulk Actions -->
<div class="card-footer bg-light" id="bulkActions" style="display: none;">
<div class="d-flex align-items-center">
<div class="me-3">
<span id="selectedCount">0</span> specimen(s) selected
</div>
<div class="btn-group btn-group-sm">
<button type="button" class="btn btn-success" onclick="bulkUpdateStatus('processing')">
<i class="fas fa-play me-1"></i>Start Processing
</button>
<button type="button" class="btn btn-info" onclick="bulkUpdateStatus('completed')">
<i class="fas fa-check me-1"></i>Mark Complete
</button>
<button type="button" class="btn btn-warning" onclick="bulkUpdateStatus('on_hold')">
<i class="fas fa-pause me-1"></i>Put on Hold
</button>
<button type="button" class="btn btn-outline-primary" onclick="bulkPrintLabels()">
<i class="fas fa-print me-1"></i>Print Labels
</button>
<button type="button" class="btn btn-outline-secondary" onclick="bulkExport()">
<i class="fas fa-download me-1"></i>Export
</button>
<button type="button" class="btn btn-danger" onclick="bulkDelete()">
<i class="fas fa-trash me-1"></i>Delete
</button>
</div>
</div>
</div>
{% else %}
<div class="text-center py-5">
<i class="fas fa-vial fa-3x text-muted mb-3"></i>
<h5>No Specimens Found</h5>
<p class="text-muted">No specimens match your current filters.</p>
<a href="{% url 'laboratory:specimen_form' %}" class="btn btn-primary">
<i class="fas fa-plus me-2"></i>Collect First Specimen
</a>
</div>
{% endif %}
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
updateBulkActions();
// Auto-refresh every 30 seconds for real-time updates
setInterval(function() {
if (document.visibilityState === 'visible') {
refreshTable();
}
}, 30000);
});
function toggleAll(checkbox) {
const checkboxes = document.querySelectorAll('.specimen-checkbox');
checkboxes.forEach(cb => cb.checked = checkbox.checked);
updateBulkActions();
}
function selectAll() {
const checkboxes = document.querySelectorAll('.specimen-checkbox');
checkboxes.forEach(cb => cb.checked = true);
document.getElementById('selectAllCheckbox').checked = true;
updateBulkActions();
}
function clearSelection() {
const checkboxes = document.querySelectorAll('.specimen-checkbox');
checkboxes.forEach(cb => cb.checked = false);
document.getElementById('selectAllCheckbox').checked = false;
updateBulkActions();
}
function updateBulkActions() {
const checkboxes = document.querySelectorAll('.specimen-checkbox:checked');
const count = checkboxes.length;
const bulkActions = document.getElementById('bulkActions');
const selectedCount = document.getElementById('selectedCount');
if (count > 0) {
bulkActions.style.display = 'block';
selectedCount.textContent = count;
} else {
bulkActions.style.display = 'none';
}
}
// Add event listeners to checkboxes
document.addEventListener('change', function(e) {
if (e.target.classList.contains('specimen-checkbox')) {
updateBulkActions();
}
});
function getSelectedSpecimens() {
const checkboxes = document.querySelectorAll('.specimen-checkbox:checked');
return Array.from(checkboxes).map(cb => cb.value);
}
function bulkUpdateStatus(status) {
const selected = getSelectedSpecimens();
if (selected.length === 0) return;
const statusText = status.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase());
if (confirm(`Update status to "${statusText}" for ${selected.length} selected specimen(s)?`)) {
// In a real implementation, this would make an AJAX call
console.log('Updating status to', status, 'for specimens:', selected);
location.reload();
}
}
function bulkPrintLabels() {
const selected = getSelectedSpecimens();
if (selected.length === 0) return;
// In a real implementation, this would print labels for selected specimens
console.log('Printing labels for specimens:', selected);
alert(`Printing labels for ${selected.length} specimen(s).`);
}
function bulkExport() {
const selected = getSelectedSpecimens();
if (selected.length === 0) return;
// In a real implementation, this would export selected specimens
console.log('Exporting specimens:', selected);
alert('Export functionality would be implemented here.');
}
function bulkDelete() {
const selected = getSelectedSpecimens();
if (selected.length === 0) return;
if (confirm(`Are you sure you want to delete ${selected.length} selected specimen(s)? This action cannot be undone.`)) {
// In a real implementation, this would make an AJAX call
console.log('Deleting specimens:', selected);
location.reload();
}
}
function printLabel(specimenId) {
// In a real implementation, this would print a single specimen label
console.log('Printing label for specimen:', specimenId);
alert('Label printing functionality would be implemented here.');
}
function refreshTable() {
// In a real implementation, this would refresh the table via AJAX
location.reload();
}
function exportSpecimens() {
// In a real implementation, this would export all specimens
alert('Export all specimens functionality would be implemented here.');
}
function printLabels() {
// In a real implementation, this would print labels for all visible specimens
alert('Print all labels functionality would be implemented here.');
}
function bulkUpdate() {
// In a real implementation, this would open a bulk update modal
alert('Bulk update functionality would be implemented here.');
}
function generateReport() {
// In a real implementation, this would generate a specimens report
alert('Generate report functionality would be implemented here.');
}
</script>
{% endblock %}