presseportale/resources/views/livewire/admin/reports/system-health.blade.php
Kevin Adametz 31da9ea8b6
Some checks are pending
linter / quality (push) Waiting to run
tests / ci (push) Waiting to run
livewire: Volt entfernt — Komponenten als Livewire-4-Single-File-Components, Route::livewire(), Livewire::test(); livewire/volt deinstalliert
- 83 Komponenten: Livewire\Volt\Component → Livewire\Component
- 73 Routen (admin, auth, customer): Volt::route() → Route::livewire()
- 88 Testdateien: Volt::test/actingAs → Livewire::test/actingAs
- VoltServiceProvider gelöscht; composer remove livewire/volt
- config/livewire.php auf v4-Schlüssel (component_layout, component_placeholder,
  component_locations, make_command: SFC ohne ⚡). Der alte Schlüssel layout war
  wirkungslos; alle gerouteten Komponenten setzen ihr Layout selbst.
- CLAUDE.md, AGENTS.md, boost.json, .cursorrules, Skill volt-development
- Roadmap: erledigt; Larastan erfasst SFCs weiterhin nicht (Blade-Dateien)

Suite 2.325 grün, einer übersprungen; Larastan und Pint ohne Meldung;
route:cache und view:cache laufen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 13:59:36 +00:00

147 lines
6.5 KiB
PHP

<?php
use App\Services\Admin\AdminSystemHealth;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;
new #[Layout('components.layouts.app'), Title('Betriebsüberwachung')] class extends Component
{
public function mount(): void
{
$this->authorize('settings:manage');
}
public function refresh(): void
{
// Löst nur ein Re-Render aus; snapshot() wird in with() neu berechnet.
}
/**
* @return array<string, mixed>
*/
public function with(AdminSystemHealth $health): array
{
return [
'health' => $health->snapshot(),
];
}
}; ?>
@php
$statusMeta = [
'ok' => ['color' => 'green', 'label' => __('OK'), 'variant' => 'ok'],
'warn' => ['color' => 'amber', 'label' => __('Achtung'), 'variant' => 'warn'],
'critical' => ['color' => 'red', 'label' => __('Kritisch'), 'variant' => 'warn'],
];
$overall = $statusMeta[$health['overall']] ?? $statusMeta['warn'];
$queue = $health['queue'];
@endphp
<div class="space-y-8">
{{-- ============== PAGE HEADER ============== --}}
<x-portal.page-header
:badge="__('Admin Backend')"
:eyebrow="__('Administration · Reports')"
:title="__('Betriebsüberwachung')"
:description="__('Zustand der Queue (Magic-Link-Login & fristrelevante Mails) sowie Deployment-Checks.')"
>
<x-slot:actions>
<flux:badge :color="$overall['color']" size="lg">{{ __('Gesamtstatus') }}: {{ $overall['label'] }}</flux:badge>
<flux:button size="sm" variant="filled" icon="arrow-path" wire:click="refresh">
{{ __('Aktualisieren') }}
</flux:button>
</x-slot:actions>
</x-portal.page-header>
<p class="text-sm text-[color:var(--color-text-muted)]">
{{ __('Stand') }}: {{ $health['generated_at']->format('d.m.Y H:i:s') }}
· {{ __('Version') }}: {{ config('app.version') ?? __('unbekannt (keine RELEASE-Datei)') }}
</p>
{{-- ============== QUEUE-KPIs ============== --}}
<section class="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4">
<x-portal.stat-card :variant="$queue['status'] === 'ok' ? 'ok' : 'warn'" :label="__('Queue-Worker')"
:value="$queue['supported'] ? ($queue['worker_running'] ? __('Aktiv') : __('Gestoppt?')) : __('n/a')">
<x-slot:meta>{{ __('Treiber') }}: {{ $queue['driver'] }}</x-slot:meta>
<x-slot:trend>
@if ($queue['oldest_pending_seconds'] !== null)
{{ __('ältester Job') }}: {{ $queue['oldest_pending_seconds'] }}s
@else
{{ __('keine Wartejobs') }}
@endif
</x-slot:trend>
</x-portal.stat-card>
<x-portal.stat-card variant="primary" :label="__('Wartende Jobs')" :value="number_format($queue['pending'])">
<x-slot:meta>{{ __('verzögert') }}: {{ number_format($queue['delayed']) }}</x-slot:meta>
<x-slot:trend>{{ __('in Verarbeitung') }}: {{ number_format($queue['reserved']) }}</x-slot:trend>
</x-portal.stat-card>
<x-portal.stat-card :variant="$queue['failed_recent'] > 0 ? 'warn' : 'muted'" :label="__('Fehlgeschlagen (24h)')"
:value="number_format($queue['failed_recent'])">
<x-slot:meta>{{ __('gesamt') }}: {{ number_format($queue['failed_total']) }}</x-slot:meta>
<x-slot:trend>{{ __('failed_jobs') }}</x-slot:trend>
</x-portal.stat-card>
<x-portal.stat-card :variant="$overall['variant']" :label="__('Gesamtstatus')" :value="$overall['label']">
<x-slot:meta>{{ __('Betrieb') }}</x-slot:meta>
<x-slot:trend>{{ __('Queue + Deployment') }}</x-slot:trend>
</x-portal.stat-card>
</section>
{{-- ============== DEPLOYMENT-CHECKS ============== --}}
<article class="panel">
<div class="panel-head">
<span class="section-eyebrow">{{ __('Deployment- & Konfigurations-Checks') }}</span>
</div>
<div class="divide-y divide-[color:var(--color-bg-rule)]">
@foreach ($health['checks'] as $check)
@php $meta = $statusMeta[$check['status']] ?? $statusMeta['warn']; @endphp
<div class="flex items-start justify-between gap-4 p-4">
<div class="min-w-0">
<p class="font-medium">{{ $check['label'] }}</p>
<p class="text-sm text-[color:var(--color-text-muted)] break-words">{{ $check['detail'] }}</p>
</div>
<flux:badge :color="$meta['color']" size="sm" class="shrink-0">{{ $meta['label'] }}</flux:badge>
</div>
@endforeach
</div>
</article>
{{-- ============== FEHLGESCHLAGENE JOBS ============== --}}
<article class="panel">
<div class="panel-head">
<span class="section-eyebrow">{{ __('Zuletzt fehlgeschlagene Jobs') }}</span>
<span class="badge hub">{{ count($queue['recent_failures']) }}</span>
</div>
@if (count($queue['recent_failures']) === 0)
<div class="p-5 text-sm text-[color:var(--color-text-muted)]">
{{ __('Keine fehlgeschlagenen Jobs.') }}
</div>
@else
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr class="text-left text-[color:var(--color-text-muted)]">
<th class="p-3">{{ __('Zeitpunkt') }}</th>
<th class="p-3">{{ __('Queue') }}</th>
<th class="p-3">{{ __('Job') }}</th>
<th class="p-3">{{ __('Fehler') }}</th>
</tr>
</thead>
<tbody class="divide-y divide-[color:var(--color-bg-rule)]">
@foreach ($queue['recent_failures'] as $failure)
<tr>
<td class="p-3 whitespace-nowrap">{{ $failure['failed_at']->format('d.m. H:i') }}</td>
<td class="p-3 whitespace-nowrap">{{ $failure['queue'] }}</td>
<td class="p-3">{{ $failure['job'] }}</td>
<td class="p-3 text-[color:var(--color-text-muted)] break-words">{{ $failure['error'] }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
</article>
</div>