mirror of
https://github.com/barkeser2002/offline-db.git
synced 2026-09-25 08:00:11 +03:00
- Create `templates/admin/index.html` extending Unfold base layout to display custom dashboard. - Add Chart.js visualization for "Bandwidth Saved" metric. - Display key stats: CPU, RAM, Bandwidth Saved, Revenue, Anime/Episode counts. - Utilize existing `core.dashboard.dashboard_callback` logic. - Ensure safe data passing using `json_script`. - Verified with unit tests in `core/tests/test_dashboard.py`. Current Focus Mode: C (The Architect) Changes Made: - Added `templates/admin/index.html` with Chart.js integration. - Fixed template to use `json_script` for safe JSON handling. - Verified backend logic in `core/dashboard.py`. Impact on System: - Admins now see a visual dashboard with real-time stats and bandwidth savings graph upon login. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
125 lines
6.7 KiB
HTML
125 lines
6.7 KiB
HTML
{% extends 'unfold/layouts/base.html' %}
|
|
{% load i18n %}
|
|
|
|
{% block content %}
|
|
<div class="p-4">
|
|
<h1 class="text-3xl font-bold mb-6 text-gray-800 dark:text-gray-200">{% trans "Dashboard" %}</h1>
|
|
|
|
<!-- Stats Grid -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
|
<!-- CPU Usage -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 border-l-4 border-blue-500">
|
|
<div class="flex items-center">
|
|
<div class="p-3 rounded-full bg-blue-100 dark:bg-blue-900 text-blue-500">
|
|
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z"></path></svg>
|
|
</div>
|
|
<div class="ml-4">
|
|
<p class="text-sm text-gray-500 dark:text-gray-400">CPU Usage</p>
|
|
<p class="text-2xl font-bold text-gray-700 dark:text-white">{{ dashboard_stats.cpu }}%</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- RAM Usage -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 border-l-4 border-purple-500">
|
|
<div class="flex items-center">
|
|
<div class="p-3 rounded-full bg-purple-100 dark:bg-purple-900 text-purple-500">
|
|
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.384-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"></path></svg>
|
|
</div>
|
|
<div class="ml-4">
|
|
<p class="text-sm text-gray-500 dark:text-gray-400">RAM Usage</p>
|
|
<p class="text-2xl font-bold text-gray-700 dark:text-white">{{ dashboard_stats.ram }}%</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bandwidth Saved -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 border-l-4 border-green-500">
|
|
<div class="flex items-center">
|
|
<div class="p-3 rounded-full bg-green-100 dark:bg-green-900 text-green-500">
|
|
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path></svg>
|
|
</div>
|
|
<div class="ml-4">
|
|
<p class="text-sm text-gray-500 dark:text-gray-400">Bandwidth Saved</p>
|
|
<p class="text-2xl font-bold text-gray-700 dark:text-white">{{ dashboard_stats.bandwidth_saved_gb }} GB</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Total Revenue -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6 border-l-4 border-yellow-500">
|
|
<div class="flex items-center">
|
|
<div class="p-3 rounded-full bg-yellow-100 dark:bg-yellow-900 text-yellow-500">
|
|
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
|
|
</div>
|
|
<div class="ml-4">
|
|
<p class="text-sm text-gray-500 dark:text-gray-400">Total Revenue</p>
|
|
<p class="text-2xl font-bold text-gray-700 dark:text-white">${{ dashboard_stats.total_revenue }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Charts Section -->
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
<!-- Bandwidth Chart -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-200 mb-4">{% trans "Bandwidth Savings (Last 7 Days)" %}</h3>
|
|
<canvas id="bandwidthChart"></canvas>
|
|
</div>
|
|
|
|
<!-- Content Stats -->
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
|
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-200 mb-4">{% trans "Content Overview" %}</h3>
|
|
<div class="space-y-4">
|
|
<div class="flex justify-between items-center border-b border-gray-200 dark:border-gray-700 pb-2">
|
|
<span class="text-gray-600 dark:text-gray-400">Total Anime</span>
|
|
<span class="font-bold text-gray-800 dark:text-white">{{ dashboard_stats.anime_count }}</span>
|
|
</div>
|
|
<div class="flex justify-between items-center border-b border-gray-200 dark:border-gray-700 pb-2">
|
|
<span class="text-gray-600 dark:text-gray-400">Total Episodes</span>
|
|
<span class="font-bold text-gray-800 dark:text-white">{{ dashboard_stats.episode_count }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{ dashboard_stats.bandwidth_chart|json_script:"bandwidth-chart-data" }}
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const ctx = document.getElementById('bandwidthChart').getContext('2d');
|
|
|
|
// dashboard_stats.bandwidth_chart is a JSON string generated by python's json.dumps.
|
|
// When passed to json_script, it's treated as a string value, so the script tag contains "{\"labels\": ...}".
|
|
// First parse gets the string.
|
|
const rawData = JSON.parse(document.getElementById('bandwidth-chart-data').textContent);
|
|
// Second parse gets the object (if it's a string).
|
|
const chartData = (typeof rawData === 'string') ? JSON.parse(rawData) : rawData;
|
|
|
|
new Chart(ctx, {
|
|
type: 'line',
|
|
data: chartData,
|
|
options: {
|
|
responsive: true,
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true,
|
|
title: {
|
|
display: true,
|
|
text: 'GB Saved'
|
|
}
|
|
}
|
|
},
|
|
plugins: {
|
|
legend: {
|
|
display: false
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %}
|