309 lines
15 KiB
HTML
309 lines
15 KiB
HTML
{% extends 'dashboard.html' %}
|
|
|
|
{% block page %}
|
|
<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 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 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 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>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-baseline">
|
|
<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 dark:text-gray-400">
|
|
({{ stats.total_http_functions }} HTTP, {{ stats.total_timer_functions }} Timer)
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Total Invocations -->
|
|
<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 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 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 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 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>
|
|
</div>
|
|
</div>
|
|
{% set total_invocations = stats.timer_invocations + stats.http_invocations %}
|
|
{% 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 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 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 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>
|
|
</div>
|
|
</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 dark:text-white">{{ "%.2f"|format(avg_time) }}s</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Charts Section -->
|
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-8">
|
|
<!-- Invocation History Chart -->
|
|
<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 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">
|
|
<div class="flex items-center">
|
|
<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 dark:text-gray-300">{{ func.name }}</span>
|
|
</div>
|
|
<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 dark:text-gray-400">No functions found.</p>
|
|
{% endfor %}
|
|
</div>
|
|
<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>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Activity Table -->
|
|
<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 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 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 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 dark:text-gray-400 uppercase tracking-wider">
|
|
Function</th>
|
|
<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 dark:text-gray-400 uppercase tracking-wider">
|
|
Status</th>
|
|
<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 dark:text-gray-400 uppercase tracking-wider">
|
|
Time
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<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 dark:text-white">{{
|
|
activity.name }}
|
|
</td>
|
|
<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 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 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 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 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 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 dark:text-gray-400">No recent
|
|
activity found.
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Activity Chart
|
|
const activityCtx = document.getElementById('activityChart').getContext('2d');
|
|
new Chart(activityCtx, {
|
|
type: 'bar',
|
|
data: {
|
|
labels: {{ hour_distribution | map(attribute = 'hour') | list | tojson }},
|
|
datasets: [{
|
|
label: 'Invocations',
|
|
data: {{ hour_distribution | map(attribute = 'count') | list | tojson }},
|
|
backgroundColor: 'rgba(79, 70, 229, 0.6)',
|
|
borderColor: 'rgb(79, 70, 229)',
|
|
borderWidth: 1,
|
|
borderRadius: 4
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: { display: false }
|
|
},
|
|
scales: {
|
|
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'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Trend Chart
|
|
const trendCtx = document.getElementById('trendChart').getContext('2d');
|
|
new Chart(trendCtx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: {{ success_trend | map(attribute = 'day_name') | list | tojson }},
|
|
datasets: [{
|
|
label: 'Success Rate',
|
|
data: {{ success_trend | map(attribute = 'success_rate') | list | tojson }},
|
|
borderColor: 'rgb(16, 185, 129)',
|
|
backgroundColor: 'rgba(16, 185, 129, 0.1)',
|
|
fill: true,
|
|
tension: 0.4
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: { display: false }
|
|
},
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true,
|
|
max: 100,
|
|
display: false
|
|
},
|
|
x: {
|
|
display: false
|
|
}
|
|
},
|
|
elements: {
|
|
point: { radius: 0, hitRadius: 10 }
|
|
}
|
|
}
|
|
});
|
|
|
|
// 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 %} |