130 lines
7.4 KiB
PHP
130 lines
7.4 KiB
PHP
@php
|
|
$svcLabel = ['online' => 'aktiv', 'warning' => 'degradiert', 'offline' => 'gestoppt'];
|
|
$list = $this->filteredServices;
|
|
$total = count($services);
|
|
$active = collect($services)->where('status', 'online')->count();
|
|
$failed = collect($services)->where('status', 'offline')->count();
|
|
$fleetTone = $failed > 0 ? 'offline' : (collect($services)->where('status', 'warning')->count() > 0 ? 'warning' : 'online');
|
|
|
|
// journal level → token color (status triad + neutral for info)
|
|
$logTone = ['info' => 'text-ink-3', 'warn' => 'text-warning', 'error' => 'text-offline'];
|
|
@endphp
|
|
|
|
<div class="space-y-4" wire:init="load">
|
|
{{-- Header --}}
|
|
<div class="flex flex-wrap items-end justify-between gap-3">
|
|
<div>
|
|
<p class="font-mono text-[11px] uppercase tracking-[0.2em] text-accent-text">Dienste</p>
|
|
<h2 class="mt-0.5 font-display text-xl font-semibold text-ink sm:text-2xl">Dienste</h2>
|
|
<p class="mt-0.5 font-mono text-[11px] text-ink-3">{{ $server }}</p>
|
|
</div>
|
|
<x-status-pill :status="$fleetTone">{{ $active }} / {{ $total }} aktiv</x-status-pill>
|
|
</div>
|
|
|
|
{{-- systemd services --}}
|
|
<x-panel title="systemd-Dienste" :subtitle="$total . ' Units · ' . $server" :padded="false">
|
|
<x-slot:actions>
|
|
<label class="relative block">
|
|
<span class="sr-only">Dienste durchsuchen</span>
|
|
<x-icon name="search" class="pointer-events-none absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-ink-4" />
|
|
<input
|
|
type="search"
|
|
wire:model.live.debounce.250ms="search"
|
|
placeholder="Dienst suchen…"
|
|
class="h-9 w-40 rounded-md border border-line bg-inset pl-8 pr-3 font-mono text-xs text-ink placeholder:text-ink-4 focus:border-accent/40 focus:outline-none sm:w-56"
|
|
/>
|
|
</label>
|
|
</x-slot:actions>
|
|
|
|
<div class="divide-y divide-line">
|
|
@if (! $ready)
|
|
@for ($i = 0; $i < 7; $i++)
|
|
<div class="flex items-center gap-3 px-4 py-3.5 sm:px-5">
|
|
<x-skeleton class="h-2.5 w-2.5 rounded-full" />
|
|
<div class="flex-1 space-y-2"><x-skeleton class="h-3 w-44 max-w-full" /><x-skeleton class="h-2.5 w-64 max-w-full" /></div>
|
|
<x-skeleton class="hidden h-6 w-24 sm:block" />
|
|
</div>
|
|
@endfor
|
|
@else
|
|
@forelse ($list as $svc)
|
|
<div class="flex flex-col gap-3 px-4 py-3 sm:px-5 lg:flex-row lg:items-center">
|
|
{{-- identity --}}
|
|
<div class="flex min-w-0 flex-1 items-center gap-3">
|
|
<x-status-dot :status="$svc['status']" :ping="$svc['status'] === 'online'" class="mt-1 self-start lg:mt-0 lg:self-center" />
|
|
<div class="min-w-0 flex-1">
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<p class="truncate font-mono text-sm text-ink">{{ $svc['name'] }}</p>
|
|
@if ($svc['enabled'])
|
|
<x-badge tone="neutral">enabled</x-badge>
|
|
@else
|
|
<x-badge tone="neutral">disabled</x-badge>
|
|
@endif
|
|
</div>
|
|
<p class="truncate text-[11px] text-ink-3">{{ $svc['desc'] }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- status + actions --}}
|
|
<div class="flex shrink-0 items-center justify-between gap-2 pl-5 lg:justify-end lg:pl-0">
|
|
<x-status-pill :status="$svc['status']">{{ $svcLabel[$svc['status']] }}</x-status-pill>
|
|
|
|
{{-- R5: wire to wire-elements/modal --}}
|
|
<div class="flex items-center gap-1.5">
|
|
<button
|
|
type="button"
|
|
wire:click="confirm('start', @js($svc['name']))"
|
|
@disabled($svc['status'] === 'online')
|
|
class="inline-flex min-h-11 items-center rounded-md border border-line bg-inset px-3 text-xs text-ink-2 transition-colors hover:border-online/30 hover:bg-online/10 hover:text-online disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:border-line disabled:hover:bg-inset disabled:hover:text-ink-2"
|
|
>Starten</button>
|
|
<button
|
|
type="button"
|
|
wire:click="confirm('stop', @js($svc['name']))"
|
|
@disabled($svc['status'] === 'offline')
|
|
class="inline-flex min-h-11 items-center rounded-md border border-line bg-inset px-3 text-xs text-ink-2 transition-colors hover:border-offline/30 hover:bg-offline/10 hover:text-offline disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:border-line disabled:hover:bg-inset disabled:hover:text-ink-2"
|
|
>Stopp</button>
|
|
<button
|
|
type="button"
|
|
wire:click="confirm('restart', @js($svc['name']))"
|
|
@disabled($svc['status'] === 'offline')
|
|
class="inline-flex min-h-11 items-center rounded-md border border-accent/25 bg-accent/10 px-3 text-xs text-accent-text transition-colors hover:bg-accent/15 disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-accent/10"
|
|
>Neustart</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@empty
|
|
<div class="px-4 py-10 text-center sm:px-5">
|
|
<p class="font-mono text-sm text-ink-3">Kein Dienst gefunden</p>
|
|
<p class="mt-1 font-mono text-[11px] text-ink-4">Suche „{{ $search }}“ ergab keine Treffer.</p>
|
|
</div>
|
|
@endforelse
|
|
@endif
|
|
</div>
|
|
</x-panel>
|
|
|
|
{{-- Journal --}}
|
|
<x-panel title="Journal" :subtitle="'journalctl -f · ' . $server" :padded="false">
|
|
<x-slot:actions>
|
|
<x-badge tone="cyan">live</x-badge>
|
|
</x-slot:actions>
|
|
|
|
<div class="overflow-x-auto">
|
|
<div class="min-w-[640px] divide-y divide-line-soft font-mono text-[11px] leading-relaxed sm:min-w-0">
|
|
@foreach ($journal as $line)
|
|
<div class="flex items-start gap-3 px-4 py-1.5 sm:px-5">
|
|
<span class="shrink-0 tabular text-ink-4">{{ $line['time'] }}</span>
|
|
<span class="w-28 shrink-0 truncate text-ink-2">{{ $line['unit'] }}[1]:</span>
|
|
<span class="min-w-0 flex-1 break-words {{ $logTone[$line['level']] ?? 'text-ink-3' }}">{{ $line['text'] }}</span>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
<div class="border-t border-line px-4 py-2 sm:px-5">
|
|
<p class="flex items-center gap-1.5 font-mono text-[11px] text-ink-4">
|
|
<span class="inline-flex h-1.5 w-1.5 rounded-full bg-online"></span>
|
|
Folge Journal · {{ count($journal) }} Zeilen
|
|
</p>
|
|
</div>
|
|
</x-panel>
|
|
</div>
|