634 lines
23 KiB
HTML
634 lines
23 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}Blood Bank Reports{% endblock %}
|
|
|
|
{% block css %}
|
|
<link href="{% static 'plugins/chart.js/dist/Chart.min.css' %}" rel="stylesheet" />
|
|
<link href="{% static 'plugins/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css' %}" rel="stylesheet" />
|
|
<style>
|
|
.report-card {
|
|
border-radius: 10px;
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
transition: transform 0.3s ease;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.report-card:hover {
|
|
transform: translateY(-5px);
|
|
}
|
|
|
|
.report-icon {
|
|
font-size: 3rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.chart-container {
|
|
position: relative;
|
|
height: 400px;
|
|
}
|
|
|
|
.kpi-card {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
color: white;
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.metric-value {
|
|
font-size: 2.5rem;
|
|
font-weight: bold;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.metric-label {
|
|
font-size: 0.9rem;
|
|
opacity: 0.9;
|
|
}
|
|
|
|
.trend-up {
|
|
color: #28a745;
|
|
}
|
|
|
|
.trend-down {
|
|
color: #dc3545;
|
|
}
|
|
|
|
.trend-stable {
|
|
color: #6c757d;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<!-- BEGIN breadcrumb -->
|
|
<ol class="breadcrumb float-xl-end">
|
|
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Home</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'blood_bank:dashboard' %}">Blood Bank</a></li>
|
|
<li class="breadcrumb-item active">Reports</li>
|
|
</ol>
|
|
<!-- END breadcrumb -->
|
|
|
|
<!-- BEGIN page-header -->
|
|
<h1 class="page-header">Blood Bank Reports <small>comprehensive analytics and reporting</small></h1>
|
|
<!-- END page-header -->
|
|
|
|
<!-- BEGIN KPI overview -->
|
|
<div class="row mb-4">
|
|
<div class="col-xl-3 col-md-6">
|
|
<div class="kpi-card">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<div class="metric-value">{{ total_donations_month }}</div>
|
|
<div class="metric-label">Donations This Month</div>
|
|
<div class="mt-2">
|
|
<i class="fa fa-arrow-up trend-up"></i>
|
|
<span class="trend-up">+12% vs last month</span>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<i class="fa fa-tint fa-3x opacity-75"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-3 col-md-6">
|
|
<div class="kpi-card">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<div class="metric-value">{{ total_transfusions_month }}</div>
|
|
<div class="metric-label">Transfusions This Month</div>
|
|
<div class="mt-2">
|
|
<i class="fa fa-arrow-up trend-up"></i>
|
|
<span class="trend-up">+8% vs last month</span>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<i class="fa fa-heartbeat fa-3x opacity-75"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-3 col-md-6">
|
|
<div class="kpi-card">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<div class="metric-value">{{ inventory_turnover }}%</div>
|
|
<div class="metric-label">Inventory Turnover</div>
|
|
<div class="mt-2">
|
|
<i class="fa fa-minus trend-stable"></i>
|
|
<span class="trend-stable">Stable</span>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<i class="fa fa-sync-alt fa-3x opacity-75"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-3 col-md-6">
|
|
<div class="kpi-card">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<div class="metric-value">{{ wastage_rate }}%</div>
|
|
<div class="metric-label">Wastage Rate</div>
|
|
<div class="mt-2">
|
|
<i class="fa fa-arrow-down trend-up"></i>
|
|
<span class="trend-up">-3% vs last month</span>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<i class="fa fa-exclamation-triangle fa-3x opacity-75"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END KPI overview -->
|
|
|
|
<!-- BEGIN report categories -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Report Categories</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="row">
|
|
<div class="col-xl-3 col-md-6 mb-3">
|
|
<div class="report-card card text-center p-4" onclick="generateReport('inventory')">
|
|
<div class="report-icon text-primary">
|
|
<i class="fa fa-boxes"></i>
|
|
</div>
|
|
<h5>Inventory Reports</h5>
|
|
<p class="text-muted">Stock levels, expiry tracking, location analysis</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-3 col-md-6 mb-3">
|
|
<div class="report-card card text-center p-4" onclick="generateReport('donation')">
|
|
<div class="report-icon text-success">
|
|
<i class="fa fa-user-plus"></i>
|
|
</div>
|
|
<h5>Donation Reports</h5>
|
|
<p class="text-muted">Donor statistics, collection trends, demographics</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-3 col-md-6 mb-3">
|
|
<div class="report-card card text-center p-4" onclick="generateReport('transfusion')">
|
|
<div class="report-icon text-info">
|
|
<i class="fa fa-heartbeat"></i>
|
|
</div>
|
|
<h5>Transfusion Reports</h5>
|
|
<p class="text-muted">Usage patterns, patient outcomes, safety metrics</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-3 col-md-6 mb-3">
|
|
<div class="report-card card text-center p-4" onclick="generateReport('quality')">
|
|
<div class="report-icon text-warning">
|
|
<i class="fa fa-shield-alt"></i>
|
|
</div>
|
|
<h5>Quality Reports</h5>
|
|
<p class="text-muted">QC results, compliance metrics, CAPA tracking</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END report categories -->
|
|
|
|
<!-- BEGIN analytics dashboard -->
|
|
<div class="row">
|
|
<!-- BEGIN col-8 -->
|
|
<div class="col-xl-8">
|
|
<!-- BEGIN donation trends -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Donation Trends</h4>
|
|
<div class="panel-heading-btn">
|
|
<select class="form-select form-select-sm" id="donationPeriod" onchange="updateDonationChart()">
|
|
<option value="7">Last 7 Days</option>
|
|
<option value="30">Last 30 Days</option>
|
|
<option value="90">Last 3 Months</option>
|
|
<option value="365">Last Year</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="chart-container">
|
|
<canvas id="donationTrendChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END donation trends -->
|
|
|
|
<!-- BEGIN blood group distribution -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Blood Group Distribution</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<h6 class="text-center">Donations</h6>
|
|
<div class="chart-container" style="height: 300px;">
|
|
<canvas id="donationBloodGroupChart"></canvas>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h6 class="text-center">Transfusions</h6>
|
|
<div class="chart-container" style="height: 300px;">
|
|
<canvas id="transfusionBloodGroupChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END blood group distribution -->
|
|
|
|
<!-- BEGIN utilization metrics -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Utilization Metrics</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="chart-container">
|
|
<canvas id="utilizationChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END utilization metrics -->
|
|
</div>
|
|
<!-- END col-8 -->
|
|
|
|
<!-- BEGIN col-4 -->
|
|
<div class="col-xl-4">
|
|
<!-- BEGIN quick reports -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Quick Reports</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="d-grid gap-2">
|
|
<button type="button" class="btn btn-outline-primary" onclick="generateQuickReport('daily')">
|
|
<i class="fa fa-calendar-day"></i> Daily Summary
|
|
</button>
|
|
<button type="button" class="btn btn-outline-success" onclick="generateQuickReport('weekly')">
|
|
<i class="fa fa-calendar-week"></i> Weekly Report
|
|
</button>
|
|
<button type="button" class="btn btn-outline-info" onclick="generateQuickReport('monthly')">
|
|
<i class="fa fa-calendar-alt"></i> Monthly Report
|
|
</button>
|
|
<button type="button" class="btn btn-outline-warning" onclick="generateQuickReport('expiry')">
|
|
<i class="fa fa-exclamation-triangle"></i> Expiry Alert
|
|
</button>
|
|
<button type="button" class="btn btn-outline-danger" onclick="generateQuickReport('critical')">
|
|
<i class="fa fa-exclamation-circle"></i> Critical Levels
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END quick reports -->
|
|
|
|
<!-- BEGIN top donors -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Top Donors This Month</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
{% for donor in top_donors %}
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<div>
|
|
<strong>{{ donor.full_name }}</strong>
|
|
<br><small class="text-muted">{{ donor.donor_id }}</small>
|
|
</div>
|
|
<div class="text-end">
|
|
<span class="badge bg-primary">{{ donor.donations_this_month }}</span>
|
|
<br><small class="text-muted">donations</small>
|
|
</div>
|
|
</div>
|
|
{% empty %}
|
|
<div class="text-center py-3">
|
|
<i class="fa fa-users fa-2x text-muted mb-2"></i>
|
|
<p class="text-muted mb-0">No donations this month</p>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
<!-- END top donors -->
|
|
|
|
<!-- BEGIN recent alerts -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Recent Alerts</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
{% for alert in recent_alerts %}
|
|
<div class="alert alert-{{ alert.severity }} d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<i class="fa {{ alert.icon }}"></i>
|
|
{{ alert.message }}
|
|
</div>
|
|
<small class="text-muted">{{ alert.timestamp|date:"H:i" }}</small>
|
|
</div>
|
|
{% empty %}
|
|
<div class="text-center py-3">
|
|
<i class="fa fa-check-circle fa-2x text-success mb-2"></i>
|
|
<p class="text-muted mb-0">No recent alerts</p>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
<!-- END recent alerts -->
|
|
|
|
<!-- BEGIN custom report builder -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Custom Report Builder</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<form id="customReportForm">
|
|
<div class="mb-3">
|
|
<label class="form-label">Report Type</label>
|
|
<select class="form-select" id="customReportType">
|
|
<option value="inventory">Inventory Analysis</option>
|
|
<option value="donor">Donor Analysis</option>
|
|
<option value="usage">Usage Analysis</option>
|
|
<option value="quality">Quality Metrics</option>
|
|
<option value="financial">Financial Report</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Date Range</label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" id="startDate" placeholder="Start Date">
|
|
<input type="text" class="form-control" id="endDate" placeholder="End Date">
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Filters</label>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="includeExpired">
|
|
<label class="form-check-label" for="includeExpired">
|
|
Include Expired Units
|
|
</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="groupByBloodType">
|
|
<label class="form-check-label" for="groupByBloodType">
|
|
Group by Blood Type
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="d-grid">
|
|
<button type="button" class="btn btn-primary" onclick="generateCustomReport()">
|
|
<i class="fa fa-chart-bar"></i> Generate Report
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<!-- END custom report builder -->
|
|
</div>
|
|
<!-- END col-4 -->
|
|
</div>
|
|
<!-- END analytics dashboard -->
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script src="{% static 'plugins/chart.js/dist/chart.umd.js' %}"></script>
|
|
<script src="{% static 'plugins/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js' %}"></script>
|
|
|
|
<script>
|
|
$(document).ready(function() {
|
|
initializeCharts();
|
|
initializeDatePickers();
|
|
});
|
|
|
|
function initializeCharts() {
|
|
// Donation Trend Chart
|
|
var donationCtx = document.getElementById('donationTrendChart').getContext('2d');
|
|
var donationChart = new Chart(donationCtx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: {{ donation_trend_labels|safe }},
|
|
datasets: [{
|
|
label: 'Donations',
|
|
data: {{ donation_trend_data|safe }},
|
|
borderColor: '#28a745',
|
|
backgroundColor: 'rgba(40, 167, 69, 0.1)',
|
|
tension: 0.4
|
|
}, {
|
|
label: 'Transfusions',
|
|
data: {{ transfusion_trend_data|safe }},
|
|
borderColor: '#007bff',
|
|
backgroundColor: 'rgba(0, 123, 255, 0.1)',
|
|
tension: 0.4
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Blood Group Distribution Charts
|
|
var donationBGCtx = document.getElementById('donationBloodGroupChart').getContext('2d');
|
|
var donationBGChart = new Chart(donationBGCtx, {
|
|
type: 'doughnut',
|
|
data: {
|
|
labels: {{ blood_group_labels|safe }},
|
|
datasets: [{
|
|
data: {{ donation_blood_group_data|safe }},
|
|
backgroundColor: ['#dc3545', '#007bff', '#28a745', '#ffc107']
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false
|
|
}
|
|
});
|
|
|
|
var transfusionBGCtx = document.getElementById('transfusionBloodGroupChart').getContext('2d');
|
|
var transfusionBGChart = new Chart(transfusionBGCtx, {
|
|
type: 'doughnut',
|
|
data: {
|
|
labels: {{ blood_group_labels|safe }},
|
|
datasets: [{
|
|
data: {{ transfusion_blood_group_data|safe }},
|
|
backgroundColor: ['#dc3545', '#007bff', '#28a745', '#ffc107']
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false
|
|
}
|
|
});
|
|
|
|
// Utilization Chart
|
|
var utilizationCtx = document.getElementById('utilizationChart').getContext('2d');
|
|
var utilizationChart = new Chart(utilizationCtx, {
|
|
type: 'bar',
|
|
data: {
|
|
labels: {{ utilization_labels|safe }},
|
|
datasets: [{
|
|
label: 'Collected',
|
|
data: {{ collected_data|safe }},
|
|
backgroundColor: '#28a745'
|
|
}, {
|
|
label: 'Used',
|
|
data: {{ used_data|safe }},
|
|
backgroundColor: '#007bff'
|
|
}, {
|
|
label: 'Expired',
|
|
data: {{ expired_data|safe }},
|
|
backgroundColor: '#dc3545'
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function initializeDatePickers() {
|
|
$('#startDate, #endDate').datepicker({
|
|
format: 'yyyy-mm-dd',
|
|
autoclose: true,
|
|
todayHighlight: true
|
|
});
|
|
}
|
|
|
|
function generateReport(category) {
|
|
Swal.fire({
|
|
title: `Generate ${category.charAt(0).toUpperCase() + category.slice(1)} Report`,
|
|
html: `
|
|
<div class="text-start">
|
|
<div class="mb-3">
|
|
<label class="form-label">Report Period</label>
|
|
<select class="form-select" id="reportPeriod">
|
|
<option value="week">Last Week</option>
|
|
<option value="month">Last Month</option>
|
|
<option value="quarter">Last Quarter</option>
|
|
<option value="year">Last Year</option>
|
|
<option value="custom">Custom Range</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Format</label>
|
|
<select class="form-select" id="reportFormat">
|
|
<option value="pdf">PDF</option>
|
|
<option value="excel">Excel</option>
|
|
<option value="csv">CSV</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Include Charts</label>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="includeCharts" checked>
|
|
<label class="form-check-label" for="includeCharts">
|
|
Include visual charts and graphs
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`,
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Generate Report',
|
|
cancelButtonText: 'Cancel'
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: 'Report Generated',
|
|
text: `${category} report is being prepared and will be available for download shortly.`,
|
|
confirmButtonText: 'OK'
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function generateQuickReport(type) {
|
|
var reportNames = {
|
|
'daily': 'Daily Summary Report',
|
|
'weekly': 'Weekly Activity Report',
|
|
'monthly': 'Monthly Statistics Report',
|
|
'expiry': 'Expiry Alert Report',
|
|
'critical': 'Critical Inventory Levels Report'
|
|
};
|
|
|
|
Swal.fire({
|
|
title: 'Generating Report...',
|
|
text: `Preparing ${reportNames[type]}`,
|
|
allowOutsideClick: false,
|
|
didOpen: () => {
|
|
Swal.showLoading();
|
|
}
|
|
});
|
|
|
|
// Simulate report generation
|
|
setTimeout(function() {
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: 'Report Ready',
|
|
text: `${reportNames[type]} has been generated successfully.`,
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Download',
|
|
cancelButtonText: 'Close'
|
|
});
|
|
}, 2000);
|
|
}
|
|
|
|
function generateCustomReport() {
|
|
var reportType = document.getElementById('customReportType').value;
|
|
var startDate = document.getElementById('startDate').value;
|
|
var endDate = document.getElementById('endDate').value;
|
|
|
|
if (!startDate || !endDate) {
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: 'Missing Information',
|
|
text: 'Please select both start and end dates.',
|
|
confirmButtonText: 'OK'
|
|
});
|
|
return;
|
|
}
|
|
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: 'Custom Report Generated',
|
|
text: `${reportType} report for ${startDate} to ${endDate} is being prepared.`,
|
|
confirmButtonText: 'OK'
|
|
});
|
|
}
|
|
|
|
function updateDonationChart() {
|
|
var period = document.getElementById('donationPeriod').value;
|
|
|
|
Swal.fire({
|
|
title: 'Updating Chart...',
|
|
text: `Loading data for last ${period} days`,
|
|
timer: 1500,
|
|
showConfirmButton: false,
|
|
didOpen: () => {
|
|
Swal.showLoading();
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|