WIP: Add light/dark theme with toggle in navbar (dark theme styling incomplete - dont care for now)

This commit is contained in:
Peter Stockings
2025-11-23 22:00:41 +11:00
parent fc494a9355
commit 89a17f68ab
15 changed files with 463 additions and 260 deletions

View File

@@ -1,21 +1,22 @@
{% extends 'dashboard.html' %}
{% block page %}
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="/static/js/chart.js"></script>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div class="mb-8">
<h1 class="text-2xl font-bold text-gray-900">Dashboard Overview</h1>
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">Dashboard Overview</h1>
</div>
<!-- Key Metrics Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<!-- Total Functions -->
<div class="bg-white rounded-xl shadow-sm p-6 border border-gray-100">
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6 border border-gray-100 dark:border-gray-700">
<div class="flex items-center justify-between mb-4">
<h3 class="text-sm font-medium text-gray-500">Total Functions</h3>
<div class="p-2 bg-indigo-50 rounded-lg">
<svg class="w-6 h-6 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">Total Functions</h3>
<div class="p-2 bg-indigo-50 dark:bg-indigo-900/20 rounded-lg">
<svg class="w-6 h-6 text-indigo-600 dark:text-indigo-400" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z">
</path>
@@ -23,37 +24,41 @@
</div>
</div>
<div class="flex items-baseline">
<p class="text-2xl font-bold text-gray-900">{{ stats.total_timer_functions + stats.total_http_functions
<p class="text-2xl font-bold text-gray-900 dark:text-white">{{ stats.total_timer_functions +
stats.total_http_functions
}}</p>
<p class="ml-2 text-sm text-gray-500">
<p class="ml-2 text-sm text-gray-500 dark:text-gray-400">
({{ stats.total_http_functions }} HTTP, {{ stats.total_timer_functions }} Timer)
</p>
</div>
</div>
<!-- Total Invocations -->
<div class="bg-white rounded-xl shadow-sm p-6 border border-gray-100">
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6 border border-gray-100 dark:border-gray-700">
<div class="flex items-center justify-between mb-4">
<h3 class="text-sm font-medium text-gray-500">Total Invocations</h3>
<div class="p-2 bg-blue-50 rounded-lg">
<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">Total Invocations</h3>
<div class="p-2 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
<svg class="w-6 h-6 text-blue-600 dark:text-blue-400" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M13 10V3L4 14h7v7l9-11h-7z"></path>
</svg>
</div>
</div>
<div class="flex items-baseline">
<p class="text-2xl font-bold text-gray-900">{{ stats.timer_invocations + stats.http_invocations }}</p>
<p class="ml-2 text-sm text-gray-500">All time</p>
<p class="text-2xl font-bold text-gray-900 dark:text-white">{{ stats.timer_invocations +
stats.http_invocations }}</p>
<p class="ml-2 text-sm text-gray-500 dark:text-gray-400">All time</p>
</div>
</div>
<!-- Success Rate -->
<div class="bg-white rounded-xl shadow-sm p-6 border border-gray-100">
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6 border border-gray-100 dark:border-gray-700">
<div class="flex items-center justify-between mb-4">
<h3 class="text-sm font-medium text-gray-500">Overall Success Rate</h3>
<div class="p-2 bg-green-50 rounded-lg">
<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">Overall Success Rate</h3>
<div class="p-2 bg-green-50 dark:bg-green-900/20 rounded-lg">
<svg class="w-6 h-6 text-green-600 dark:text-green-400" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
@@ -63,19 +68,20 @@
{% set total_success = stats.timer_successful_invocations + stats.http_successful_invocations %}
{% set success_rate = (total_success / total_invocations * 100)|round(1) if total_invocations > 0 else 0 %}
<div class="flex items-baseline">
<p class="text-2xl font-bold text-gray-900">{{ success_rate }}%</p>
<p class="ml-2 text-sm text-gray-500">
<p class="text-2xl font-bold text-gray-900 dark:text-white">{{ success_rate }}%</p>
<p class="ml-2 text-sm text-gray-500 dark:text-gray-400">
{{ total_success }}/{{ total_invocations }}
</p>
</div>
</div>
<!-- Avg Execution Time -->
<div class="bg-white rounded-xl shadow-sm p-6 border border-gray-100">
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6 border border-gray-100 dark:border-gray-700">
<div class="flex items-center justify-between mb-4">
<h3 class="text-sm font-medium text-gray-500">Avg Execution Time</h3>
<div class="p-2 bg-yellow-50 rounded-lg">
<svg class="w-6 h-6 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<h3 class="text-sm font-medium text-gray-500 dark:text-gray-400">Avg Execution Time</h3>
<div class="p-2 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg">
<svg class="w-6 h-6 text-yellow-600 dark:text-yellow-400" fill="none" stroke="currentColor"
viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
@@ -83,7 +89,7 @@
</div>
{% set avg_time = ((stats.avg_timer_execution_time or 0) + (stats.avg_http_execution_time or 0)) / 2 %}
<div class="flex items-baseline">
<p class="text-2xl font-bold text-gray-900">{{ "%.2f"|format(avg_time) }}s</p>
<p class="text-2xl font-bold text-gray-900 dark:text-white">{{ "%.2f"|format(avg_time) }}s</p>
</div>
</div>
</div>
@@ -91,16 +97,17 @@
<!-- Charts Section -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-8">
<!-- Invocation History Chart -->
<div class="bg-white rounded-xl shadow-sm p-6 border border-gray-100 lg:col-span-2">
<h3 class="text-lg font-semibold text-gray-900 mb-4">24-Hour Activity</h3>
<div
class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6 border border-gray-100 dark:border-gray-700 lg:col-span-2">
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">24-Hour Activity</h3>
<div class="h-64">
<canvas id="activityChart"></canvas>
</div>
</div>
<!-- Top Functions -->
<div class="bg-white rounded-xl shadow-sm p-6 border border-gray-100">
<h3 class="text-lg font-semibold text-gray-900 mb-4">Top Functions</h3>
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6 border border-gray-100 dark:border-gray-700">
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Top Functions</h3>
<div class="space-y-4">
{% for func in top_functions %}
<div class="flex items-center justify-between">
@@ -108,16 +115,16 @@
<div
class="w-2 h-2 rounded-full {% if func.type == 'HTTP' %}bg-indigo-500{% else %}bg-blue-500{% endif %} mr-2">
</div>
<span class="text-sm font-medium text-gray-700">{{ func.name }}</span>
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">{{ func.name }}</span>
</div>
<span class="text-sm text-gray-500">{{ func.invocation_count }} calls</span>
<span class="text-sm text-gray-500 dark:text-gray-400">{{ func.invocation_count }} calls</span>
</div>
{% else %}
<p class="text-sm text-gray-500">No functions found.</p>
<p class="text-sm text-gray-500 dark:text-gray-400">No functions found.</p>
{% endfor %}
</div>
<div class="mt-6 pt-6 border-t border-gray-100">
<h4 class="text-sm font-medium text-gray-900 mb-2">Success Trend (7 Days)</h4>
<div class="mt-6 pt-6 border-t border-gray-100 dark:border-gray-700">
<h4 class="text-sm font-medium text-gray-900 dark:text-white mb-2">Success Trend (7 Days)</h4>
<div class="h-32">
<canvas id="trendChart"></canvas>
</div>
@@ -126,62 +133,74 @@
</div>
<!-- Recent Activity Table -->
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
<div class="px-6 py-4 border-b border-gray-100 flex justify-between items-center">
<h3 class="text-lg font-semibold text-gray-900">Recent Activity</h3>
<div
class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-100 dark:border-gray-700 overflow-hidden">
<div class="px-6 py-4 border-b border-gray-100 dark:border-gray-700 flex justify-between items-center">
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">Recent Activity</h3>
<div class="flex space-x-2">
<a href="{{ url_for('http.overview') }}"
class="text-sm text-indigo-600 hover:text-indigo-800 font-medium">View HTTP</a>
<span class="text-gray-300">|</span>
class="text-sm text-indigo-600 dark:text-indigo-400 hover:text-indigo-800 dark:hover:text-indigo-300 font-medium">View
HTTP</a>
<span class="text-gray-300 dark:text-gray-600">|</span>
<a href="{{ url_for('timer.overview') }}"
class="text-sm text-blue-600 hover:text-blue-800 font-medium">View Timers</a>
class="text-sm text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 font-medium">View
Timers</a>
</div>
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead class="bg-gray-50 dark:bg-gray-900/50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Function</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Type
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Type
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Status</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Duration</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Time
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Time
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
{% for activity in recent_activity %}
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{{ activity.name }}
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">{{
activity.name }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
<span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full {% if activity.type == 'HTTP' %}bg-indigo-100 text-indigo-800{% else %}bg-blue-100 text-blue-800{% endif %}">
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full {% if activity.type == 'HTTP' %}bg-indigo-100 text-indigo-800 dark:bg-indigo-900/30 dark:text-indigo-300{% else %}bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300{% endif %}">
{{ activity.type }}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap">
{% if activity.status == 'SUCCESS' %}
<span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">Success</span>
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300">Success</span>
{% else %}
<span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">{{
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300">{{
activity.status }}</span>
{% endif %}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{{
"%.2f"|format(activity.execution_time or 0) }}s</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{{
activity.invocation_time.strftime('%Y-%m-%d %H:%M:%S') }}</td>
</tr>
{% else %}
<tr>
<td colspan="5" class="px-6 py-4 text-center text-sm text-gray-500">No recent activity found.
<td colspan="5" class="px-6 py-4 text-center text-sm text-gray-500 dark:text-gray-400">No recent
activity found.
</td>
</tr>
{% endfor %}
@@ -214,8 +233,22 @@
legend: { display: false }
},
scales: {
y: { beginAtZero: true, grid: { display: false } },
x: { grid: { display: false } }
y: {
beginAtZero: true,
grid: {
display: false,
color: document.documentElement.classList.contains('dark') ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'
},
ticks: {
color: document.documentElement.classList.contains('dark') ? '#9ca3af' : '#6b7280'
}
},
x: {
grid: { display: false },
ticks: {
color: document.documentElement.classList.contains('dark') ? '#9ca3af' : '#6b7280'
}
}
}
}
});
@@ -256,5 +289,21 @@
}
}
});
// Update charts on theme change
window.addEventListener('themeChanged', (e) => {
const isDark = e.detail.theme === 'dark';
const textColor = isDark ? '#9ca3af' : '#6b7280';
const gridColor = isDark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)';
// Update Activity Chart
const activityChart = Chart.getChart('activityChart');
if (activityChart) {
activityChart.options.scales.x.ticks.color = textColor;
activityChart.options.scales.y.ticks.color = textColor;
activityChart.options.scales.y.grid.color = gridColor;
activityChart.update();
}
});
</script>
{% endblock %}