hospital-management/templates/quality/quality_metrics.html
2025-08-12 13:33:25 +03:00

803 lines
36 KiB
HTML

{% extends 'base.html' %}
{% load static %}
{% block title %}Quality Metrics - Quality Management{% endblock %}
{% block content %}
<div class="content">
<div class="container-fluid">
<!-- Page Header -->
<div class="row">
<div class="col-12">
<div class="page-header">
<div class="page-title">
<h4>Quality Metrics Dashboard</h4>
<h6>Monitor and analyze healthcare quality indicators and performance metrics</h6>
</div>
<div class="page-btn">
<div class="btn-group">
<button type="button" class="btn btn-primary" onclick="refreshMetrics()">
<i class="fa fa-sync"></i> Refresh
</button>
<button type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#exportMetricsModal">
<i class="fa fa-download"></i> Export
</button>
<button type="button" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#addMetricModal">
<i class="fa fa-plus"></i> Add Metric
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Key Performance Indicators -->
<div class="row">
<div class="col-xl-3 col-sm-6 col-12">
<div class="card">
<div class="card-body">
<div class="dash-widget-header">
<span class="dash-widget-icon text-primary border-primary">
<i class="fa fa-heart"></i>
</span>
<div class="dash-count">
<h3>{{ metrics.patient_satisfaction|default:4.2 }}/5</h3>
</div>
</div>
<div class="dash-widget-info">
<h6 class="text-muted">Patient Satisfaction</h6>
<div class="progress progress-sm">
<div class="progress-bar bg-primary" style="width: {{ metrics.satisfaction_percentage|default:84 }}%"></div>
</div>
<small class="text-success">
<i class="fa fa-arrow-up"></i> +2.3% from last month
</small>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-sm-6 col-12">
<div class="card">
<div class="card-body">
<div class="dash-widget-header">
<span class="dash-widget-icon text-success border-success">
<i class="fa fa-shield-alt"></i>
</span>
<div class="dash-count">
<h3>{{ metrics.safety_score|default:96.8 }}%</h3>
</div>
</div>
<div class="dash-widget-info">
<h6 class="text-muted">Safety Score</h6>
<div class="progress progress-sm">
<div class="progress-bar bg-success" style="width: {{ metrics.safety_score|default:96.8 }}%"></div>
</div>
<small class="text-success">
<i class="fa fa-arrow-up"></i> +1.2% from last month
</small>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-sm-6 col-12">
<div class="card">
<div class="card-body">
<div class="dash-widget-header">
<span class="dash-widget-icon text-warning border-warning">
<i class="fa fa-clock"></i>
</span>
<div class="dash-count">
<h3>{{ metrics.avg_wait_time|default:18 }} min</h3>
</div>
</div>
<div class="dash-widget-info">
<h6 class="text-muted">Average Wait Time</h6>
<div class="progress progress-sm">
<div class="progress-bar bg-warning" style="width: 65%"></div>
</div>
<small class="text-danger">
<i class="fa fa-arrow-down"></i> -3 min from last month
</small>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-sm-6 col-12">
<div class="card">
<div class="card-body">
<div class="dash-widget-header">
<span class="dash-widget-icon text-info border-info">
<i class="fa fa-chart-line"></i>
</span>
<div class="dash-count">
<h3>{{ metrics.quality_index|default:87.5 }}%</h3>
</div>
</div>
<div class="dash-widget-info">
<h6 class="text-muted">Overall Quality Index</h6>
<div class="progress progress-sm">
<div class="progress-bar bg-info" style="width: {{ metrics.quality_index|default:87.5 }}%"></div>
</div>
<small class="text-success">
<i class="fa fa-arrow-up"></i> +4.1% from last month
</small>
</div>
</div>
</div>
</div>
</div>
<!-- Quality Metrics Charts -->
<div class="row">
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h5 class="card-title">Quality Trends</h5>
<div class="card-tools">
<select class="form-select form-select-sm" id="trendPeriod">
<option value="7">Last 7 days</option>
<option value="30" selected>Last 30 days</option>
<option value="90">Last 90 days</option>
<option value="365">Last year</option>
</select>
</div>
</div>
<div class="card-body">
<canvas id="qualityTrendsChart" height="300"></canvas>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h5 class="card-title">Quality Score Distribution</h5>
</div>
<div class="card-body">
<canvas id="qualityDistributionChart" height="300"></canvas>
</div>
</div>
</div>
</div>
<!-- Clinical Quality Indicators -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title">Clinical Quality Indicators</h5>
<div class="card-tools">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-primary active" data-category="all">All</button>
<button type="button" class="btn btn-sm btn-outline-primary" data-category="safety">Safety</button>
<button type="button" class="btn btn-sm btn-outline-primary" data-category="effectiveness">Effectiveness</button>
<button type="button" class="btn btn-sm btn-outline-primary" data-category="efficiency">Efficiency</button>
</div>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped" id="qualityIndicatorsTable">
<thead>
<tr>
<th>Indicator</th>
<th>Category</th>
<th>Current Value</th>
<th>Target</th>
<th>Benchmark</th>
<th>Trend</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr data-category="safety">
<td>
<strong>Hospital-Acquired Infection Rate</strong><br>
<small class="text-muted">Per 1,000 patient days</small>
</td>
<td><span class="badge bg-danger">Safety</span></td>
<td>{{ indicators.hai_rate|default:2.1 }}</td>
<td>< 2.0</td>
<td>1.8</td>
<td>
<span class="text-warning">
<i class="fa fa-arrow-up"></i> +0.2
</span>
</td>
<td><span class="badge bg-warning">Needs Attention</span></td>
<td>
<button class="btn btn-sm btn-outline-primary" onclick="viewDetails('hai_rate')">
<i class="fa fa-eye"></i>
</button>
</td>
</tr>
<tr data-category="safety">
<td>
<strong>Medication Error Rate</strong><br>
<small class="text-muted">Per 1,000 doses</small>
</td>
<td><span class="badge bg-danger">Safety</span></td>
<td>{{ indicators.med_error_rate|default:0.8 }}</td>
<td>< 1.0</td>
<td>0.6</td>
<td>
<span class="text-success">
<i class="fa fa-arrow-down"></i> -0.1
</span>
</td>
<td><span class="badge bg-success">On Target</span></td>
<td>
<button class="btn btn-sm btn-outline-primary" onclick="viewDetails('med_error_rate')">
<i class="fa fa-eye"></i>
</button>
</td>
</tr>
<tr data-category="effectiveness">
<td>
<strong>30-Day Readmission Rate</strong><br>
<small class="text-muted">Percentage of discharges</small>
</td>
<td><span class="badge bg-primary">Effectiveness</span></td>
<td>{{ indicators.readmission_rate|default:8.2 }}%</td>
<td>< 10%</td>
<td>7.5%</td>
<td>
<span class="text-success">
<i class="fa fa-arrow-down"></i> -1.1%
</span>
</td>
<td><span class="badge bg-success">Exceeds Target</span></td>
<td>
<button class="btn btn-sm btn-outline-primary" onclick="viewDetails('readmission_rate')">
<i class="fa fa-eye"></i>
</button>
</td>
</tr>
<tr data-category="effectiveness">
<td>
<strong>Mortality Rate</strong><br>
<small class="text-muted">Risk-adjusted</small>
</td>
<td><span class="badge bg-primary">Effectiveness</span></td>
<td>{{ indicators.mortality_rate|default:1.2 }}%</td>
<td>< 1.5%</td>
<td>1.1%</td>
<td>
<span class="text-success">
<i class="fa fa-minus"></i> 0.0
</span>
</td>
<td><span class="badge bg-success">On Target</span></td>
<td>
<button class="btn btn-sm btn-outline-primary" onclick="viewDetails('mortality_rate')">
<i class="fa fa-eye"></i>
</button>
</td>
</tr>
<tr data-category="efficiency">
<td>
<strong>Average Length of Stay</strong><br>
<small class="text-muted">Days</small>
</td>
<td><span class="badge bg-info">Efficiency</span></td>
<td>{{ indicators.avg_los|default:4.8 }}</td>
<td>< 5.0</td>
<td>4.2</td>
<td>
<span class="text-success">
<i class="fa fa-arrow-down"></i> -0.3
</span>
</td>
<td><span class="badge bg-success">On Target</span></td>
<td>
<button class="btn btn-sm btn-outline-primary" onclick="viewDetails('avg_los')">
<i class="fa fa-eye"></i>
</button>
</td>
</tr>
<tr data-category="efficiency">
<td>
<strong>Emergency Department Wait Time</strong><br>
<small class="text-muted">Minutes to provider</small>
</td>
<td><span class="badge bg-info">Efficiency</span></td>
<td>{{ indicators.ed_wait_time|default:32 }}</td>
<td>< 30</td>
<td>25</td>
<td>
<span class="text-warning">
<i class="fa fa-arrow-up"></i> +5
</span>
</td>
<td><span class="badge bg-warning">Needs Attention</span></td>
<td>
<button class="btn btn-sm btn-outline-primary" onclick="viewDetails('ed_wait_time')">
<i class="fa fa-eye"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Quality Improvement Initiatives -->
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="card-title">Active Quality Initiatives</h5>
</div>
<div class="card-body">
{% for initiative in quality_initiatives %}
<div class="initiative-item mb-3">
<div class="d-flex justify-content-between align-items-start">
<div>
<h6 class="mb-1">{{ initiative.title }}</h6>
<p class="text-muted small mb-2">{{ initiative.description|truncatewords:15 }}</p>
<div class="d-flex align-items-center">
<span class="badge bg-{{ initiative.status_color }} me-2">{{ initiative.status|title }}</span>
<small class="text-muted">Due: {{ initiative.due_date|date:"M d, Y" }}</small>
</div>
</div>
<div class="text-end">
<div class="progress" style="width: 60px; height: 6px;">
<div class="progress-bar bg-{{ initiative.status_color }}"
style="width: {{ initiative.progress }}%"></div>
</div>
<small class="text-muted">{{ initiative.progress }}%</small>
</div>
</div>
</div>
{% empty %}
<div class="text-center text-muted py-4">
<i class="fa fa-clipboard-list fa-2x mb-2"></i>
<p>No active quality initiatives</p>
<button class="btn btn-outline-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addInitiativeModal">
<i class="fa fa-plus"></i> Add Initiative
</button>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="card-title">Quality Alerts</h5>
</div>
<div class="card-body">
{% for alert in quality_alerts %}
<div class="alert alert-{{ alert.severity }} alert-sm mb-2">
<div class="d-flex justify-content-between">
<div>
<strong>{{ alert.title }}</strong><br>
<small>{{ alert.message }}</small>
</div>
<div class="text-end">
<small class="text-muted">{{ alert.created_at|timesince }} ago</small><br>
<button class="btn btn-sm btn-outline-secondary" onclick="dismissAlert({{ alert.id }})">
<i class="fa fa-times"></i>
</button>
</div>
</div>
</div>
{% empty %}
<div class="text-center text-muted py-4">
<i class="fa fa-check-circle fa-2x mb-2 text-success"></i>
<p>No active quality alerts</p>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
<!-- Benchmarking -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title">Benchmarking Comparison</h5>
<div class="card-tools">
<select class="form-select form-select-sm" id="benchmarkType">
<option value="national">National Average</option>
<option value="regional">Regional Average</option>
<option value="peer">Peer Hospitals</option>
<option value="top_decile">Top 10%</option>
</select>
</div>
</div>
<div class="card-body">
<canvas id="benchmarkChart" height="200"></canvas>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Export Metrics Modal -->
<div class="modal fade" id="exportMetricsModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Export Quality Metrics</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form id="exportForm">
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Date Range</label>
<div class="row">
<div class="col-6">
<input type="date" name="start_date" class="form-control" required>
</div>
<div class="col-6">
<input type="date" name="end_date" class="form-control" required>
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">Metrics to Include</label>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="metrics" value="all" id="allMetrics" checked>
<label class="form-check-label" for="allMetrics">All Metrics</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="metrics" value="safety" id="safetyMetrics">
<label class="form-check-label" for="safetyMetrics">Safety Indicators</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="metrics" value="effectiveness" id="effectivenessMetrics">
<label class="form-check-label" for="effectivenessMetrics">Effectiveness Indicators</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="metrics" value="efficiency" id="efficiencyMetrics">
<label class="form-check-label" for="efficiencyMetrics">Efficiency Indicators</label>
</div>
</div>
<div class="mb-3">
<label class="form-label">Export Format</label>
<select name="format" class="form-select" required>
<option value="pdf">PDF Report</option>
<option value="excel">Excel Spreadsheet</option>
<option value="csv">CSV Data</option>
</select>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Export</button>
</div>
</form>
</div>
</div>
</div>
<!-- Add Metric Modal -->
<div class="modal fade" id="addMetricModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Add Quality Metric</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form id="addMetricForm">
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Metric Name</label>
<input type="text" name="metric_name" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Category</label>
<select name="category" class="form-select" required>
<option value="">Select category...</option>
<option value="safety">Safety</option>
<option value="effectiveness">Effectiveness</option>
<option value="efficiency">Efficiency</option>
<option value="patient_experience">Patient Experience</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">Target Value</label>
<input type="number" name="target_value" class="form-control" step="0.01" required>
</div>
<div class="mb-3">
<label class="form-label">Unit of Measurement</label>
<input type="text" name="unit" class="form-control" placeholder="e.g., %, per 1000, days" required>
</div>
<div class="mb-3">
<label class="form-label">Description</label>
<textarea name="description" class="form-control" rows="3"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-success">Add Metric</button>
</div>
</form>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Initialize charts
initializeQualityTrendsChart();
initializeQualityDistributionChart();
initializeBenchmarkChart();
// Category filter functionality
const categoryButtons = document.querySelectorAll('[data-category]');
categoryButtons.forEach(button => {
button.addEventListener('click', function() {
const category = this.getAttribute('data-category');
filterIndicators(category);
// Update active button
categoryButtons.forEach(btn => btn.classList.remove('active'));
this.classList.add('active');
});
});
// Export form handler
document.getElementById('exportForm').addEventListener('submit', function(e) {
e.preventDefault();
handleExport();
});
// Add metric form handler
document.getElementById('addMetricForm').addEventListener('submit', function(e) {
e.preventDefault();
handleAddMetric();
});
// Set default dates for export
const today = new Date();
const thirtyDaysAgo = new Date(today.getTime() - (30 * 24 * 60 * 60 * 1000));
document.querySelector('input[name="start_date"]').value = thirtyDaysAgo.toISOString().split('T')[0];
document.querySelector('input[name="end_date"]').value = today.toISOString().split('T')[0];
});
function initializeQualityTrendsChart() {
const ctx = document.getElementById('qualityTrendsChart').getContext('2d');
new Chart(ctx, {
type: 'line',
data: {
labels: ['Week 1', 'Week 2', 'Week 3', 'Week 4'],
datasets: [{
label: 'Patient Satisfaction',
data: [4.1, 4.0, 4.2, 4.2],
borderColor: '#007bff',
backgroundColor: 'rgba(0, 123, 255, 0.1)',
tension: 0.4
}, {
label: 'Safety Score',
data: [95.5, 96.2, 96.8, 96.8],
borderColor: '#28a745',
backgroundColor: 'rgba(40, 167, 69, 0.1)',
tension: 0.4
}, {
label: 'Quality Index',
data: [85.2, 86.1, 87.0, 87.5],
borderColor: '#17a2b8',
backgroundColor: 'rgba(23, 162, 184, 0.1)',
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: false,
min: 80
}
},
plugins: {
legend: {
position: 'top'
}
}
}
});
}
function initializeQualityDistributionChart() {
const ctx = document.getElementById('qualityDistributionChart').getContext('2d');
new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['Excellent (90-100%)', 'Good (80-89%)', 'Fair (70-79%)', 'Poor (<70%)'],
datasets: [{
data: [45, 35, 15, 5],
backgroundColor: ['#28a745', '#17a2b8', '#ffc107', '#dc3545']
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom'
}
}
}
});
}
function initializeBenchmarkChart() {
const ctx = document.getElementById('benchmarkChart').getContext('2d');
new Chart(ctx, {
type: 'radar',
data: {
labels: ['Safety', 'Effectiveness', 'Efficiency', 'Patient Experience', 'Timeliness'],
datasets: [{
label: 'Our Hospital',
data: [96.8, 87.5, 82.3, 84.0, 78.5],
borderColor: '#007bff',
backgroundColor: 'rgba(0, 123, 255, 0.2)',
pointBackgroundColor: '#007bff'
}, {
label: 'National Average',
data: [92.1, 85.2, 79.8, 81.5, 75.2],
borderColor: '#6c757d',
backgroundColor: 'rgba(108, 117, 125, 0.2)',
pointBackgroundColor: '#6c757d'
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
r: {
beginAtZero: true,
max: 100
}
}
}
});
}
function filterIndicators(category) {
const rows = document.querySelectorAll('#qualityIndicatorsTable tbody tr');
rows.forEach(row => {
if (category === 'all' || row.getAttribute('data-category') === category) {
row.style.display = '';
} else {
row.style.display = 'none';
}
});
}
function viewDetails(indicator) {
console.log(`Viewing details for indicator: ${indicator}`);
alert(`Detailed view for ${indicator} would be displayed here.`);
}
function refreshMetrics() {
console.log('Refreshing quality metrics...');
// Show loading state
const refreshBtn = document.querySelector('button[onclick="refreshMetrics()"]');
const originalContent = refreshBtn.innerHTML;
refreshBtn.innerHTML = '<i class="fa fa-spinner fa-spin"></i> Refreshing...';
refreshBtn.disabled = true;
// Simulate API call
setTimeout(() => {
refreshBtn.innerHTML = originalContent;
refreshBtn.disabled = false;
alert('Quality metrics refreshed successfully!');
}, 2000);
}
function handleExport() {
const formData = new FormData(document.getElementById('exportForm'));
console.log('Exporting quality metrics with parameters:', Object.fromEntries(formData));
// Close modal
bootstrap.Modal.getInstance(document.getElementById('exportMetricsModal')).hide();
// Simulate export
alert('Quality metrics export started. You will receive an email when the report is ready.');
}
function handleAddMetric() {
const formData = new FormData(document.getElementById('addMetricForm'));
console.log('Adding new quality metric:', Object.fromEntries(formData));
// Close modal
bootstrap.Modal.getInstance(document.getElementById('addMetricModal')).hide();
// Reset form
document.getElementById('addMetricForm').reset();
alert('Quality metric added successfully!');
}
function dismissAlert(alertId) {
console.log(`Dismissing alert: ${alertId}`);
// Find and remove the alert element
const alertElement = event.target.closest('.alert');
if (alertElement) {
alertElement.remove();
}
}
// Auto-refresh functionality
setInterval(() => {
console.log('Auto-refreshing quality metrics...');
// In real implementation, this would update the metrics without full page reload
}, 300000); // Refresh every 5 minutes
</script>
<style>
.dash-widget-header {
display: flex;
align-items: center;
margin-bottom: 1rem;
}
.dash-widget-icon {
width: 60px;
height: 60px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
margin-right: 1rem;
border: 2px solid;
}
.dash-count h3 {
margin: 0;
font-size: 2rem;
font-weight: bold;
}
.initiative-item {
border-left: 3px solid #dee2e6;
padding-left: 1rem;
margin-bottom: 1rem;
}
.alert-sm {
padding: 0.5rem;
font-size: 0.875rem;
}
.card-tools {
margin-left: auto;
}
.progress {
height: 6px;
}
@media (max-width: 768px) {
.dash-widget-header {
flex-direction: column;
text-align: center;
}
.dash-widget-icon {
margin-right: 0;
margin-bottom: 0.5rem;
}
}
</style>
{% endblock %}