HH/templates/accounts/onboarding/dashboard.html

383 lines
18 KiB
HTML

{% extends 'layouts/base.html' %}
{% load i18n %}
{% block title %}{{ page_title }} - PX360{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<!-- Header -->
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h4 class="mb-1">
<i class="fas fa-chart-pie text-primary me-2"></i>{% trans "Acknowledgement Dashboard" %}
</h4>
<p class="text-muted mb-0">{% trans "Track onboarding progress and user acknowledgements" %}</p>
</div>
<div class="d-flex gap-2">
<a href="{% url 'accounts:provisional-user-list' %}" class="btn btn-outline-primary">
<i class="fas fa-users me-1"></i>{% trans "Manage Users" %}
</a>
<a href="{% url 'accounts:acknowledgement-content-list' %}" class="btn btn-outline-secondary">
<i class="fas fa-file-alt me-1"></i>{% trans "Manage Content" %}
</a>
</div>
</div>
<!-- Statistics Cards -->
<div class="row g-3 mb-4">
<div class="col-md-3">
<div class="card bg-primary text-white h-100">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="card-title mb-0">{% trans "Total Users" %}</h6>
<h2 class="mb-0">{{ stats.completed_onboarding|add:stats.in_progress }}</h2>
</div>
<i class="fas fa-users fa-2x opacity-50"></i>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-success text-white h-100">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="card-title mb-0">{% trans "Completed" %}</h6>
<h2 class="mb-0">{{ stats.completed_onboarding }}</h2>
<small>{{ stats.completion_rate }}% {% trans "completion rate" %}</small>
</div>
<i class="fas fa-check-circle fa-2x opacity-50"></i>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-warning text-dark h-100">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="card-title mb-0">{% trans "In Progress" %}</h6>
<h2 class="mb-0">{{ stats.in_progress }}</h2>
<small>{% trans "awaiting completion" %}</small>
</div>
<i class="fas fa-clock fa-2x opacity-50"></i>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-danger text-white h-100">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="card-title mb-0">{% trans "Expired" %}</h6>
<h2 class="mb-0">{{ stats.expired_invitations }}</h2>
<small>{% trans "need renewal" %}</small>
</div>
<i class="fas fa-exclamation-triangle fa-2x opacity-50"></i>
</div>
</div>
</div>
</div>
</div>
<!-- Charts Row -->
<div class="row g-4 mb-4">
<!-- Daily Activity Chart -->
<div class="col-lg-8">
<div class="card h-100">
<div class="card-header bg-white">
<h5 class="card-title mb-0">
<i class="fas fa-chart-line text-primary me-2"></i>{% trans "Daily Acknowledgements (Last 30 Days)" %}
</h5>
</div>
<div class="card-body">
<canvas id="dailyActivityChart" height="250"></canvas>
</div>
</div>
</div>
<!-- Completion by Role -->
<div class="col-lg-4">
<div class="card h-100">
<div class="card-header bg-white">
<h5 class="card-title mb-0">
<i class="fas fa-user-tag text-info me-2"></i>{% trans "Completion by Role" %}
</h5>
</div>
<div class="card-body">
{% for role_data in completion_by_role %}
<div class="mb-3">
<div class="d-flex justify-content-between mb-1">
<span>{{ role_data.role }}</span>
<span class="fw-bold">{{ role_data.rate }}%</span>
</div>
<div class="progress" style="height: 8px;">
<div class="progress-bar {% if role_data.rate >= 80 %}bg-success{% elif role_data.rate >= 50 %}bg-warning{% else %}bg-danger{% endif %}"
role="progressbar"
style="width: {{ role_data.rate }}%"
aria-valuenow="{{ role_data.rate }}"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
<small class="text-muted">{{ role_data.completed }}/{{ role_data.total }} {% trans "completed" %}</small>
</div>
{% empty %}
<p class="text-muted text-center">{% trans "No data available" %}</p>
{% endfor %}
</div>
</div>
</div>
</div>
<!-- Pending Users & Checklist Stats -->
<div class="row g-4 mb-4">
<!-- Pending Users -->
<div class="col-lg-6">
<div class="card h-100">
<div class="card-header bg-white d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">
<i class="fas fa-user-clock text-warning me-2"></i>{% trans "Pending Users" %}
</h5>
<span class="badge bg-warning">{{ pending_users|length }}</span>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>{% trans "User" %}</th>
<th>{% trans "Role" %}</th>
<th>{% trans "Days Left" %}</th>
<th>{% trans "Action" %}</th>
</tr>
</thead>
<tbody>
{% for user in pending_users %}
<tr {% if user.is_expiring_soon %}class="table-danger"{% endif %}>
<td>
<div class="d-flex align-items-center">
<div class="bg-light rounded-circle d-flex align-items-center justify-content-center me-2" style="width: 32px; height: 32px;">
<i class="fas fa-user text-muted small"></i>
</div>
<div>
<div class="fw-semibold">{{ user.get_full_name|default:user.email }}</div>
<small class="text-muted">{{ user.email }}</small>
</div>
</div>
</td>
<td>
{% if user.groups.first %}
<span class="badge bg-secondary">{{ user.groups.first.name }}</span>
{% else %}-{% endif %}
</td>
<td>
{% if user.days_remaining is not None %}
{% if user.is_expiring_soon %}
<span class="badge bg-danger">{{ user.days_remaining }} {% trans "days" %}</span>
{% else %}
<span class="badge bg-info">{{ user.days_remaining }} {% trans "days" %}</span>
{% endif %}
{% else %}-
{% endif %}
</td>
<td>
<a href="{% url 'accounts:provisional-user-progress' user.id %}" class="btn btn-sm btn-outline-primary">
<i class="fas fa-eye"></i>
</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="4" class="text-center text-muted py-4">
<i class="fas fa-check-circle text-success fa-2x mb-2"></i>
<p class="mb-0">{% trans "No pending users!" %}</p>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Checklist Item Stats -->
<div class="col-lg-6">
<div class="card h-100">
<div class="card-header bg-white">
<h5 class="card-title mb-0">
<i class="fas fa-tasks text-secondary me-2"></i>{% trans "Items Needing Attention" %}
</h5>
</div>
<div class="card-body">
{% for stat in checklist_stats %}
<div class="d-flex align-items-center mb-3 p-2 bg-light rounded">
<div class="flex-grow-1">
<div class="fw-semibold">{{ stat.item.text_en|truncatechars:50 }}</div>
<small class="text-muted">{{ stat.acknowledged_count }}/{{ stat.eligible_count }} {% trans "acknowledged" %}</small>
</div>
<div class="text-end ms-3">
<span class="badge {% if stat.completion_rate >= 80 %}bg-success{% elif stat.completion_rate >= 50 %}bg-warning{% else %}bg-danger{% endif %}">
{{ stat.completion_rate }}%
</span>
</div>
</div>
{% empty %}
<p class="text-muted text-center py-4">
<i class="fas fa-check-circle text-success fa-2x mb-2"></i><br>
{% trans "All items have good completion rates!" %}
</p>
{% endfor %}
</div>
</div>
</div>
</div>
<!-- Recent Activations -->
<div class="card">
<div class="card-header bg-white">
<h5 class="card-title mb-0">
<i class="fas fa-history text-success me-2"></i>{% trans "Recent Activations" %}
</h5>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>{% trans "User" %}</th>
<th>{% trans "Role" %}</th>
<th>{% trans "Hospital" %}</th>
<th>{% trans "Department" %}</th>
<th>{% trans "Completed At" %}</th>
</tr>
</thead>
<tbody>
{% for user in recent_activations %}
<tr>
<td>
<div class="d-flex align-items-center">
<div class="bg-success bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center me-2" style="width: 32px; height: 32px;">
<i class="fas fa-check text-success small"></i>
</div>
<div>
<div class="fw-semibold">{{ user.get_full_name }}</div>
<small class="text-muted">{{ user.email }}</small>
</div>
</div>
</td>
<td>
{% if user.groups.first %}
<span class="badge bg-secondary">{{ user.groups.first.name }}</span>
{% else %}-{% endif %}
</td>
<td>{{ user.hospital.name|default:"-" }}</td>
<td>{{ user.department.name|default:"-" }}</td>
<td>
<span class="text-success">
<i class="fas fa-calendar-check me-1"></i>
{{ user.acknowledgement_completed_at|date:"M d, Y H:i" }}
</span>
</td>
</tr>
{% empty %}
<tr>
<td colspan="5" class="text-center text-muted py-4">
{% trans "No recent activations" %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% if is_px_admin and completion_by_hospital %}
<!-- Hospital Stats (PX Admin only) -->
<div class="card mt-4">
<div class="card-header bg-white">
<h5 class="card-title mb-0">
<i class="fas fa-hospital text-primary me-2"></i>{% trans "Completion by Hospital" %}
</h5>
</div>
<div class="card-body">
<div class="row">
{% for hosp_data in completion_by_hospital %}
<div class="col-md-4 mb-3">
<div class="border rounded p-3">
<div class="d-flex justify-content-between align-items-center mb-2">
<h6 class="mb-0">{{ hosp_data.hospital.name }}</h6>
<span class="badge {% if hosp_data.rate >= 80 %}bg-success{% elif hosp_data.rate >= 50 %}bg-warning{% else %}bg-danger{% endif %}">
{{ hosp_data.rate }}%
</span>
</div>
<div class="progress mb-2" style="height: 6px;">
<div class="progress-bar {% if hosp_data.rate >= 80 %}bg-success{% elif hosp_data.rate >= 50 %}bg-warning{% else %}bg-danger{% endif %}"
role="progressbar"
style="width: {{ hosp_data.rate }}%">
</div>
</div>
<small class="text-muted">{{ hosp_data.completed }}/{{ hosp_data.total }} {% trans "users completed" %}</small>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endif %}
</div>
{% endblock %}
{% block extra_js %}
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Daily Activity Chart
const dailyCtx = document.getElementById('dailyActivityChart').getContext('2d');
const dailyData = {{ daily_data|safe }};
new Chart(dailyCtx, {
type: 'line',
data: {
labels: dailyData.map(d => d.date),
datasets: [{
label: '{% trans "Acknowledgements" %}',
data: dailyData.map(d => d.count),
borderColor: '#0d6efd',
backgroundColor: 'rgba(13, 110, 253, 0.1)',
borderWidth: 2,
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true,
ticks: {
stepSize: 1
}
},
x: {
grid: {
display: false
}
}
}
}
});
});
</script>
{% endblock %}