464 lines
23 KiB
HTML
464 lines
23 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}{% if object %}Edit Lab Result{% else %}New Lab Result{% endif %}{% 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"><a href="{% url 'laboratory:lab_result_list' %}">Results</a></li>
|
|
<li class="breadcrumb-item active">{% if object %}Edit{% else %}New{% endif %}</li>
|
|
</ol>
|
|
<h1 class="page-header mb-0">
|
|
{% if object %}Edit Lab Result{% else %}Create Lab Result{% endif %}
|
|
</h1>
|
|
</div>
|
|
<div class="ms-auto">
|
|
<a href="{% url 'laboratory:lab_result_list' %}" class="btn btn-secondary">
|
|
<i class="fas fa-arrow-left me-2"></i>Back to Results
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-xl-8">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">
|
|
<i class="fas fa-flask me-2"></i>
|
|
Lab Result Information
|
|
</h4>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if messages %}
|
|
{% for message in messages %}
|
|
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
|
|
{{ message }}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
</div>
|
|
{% endfor %}
|
|
{% endif %}
|
|
|
|
<form method="post" novalidate>
|
|
{% csrf_token %}
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.order.id_for_label }}" class="form-label">Lab Order *</label>
|
|
<select class="form-select {% if form.order.errors %}is-invalid{% endif %}"
|
|
id="{{ form.order.id_for_label }}"
|
|
name="{{ form.order.name }}"
|
|
required>
|
|
<option value="">Select Lab Order</option>
|
|
{% for choice in form.order.field.choices %}
|
|
{% if choice.0 %}
|
|
<option value="{{ choice.0 }}" {% if choice.0 == form.order.value %}selected{% endif %}>
|
|
{{ choice.1 }}
|
|
</option>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</select>
|
|
{% if form.order.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.order.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.test.id_for_label }}" class="form-label">Test *</label>
|
|
<select class="form-select {% if form.test.errors %}is-invalid{% endif %}"
|
|
id="{{ form.test.id_for_label }}"
|
|
name="{{ form.test.name }}"
|
|
required>
|
|
<option value="">Select Test</option>
|
|
{% for choice in form.test.field.choices %}
|
|
{% if choice.0 %}
|
|
<option value="{{ choice.0 }}" {% if choice.0 == form.test.value %}selected{% endif %}>
|
|
{{ choice.1 }}
|
|
</option>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</select>
|
|
{% if form.test.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.test.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-4">
|
|
<div class="mb-3">
|
|
<label for="{{ form.result_value.id_for_label }}" class="form-label">Result Value *</label>
|
|
<input type="text"
|
|
class="form-control {% if form.result_value.errors %}is-invalid{% endif %}"
|
|
id="{{ form.result_value.id_for_label }}"
|
|
name="{{ form.result_value.name }}"
|
|
value="{{ form.result_value.value|default:'' }}"
|
|
placeholder="Enter result value"
|
|
required>
|
|
{% if form.result_value.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.result_value.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="mb-3">
|
|
<label for="{{ form.unit.id_for_label }}" class="form-label">Unit</label>
|
|
<input type="text"
|
|
class="form-control {% if form.unit.errors %}is-invalid{% endif %}"
|
|
id="{{ form.unit.id_for_label }}"
|
|
name="{{ form.unit.name }}"
|
|
value="{{ form.unit.value|default:'' }}"
|
|
placeholder="e.g., mg/dL, mmol/L">
|
|
{% if form.unit.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.unit.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="mb-3">
|
|
<label for="{{ form.status.id_for_label }}" class="form-label">Status *</label>
|
|
<select class="form-select {% if form.status.errors %}is-invalid{% endif %}"
|
|
id="{{ form.status.id_for_label }}"
|
|
name="{{ form.status.name }}"
|
|
required>
|
|
<option value="">Select Status</option>
|
|
{% for choice in form.status.field.choices %}
|
|
<option value="{{ choice.0 }}" {% if choice.0 == form.status.value %}selected{% endif %}>
|
|
{{ choice.1 }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
{% if form.status.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.status.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.reference_range.id_for_label }}" class="form-label">Reference Range</label>
|
|
<input type="text"
|
|
class="form-control {% if form.reference_range.errors %}is-invalid{% endif %}"
|
|
id="{{ form.reference_range.id_for_label }}"
|
|
name="{{ form.reference_range.name }}"
|
|
value="{{ form.reference_range.value|default:'' }}"
|
|
placeholder="e.g., 70-100 mg/dL">
|
|
{% if form.reference_range.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.reference_range.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.abnormal_flag.id_for_label }}" class="form-label">Abnormal Flag</label>
|
|
<select class="form-select {% if form.abnormal_flag.errors %}is-invalid{% endif %}"
|
|
id="{{ form.abnormal_flag.id_for_label }}"
|
|
name="{{ form.abnormal_flag.name }}">
|
|
<option value="">Normal</option>
|
|
{% for choice in form.abnormal_flag.field.choices %}
|
|
<option value="{{ choice.0 }}" {% if choice.0 == form.abnormal_flag.value %}selected{% endif %}>
|
|
{{ choice.1 }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
{% if form.abnormal_flag.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.abnormal_flag.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.performed_by.id_for_label }}" class="form-label">Performed By *</label>
|
|
<select class="form-select {% if form.performed_by.errors %}is-invalid{% endif %}"
|
|
id="{{ form.performed_by.id_for_label }}"
|
|
name="{{ form.performed_by.name }}"
|
|
required>
|
|
<option value="">Select Technician</option>
|
|
{% for choice in form.performed_by.field.choices %}
|
|
{% if choice.0 %}
|
|
<option value="{{ choice.0 }}" {% if choice.0 == form.performed_by.value %}selected{% endif %}>
|
|
{{ choice.1 }}
|
|
</option>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</select>
|
|
{% if form.performed_by.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.performed_by.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.verified_by.id_for_label }}" class="form-label">Verified By</label>
|
|
<select class="form-select {% if form.verified_by.errors %}is-invalid{% endif %}"
|
|
id="{{ form.verified_by.id_for_label }}"
|
|
name="{{ form.verified_by.name }}">
|
|
<option value="">Select Pathologist</option>
|
|
{% for choice in form.verified_by.field.choices %}
|
|
{% if choice.0 %}
|
|
<option value="{{ choice.0 }}" {% if choice.0 == form.verified_by.value %}selected{% endif %}>
|
|
{{ choice.1 }}
|
|
</option>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</select>
|
|
{% if form.verified_by.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.verified_by.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.performed_at.id_for_label }}" class="form-label">Performed Date/Time *</label>
|
|
<input type="datetime-local"
|
|
class="form-control {% if form.performed_at.errors %}is-invalid{% endif %}"
|
|
id="{{ form.performed_at.id_for_label }}"
|
|
name="{{ form.performed_at.name }}"
|
|
value="{{ form.performed_at.value|default:'' }}"
|
|
required>
|
|
{% if form.performed_at.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.performed_at.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label for="{{ form.verified_at.id_for_label }}" class="form-label">Verified Date/Time</label>
|
|
<input type="datetime-local"
|
|
class="form-control {% if form.verified_at.errors %}is-invalid{% endif %}"
|
|
id="{{ form.verified_at.id_for_label }}"
|
|
name="{{ form.verified_at.name }}"
|
|
value="{{ form.verified_at.value|default:'' }}">
|
|
{% if form.verified_at.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.verified_at.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="{{ form.comments.id_for_label }}" class="form-label">Comments</label>
|
|
<textarea class="form-control {% if form.comments.errors %}is-invalid{% endif %}"
|
|
id="{{ form.comments.id_for_label }}"
|
|
name="{{ form.comments.name }}"
|
|
rows="4"
|
|
placeholder="Enter any comments or observations about the result...">{{ form.comments.value|default:'' }}</textarea>
|
|
{% if form.comments.errors %}
|
|
<div class="invalid-feedback">
|
|
{{ form.comments.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-between">
|
|
<div>
|
|
{% if object %}
|
|
<a href="{% url 'laboratory:lab_result_detail' object.pk %}" class="btn btn-secondary">
|
|
<i class="fas fa-times me-2"></i>Cancel
|
|
</a>
|
|
{% else %}
|
|
<a href="{% url 'laboratory:lab_result_list' %}" class="btn btn-secondary">
|
|
<i class="fas fa-times me-2"></i>Cancel
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
<div>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-save me-2"></i>
|
|
{% if object %}Update Result{% else %}Create Result{% endif %}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-xl-4">
|
|
<!-- Reference Information -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-info-circle me-2"></i>
|
|
Reference Information
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<h6><i class="fas fa-flag text-success me-2"></i>Normal Range</h6>
|
|
<p class="small text-muted">
|
|
Values within the reference range are considered normal for the general population.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<h6><i class="fas fa-exclamation-triangle text-warning me-2"></i>Abnormal Flags</h6>
|
|
<ul class="small text-muted">
|
|
<li><strong>H (High):</strong> Above normal range</li>
|
|
<li><strong>L (Low):</strong> Below normal range</li>
|
|
<li><strong>HH (Critical High):</strong> Critically high</li>
|
|
<li><strong>LL (Critical Low):</strong> Critically low</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="alert alert-info">
|
|
<small>
|
|
<strong>Note:</strong> Always verify results with reference ranges
|
|
specific to your laboratory and patient demographics.
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-bolt me-2"></i>
|
|
Quick Actions
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="d-grid gap-2">
|
|
<button type="button" class="btn btn-outline-primary" onclick="loadTestDefaults()">
|
|
<i class="fas fa-download me-2"></i>Load Test Defaults
|
|
</button>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="validateResult()">
|
|
<i class="fas fa-check me-2"></i>Validate Result
|
|
</button>
|
|
<button type="button" class="btn btn-outline-info" onclick="viewHistory()">
|
|
<i class="fas fa-history me-2"></i>View Patient History
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const testSelect = document.getElementById('{{ form.test.id_for_label }}');
|
|
const resultValueInput = document.getElementById('{{ form.result_value.id_for_label }}');
|
|
const unitInput = document.getElementById('{{ form.unit.id_for_label }}');
|
|
const referenceRangeInput = document.getElementById('{{ form.reference_range.id_for_label }}');
|
|
const abnormalFlagSelect = document.getElementById('{{ form.abnormal_flag.id_for_label }}');
|
|
|
|
// Load test defaults when test changes
|
|
testSelect.addEventListener('change', function() {
|
|
const testId = this.value;
|
|
if (testId) {
|
|
loadTestDefaults();
|
|
}
|
|
});
|
|
|
|
// Auto-validate result when value changes
|
|
resultValueInput.addEventListener('blur', function() {
|
|
validateResult();
|
|
});
|
|
|
|
// Set current date/time for performed_at if empty
|
|
const performedAtInput = document.getElementById('{{ form.performed_at.id_for_label }}');
|
|
if (!performedAtInput.value) {
|
|
const now = new Date();
|
|
now.setMinutes(now.getMinutes() - now.getTimezoneOffset());
|
|
performedAtInput.value = now.toISOString().slice(0, 16);
|
|
}
|
|
});
|
|
|
|
function loadTestDefaults() {
|
|
const testSelect = document.getElementById('{{ form.test.id_for_label }}');
|
|
const testId = testSelect.value;
|
|
|
|
if (!testId) return;
|
|
|
|
// Simulate loading test defaults
|
|
// In a real implementation, this would make an AJAX call
|
|
const testName = testSelect.options[testSelect.selectedIndex].text;
|
|
|
|
// Example defaults based on common tests
|
|
const defaults = {
|
|
'Glucose': { unit: 'mg/dL', range: '70-100' },
|
|
'Hemoglobin': { unit: 'g/dL', range: '12.0-15.5' },
|
|
'Cholesterol': { unit: 'mg/dL', range: '<200' },
|
|
'Creatinine': { unit: 'mg/dL', range: '0.6-1.2' }
|
|
};
|
|
|
|
for (const [test, data] of Object.entries(defaults)) {
|
|
if (testName.includes(test)) {
|
|
document.getElementById('{{ form.unit.id_for_label }}').value = data.unit;
|
|
document.getElementById('{{ form.reference_range.id_for_label }}').value = data.range;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
function validateResult() {
|
|
const resultValue = document.getElementById('{{ form.result_value.id_for_label }}').value;
|
|
const referenceRange = document.getElementById('{{ form.reference_range.id_for_label }}').value;
|
|
const abnormalFlagSelect = document.getElementById('{{ form.abnormal_flag.id_for_label }}');
|
|
|
|
if (!resultValue || !referenceRange) return;
|
|
|
|
// Simple validation logic
|
|
const numericValue = parseFloat(resultValue);
|
|
if (isNaN(numericValue)) return;
|
|
|
|
// Parse reference range (simplified)
|
|
const rangeMatch = referenceRange.match(/(\d+\.?\d*)-(\d+\.?\d*)/);
|
|
if (rangeMatch) {
|
|
const min = parseFloat(rangeMatch[1]);
|
|
const max = parseFloat(rangeMatch[2]);
|
|
|
|
if (numericValue < min) {
|
|
abnormalFlagSelect.value = 'L';
|
|
} else if (numericValue > max) {
|
|
abnormalFlagSelect.value = 'H';
|
|
} else {
|
|
abnormalFlagSelect.value = '';
|
|
}
|
|
}
|
|
}
|
|
|
|
function viewHistory() {
|
|
// In a real implementation, this would show patient's test history
|
|
alert('Patient test history would be displayed here.');
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|