You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
7.9 KiB
173 lines
7.9 KiB
{% extends "admin/base_site.html" %}
|
|
{% load i18n unfold %}
|
|
|
|
{% block breadcrumbs %}{% endblock %}
|
|
|
|
{% block title %}
|
|
{% trans 'Dashboard' %} | {{ site_title|default:_('Django site admin') }}
|
|
{% endblock %}
|
|
|
|
{% block branding %}
|
|
{% include "unfold/helpers/site_branding.html" %}
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
{% include "unfold/helpers/messages.html" %}
|
|
|
|
<div class="mb-6">
|
|
<h1 class="text-2xl font-semibold text-gray-900 dark:text-gray-100">
|
|
{% trans 'System Overview' %}
|
|
</h1>
|
|
</div>
|
|
|
|
<!-- Top Row: KPI Cards using Unfold Native Components -->
|
|
<div class="grid gap-4 mb-8 md:grid-cols-2 lg:grid-cols-3">
|
|
{% for item in kpi %}
|
|
{% component "unfold/components/card.html" %}
|
|
|
|
{% component "unfold/components/text.html" %}
|
|
{{ item.title }}
|
|
{% endcomponent %}
|
|
|
|
<div class="mt-2">
|
|
{% component "unfold/components/title.html" %}
|
|
{{ item.metric }}
|
|
{% endcomponent %}
|
|
</div>
|
|
|
|
{% if item.footer %}
|
|
<div class="mt-4 text-sm">
|
|
{{ item.footer|safe }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% endcomponent %}
|
|
{% empty %}
|
|
<p class="text-gray-500 dark:text-gray-400 col-span-full">
|
|
{% trans "No statistics available for this panel." %}
|
|
</p>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<!-- Bottom Row: Top Courses List & Donut Chart -->
|
|
{% if top_courses %}
|
|
<div class="grid gap-4 md:grid-cols-3 items-start">
|
|
|
|
<!-- Left Column: Top 5 Courses -->
|
|
<div class="md:col-span-2">
|
|
{% component "unfold/components/card.html" %}
|
|
<div class="border-b border-gray-200 dark:border-gray-700 pb-4 mb-2">
|
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white">{% trans "Top 5 Popular Courses" %}</h3>
|
|
</div>
|
|
|
|
<ul class="divide-y divide-gray-200 dark:divide-gray-700">
|
|
{% for course in top_courses %}
|
|
<li class="py-5 flex items-center justify-between transition-colors">
|
|
<div class="flex items-center gap-5">
|
|
|
|
<!-- UPGRADED PERFECT CIRCLE BADGE -->
|
|
<span
|
|
class="flex-shrink-0 flex items-center justify-center rounded-full bg-primary-100 text-primary-700 dark:bg-primary-900/30 dark:text-primary-400 font-extrabold text-xl border-2 border-primary-300 dark:border-primary-700"
|
|
style="width: 2rem; height: 2rem; min-width: 2rem; min-height: 2rem;">
|
|
{{ forloop.counter }}
|
|
</span>
|
|
|
|
<div>
|
|
<p class="text-base font-semibold text-gray-900 dark:text-white mb-1">{{ course.title }}</p>
|
|
<p class="text-sm text-gray-500 dark:text-gray-400">By
|
|
{% if course.professor.fullname %}
|
|
{{ course.professor.fullname }}
|
|
{% else %}
|
|
{{ course.professor.email|default:"Unknown" }}
|
|
{% endif %}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<span
|
|
class="flex-shrink-0 inline-flex items-center px-4 py-1.5 rounded-full text-sm font-bold bg-primary-100 text-primary-800 dark:bg-primary-900/40 dark:text-primary-300 border border-primary-200 dark:border-primary-700 shadow-sm">
|
|
{{ course.participant_count }} {% trans "Participants" %}
|
|
</span>
|
|
</li>
|
|
{% empty %}
|
|
<li class="py-4 text-sm text-gray-500">{% trans "No courses found." %}</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% endcomponent %}
|
|
</div>
|
|
|
|
<!-- Right Column: Success Rate Donut Chart -->
|
|
<div>
|
|
{% component "unfold/components/card.html" %}
|
|
<div class="flex flex-col items-center justify-center text-center py-2">
|
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-6">{% trans "Transaction Status" %}
|
|
</h3>
|
|
|
|
{% if tx_stats.total > 0 %}
|
|
<!-- Multi-Segment CSS SVG Donut -->
|
|
<div class="relative w-40 h-40 mb-8">
|
|
<svg class="w-full h-full transform -rotate-90" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
<!-- Base Background (Failed - Red) -->
|
|
<circle cx="18" cy="18" r="16" fill="none" style="color: #ef4444;" stroke="currentColor"
|
|
stroke-width="4" />
|
|
|
|
<!-- Layer 3: Waiting Approval (Bright Blue) -->
|
|
<circle cx="18" cy="18" r="16" fill="none" class="transition-all duration-1000 ease-out"
|
|
style="color: #3b82f6;" stroke="currentColor" stroke-width="4" stroke-dasharray="100 100"
|
|
stroke-dashoffset="{{ tx_stats.offset_waiting }}" />
|
|
|
|
<!-- Layer 2: Pending (Yellow) -->
|
|
<circle cx="18" cy="18" r="16" fill="none" class="transition-all duration-1000 ease-out"
|
|
style="color: #facc15;" stroke="currentColor" stroke-width="4" stroke-dasharray="100 100"
|
|
stroke-dashoffset="{{ tx_stats.offset_pending }}" />
|
|
|
|
<!-- Layer 1: Success (Green) -->
|
|
<circle cx="18" cy="18" r="16" fill="none" class="transition-all duration-1000 ease-out"
|
|
style="color: #22c55e;" stroke="currentColor" stroke-width="4" stroke-dasharray="100 100"
|
|
stroke-dashoffset="{{ tx_stats.offset_success }}" />
|
|
|
|
</svg>
|
|
<div class="absolute inset-0 flex flex-col items-center justify-center">
|
|
<span class="text-3xl font-bold text-gray-900 dark:text-white">{{ tx_stats.pct_success }}%</span>
|
|
<span class="text-xs text-gray-500">{% trans "Success" %}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Legend -->
|
|
<div class="w-full grid grid-cols-2 gap-y-3 gap-x-2 text-sm text-left">
|
|
<div class="flex items-center gap-2">
|
|
<span class="w-3 h-3 rounded-full flex-shrink-0" style="background-color: #22c55e;"></span>
|
|
<span class="text-gray-600 dark:text-gray-300">{% trans "Success" %}: <strong
|
|
class="text-gray-900 dark:text-white">{{ tx_stats.pct_success }}%</strong></span>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<span class="w-3 h-3 rounded-full flex-shrink-0" style="background-color: #facc15;"></span>
|
|
<span class="text-gray-600 dark:text-gray-300">{% trans "Pending" %}: <strong
|
|
class="text-gray-900 dark:text-white">{{ tx_stats.pct_pending }}%</strong></span>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<span class="w-3 h-3 rounded-full flex-shrink-0" style="background-color: #3b82f6;"></span>
|
|
<span class="text-gray-600 dark:text-gray-300">{% trans "Waiting" %}: <strong
|
|
class="text-gray-900 dark:text-white">{{ tx_stats.pct_waiting }}%</strong></span>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<span class="w-3 h-3 rounded-full flex-shrink-0" style="background-color: #ef4444;"></span>
|
|
<span class="text-gray-600 dark:text-gray-300">{% trans "Failed" %}: <strong
|
|
class="text-gray-900 dark:text-white">{{ tx_stats.pct_failed }}%</strong></span>
|
|
</div>
|
|
</div>
|
|
|
|
{% else %}
|
|
<div class="py-12 flex flex-col items-center justify-center">
|
|
<span class="material-symbols-outlined text-4xl text-gray-300 dark:text-gray-600 mb-2">payments</span>
|
|
<p class="text-sm text-gray-500 dark:text-gray-400">{% trans "No transactions recorded yet." %}</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endcomponent %}
|
|
</div>
|
|
|
|
</div>
|
|
{% endif %}
|
|
{% endblock %}
|