Files
trackbugs/resources/views/dashboard.blade.php
T
2026-03-19 19:40:49 +02:00

145 lines
7.9 KiB
PHP

<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">Dashboard</h2>
</x-slot>
<div class="py-8">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
{{-- Summary Cards --}}
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg p-6">
<div class="text-sm font-medium text-gray-500">Unresolved Issues</div>
<div class="mt-1 text-3xl font-bold text-gray-900">{{ number_format($unresolvedCount) }}</div>
</div>
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg p-6">
<div class="text-sm font-medium text-gray-500">Events (24h)</div>
<div class="mt-1 text-3xl font-bold text-gray-900">{{ number_format($events24h) }}</div>
</div>
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg p-6">
<div class="text-sm font-medium text-gray-500">Active Projects</div>
<div class="mt-1 text-3xl font-bold text-gray-900">{{ $mostActiveProjects->count() }}</div>
</div>
</div>
{{-- Events Over Time Chart --}}
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-medium text-gray-900">Events Over Time</h3>
<div class="flex gap-2">
@foreach([7, 14, 30] as $d)
<a href="{{ route('dashboard', ['days' => $d]) }}"
class="px-3 py-1 text-sm rounded-md {{ $days === $d ? 'bg-gray-800 text-white' : 'bg-gray-100 text-gray-600 hover:bg-gray-200' }}">
{{ $d }}d
</a>
@endforeach
</div>
</div>
<div x-data="eventsChart()" x-init="init()" class="h-48">
<canvas x-ref="canvas" class="w-full h-full"></canvas>
</div>
<script>
function eventsChart() {
return {
init() {
const data = @json($eventsOverTime);
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');
const dpr = window.devicePixelRatio || 1;
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
ctx.scale(dpr, dpr);
const w = rect.width;
const h = rect.height;
const padding = { top: 10, right: 10, bottom: 30, left: 50 };
const chartW = w - padding.left - padding.right;
const chartH = h - padding.top - padding.bottom;
const counts = data.map(d => d.count);
const maxVal = Math.max(...counts, 1);
ctx.strokeStyle = '#e5e7eb';
ctx.lineWidth = 1;
for (let i = 0; i <= 4; i++) {
const y = padding.top + chartH - (chartH * i / 4);
ctx.beginPath();
ctx.moveTo(padding.left, y);
ctx.lineTo(w - padding.right, y);
ctx.stroke();
ctx.fillStyle = '#9ca3af';
ctx.font = '11px sans-serif';
ctx.textAlign = 'right';
ctx.fillText(Math.round(maxVal * i / 4), padding.left - 8, y + 4);
}
ctx.beginPath();
ctx.strokeStyle = '#4f46e5';
ctx.lineWidth = 2;
data.forEach((d, i) => {
const x = padding.left + (i / (data.length - 1 || 1)) * chartW;
const y = padding.top + chartH - (d.count / maxVal) * chartH;
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
});
ctx.stroke();
ctx.fillStyle = '#9ca3af';
ctx.font = '10px sans-serif';
ctx.textAlign = 'center';
const step = Math.max(1, Math.floor(data.length / 7));
data.forEach((d, i) => {
if (i % step === 0 || i === data.length - 1) {
const x = padding.left + (i / (data.length - 1 || 1)) * chartW;
ctx.fillText(d.date.slice(5), x, h - 5);
}
});
}
}
}
</script>
</div>
{{-- Most Active Projects --}}
@if($mostActiveProjects->isNotEmpty())
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6">
<h3 class="text-lg font-medium text-gray-900 mb-4">Most Active Projects</h3>
</div>
<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">Project</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">Platform</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase">Unresolved</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase">Events (24h)</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@foreach($mostActiveProjects as $p)
<tr>
<td class="px-6 py-4 whitespace-nowrap">
<a href="{{ route('projects.issues.index', $p) }}" class="text-indigo-600 hover:text-indigo-900 font-medium">{{ $p->name }}</a>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ $p->platform ?? '—' }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-right">{{ $p->issues_count }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-right">{{ $p->events_count }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg p-6 text-center text-gray-500">
No projects yet.
<a href="{{ route('projects.create') }}" class="text-indigo-600 hover:underline">Create your first project</a>
</div>
@endif
</div>
</div>
</x-app-layout>