260 lines
12 KiB
HTML
260 lines
12 KiB
HTML
{% extends 'dashboard.html' %}
|
|
|
|
{% block page %}
|
|
<script src="https://cdn.jsdelivr.net/npm/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>
|
|
</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="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">
|
|
<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">{{ stats.total_timer_functions + stats.total_http_functions
|
|
}}</p>
|
|
<p class="ml-2 text-sm text-gray-500">
|
|
({{ 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="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">
|
|
<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>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Success Rate -->
|
|
<div class="bg-white rounded-xl shadow-sm p-6 border border-gray-100">
|
|
<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">
|
|
<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">{{ success_rate }}%</p>
|
|
<p class="ml-2 text-sm text-gray-500">
|
|
{{ 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="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">
|
|
<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">{{ "%.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 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="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="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">{{ func.name }}</span>
|
|
</div>
|
|
<span class="text-sm text-gray-500">{{ func.invocation_count }} calls</span>
|
|
</div>
|
|
{% else %}
|
|
<p class="text-sm text-gray-500">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="h-32">
|
|
<canvas id="trendChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</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="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>
|
|
<a href="{{ url_for('timer.overview') }}"
|
|
class="text-sm text-blue-600 hover:text-blue-800 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">
|
|
<tr>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 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>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
Status</th>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 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>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="bg-white divide-y divide-gray-200">
|
|
{% for activity in recent_activity %}
|
|
<tr>
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{{ activity.name }}
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
|
<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 %}">
|
|
{{ 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>
|
|
{% else %}
|
|
<span
|
|
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">{{
|
|
activity.status }}</span>
|
|
{% endif %}
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{
|
|
"%.2f"|format(activity.execution_time or 0) }}s</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{
|
|
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>
|
|
</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 } },
|
|
x: { grid: { display: false } }
|
|
}
|
|
}
|
|
});
|
|
|
|
// 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 }
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %} |