2025-08-12 13:33:25 +03:00

741 lines
35 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}{% if object %}Edit Vital Signs{% else %}Record Vital Signs{% endif %} - {{ 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">
{% if object %}
<i class="fas fa-edit me-2"></i>Edit Vital Signs
{% else %}
<i class="fas fa-heartbeat me-2"></i>Record Vital Signs
{% endif %}
</h1>
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="{% url 'emr:dashboard' %}">EMR</a></li>
<li class="breadcrumb-item"><a href="{% url 'emr:vital_signs_list' %}">Vital Signs</a></li>
{% if object %}
<li class="breadcrumb-item active">Edit</li>
{% else %}
<li class="breadcrumb-item active">New</li>
{% endif %}
</ol>
</nav>
</div>
<div class="btn-group">
<a href="{% url 'emr:vital_signs_list' %}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-2"></i>Back to List
</a>
</div>
</div>
<form method="post" novalidate>
{% csrf_token %}
<div class="row">
<!-- Main Form -->
<div class="col-lg-8">
<!-- Patient Information -->
<div class="card mb-4">
<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="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.patient.id_for_label }}" class="form-label">
Patient <span class="text-danger">*</span>
</label>
{{ form.patient }}
{% if form.patient.errors %}
<div class="invalid-feedback d-block">
{{ form.patient.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.encounter.id_for_label }}" class="form-label">
Encounter
</label>
{{ form.encounter }}
{% if form.encounter.errors %}
<div class="invalid-feedback d-block">
{{ form.encounter.errors.0 }}
</div>
{% endif %}
<div class="form-text">Optional: Associate with specific encounter</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.measured_datetime.id_for_label }}" class="form-label">
Measurement Date & Time <span class="text-danger">*</span>
</label>
{{ form.measured_datetime }}
{% if form.measured_datetime.errors %}
<div class="invalid-feedback d-block">
{{ form.measured_datetime.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.measured_by.id_for_label }}" class="form-label">
Measured By
</label>
{{ form.measured_by }}
{% if form.measured_by.errors %}
<div class="invalid-feedback d-block">
{{ form.measured_by.errors.0 }}
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<!-- Vital Signs Measurements -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-thermometer-half me-2"></i>Vital Signs Measurements
</h5>
</div>
<div class="card-body">
<!-- Blood Pressure -->
<div class="row mb-4">
<div class="col-12">
<h6 class="text-primary mb-3">
<i class="fas fa-heart me-2"></i>Blood Pressure
</h6>
</div>
<div class="col-md-3">
<div class="mb-3">
<label for="{{ form.systolic_bp.id_for_label }}" class="form-label">
Systolic (mmHg)
</label>
<div class="input-group">
{{ form.systolic_bp }}
<span class="input-group-text">mmHg</span>
</div>
{% if form.systolic_bp.errors %}
<div class="invalid-feedback d-block">
{{ form.systolic_bp.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label for="{{ form.diastolic_bp.id_for_label }}" class="form-label">
Diastolic (mmHg)
</label>
<div class="input-group">
{{ form.diastolic_bp }}
<span class="input-group-text">mmHg</span>
</div>
{% if form.diastolic_bp.errors %}
<div class="invalid-feedback d-block">
{{ form.diastolic_bp.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label for="{{ form.bp_position.id_for_label }}" class="form-label">
Position
</label>
{{ form.bp_position }}
{% if form.bp_position.errors %}
<div class="invalid-feedback d-block">
{{ form.bp_position.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label for="{{ form.bp_cuff_size.id_for_label }}" class="form-label">
Cuff Size
</label>
{{ form.bp_cuff_size }}
{% if form.bp_cuff_size.errors %}
<div class="invalid-feedback d-block">
{{ form.bp_cuff_size.errors.0 }}
</div>
{% endif %}
</div>
</div>
</div>
<!-- Heart Rate and Temperature -->
<div class="row mb-4">
<div class="col-md-3">
<div class="mb-3">
<label for="{{ form.heart_rate.id_for_label }}" class="form-label">
<i class="fas fa-heartbeat me-1"></i>Heart Rate
</label>
<div class="input-group">
{{ form.heart_rate }}
<span class="input-group-text">bpm</span>
</div>
{% if form.heart_rate.errors %}
<div class="invalid-feedback d-block">
{{ form.heart_rate.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label for="{{ form.temperature.id_for_label }}" class="form-label">
<i class="fas fa-thermometer-half me-1"></i>Temperature
</label>
<div class="input-group">
{{ form.temperature }}
<span class="input-group-text">°F</span>
</div>
{% if form.temperature.errors %}
<div class="invalid-feedback d-block">
{{ form.temperature.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label for="{{ form.temperature_route.id_for_label }}" class="form-label">
Temperature Route
</label>
{{ form.temperature_route }}
{% if form.temperature_route.errors %}
<div class="invalid-feedback d-block">
{{ form.temperature_route.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label for="{{ form.respiratory_rate.id_for_label }}" class="form-label">
<i class="fas fa-lungs me-1"></i>Respiratory Rate
</label>
<div class="input-group">
{{ form.respiratory_rate }}
<span class="input-group-text">rpm</span>
</div>
{% if form.respiratory_rate.errors %}
<div class="invalid-feedback d-block">
{{ form.respiratory_rate.errors.0 }}
</div>
{% endif %}
</div>
</div>
</div>
<!-- Oxygen and Physical Measurements -->
<div class="row mb-4">
<div class="col-md-3">
<div class="mb-3">
<label for="{{ form.oxygen_saturation.id_for_label }}" class="form-label">
<i class="fas fa-lungs me-1"></i>Oxygen Saturation
</label>
<div class="input-group">
{{ form.oxygen_saturation }}
<span class="input-group-text">%</span>
</div>
{% if form.oxygen_saturation.errors %}
<div class="invalid-feedback d-block">
{{ form.oxygen_saturation.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label for="{{ form.height.id_for_label }}" class="form-label">
<i class="fas fa-ruler-vertical me-1"></i>Height
</label>
<div class="input-group">
{{ form.height }}
<span class="input-group-text">cm</span>
</div>
{% if form.height.errors %}
<div class="invalid-feedback d-block">
{{ form.height.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label for="{{ form.weight.id_for_label }}" class="form-label">
<i class="fas fa-weight me-1"></i>Weight
</label>
<div class="input-group">
{{ form.weight }}
<span class="input-group-text">kg</span>
</div>
{% if form.weight.errors %}
<div class="invalid-feedback d-block">
{{ form.weight.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-3">
<div class="mb-3">
<label class="form-label">
<i class="fas fa-calculator me-1"></i>BMI
</label>
<div class="input-group">
<input type="text" class="form-control" id="bmi_display" readonly placeholder="Auto-calculated">
<span class="input-group-text">kg/m²</span>
</div>
<div class="form-text">Automatically calculated from height and weight</div>
</div>
</div>
</div>
<!-- Pain and Additional Measurements -->
<div class="row">
<div class="col-md-4">
<div class="mb-3">
<label for="{{ form.pain_scale.id_for_label }}" class="form-label">
<i class="fas fa-exclamation-triangle me-1"></i>Pain Scale (0-10)
</label>
{{ form.pain_scale }}
{% if form.pain_scale.errors %}
<div class="invalid-feedback d-block">
{{ form.pain_scale.errors.0 }}
</div>
{% endif %}
<div class="form-text">0 = No pain, 10 = Worst possible pain</div>
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="{{ form.blood_glucose.id_for_label }}" class="form-label">
<i class="fas fa-tint me-1"></i>Blood Glucose
</label>
<div class="input-group">
{{ form.blood_glucose }}
<span class="input-group-text">mg/dL</span>
</div>
{% if form.blood_glucose.errors %}
<div class="invalid-feedback d-block">
{{ form.blood_glucose.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="{{ form.head_circumference.id_for_label }}" class="form-label">
<i class="fas fa-circle me-1"></i>Head Circumference
</label>
<div class="input-group">
{{ form.head_circumference }}
<span class="input-group-text">cm</span>
</div>
{% if form.head_circumference.errors %}
<div class="invalid-feedback d-block">
{{ form.head_circumference.errors.0 }}
</div>
{% endif %}
<div class="form-text">Typically for pediatric patients</div>
</div>
</div>
</div>
</div>
</div>
<!-- Additional Information -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-notes-medical me-2"></i>Additional Information
</h5>
</div>
<div class="card-body">
<div class="mb-3">
<label for="{{ form.notes.id_for_label }}" class="form-label">
Clinical Notes
</label>
{{ form.notes }}
{% if form.notes.errors %}
<div class="invalid-feedback d-block">
{{ form.notes.errors.0 }}
</div>
{% endif %}
<div class="form-text">Any additional observations or notes about the vital signs</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.device_used.id_for_label }}" class="form-label">
Device Used
</label>
{{ form.device_used }}
{% if form.device_used.errors %}
<div class="invalid-feedback d-block">
{{ form.device_used.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ form.location.id_for_label }}" class="form-label">
Location
</label>
{{ form.location }}
{% if form.location.errors %}
<div class="invalid-feedback d-block">
{{ form.location.errors.0 }}
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Sidebar -->
<div class="col-lg-4">
<!-- Critical Values Alert -->
<div class="card mb-4" id="critical-values-card" style="display: none;">
<div class="card-header bg-danger text-white">
<h5 class="mb-0">
<i class="fas fa-exclamation-triangle me-2"></i>Critical Values Detected
</h5>
</div>
<div class="card-body">
<div id="critical-values-list"></div>
<div class="alert alert-warning mt-3 mb-0">
<i class="fas fa-info-circle me-2"></i>
<small>Please verify these values and notify the physician immediately if confirmed.</small>
</div>
</div>
</div>
<!-- Normal Ranges Reference -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-chart-line me-2"></i>Normal Ranges
</h5>
</div>
<div class="card-body">
<div class="accordion" id="normalRangesAccordion">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#adultRanges">
Adult Normal Ranges
</button>
</h2>
<div id="adultRanges" class="accordion-collapse collapse" data-bs-parent="#normalRangesAccordion">
<div class="accordion-body">
<ul class="list-unstyled mb-0">
<li><strong>Blood Pressure:</strong> 90-120/60-80 mmHg</li>
<li><strong>Heart Rate:</strong> 60-100 bpm</li>
<li><strong>Temperature:</strong> 97.8-99.1°F</li>
<li><strong>Respiratory Rate:</strong> 12-20 rpm</li>
<li><strong>Oxygen Saturation:</strong> 95-100%</li>
<li><strong>BMI:</strong> 18.5-24.9 kg/m²</li>
</ul>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#pediatricRanges">
Pediatric Ranges
</button>
</h2>
<div id="pediatricRanges" class="accordion-collapse collapse" data-bs-parent="#normalRangesAccordion">
<div class="accordion-body">
<p class="text-muted mb-2">Ranges vary by age. Consult pediatric reference charts.</p>
<ul class="list-unstyled mb-0">
<li><strong>Newborn HR:</strong> 120-160 bpm</li>
<li><strong>Infant HR:</strong> 80-140 bpm</li>
<li><strong>Child HR:</strong> 70-120 bpm</li>
<li><strong>Adolescent HR:</strong> 60-90 bpm</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Actions -->
<div class="card">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-cog me-2"></i>Actions
</h5>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-2"></i>
{% if object %}Update Vital Signs{% else %}Record Vital Signs{% endif %}
</button>
<a href="{% url 'emr:vital_signs_list' %}" class="btn btn-outline-secondary">
<i class="fas fa-times me-2"></i>Cancel
</a>
<button type="button" class="btn btn-outline-info" onclick="clearForm()">
<i class="fas fa-eraser me-2"></i>Clear Form
</button>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
<script>
// Vital signs form functionality
document.addEventListener('DOMContentLoaded', function() {
// Add Bootstrap classes to form elements
const form = document.querySelector('form');
const inputs = form.querySelectorAll('input, select, textarea');
inputs.forEach(input => {
if (!input.classList.contains('form-control') && !input.classList.contains('form-select')) {
if (input.tagName === 'SELECT') {
input.classList.add('form-select');
} else {
input.classList.add('form-control');
}
}
});
// Set default measurement time to now if creating new record
{% if not object %}
const measuredDatetime = document.getElementById('{{ form.measured_datetime.id_for_label }}');
if (measuredDatetime && !measuredDatetime.value) {
const now = new Date();
const localISOTime = new Date(now.getTime() - now.getTimezoneOffset() * 60000).toISOString().slice(0, 16);
measuredDatetime.value = localISOTime;
}
{% endif %}
// BMI calculation
const heightInput = document.getElementById('{{ form.height.id_for_label }}');
const weightInput = document.getElementById('{{ form.weight.id_for_label }}');
const bmiDisplay = document.getElementById('bmi_display');
function calculateBMI() {
const height = parseFloat(heightInput.value);
const weight = parseFloat(weightInput.value);
if (height && weight && height > 0) {
const heightInMeters = height / 100;
const bmi = weight / (heightInMeters * heightInMeters);
bmiDisplay.value = bmi.toFixed(1);
// Add BMI category
let category = '';
if (bmi < 18.5) category = ' (Underweight)';
else if (bmi < 25) category = ' (Normal)';
else if (bmi < 30) category = ' (Overweight)';
else category = ' (Obese)';
bmiDisplay.value += category;
} else {
bmiDisplay.value = '';
}
}
if (heightInput && weightInput) {
heightInput.addEventListener('input', calculateBMI);
weightInput.addEventListener('input', calculateBMI);
calculateBMI(); // Calculate on page load
}
// Critical values monitoring
const criticalRanges = {
systolic_bp: { min: 90, max: 180, unit: 'mmHg' },
diastolic_bp: { min: 60, max: 110, unit: 'mmHg' },
heart_rate: { min: 60, max: 100, unit: 'bpm' },
temperature: { min: 97.0, max: 100.4, unit: '°F' },
respiratory_rate: { min: 12, max: 20, unit: 'rpm' },
oxygen_saturation: { min: 95, max: 100, unit: '%' }
};
function checkCriticalValues() {
const criticalValues = [];
Object.keys(criticalRanges).forEach(fieldName => {
const input = document.getElementById(`id_${fieldName}`);
if (input && input.value) {
const value = parseFloat(input.value);
const range = criticalRanges[fieldName];
if (value < range.min || value > range.max) {
criticalValues.push({
field: fieldName.replace('_', ' ').toUpperCase(),
value: value,
unit: range.unit,
range: `${range.min}-${range.max} ${range.unit}`
});
}
}
});
const criticalCard = document.getElementById('critical-values-card');
const criticalList = document.getElementById('critical-values-list');
if (criticalValues.length > 0) {
criticalList.innerHTML = criticalValues.map(cv =>
`<div class="alert alert-danger py-2 mb-2">
<strong>${cv.field}:</strong> ${cv.value} ${cv.unit}<br>
<small>Normal: ${cv.range}</small>
</div>`
).join('');
criticalCard.style.display = 'block';
} else {
criticalCard.style.display = 'none';
}
}
// Add event listeners for critical value checking
Object.keys(criticalRanges).forEach(fieldName => {
const input = document.getElementById(`id_${fieldName}`);
if (input) {
input.addEventListener('input', checkCriticalValues);
}
});
// Check on page load
checkCriticalValues();
});
function clearForm() {
if (confirm('Are you sure you want to clear all form data?')) {
const form = document.querySelector('form');
const inputs = form.querySelectorAll('input, select, textarea');
inputs.forEach(input => {
if (input.type !== 'hidden' && input.name !== 'csrfmiddlewaretoken') {
if (input.type === 'checkbox') {
input.checked = false;
} else {
input.value = '';
}
}
});
// Reset BMI display
document.getElementById('bmi_display').value = '';
// Hide critical values card
document.getElementById('critical-values-card').style.display = 'none';
// Set default measurement time
{% if not object %}
const measuredDatetime = document.getElementById('{{ form.measured_datetime.id_for_label }}');
if (measuredDatetime) {
const now = new Date();
const localISOTime = new Date(now.getTime() - now.getTimezoneOffset() * 60000).toISOString().slice(0, 16);
measuredDatetime.value = localISOTime;
}
{% endif %}
}
}
// Form submission with loading state
document.querySelector('form').addEventListener('submit', function() {
const submitBtn = document.querySelector('button[type="submit"]');
const originalText = submitBtn.innerHTML;
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Saving...';
submitBtn.disabled = true;
// Re-enable if form validation fails
setTimeout(() => {
if (document.querySelector('.is-invalid')) {
submitBtn.innerHTML = originalText;
submitBtn.disabled = false;
}
}, 100);
});
</script>
<style>
.input-group-text {
background-color: #f8f9fa;
border-color: #ced4da;
}
.is-invalid {
border-color: #dc3545;
}
.invalid-feedback {
display: block;
width: 100%;
margin-top: 0.25rem;
font-size: 0.875em;
color: #dc3545;
}
.accordion-button:not(.collapsed) {
background-color: #e7f1ff;
color: #0d6efd;
}
.card-header.bg-danger {
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}
.alert-danger {
border-left: 4px solid #dc3545;
}
.alert-warning {
border-left: 4px solid #ffc107;
}
#bmi_display {
background-color: #f8f9fa;
font-weight: 500;
}
.text-primary {
color: #0d6efd !important;
}
@media (max-width: 768px) {
.row.mb-4 .col-md-3,
.row.mb-4 .col-md-4,
.row.mb-4 .col-md-6 {
margin-bottom: 1rem;
}
}
</style>
{% endblock %}