472 lines
20 KiB
HTML
472 lines
20 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}Electronic Medical Records - {{ 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-file-medical me-2"></i>Electronic Medical Records
|
|
</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">EMR</li>
|
|
</ol>
|
|
</nav>
|
|
</div>
|
|
<div class="btn-group">
|
|
<button type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown">
|
|
<i class="fas fa-download me-2"></i>Export
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
<li><a class="dropdown-item" href="#" onclick="exportEMRData('csv')">
|
|
<i class="fas fa-file-csv me-2"></i>Export as CSV
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="exportEMRData('excel')">
|
|
<i class="fas fa-file-excel me-2"></i>Export as Excel
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#" onclick="exportEMRData('pdf')">
|
|
<i class="fas fa-file-pdf me-2"></i>Export as PDF
|
|
</a></li>
|
|
</ul>
|
|
<button type="button" class="btn btn-primary" onclick="createNewEncounter()">
|
|
<i class="fas fa-plus me-2"></i>New Encounter
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Key Metrics -->
|
|
<div class="row mb-4" id="emr-stats" hx-get="{% url 'emr:emr_stats' %}" hx-trigger="load, every 30s">
|
|
{% include 'emr/partials/emr_stats.html' %}
|
|
</div>
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="row mb-4">
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<div class="card h-100 border-0 shadow-sm">
|
|
<div class="card-body text-center">
|
|
<div class="bg-primary bg-gradient rounded-circle d-inline-flex align-items-center justify-content-center mb-3" style="width: 60px; height: 60px;">
|
|
<i class="fas fa-user-md fa-lg text-white"></i>
|
|
</div>
|
|
<h6 class="card-title">Encounters</h6>
|
|
<p class="card-text text-muted small">Manage patient encounters and visits</p>
|
|
<a href="{% url 'emr:encounter_list' %}" class="btn btn-primary btn-sm">
|
|
<i class="fas fa-list me-1"></i>View Encounters
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<div class="card h-100 border-0 shadow-sm">
|
|
<div class="card-body text-center">
|
|
<div class="bg-success bg-gradient rounded-circle d-inline-flex align-items-center justify-content-center mb-3" style="width: 60px; height: 60px;">
|
|
<i class="fas fa-heartbeat fa-lg text-white"></i>
|
|
</div>
|
|
<h6 class="card-title">Vital Signs</h6>
|
|
<p class="card-text text-muted small">Record and monitor vital signs</p>
|
|
<a href="{% url 'emr:vital_signs_list' %}" class="btn btn-success btn-sm">
|
|
<i class="fas fa-chart-line me-1"></i>View Vitals
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<div class="card h-100 border-0 shadow-sm">
|
|
<div class="card-body text-center">
|
|
<div class="bg-warning bg-gradient rounded-circle d-inline-flex align-items-center justify-content-center mb-3" style="width: 60px; height: 60px;">
|
|
<i class="fas fa-list-alt fa-lg text-white"></i>
|
|
</div>
|
|
<h6 class="card-title">Problem List</h6>
|
|
<p class="card-text text-muted small">Manage patient problems and diagnoses</p>
|
|
<a href="{% url 'emr:problem_list' %}" class="btn btn-warning btn-sm">
|
|
<i class="fas fa-clipboard-list me-1"></i>View Problems
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<div class="card h-100 border-0 shadow-sm">
|
|
<div class="card-body text-center">
|
|
<div class="bg-info bg-gradient rounded-circle d-inline-flex align-items-center justify-content-center mb-3" style="width: 60px; height: 60px;">
|
|
<i class="fas fa-notes-medical fa-lg text-white"></i>
|
|
</div>
|
|
<h6 class="card-title">Clinical Notes</h6>
|
|
<p class="card-text text-muted small">Create and manage clinical documentation</p>
|
|
<a href="{% url 'emr:clinical_note_list' %}" class="btn btn-info btn-sm">
|
|
<i class="fas fa-file-alt me-1"></i>View Notes
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Content -->
|
|
<div class="row">
|
|
<div class="col-lg-8">
|
|
<!-- Recent Encounters -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-clock me-2"></i>Recent Encounters
|
|
</h5>
|
|
<div class="btn-group btn-group-sm">
|
|
<button type="button" class="btn btn-outline-secondary" onclick="refreshEncounters()">
|
|
<i class="fas fa-sync-alt me-1"></i>Refresh
|
|
</button>
|
|
<a href="{% url 'emr:encounter_list' %}" class="btn btn-outline-primary">
|
|
<i class="fas fa-expand me-1"></i>View All
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Patient</th>
|
|
<th>Type</th>
|
|
<th>Provider</th>
|
|
<th>Date/Time</th>
|
|
<th>Status</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for encounter in encounters %}
|
|
<tr>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<div class="avatar-sm bg-primary bg-gradient rounded-circle d-flex align-items-center justify-content-center me-2">
|
|
<span class="text-white small fw-bold">
|
|
{{ encounter.patient.first_name.0 }}{{ encounter.patient.last_name.0 }}
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<div class="fw-semibold">{{ encounter.patient.get_full_name }}</div>
|
|
<small class="text-muted">MRN: {{ encounter.patient.mrn }}</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-primary">{{ encounter.get_encounter_type_display }}</span>
|
|
</td>
|
|
<td>{{ encounter.provider.get_full_name }}</td>
|
|
<td>
|
|
<div>{{ encounter.start_datetime|date:"M d, Y" }}</div>
|
|
<small class="text-muted">{{ encounter.start_datetime|time:"H:i" }}</small>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-{% if encounter.status == 'FINISHED' %}success{% elif encounter.status == 'CANCELLED' %}danger{% elif encounter.status == 'IN_PROGRESS' %}warning{% else %}secondary{% endif %}">
|
|
{{ encounter.get_status_display }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<div class="btn-group btn-group-sm">
|
|
<a href="{% url 'emr:encounter_detail' encounter.pk %}" class="btn btn-outline-primary btn-sm" title="View Details">
|
|
<i class="fas fa-eye"></i>
|
|
</a>
|
|
<button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown">
|
|
<span class="visually-hidden">Toggle Dropdown</span>
|
|
<i class="fas fa-ellipsis-h"></i>
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
<li><a class="dropdown-item" href="#">
|
|
<i class="fas fa-edit me-2"></i>Edit
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="{% url 'emr:add_vital_signs' encounter.id %}">
|
|
<i class="fas fa-heartbeat me-2"></i>Add Vitals
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="#">
|
|
<i class="fas fa-notes-medical me-2"></i>Add Note
|
|
</a></li>
|
|
</ul>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="6" class="text-center py-4 text-muted">
|
|
<i class="fas fa-inbox fa-2x mb-2"></i>
|
|
<div>No recent encounters found</div>
|
|
<small>Create a new encounter to get started</small>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pending Tasks -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-tasks me-2"></i>Pending Tasks
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="d-flex align-items-center p-3 bg-warning bg-opacity-10 rounded mb-3">
|
|
<div class="bg-warning rounded-circle d-flex align-items-center justify-content-center me-3" style="width: 40px; height: 40px;">
|
|
<i class="fas fa-file-signature text-white"></i>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<h6 class="mb-0">{{ unsigned_notes }}</h6>
|
|
<small class="text-muted">Unsigned Notes</small>
|
|
</div>
|
|
<a href="{% url 'emr:clinical_note_list' %}?status=unsigned" class="btn btn-warning btn-sm">
|
|
<i class="fas fa-arrow-right"></i>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="d-flex align-items-center p-3 bg-danger bg-opacity-10 rounded mb-3">
|
|
<div class="bg-danger rounded-circle d-flex align-items-center justify-content-center me-3" style="width: 40px; height: 40px;">
|
|
<i class="fas fa-clipboard-check text-white"></i>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<h6 class="mb-0">{{ pending_documentation }}</h6>
|
|
<small class="text-muted">Incomplete Documentation</small>
|
|
</div>
|
|
<a href="{% url 'emr:encounter_list' %}?documentation_complete=false" class="btn btn-danger btn-sm">
|
|
<i class="fas fa-arrow-right"></i>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sidebar -->
|
|
<div class="col-lg-4">
|
|
<!-- Quick Search -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-search me-2"></i>Quick Search
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="input-group mb-3">
|
|
<input type="text" class="form-control" placeholder="Search patients..."
|
|
hx-get="{% url 'emr:encounter_search' %}"
|
|
hx-trigger="keyup changed delay:300ms"
|
|
hx-target="#search-results">
|
|
<button class="btn btn-outline-secondary" type="button">
|
|
<i class="fas fa-search"></i>
|
|
</button>
|
|
</div>
|
|
<div id="search-results"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Today's Schedule -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-calendar-day me-2"></i>Today's Schedule
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<span class="text-muted">Total Encounters</span>
|
|
<span class="badge bg-primary">{{ todays_encounters }}</span>
|
|
</div>
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<span class="text-muted">Active Now</span>
|
|
<span class="badge bg-success">{{ active_encounters }}</span>
|
|
</div>
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<span class="text-muted">Critical Vitals</span>
|
|
<span class="badge bg-danger">{{ critical_vitals }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Activity -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-history me-2"></i>Recent Activity
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="timeline">
|
|
<div class="timeline-item">
|
|
<div class="timeline-marker bg-primary"></div>
|
|
<div class="timeline-content">
|
|
<h6 class="timeline-title">New encounter created</h6>
|
|
<p class="timeline-text">Patient: John Doe</p>
|
|
<small class="text-muted">2 minutes ago</small>
|
|
</div>
|
|
</div>
|
|
<div class="timeline-item">
|
|
<div class="timeline-marker bg-success"></div>
|
|
<div class="timeline-content">
|
|
<h6 class="timeline-title">Vital signs recorded</h6>
|
|
<p class="timeline-text">Patient: Jane Smith</p>
|
|
<small class="text-muted">15 minutes ago</small>
|
|
</div>
|
|
</div>
|
|
<div class="timeline-item">
|
|
<div class="timeline-marker bg-warning"></div>
|
|
<div class="timeline-content">
|
|
<h6 class="timeline-title">Clinical note signed</h6>
|
|
<p class="timeline-text">Dr. Johnson</p>
|
|
<small class="text-muted">1 hour ago</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// EMR Dashboard functionality
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Auto-refresh stats every 30 seconds
|
|
setInterval(function() {
|
|
const statsContainer = document.getElementById('emr-stats');
|
|
if (statsContainer) {
|
|
htmx.trigger(statsContainer, 'refresh');
|
|
}
|
|
}, 30000);
|
|
});
|
|
|
|
function refreshEncounters() {
|
|
location.reload();
|
|
}
|
|
|
|
function createNewEncounter() {
|
|
// Implementation would depend on your encounter creation workflow
|
|
window.location.href = "{% url 'emr:encounter_list' %}";
|
|
}
|
|
|
|
function exportEMRData(format) {
|
|
const params = new URLSearchParams();
|
|
params.append('format', format);
|
|
|
|
window.open(`{% url 'emr:dashboard' %}export/?${params}`, '_blank');
|
|
}
|
|
|
|
// Keyboard shortcuts
|
|
document.addEventListener('keydown', function(e) {
|
|
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
|
|
return;
|
|
}
|
|
|
|
switch (e.key) {
|
|
case 'n':
|
|
e.preventDefault();
|
|
createNewEncounter();
|
|
break;
|
|
case 'r':
|
|
e.preventDefault();
|
|
refreshEncounters();
|
|
break;
|
|
case '/':
|
|
e.preventDefault();
|
|
document.querySelector('input[placeholder*="Search"]').focus();
|
|
break;
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
.avatar-sm {
|
|
width: 32px;
|
|
height: 32px;
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
.timeline {
|
|
position: relative;
|
|
padding-left: 1.5rem;
|
|
}
|
|
|
|
.timeline-item {
|
|
position: relative;
|
|
padding-bottom: 1.5rem;
|
|
}
|
|
|
|
.timeline-item:not(:last-child)::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: -1.5rem;
|
|
top: 1rem;
|
|
width: 2px;
|
|
height: calc(100% - 0.5rem);
|
|
background-color: #dee2e6;
|
|
}
|
|
|
|
.timeline-marker {
|
|
position: absolute;
|
|
left: -1.75rem;
|
|
top: 0.25rem;
|
|
width: 0.5rem;
|
|
height: 0.5rem;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.timeline-content {
|
|
margin-left: 0.5rem;
|
|
}
|
|
|
|
.timeline-title {
|
|
font-size: 0.875rem;
|
|
font-weight: 600;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.timeline-text {
|
|
font-size: 0.8rem;
|
|
color: #6c757d;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.bg-gradient {
|
|
background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
|
}
|
|
|
|
.card {
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.card:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1) !important;
|
|
}
|
|
|
|
.table-hover tbody tr:hover {
|
|
background-color: rgba(0, 0, 0, 0.02);
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.btn-group-sm .btn {
|
|
padding: 0.25rem 0.5rem;
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
.timeline {
|
|
padding-left: 1rem;
|
|
}
|
|
|
|
.timeline-marker {
|
|
left: -1.25rem;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|