clusev/resources/views/livewire/services/index.blade.php

120 lines
6.8 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">
{{-- 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">
@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="start('{{ $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"
>Start</button>
<button
type="button"
wire:click="stop('{{ $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="restart('{{ $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
</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>