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

578 lines
26 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}Medication Management - {{ 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-pills me-2"></i>Medication Management
</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"><a href="{% url 'pharmacy:dashboard' %}">Pharmacy</a></li>
<li class="breadcrumb-item active">Medications</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="?export=csv"><i class="fas fa-file-csv me-2"></i>CSV</a></li>
<li><a class="dropdown-item" href="?export=excel"><i class="fas fa-file-excel me-2"></i>Excel</a></li>
<li><a class="dropdown-item" href="?export=pdf"><i class="fas fa-file-pdf me-2"></i>PDF</a></li>
</ul>
<a href="{% url 'pharmacy:medication_create' %}" class="btn btn-primary">
<i class="fas fa-plus me-2"></i>Add Medication
</a>
</div>
</div>
<!-- Medication Statistics -->
<div class="row mb-4">
<div class="col-lg-3 col-md-6 mb-3">
<div class="card border-0 shadow-sm h-100" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
<div class="card-body text-white">
<div class="d-flex justify-content-between align-items-center">
<div>
<h3 class="mb-1 fw-bold">{{ total_medications|default:0 }}</h3>
<p class="mb-0 opacity-75">Total Medications</p>
</div>
<div class="text-white-50">
<i class="fas fa-pills fa-2x"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6 mb-3">
<div class="card border-0 shadow-sm h-100" style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);">
<div class="card-body text-white">
<div class="d-flex justify-content-between align-items-center">
<div>
<h3 class="mb-1 fw-bold">{{ active_medications|default:0 }}</h3>
<p class="mb-0 opacity-75">Active Medications</p>
</div>
<div class="text-white-50">
<i class="fas fa-check-circle fa-2x"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6 mb-3">
<div class="card border-0 shadow-sm h-100" style="background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);">
<div class="card-body text-white">
<div class="d-flex justify-content-between align-items-center">
<div>
<h3 class="mb-1 fw-bold">{{ controlled_substances|default:0 }}</h3>
<p class="mb-0 opacity-75">Controlled Substances</p>
</div>
<div class="text-white-50">
<i class="fas fa-shield-alt fa-2x"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6 mb-3">
<div class="card border-0 shadow-sm h-100" style="background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);">
<div class="card-body text-white">
<div class="d-flex justify-content-between align-items-center">
<div>
<h3 class="mb-1 fw-bold">{{ formulary_preferred|default:0 }}</h3>
<p class="mb-0 opacity-75">Formulary Preferred</p>
</div>
<div class="text-white-50">
<i class="fas fa-star fa-2x"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Search and Filters -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-filter me-2"></i>Search & Filter Medications
</h5>
</div>
<div class="card-body">
<form method="get" class="row g-3" id="filterForm">
<div class="col-lg-4 col-md-6">
<label for="search" class="form-label">Search Medications</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-search"></i></span>
<input type="text"
class="form-control"
id="search"
name="search"
value="{{ request.GET.search }}"
placeholder="Generic name, brand name, NDC..."
hx-get="{% url 'pharmacy:medication_search' %}"
hx-target="#medication-list-container"
hx-trigger="keyup changed delay:500ms"
hx-include="#filterForm">
</div>
</div>
<div class="col-lg-2 col-md-6">
<label for="drug_class" class="form-label">Drug Class</label>
<select class="form-select" id="drug_class" name="drug_class"
hx-get="{% url 'pharmacy:medication_search' %}"
hx-target="#medication-list-container"
hx-trigger="change"
hx-include="#filterForm">
<option value="">All Classes</option>
{% for class in drug_classes %}
<option value="{{ class }}" {% if request.GET.drug_class == class %}selected{% endif %}>{{ class }}</option>
{% endfor %}
</select>
</div>
<div class="col-lg-2 col-md-6">
<label for="controlled_schedule" class="form-label">Schedule</label>
<select class="form-select" id="controlled_schedule" name="controlled_schedule"
hx-get="{% url 'pharmacy:medication_search' %}"
hx-target="#medication-list-container"
hx-trigger="change"
hx-include="#filterForm">
<option value="">All Schedules</option>
<option value="NON" {% if request.GET.controlled_schedule == 'NON' %}selected{% endif %}>Non-Controlled</option>
<option value="CI" {% if request.GET.controlled_schedule == 'CI' %}selected{% endif %}>Schedule I</option>
<option value="CII" {% if request.GET.controlled_schedule == 'CII' %}selected{% endif %}>Schedule II</option>
<option value="CIII" {% if request.GET.controlled_schedule == 'CIII' %}selected{% endif %}>Schedule III</option>
<option value="CIV" {% if request.GET.controlled_schedule == 'CIV' %}selected{% endif %}>Schedule IV</option>
<option value="CV" {% if request.GET.controlled_schedule == 'CV' %}selected{% endif %}>Schedule V</option>
</select>
</div>
<div class="col-lg-2 col-md-6">
<label for="formulary_status" class="form-label">Formulary Status</label>
<select class="form-select" id="formulary_status" name="formulary_status"
hx-get="{% url 'pharmacy:medication_search' %}"
hx-target="#medication-list-container"
hx-trigger="change"
hx-include="#filterForm">
<option value="">All Status</option>
<option value="PREFERRED" {% if request.GET.formulary_status == 'PREFERRED' %}selected{% endif %}>Preferred</option>
<option value="NON_PREFERRED" {% if request.GET.formulary_status == 'NON_PREFERRED' %}selected{% endif %}>Non-Preferred</option>
<option value="RESTRICTED" {% if request.GET.formulary_status == 'RESTRICTED' %}selected{% endif %}>Restricted</option>
<option value="PRIOR_AUTH" {% if request.GET.formulary_status == 'PRIOR_AUTH' %}selected{% endif %}>Prior Auth Required</option>
</select>
</div>
<div class="col-lg-2 col-md-6">
<label class="form-label">&nbsp;</label>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary">
<i class="fas fa-search me-2"></i>Filter
</button>
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="clearFilters()">
<i class="fas fa-times me-2"></i>Clear
</button>
</div>
</div>
</form>
</div>
</div>
<!-- Medication List -->
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="fas fa-list me-2"></i>Medication List
</h5>
<div class="d-flex align-items-center gap-3">
<span class="badge bg-primary">{{ page_obj.paginator.count|default:medications.count }} total</span>
<div class="btn-group btn-group-sm">
<button type="button" class="btn btn-outline-primary" 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>
</div>
<div class="dropdown">
<button class="btn btn-outline-secondary btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown">
<i class="fas fa-cog me-1"></i>Actions
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" onclick="bulkAction('export')">
<i class="fas fa-download me-2"></i>Export Selected
</a></li>
<li><a class="dropdown-item" href="#" onclick="bulkAction('deactivate')">
<i class="fas fa-ban me-2"></i>Deactivate Selected
</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item text-danger" href="#" onclick="bulkAction('delete')">
<i class="fas fa-trash me-2"></i>Delete Selected
</a></li>
</ul>
</div>
</div>
</div>
<div class="card-body p-0" id="medication-list-container">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th width="40">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="selectAllCheckbox" onchange="toggleSelectAll()">
</div>
</th>
<th>Medication</th>
<th>Strength</th>
<th>Drug Class</th>
<th>Schedule</th>
<th>Formulary Status</th>
<th>Status</th>
<th width="120">Actions</th>
</tr>
</thead>
<tbody>
{% for medication in medications %}
<tr>
<td>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="selected_medications" value="{{ medication.id }}">
</div>
</td>
<td>
<div class="d-flex align-items-center">
<div class="me-3">
{% if medication.is_controlled_substance %}
<i class="fas fa-shield-alt text-warning" title="Controlled Substance"></i>
{% else %}
<i class="fas fa-pill text-primary"></i>
{% endif %}
</div>
<div>
<div class="fw-semibold">{{ medication.generic_name }}</div>
{% if medication.brand_name %}
<small class="text-muted">{{ medication.brand_name }}</small>
{% endif %}
{% if medication.ndc_number %}
<br><small class="text-muted">NDC: {{ medication.ndc_number }}</small>
{% endif %}
</div>
</div>
</td>
<td>
<span class="fw-medium">{{ medication.strength }}</span>
<br><small class="text-muted">{{ medication.get_unit_of_measure_display }}</small>
</td>
<td>
<span class="badge bg-info">{{ medication.drug_class }}</span>
</td>
<td>
{% if medication.is_controlled_substance %}
<span class="badge bg-warning text-dark">{{ medication.get_controlled_substance_schedule_display }}</span>
{% else %}
<span class="badge bg-secondary">Non-Controlled</span>
{% endif %}
</td>
<td>
{% if medication.formulary_status == 'PREFERRED' %}
<span class="badge bg-success">Preferred</span>
{% elif medication.formulary_status == 'NON_PREFERRED' %}
<span class="badge bg-warning text-dark">Non-Preferred</span>
{% elif medication.formulary_status == 'RESTRICTED' %}
<span class="badge bg-danger">Restricted</span>
{% elif medication.formulary_status == 'PRIOR_AUTH' %}
<span class="badge bg-info">Prior Auth</span>
{% else %}
<span class="badge bg-secondary">Not Covered</span>
{% endif %}
</td>
<td>
{% if medication.is_active %}
<span class="badge bg-success">Active</span>
{% else %}
<span class="badge bg-secondary">Inactive</span>
{% endif %}
</td>
<td>
<div class="btn-group btn-group-sm">
<a href="{% url 'pharmacy:medication_detail' medication.pk %}" class="btn btn-outline-primary btn-sm" title="View Details">
<i class="fas fa-eye"></i>
</a>
<a href="{% url 'pharmacy:medication_edit' medication.pk %}" class="btn btn-outline-secondary btn-sm" title="Edit">
<i class="fas fa-edit"></i>
</a>
<button type="button" class="btn btn-outline-danger btn-sm" onclick="deleteMedication({{ medication.pk }})" title="Delete">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="8" class="text-center py-4">
<div class="text-muted">
<i class="fas fa-pills fa-3x mb-3"></i>
<p class="mb-0">No medications found</p>
<small>Try adjusting your search criteria or add a new medication</small>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Pagination -->
{% if page_obj.has_other_pages %}
<div class="card-footer">
<nav aria-label="Medication pagination">
<ul class="pagination justify-content-center mb-0">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}">Previous</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active">
<span class="page-link">{{ num }}</span>
</li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}">Next</a>
</li>
{% endif %}
</ul>
</nav>
</div>
{% endif %}
</div>
</div>
</div>
<script>
// Clear filters function
function clearFilters() {
document.getElementById('filterForm').reset();
window.location.href = '{% url "pharmacy:medication_list" %}';
}
// Selection functions
function selectAll() {
const checkboxes = document.querySelectorAll('input[name="selected_medications"]');
checkboxes.forEach(cb => cb.checked = true);
updateBulkActionButtons();
}
function clearSelection() {
const checkboxes = document.querySelectorAll('input[name="selected_medications"]');
checkboxes.forEach(cb => cb.checked = false);
document.getElementById('selectAllCheckbox').checked = false;
updateBulkActionButtons();
}
function toggleSelectAll() {
const selectAllCheckbox = document.getElementById('selectAllCheckbox');
const checkboxes = document.querySelectorAll('input[name="selected_medications"]');
checkboxes.forEach(cb => cb.checked = selectAllCheckbox.checked);
updateBulkActionButtons();
}
function updateBulkActionButtons() {
const selectedCount = document.querySelectorAll('input[name="selected_medications"]:checked').length;
const actionButtons = document.querySelectorAll('[onclick^="bulkAction"]');
actionButtons.forEach(btn => {
if (selectedCount > 0) {
btn.classList.remove('disabled');
btn.removeAttribute('disabled');
} else {
btn.classList.add('disabled');
btn.setAttribute('disabled', 'disabled');
}
});
}
// Bulk actions
function bulkAction(action) {
const selectedMedications = Array.from(document.querySelectorAll('input[name="selected_medications"]:checked'))
.map(cb => cb.value);
if (selectedMedications.length === 0) {
alert('Please select at least one medication.');
return;
}
let confirmMessage = '';
switch (action) {
case 'export':
confirmMessage = `Export ${selectedMedications.length} selected medication(s)?`;
break;
case 'deactivate':
confirmMessage = `Deactivate ${selectedMedications.length} selected medication(s)?`;
break;
case 'delete':
confirmMessage = `Delete ${selectedMedications.length} selected medication(s)? This action cannot be undone.`;
break;
}
if (confirm(confirmMessage)) {
// Implement bulk action logic here
console.log(`Performing ${action} on medications:`, selectedMedications);
}
}
// Delete single medication
function deleteMedication(medicationId) {
if (confirm('Are you sure you want to delete this medication? This action cannot be undone.')) {
// Implement delete logic here
console.log('Deleting medication:', medicationId);
}
}
// Initialize
document.addEventListener('DOMContentLoaded', function() {
updateBulkActionButtons();
// Add event listeners to checkboxes
document.addEventListener('change', function(e) {
if (e.target.name === 'selected_medications') {
updateBulkActionButtons();
// Update select all checkbox
const checkboxes = document.querySelectorAll('input[name="selected_medications"]');
const checkedBoxes = document.querySelectorAll('input[name="selected_medications"]:checked');
const selectAllCheckbox = document.getElementById('selectAllCheckbox');
if (checkedBoxes.length === 0) {
selectAllCheckbox.indeterminate = false;
selectAllCheckbox.checked = false;
} else if (checkedBoxes.length === checkboxes.length) {
selectAllCheckbox.indeterminate = false;
selectAllCheckbox.checked = true;
} else {
selectAllCheckbox.indeterminate = true;
}
}
});
});
// Keyboard shortcuts
document.addEventListener('keydown', function(e) {
// Ctrl+A to select all
if (e.ctrlKey && e.key === 'a' && e.target.tagName !== 'INPUT') {
e.preventDefault();
selectAll();
}
// Escape to clear selection
if (e.key === 'Escape') {
clearSelection();
}
// Ctrl+N for new medication
if (e.ctrlKey && e.key === 'n') {
e.preventDefault();
window.location.href = '{% url "pharmacy:medication_create" %}';
}
});
</script>
<style>
.card {
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
border: 1px solid rgba(0, 0, 0, 0.125);
}
.card-header {
background-color: rgba(13, 110, 253, 0.1);
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
}
.btn {
border-radius: 0.375rem;
transition: all 0.15s ease-in-out;
}
.btn:hover:not(:disabled) {
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.form-control, .form-select {
border-radius: 0.375rem;
border: 1px solid #ced4da;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
.form-control:focus, .form-select:focus {
border-color: #86b7fe;
outline: 0;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
.table th {
border-top: none;
font-weight: 600;
color: #495057;
}
.table-hover tbody tr:hover {
background-color: rgba(13, 110, 253, 0.05);
}
.badge {
font-size: 0.75rem;
}
.fw-semibold {
font-weight: 600;
}
@media (max-width: 768px) {
.btn-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.d-flex.justify-content-between {
flex-direction: column;
gap: 1rem;
}
.card-header .d-flex {
flex-direction: column;
align-items: flex-start !important;
gap: 1rem;
}
.table-responsive {
font-size: 0.875rem;
}
}
</style>
{% endblock %}