505 lines
23 KiB
HTML
505 lines
23 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}Pharmacy Dashboard - {{ 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>Pharmacy Dashboard
|
|
</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">Pharmacy</li>
|
|
</ol>
|
|
</nav>
|
|
</div>
|
|
<div class="btn-group">
|
|
<a href="{% url 'pharmacy:prescription_list' %}" class="btn btn-primary">
|
|
<i class="fas fa-prescription me-2"></i>Prescriptions
|
|
</a>
|
|
<a href="{% url 'pharmacy:medication_list' %}" class="btn btn-outline-primary">
|
|
<i class="fas fa-pills me-2"></i>Medications
|
|
</a>
|
|
<a href="{% url 'pharmacy:inventory_list' %}" class="btn btn-outline-primary">
|
|
<i class="fas fa-boxes me-2"></i>Inventory
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Statistics Cards -->
|
|
<div class="row mb-4" hx-get="{% url 'pharmacy:pharmacy_stats' %}" hx-trigger="load, every 30s">
|
|
<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">{{ pending_prescriptions|default:0 }}</h3>
|
|
<p class="mb-0 opacity-75">Pending Prescriptions</p>
|
|
</div>
|
|
<div class="text-white-50">
|
|
<i class="fas fa-prescription fa-2x"></i>
|
|
</div>
|
|
</div>
|
|
<div class="mt-3">
|
|
<small class="opacity-75">
|
|
<i class="fas fa-clock me-1"></i>Requires verification
|
|
</small>
|
|
</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">{{ pending_dispensing|default:0 }}</h3>
|
|
<p class="mb-0 opacity-75">Ready to Dispense</p>
|
|
</div>
|
|
<div class="text-white-50">
|
|
<i class="fas fa-hand-holding-medical fa-2x"></i>
|
|
</div>
|
|
</div>
|
|
<div class="mt-3">
|
|
<small class="opacity-75">
|
|
<i class="fas fa-check me-1"></i>Verified prescriptions
|
|
</small>
|
|
</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">{{ low_stock_items|default:0 }}</h3>
|
|
<p class="mb-0 opacity-75">Low Stock Items</p>
|
|
</div>
|
|
<div class="text-white-50">
|
|
<i class="fas fa-exclamation-triangle fa-2x"></i>
|
|
</div>
|
|
</div>
|
|
<div class="mt-3">
|
|
<small class="opacity-75">
|
|
<i class="fas fa-boxes me-1"></i>Needs restocking
|
|
</small>
|
|
</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">{{ expired_medications|default:0 }}</h3>
|
|
<p class="mb-0 opacity-75">Expired Items</p>
|
|
</div>
|
|
<div class="text-white-50">
|
|
<i class="fas fa-calendar-times fa-2x"></i>
|
|
</div>
|
|
</div>
|
|
<div class="mt-3">
|
|
<small class="opacity-75">
|
|
<i class="fas fa-trash me-1"></i>Requires disposal
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-bolt me-2"></i>Quick Actions
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-lg-2 col-md-4 col-sm-6">
|
|
<a href="{% url 'pharmacy:prescription_create' %}" class="btn btn-outline-primary w-100 h-100 d-flex flex-column align-items-center justify-content-center py-3">
|
|
<i class="fas fa-plus fa-2x mb-2"></i>
|
|
<span>New Prescription</span>
|
|
</a>
|
|
</div>
|
|
<div class="col-lg-2 col-md-4 col-sm-6">
|
|
<a href="{% url 'pharmacy:medication_create' %}" class="btn btn-outline-success w-100 h-100 d-flex flex-column align-items-center justify-content-center py-3">
|
|
<i class="fas fa-pills fa-2x mb-2"></i>
|
|
<span>Add Medication</span>
|
|
</a>
|
|
</div>
|
|
<div class="col-lg-2 col-md-4 col-sm-6">
|
|
<a href="{% url 'pharmacy:inventory_create' %}" class="btn btn-outline-info w-100 h-100 d-flex flex-column align-items-center justify-content-center py-3">
|
|
<i class="fas fa-boxes fa-2x mb-2"></i>
|
|
<span>Stock Item</span>
|
|
</a>
|
|
</div>
|
|
<div class="col-lg-2 col-md-4 col-sm-6">
|
|
<button type="button" class="btn btn-outline-warning w-100 h-100 d-flex flex-column align-items-center justify-content-center py-3" onclick="checkInteractions()">
|
|
<i class="fas fa-search fa-2x mb-2"></i>
|
|
<span>Drug Interactions</span>
|
|
</button>
|
|
</div>
|
|
<div class="col-lg-2 col-md-4 col-sm-6">
|
|
<a href="" class="btn btn-outline-secondary w-100 h-100 d-flex flex-column align-items-center justify-content-center py-3">
|
|
<i class="fas fa-chart-bar fa-2x mb-2"></i>
|
|
<span>Reports</span>
|
|
</a>
|
|
</div>
|
|
<div class="col-lg-2 col-md-4 col-sm-6">
|
|
<a href="" class="btn btn-outline-dark w-100 h-100 d-flex flex-column align-items-center justify-content-center py-3">
|
|
<i class="fas fa-cog fa-2x mb-2"></i>
|
|
<span>Settings</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<!-- Recent Prescriptions -->
|
|
<div class="col-lg-8">
|
|
<div class="card">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-prescription me-2"></i>Recent Prescriptions
|
|
</h5>
|
|
<div class="btn-group btn-group-sm">
|
|
<button type="button" class="btn btn-outline-secondary" onclick="refreshPrescriptions()">
|
|
<i class="fas fa-sync-alt"></i>
|
|
</button>
|
|
<a href="{% url 'pharmacy:prescription_list' %}" class="btn btn-outline-primary">
|
|
<i class="fas fa-list me-1"></i>View All
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
{% if prescriptions %}
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Patient</th>
|
|
<th>Medication</th>
|
|
<th>Provider</th>
|
|
<th>Status</th>
|
|
<th>Priority</th>
|
|
<th width="120">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for prescription in prescriptions %}
|
|
<tr>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<div class="avatar-circle bg-primary text-white me-3">
|
|
{{ prescription.patient.first_name.0 }}{{ prescription.patient.last_name.0 }}
|
|
</div>
|
|
<div>
|
|
<div class="fw-semibold">{{ prescription.patient.get_full_name }}</div>
|
|
<small class="text-muted">MRN: {{ prescription.patient.mrn }}</small>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div>
|
|
<div class="fw-semibold">{{ prescription.medication.generic_name }}</div>
|
|
{% if prescription.medication.brand_name %}
|
|
<small class="text-muted">{{ prescription.medication.brand_name }}</small>
|
|
{% endif %}
|
|
<br><small class="text-muted">{{ prescription.dosage_instructions|truncatechars:30 }}</small>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div>{{ prescription.prescriber.get_full_name }}</div>
|
|
<small class="text-muted">{{ prescription.date_prescribed|date:"M d, Y" }}</small>
|
|
</td>
|
|
<td>
|
|
{% if prescription.status == 'PENDING' %}
|
|
<span class="badge bg-warning text-dark">Pending</span>
|
|
{% elif prescription.status == 'VERIFIED' %}
|
|
<span class="badge bg-info">Verified</span>
|
|
{% elif prescription.status == 'IN_PROGRESS' %}
|
|
<span class="badge bg-primary">In Progress</span>
|
|
{% elif prescription.status == 'DISPENSED' %}
|
|
<span class="badge bg-success">Dispensed</span>
|
|
{% elif prescription.status == 'CANCELLED' %}
|
|
<span class="badge bg-danger">Cancelled</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary">{{ prescription.get_status_display }}</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
{% if prescription.priority == 'STAT' %}
|
|
<span class="badge bg-danger">STAT</span>
|
|
{% elif prescription.priority == 'URGENT' %}
|
|
<span class="badge bg-warning text-dark">Urgent</span>
|
|
{% elif prescription.priority == 'ROUTINE' %}
|
|
<span class="badge bg-info">Routine</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary">{{ prescription.get_priority_display }}</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<div class="btn-group btn-group-sm">
|
|
<a href="{% url 'pharmacy:prescription_detail' prescription.pk %}"
|
|
class="btn btn-outline-primary" title="View Details">
|
|
<i class="fas fa-eye"></i>
|
|
</a>
|
|
{% if prescription.status == 'PENDING' %}
|
|
<button class="btn btn-outline-success"
|
|
title="Verify"
|
|
onclick="verifyPrescription({{ prescription.pk }})">
|
|
<i class="fas fa-check"></i>
|
|
</button>
|
|
{% elif prescription.status == 'VERIFIED' %}
|
|
<button class="btn btn-outline-info"
|
|
title="Dispense"
|
|
onclick="dispenseMedication({{ prescription.pk }})">
|
|
<i class="fas fa-hand-holding-medical"></i>
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-5">
|
|
<i class="fas fa-prescription fa-3x text-muted mb-3"></i>
|
|
<h5 class="text-muted">No Recent Prescriptions</h5>
|
|
<p class="text-muted mb-3">No prescriptions requiring attention at this time.</p>
|
|
<a href="{% url 'pharmacy:prescription_create' %}" class="btn btn-primary">
|
|
<i class="fas fa-plus me-2"></i>Create New Prescription
|
|
</a>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sidebar -->
|
|
<div class="col-lg-4">
|
|
<!-- Inventory Alerts -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-exclamation-triangle me-2"></i>Inventory Alerts
|
|
</h5>
|
|
</div>
|
|
<div class="card-body" hx-get="{% url 'pharmacy:inventory_alerts' %}" hx-trigger="load, every 60s">
|
|
<div class="d-flex justify-content-center py-3">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden">Loading...</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-footer">
|
|
<a href="{% url 'pharmacy:inventory_list' %}" class="btn btn-outline-primary btn-sm">
|
|
<i class="fas fa-boxes me-2"></i>View All Inventory
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Today's Statistics -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-calendar-day me-2"></i>Today's Activity
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-6">
|
|
<div class="text-center">
|
|
<h4 class="text-success mb-1">{{ todays_dispensed|default:0 }}</h4>
|
|
<small class="text-muted">Dispensed</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-center">
|
|
<h4 class="text-warning mb-1">{{ drug_interactions|default:0 }}</h4>
|
|
<small class="text-muted">Interactions</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-center">
|
|
<h4 class="text-info mb-1">{{ pending_orders|default:0 }}</h4>
|
|
<small class="text-muted">Orders</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-center">
|
|
<h4 class="text-primary mb-1">{{ total_medications|default:0 }}</h4>
|
|
<small class="text-muted">Medications</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Links -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-link me-2"></i>Quick Links
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="d-grid gap-2">
|
|
<a href="#" class="btn btn-outline-warning btn-sm">
|
|
<i class="fas fa-search me-2"></i>Drug Interaction Checker
|
|
</a>
|
|
<a href="#" class="btn btn-outline-info btn-sm">
|
|
<i class="fas fa-book me-2"></i>Medication Guide
|
|
</a>
|
|
<a href="#" class="btn btn-outline-danger btn-sm">
|
|
<i class="fas fa-shield-alt me-2"></i>Controlled Substances
|
|
</a>
|
|
<a href="#" class="btn btn-outline-secondary btn-sm">
|
|
<i class="fas fa-chart-bar me-2"></i>Pharmacy Reports
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Refresh prescriptions
|
|
function refreshPrescriptions() {
|
|
location.reload();
|
|
}
|
|
|
|
// Verify prescription
|
|
function verifyPrescription(prescriptionId) {
|
|
if (confirm('Verify this prescription? This will mark it as ready for dispensing.')) {
|
|
fetch(`/pharmacy/prescriptions/${prescriptionId}/verify/`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Error verifying prescription: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Dispense medication
|
|
function dispenseMedication(prescriptionId) {
|
|
window.location.href = `/pharmacy/prescriptions/${prescriptionId}/dispense/`;
|
|
}
|
|
|
|
// Check drug interactions
|
|
function checkInteractions(prescriptionId) {
|
|
window.location.href = `pharmacy/drug-interaction-check/${prescriptionId}/`;
|
|
}
|
|
|
|
// Auto-refresh dashboard every 5 minutes
|
|
setInterval(function() {
|
|
// Refresh statistics
|
|
htmx.trigger(document.querySelector('[hx-get*="pharmacy_stats"]'), 'load');
|
|
// Refresh inventory alerts
|
|
htmx.trigger(document.querySelector('[hx-get*="inventory_alerts"]'), 'load');
|
|
}, 300000);
|
|
</script>
|
|
|
|
<style>
|
|
.avatar-circle {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-weight: bold;
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
.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);
|
|
}
|
|
|
|
.table th {
|
|
border-top: none;
|
|
font-weight: 600;
|
|
color: #495057;
|
|
}
|
|
|
|
.table-hover tbody tr:hover {
|
|
background-color: rgba(13, 110, 253, 0.05);
|
|
}
|
|
|
|
.fw-semibold {
|
|
font-weight: 600;
|
|
}
|
|
|
|
.badge {
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.d-flex.justify-content-between {
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.btn-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.card-body {
|
|
padding: 1rem;
|
|
}
|
|
|
|
.table-responsive {
|
|
font-size: 0.875rem;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|