1040 lines
44 KiB
HTML
1040 lines
44 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}Medication Reconciliation{% endblock %}
|
|
|
|
{% block content %}
|
|
<div id="content" class="app-content">
|
|
<div class="container">
|
|
<ul class="breadcrumb">
|
|
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'emr:dashboard' %}">EMR</a></li>
|
|
<li class="breadcrumb-item active">Medication Reconciliation</li>
|
|
</ul>
|
|
|
|
<div class="row align-items-center mb-3">
|
|
<div class="col">
|
|
<h1 class="page-header">Medication Reconciliation</h1>
|
|
<p class="text-muted">Compare and reconcile patient medications across care transitions</p>
|
|
</div>
|
|
<div class="col-auto">
|
|
<div class="btn-group">
|
|
<button class="btn btn-primary" onclick="startReconciliation()">
|
|
<i class="fa fa-play me-2"></i>Start Reconciliation
|
|
</button>
|
|
<button class="btn btn-outline-secondary" onclick="printReconciliation()">
|
|
<i class="fa fa-print me-2"></i>Print
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Patient Information -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">Patient Information</h4>
|
|
<div class="card-tools">
|
|
<button class="btn btn-outline-primary btn-sm" onclick="selectPatient()">
|
|
<i class="fa fa-search me-1"></i>Select Patient
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if patient %}
|
|
<div class="row">
|
|
<div class="col-md-3">
|
|
<p><strong>Name:</strong> {{ patient.first_name }} {{ patient.last_name }}</p>
|
|
<p><strong>DOB:</strong> {{ patient.date_of_birth|date:"M d, Y" }}</p>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<p><strong>MRN:</strong> {{ patient.patient_id }}</p>
|
|
<p><strong>Age:</strong> {{ patient.age }}</p>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<p><strong>Allergies:</strong> {{ patient.allergies|default:"None documented" }}</p>
|
|
<p><strong>Weight:</strong> {{ patient.weight|default:"Not recorded" }} kg</p>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<p><strong>Primary Care Provider:</strong> {{ patient.primary_care_provider|default:"Not assigned" }}</p>
|
|
<p><strong>Insurance:</strong> {{ patient.primary_insurance|default:"Not provided" }}</p>
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-3">
|
|
<i class="fa fa-user fa-2x text-muted mb-2"></i>
|
|
<p class="text-muted">No patient selected. Please select a patient to begin medication reconciliation.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if patient %}
|
|
<!-- Reconciliation Status -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">Reconciliation Status</h4>
|
|
<div class="card-tools">
|
|
<span class="badge bg-{{ reconciliation.status|default:'secondary' }}">
|
|
{{ reconciliation.get_status_display|default:'Not Started' }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-3">
|
|
<div class="text-center">
|
|
<div class="display-6 text-primary">{{ medication_sources|length }}</div>
|
|
<small class="text-muted">Medication Sources</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="text-center">
|
|
<div class="display-6 text-warning">{{ discrepancies|length }}</div>
|
|
<small class="text-muted">Discrepancies Found</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="text-center">
|
|
<div class="display-6 text-success">{{ resolved_count }}</div>
|
|
<small class="text-muted">Resolved</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="text-center">
|
|
<div class="display-6 text-info">{{ completion_percentage }}%</div>
|
|
<small class="text-muted">Complete</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if reconciliation.started_at %}
|
|
<div class="mt-3">
|
|
<div class="progress">
|
|
<div class="progress-bar" role="progressbar" style="width: {{ completion_percentage }}%">
|
|
{{ completion_percentage }}%
|
|
</div>
|
|
</div>
|
|
<small class="text-muted">
|
|
Started: {{ reconciliation.started_at|date:"M d, Y g:i A" }}
|
|
{% if reconciliation.completed_at %}
|
|
| Completed: {{ reconciliation.completed_at|date:"M d, Y g:i A" }}
|
|
{% endif %}
|
|
</small>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Medication Sources -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">Medication Sources</h4>
|
|
<div class="card-tools">
|
|
<button class="btn btn-outline-secondary btn-sm" onclick="refreshSources()">
|
|
<i class="fa fa-refresh me-1"></i>Refresh
|
|
</button>
|
|
<button class="btn btn-outline-primary btn-sm" onclick="addMedicationSource()">
|
|
<i class="fa fa-plus me-1"></i>Add Source
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
{% for source in medication_sources %}
|
|
<div class="col-md-4 mb-3">
|
|
<div class="card border-{{ source.status|lower }}">
|
|
<div class="card-header bg-{{ source.status|lower }} text-white">
|
|
<h6 class="card-title mb-0">{{ source.name }}</h6>
|
|
<small>{{ source.get_type_display }}</small>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="card-text">
|
|
<strong>Date:</strong> {{ source.date|date:"M d, Y" }}<br>
|
|
<strong>Provider:</strong> {{ source.provider|default:"Unknown" }}<br>
|
|
<strong>Medications:</strong> {{ source.medication_count }}
|
|
</p>
|
|
<div class="d-flex justify-content-between">
|
|
<button class="btn btn-outline-primary btn-sm" onclick="viewSource('{{ source.id }}')">
|
|
<i class="fa fa-eye me-1"></i>View
|
|
</button>
|
|
<button class="btn btn-outline-secondary btn-sm" onclick="editSource('{{ source.id }}')">
|
|
<i class="fa fa-edit me-1"></i>Edit
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% empty %}
|
|
<div class="col-12">
|
|
<div class="text-center py-3">
|
|
<i class="fa fa-pills fa-2x text-muted mb-2"></i>
|
|
<p class="text-muted">No medication sources found. Add sources to begin reconciliation.</p>
|
|
<button class="btn btn-primary" onclick="addMedicationSource()">
|
|
<i class="fa fa-plus me-2"></i>Add First Source
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Medication Comparison -->
|
|
{% if medication_sources %}
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">Medication Comparison</h4>
|
|
<div class="card-tools">
|
|
<div class="btn-group">
|
|
<button class="btn btn-outline-secondary btn-sm" onclick="showAllMedications()">All</button>
|
|
<button class="btn btn-outline-warning btn-sm" onclick="showDiscrepancies()">Discrepancies</button>
|
|
<button class="btn btn-outline-success btn-sm" onclick="showResolved()">Resolved</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-striped" id="medicationComparisonTable">
|
|
<thead>
|
|
<tr>
|
|
<th>Medication</th>
|
|
<th>Strength</th>
|
|
<th>Frequency</th>
|
|
<th>Route</th>
|
|
{% for source in medication_sources %}
|
|
<th class="text-center">{{ source.name }}</th>
|
|
{% endfor %}
|
|
<th>Status</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for medication in compared_medications %}
|
|
<tr class="medication-row" data-status="{{ medication.status }}">
|
|
<td>
|
|
<strong>{{ medication.name }}</strong>
|
|
{% if medication.generic_name %}
|
|
<br><small class="text-muted">{{ medication.generic_name }}</small>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ medication.strength|default:"-" }}</td>
|
|
<td>{{ medication.frequency|default:"-" }}</td>
|
|
<td>{{ medication.route|default:"-" }}</td>
|
|
{% for source in medication_sources %}
|
|
<td class="text-center">
|
|
{% with medication.sources|get_item:source.id as source_med %}
|
|
{% if source_med %}
|
|
<span class="badge bg-success">
|
|
<i class="fa fa-check"></i>
|
|
</span>
|
|
{% if source_med.different %}
|
|
<br><small class="text-warning">Different</small>
|
|
{% endif %}
|
|
{% else %}
|
|
<span class="badge bg-secondary">
|
|
<i class="fa fa-minus"></i>
|
|
</span>
|
|
{% endif %}
|
|
{% endwith %}
|
|
</td>
|
|
{% endfor %}
|
|
<td>
|
|
<span class="badge bg-{{ medication.status|lower }}">
|
|
{{ medication.get_status_display }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<div class="btn-group btn-group-sm">
|
|
<button class="btn btn-outline-primary" onclick="viewMedicationDetails('{{ medication.id }}')">
|
|
<i class="fa fa-eye"></i>
|
|
</button>
|
|
{% if medication.status == 'discrepancy' %}
|
|
<button class="btn btn-outline-warning" onclick="resolveMedication('{{ medication.id }}')">
|
|
<i class="fa fa-check"></i>
|
|
</button>
|
|
{% endif %}
|
|
<button class="btn btn-outline-secondary" onclick="editMedication('{{ medication.id }}')">
|
|
<i class="fa fa-edit"></i>
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="100%" class="text-center text-muted">
|
|
No medications to compare. Add medication sources first.
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Discrepancies -->
|
|
{% if discrepancies %}
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="card border-warning">
|
|
<div class="card-header bg-warning text-dark">
|
|
<h4 class="card-title mb-0">
|
|
<i class="fa fa-exclamation-triangle me-2"></i>Discrepancies Requiring Attention
|
|
</h4>
|
|
</div>
|
|
<div class="card-body">
|
|
{% for discrepancy in discrepancies %}
|
|
<div class="alert alert-warning d-flex align-items-start" role="alert">
|
|
<i class="fa fa-exclamation-triangle me-3 mt-1"></i>
|
|
<div class="flex-grow-1">
|
|
<h6 class="alert-heading">{{ discrepancy.medication_name }}</h6>
|
|
<p class="mb-2">{{ discrepancy.description }}</p>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<strong>Source 1:</strong> {{ discrepancy.source1_details }}<br>
|
|
<strong>Source 2:</strong> {{ discrepancy.source2_details }}
|
|
</div>
|
|
<div class="col-md-6">
|
|
<strong>Type:</strong> {{ discrepancy.get_type_display }}<br>
|
|
<strong>Severity:</strong>
|
|
<span class="badge bg-{{ discrepancy.severity|lower }}">
|
|
{{ discrepancy.get_severity_display }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="ms-3">
|
|
<div class="btn-group-vertical btn-group-sm">
|
|
<button class="btn btn-outline-success" onclick="resolveDiscrepancy('{{ discrepancy.id }}', 'accept_source1')">
|
|
Accept Source 1
|
|
</button>
|
|
<button class="btn btn-outline-success" onclick="resolveDiscrepancy('{{ discrepancy.id }}', 'accept_source2')">
|
|
Accept Source 2
|
|
</button>
|
|
<button class="btn btn-outline-primary" onclick="resolveDiscrepancy('{{ discrepancy.id }}', 'manual')">
|
|
Manual Resolution
|
|
</button>
|
|
<button class="btn btn-outline-secondary" onclick="deferDiscrepancy('{{ discrepancy.id }}')">
|
|
Defer
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Reconciliation Actions -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">Reconciliation Actions</h4>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<h6>Quick Actions</h6>
|
|
<div class="d-grid gap-2">
|
|
<button class="btn btn-outline-primary" onclick="autoReconcile()">
|
|
<i class="fa fa-magic me-2"></i>Auto-Reconcile Similar Medications
|
|
</button>
|
|
<button class="btn btn-outline-warning" onclick="flagAllDiscrepancies()">
|
|
<i class="fa fa-flag me-2"></i>Flag All Discrepancies for Review
|
|
</button>
|
|
<button class="btn btn-outline-info" onclick="generateReport()">
|
|
<i class="fa fa-file-text me-2"></i>Generate Reconciliation Report
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h6>Completion Actions</h6>
|
|
<div class="d-grid gap-2">
|
|
{% if reconciliation.status != 'completed' %}
|
|
<button class="btn btn-success" onclick="completeReconciliation()" {% if discrepancies %}disabled{% endif %}>
|
|
<i class="fa fa-check me-2"></i>Complete Reconciliation
|
|
</button>
|
|
{% endif %}
|
|
<button class="btn btn-outline-secondary" onclick="saveProgress()">
|
|
<i class="fa fa-save me-2"></i>Save Progress
|
|
</button>
|
|
<button class="btn btn-outline-danger" onclick="resetReconciliation()">
|
|
<i class="fa fa-refresh me-2"></i>Reset Reconciliation
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if discrepancies %}
|
|
<div class="alert alert-info mt-3" role="alert">
|
|
<i class="fa fa-info-circle me-2"></i>
|
|
<strong>Note:</strong> All discrepancies must be resolved before completing the reconciliation.
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Reconciliation History -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">Reconciliation History</h4>
|
|
<div class="card-tools">
|
|
<button class="btn btn-outline-secondary btn-sm" onclick="refreshHistory()">
|
|
<i class="fa fa-refresh me-1"></i>Refresh
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-sm">
|
|
<thead>
|
|
<tr>
|
|
<th>Date</th>
|
|
<th>Type</th>
|
|
<th>Provider</th>
|
|
<th>Status</th>
|
|
<th>Sources</th>
|
|
<th>Discrepancies</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for history in reconciliation_history %}
|
|
<tr>
|
|
<td>{{ history.date|date:"M d, Y" }}</td>
|
|
<td>{{ history.get_type_display }}</td>
|
|
<td>{{ history.provider }}</td>
|
|
<td>
|
|
<span class="badge bg-{{ history.status|lower }}">
|
|
{{ history.get_status_display }}
|
|
</span>
|
|
</td>
|
|
<td>{{ history.source_count }}</td>
|
|
<td>{{ history.discrepancy_count }}</td>
|
|
<td>
|
|
<div class="btn-group btn-group-sm">
|
|
<button class="btn btn-outline-primary" onclick="viewReconciliation('{{ history.id }}')">
|
|
<i class="fa fa-eye"></i>
|
|
</button>
|
|
{% if history.status == 'completed' %}
|
|
<button class="btn btn-outline-secondary" onclick="printReconciliation('{{ history.id }}')">
|
|
<i class="fa fa-print"></i>
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="7" class="text-center text-muted">
|
|
No previous reconciliations found
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Patient Selection Modal -->
|
|
<div class="modal fade" id="patientSelectionModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Select Patient</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<input type="text" class="form-control" id="patientSearchInput" placeholder="Search by name, ID, or phone number...">
|
|
</div>
|
|
<div id="patientSearchResults">
|
|
<!-- Search results will be loaded here -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Medication Source Modal -->
|
|
<div class="modal fade" id="medicationSourceModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Add Medication Source</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="medicationSourceForm">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Source Name</label>
|
|
<input type="text" class="form-control" name="name" required>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Source Type</label>
|
|
<select class="form-select" name="type" required>
|
|
<option value="">Select type...</option>
|
|
<option value="admission">Admission Medications</option>
|
|
<option value="discharge">Discharge Medications</option>
|
|
<option value="home">Home Medications</option>
|
|
<option value="pharmacy">Pharmacy Records</option>
|
|
<option value="provider">Provider List</option>
|
|
<option value="other">Other</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Date</label>
|
|
<input type="date" class="form-control" name="date" required>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label">Provider</label>
|
|
<input type="text" class="form-control" name="provider">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Notes</label>
|
|
<textarea class="form-control" name="notes" rows="3"></textarea>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-primary" onclick="saveMedicationSource()">Save Source</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Medication Details Modal -->
|
|
<div class="modal fade" id="medicationDetailsModal" tabindex="-1">
|
|
<div class="modal-dialog modal-xl">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Medication Details</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div id="medicationDetailsContent">
|
|
<!-- Medication details will be loaded here -->
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
<button type="button" class="btn btn-primary" onclick="saveMedicationChanges()">Save Changes</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Resolution Modal -->
|
|
<div class="modal fade" id="resolutionModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Resolve Discrepancy</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div id="resolutionContent">
|
|
<!-- Resolution form will be loaded here -->
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-primary" onclick="saveResolution()">Save Resolution</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script>
|
|
$(document).ready(function() {
|
|
setupEventHandlers();
|
|
{% if patient %}
|
|
loadReconciliationData();
|
|
{% endif %}
|
|
});
|
|
|
|
function setupEventHandlers() {
|
|
// Patient search
|
|
$('#patientSearchInput').on('input', function() {
|
|
var query = $(this).val();
|
|
if (query.length >= 3) {
|
|
searchPatients(query);
|
|
}
|
|
});
|
|
}
|
|
|
|
function selectPatient() {
|
|
$('#patientSelectionModal').modal('show');
|
|
}
|
|
|
|
function searchPatients(query) {
|
|
$.get('{% url "emr:patient_search_api" %}', {q: query}, function(data) {
|
|
var html = '';
|
|
|
|
if (data.patients.length === 0) {
|
|
html = '<div class="alert alert-info">No patients found matching "' + query + '"</div>';
|
|
} else {
|
|
html = '<div class="list-group">';
|
|
data.patients.forEach(function(patient) {
|
|
html += '<button type="button" class="list-group-item list-group-item-action" onclick="selectPatientForReconciliation(\'' + patient.patient_id + '\')">' +
|
|
'<div class="d-flex w-100 justify-content-between">' +
|
|
'<h6 class="mb-1">' + patient.first_name + ' ' + patient.last_name + '</h6>' +
|
|
'<small>' + patient.patient_id + '</small>' +
|
|
'</div>' +
|
|
'<p class="mb-1">DOB: ' + patient.date_of_birth + ' | Gender: ' + patient.gender + '</p>' +
|
|
'<small>Phone: ' + (patient.phone_primary || 'Not provided') + '</small>' +
|
|
'</button>';
|
|
});
|
|
html += '</div>';
|
|
}
|
|
|
|
$('#patientSearchResults').html(html);
|
|
});
|
|
}
|
|
|
|
function selectPatientForReconciliation(patientId) {
|
|
window.location.href = '{% url "emr:medication_reconciliation" %}?patient_id=' + patientId;
|
|
}
|
|
|
|
function loadReconciliationData() {
|
|
// Load reconciliation data for the selected patient
|
|
refreshSources();
|
|
refreshHistory();
|
|
}
|
|
|
|
function startReconciliation() {
|
|
var patientId = '{{ patient.patient_id|default:"" }}';
|
|
if (!patientId) {
|
|
toastr.error('Please select a patient first');
|
|
return;
|
|
}
|
|
|
|
$.post('{% url "emr:start_reconciliation" %}', {
|
|
patient_id: patientId,
|
|
csrfmiddlewaretoken: '{{ csrf_token }}'
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Reconciliation started');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to start reconciliation: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function addMedicationSource() {
|
|
$('#medicationSourceModal').modal('show');
|
|
$('#medicationSourceForm')[0].reset();
|
|
$('#medicationSourceForm input[name="date"]').val(new Date().toISOString().split('T')[0]);
|
|
}
|
|
|
|
function saveMedicationSource() {
|
|
var formData = $('#medicationSourceForm').serialize();
|
|
formData += '&patient_id={{ patient.patient_id|default:"" }}&csrfmiddlewaretoken={{ csrf_token }}';
|
|
|
|
$.post('{% url "emr:add_medication_source" %}', formData, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Medication source added');
|
|
$('#medicationSourceModal').modal('hide');
|
|
refreshSources();
|
|
} else {
|
|
toastr.error('Failed to add medication source: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function refreshSources() {
|
|
var patientId = '{{ patient.patient_id|default:"" }}';
|
|
if (!patientId) return;
|
|
|
|
$.get('{% url "emr:get_medication_sources" %}', {patient_id: patientId}, function(data) {
|
|
// Update medication sources display
|
|
updateSourcesDisplay(data.sources);
|
|
toastr.success('Sources refreshed');
|
|
});
|
|
}
|
|
|
|
function viewSource(sourceId) {
|
|
$.get('{% url "emr:view_medication_source" %}', {source_id: sourceId}, function(data) {
|
|
$('#medicationDetailsContent').html(data.html);
|
|
$('#medicationDetailsModal').modal('show');
|
|
});
|
|
}
|
|
|
|
function editSource(sourceId) {
|
|
$.get('{% url "emr:edit_medication_source" %}', {source_id: sourceId}, function(data) {
|
|
$('#medicationSourceForm').html(data.form_html);
|
|
$('#medicationSourceModal').modal('show');
|
|
});
|
|
}
|
|
|
|
function showAllMedications() {
|
|
$('.medication-row').show();
|
|
updateFilterButtons('all');
|
|
}
|
|
|
|
function showDiscrepancies() {
|
|
$('.medication-row').hide();
|
|
$('.medication-row[data-status="discrepancy"]').show();
|
|
updateFilterButtons('discrepancy');
|
|
}
|
|
|
|
function showResolved() {
|
|
$('.medication-row').hide();
|
|
$('.medication-row[data-status="resolved"]').show();
|
|
updateFilterButtons('resolved');
|
|
}
|
|
|
|
function updateFilterButtons(activeFilter) {
|
|
$('.btn-group .btn').removeClass('active');
|
|
if (activeFilter === 'all') {
|
|
$('.btn-group .btn:first').addClass('active');
|
|
} else if (activeFilter === 'discrepancy') {
|
|
$('.btn-group .btn:nth-child(2)').addClass('active');
|
|
} else if (activeFilter === 'resolved') {
|
|
$('.btn-group .btn:last').addClass('active');
|
|
}
|
|
}
|
|
|
|
function viewMedicationDetails(medicationId) {
|
|
$.get('{% url "emr:view_medication_details" %}', {medication_id: medicationId}, function(data) {
|
|
$('#medicationDetailsContent').html(data.html);
|
|
$('#medicationDetailsModal').modal('show');
|
|
});
|
|
}
|
|
|
|
function resolveMedication(medicationId) {
|
|
$.get('{% url "emr:resolve_medication_form" %}', {medication_id: medicationId}, function(data) {
|
|
$('#resolutionContent').html(data.html);
|
|
$('#resolutionModal').modal('show');
|
|
});
|
|
}
|
|
|
|
function editMedication(medicationId) {
|
|
$.get('{% url "emr:edit_medication_form" %}', {medication_id: medicationId}, function(data) {
|
|
$('#medicationDetailsContent').html(data.html);
|
|
$('#medicationDetailsModal').modal('show');
|
|
});
|
|
}
|
|
|
|
function resolveDiscrepancy(discrepancyId, resolution) {
|
|
if (resolution === 'manual') {
|
|
$.get('{% url "emr:resolve_discrepancy_form" %}', {discrepancy_id: discrepancyId}, function(data) {
|
|
$('#resolutionContent').html(data.html);
|
|
$('#resolutionModal').modal('show');
|
|
});
|
|
} else {
|
|
$.post('{% url "emr:resolve_discrepancy" %}', {
|
|
discrepancy_id: discrepancyId,
|
|
resolution: resolution,
|
|
csrfmiddlewaretoken: '{{ csrf_token }}'
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Discrepancy resolved');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to resolve discrepancy: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function deferDiscrepancy(discrepancyId) {
|
|
$.post('{% url "emr:defer_discrepancy" %}', {
|
|
discrepancy_id: discrepancyId,
|
|
csrfmiddlewaretoken: '{{ csrf_token }}'
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.info('Discrepancy deferred');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to defer discrepancy: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function autoReconcile() {
|
|
var patientId = '{{ patient.patient_id|default:"" }}';
|
|
|
|
$.post('{% url "emr:auto_reconcile" %}', {
|
|
patient_id: patientId,
|
|
csrfmiddlewaretoken: '{{ csrf_token }}'
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Auto-reconciliation completed: ' + data.reconciled_count + ' medications reconciled');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Auto-reconciliation failed: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function flagAllDiscrepancies() {
|
|
$.post('{% url "emr:flag_discrepancies" %}', {
|
|
patient_id: '{{ patient.patient_id|default:"" }}',
|
|
csrfmiddlewaretoken: '{{ csrf_token }}'
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('All discrepancies flagged for review');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to flag discrepancies: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function generateReport() {
|
|
var patientId = '{{ patient.patient_id|default:"" }}';
|
|
window.open('{% url "emr:reconciliation_report" %}?patient_id=' + patientId, '_blank');
|
|
}
|
|
|
|
function completeReconciliation() {
|
|
if (confirm('Are you sure you want to complete this reconciliation? This action cannot be undone.')) {
|
|
$.post('{% url "emr:complete_reconciliation" %}', {
|
|
patient_id: '{{ patient.patient_id|default:"" }}',
|
|
csrfmiddlewaretoken: '{{ csrf_token }}'
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Reconciliation completed successfully');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to complete reconciliation: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function saveProgress() {
|
|
$.post('{% url "emr:save_reconciliation_progress" %}', {
|
|
patient_id: '{{ patient.patient_id|default:"" }}',
|
|
csrfmiddlewaretoken: '{{ csrf_token }}'
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Progress saved');
|
|
} else {
|
|
toastr.error('Failed to save progress: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function resetReconciliation() {
|
|
if (confirm('Are you sure you want to reset this reconciliation? All progress will be lost.')) {
|
|
$.post('{% url "emr:reset_reconciliation" %}', {
|
|
patient_id: '{{ patient.patient_id|default:"" }}',
|
|
csrfmiddlewaretoken: '{{ csrf_token }}'
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Reconciliation reset');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to reset reconciliation: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function refreshHistory() {
|
|
var patientId = '{{ patient.patient_id|default:"" }}';
|
|
if (!patientId) return;
|
|
|
|
$.get('{% url "emr:get_reconciliation_history" %}', {patient_id: patientId}, function(data) {
|
|
updateHistoryDisplay(data.history);
|
|
});
|
|
}
|
|
|
|
function viewReconciliation(reconciliationId) {
|
|
window.location.href = '{% url "emr:view_reconciliation" %}?reconciliation_id=' + reconciliationId;
|
|
}
|
|
|
|
function printReconciliation(reconciliationId = null) {
|
|
var url = '{% url "emr:print_reconciliation" %}';
|
|
if (reconciliationId) {
|
|
url += '?reconciliation_id=' + reconciliationId;
|
|
} else {
|
|
url += '?patient_id={{ patient.patient_id|default:"" }}';
|
|
}
|
|
window.open(url, '_blank');
|
|
}
|
|
|
|
function saveResolution() {
|
|
var formData = $('#resolutionForm').serialize();
|
|
formData += '&csrfmiddlewaretoken={{ csrf_token }}';
|
|
|
|
$.post('{% url "emr:save_resolution" %}', formData, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Resolution saved');
|
|
$('#resolutionModal').modal('hide');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to save resolution: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function saveMedicationChanges() {
|
|
var formData = $('#medicationEditForm').serialize();
|
|
formData += '&csrfmiddlewaretoken={{ csrf_token }}';
|
|
|
|
$.post('{% url "emr:save_medication_changes" %}', formData, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Medication updated');
|
|
$('#medicationDetailsModal').modal('hide');
|
|
location.reload();
|
|
} else {
|
|
toastr.error('Failed to update medication: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function updateSourcesDisplay(sources) {
|
|
// Update the medication sources display
|
|
// This would be implemented based on the specific UI requirements
|
|
}
|
|
|
|
function updateHistoryDisplay(history) {
|
|
// Update the reconciliation history display
|
|
// This would be implemented based on the specific UI requirements
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.display-6 {
|
|
font-size: 2rem;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.card-tools {
|
|
margin-left: auto;
|
|
}
|
|
|
|
.btn-group-vertical .btn {
|
|
margin-bottom: 2px;
|
|
}
|
|
|
|
.btn-group-vertical .btn:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.medication-row {
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.medication-row:hover {
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
.badge {
|
|
font-size: 0.75em;
|
|
}
|
|
|
|
.alert-heading {
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.list-group-item-action:hover {
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
.progress {
|
|
height: 8px;
|
|
}
|
|
|
|
.card-header h4 {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.text-high {
|
|
color: #dc3545;
|
|
}
|
|
|
|
.text-medium {
|
|
color: #fd7e14;
|
|
}
|
|
|
|
.text-low {
|
|
color: #198754;
|
|
}
|
|
|
|
.bg-high {
|
|
background-color: #dc3545 !important;
|
|
}
|
|
|
|
.bg-medium {
|
|
background-color: #fd7e14 !important;
|
|
}
|
|
|
|
.bg-low {
|
|
background-color: #198754 !important;
|
|
}
|
|
|
|
.border-high {
|
|
border-color: #dc3545 !important;
|
|
}
|
|
|
|
.border-medium {
|
|
border-color: #fd7e14 !important;
|
|
}
|
|
|
|
.border-low {
|
|
border-color: #198754 !important;
|
|
}
|
|
|
|
.table th {
|
|
border-top: none;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.btn-group .btn.active {
|
|
background-color: #0d6efd;
|
|
border-color: #0d6efd;
|
|
color: white;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|