fix(ui): uniform action-element height + live fleet status / alerts

Two things the badge/live-update fixes missed:

Height — the enable/disable TOGGLES (Alerts, Servers, Commands, Settings)
were custom buttons at min-h-9 (36px) while x-btn / x-modal-trigger /
status-pill are h-8 (32px), so a status toggle never lined up with the
Delete/Create buttons next to it. Normalise those toggles to h-8.
Verified: Enabled/Delete/Create all render at 32px now.

Live — the topbar "X/Y online" pill and the sidebar server-switcher were
static Blade, so a server going up/down (shown live on the dashboard via
Reverb) didn't reach them until a navigate/refresh. Make the topbar pill
a small polled Livewire island (Shell\FleetStatus) and add wire:poll to
the server-switcher + the Alerts page, so fleet status and firing/
resolved incidents update live without a refresh.

(The alert e-mail is confirmed sending over SMTP — queued mail no longer
hits the log mailer, and a synchronous send to the admin returned SEND_OK
from mail.thinkidoo.com:587. Non-delivery is downstream: spam filtering
or missing SPF/DKIM for hello@clusev.com via that relay — a DNS/mail-
server config, not a panel code issue.)

750 tests; R12-verified (uniform heights, topbar is a live island, zero
console errors).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat/v1-foundation
boban 2026-07-06 20:42:09 +02:00
parent 6dbe2303a4
commit f9a08a98d2
8 changed files with 36 additions and 12 deletions

View File

@ -0,0 +1,21 @@
<?php
namespace App\Livewire\Shell;
use App\Models\Server;
use Livewire\Component;
/**
* Topbar "X / Y online" pill. A tiny polled Livewire island so the fleet-online count reflects a
* server going up or down live (like the dashboard), instead of only on a page navigate/refresh.
*/
class FleetStatus extends Component
{
public function render()
{
return view('livewire.shell.fleet-status', [
'online' => Server::where('status', 'online')->count(),
'total' => Server::count(),
]);
}
}

View File

@ -22,10 +22,7 @@
<x-icon name="command" class="h-3.5 w-3.5" />
<span class="font-mono text-[10px] uppercase tracking-wider">{{ __('shell.shortcut_ctrl_k') }}</span>
</button>
@php
$fleetTotal = \App\Models\Server::count();
$fleetOnline = \App\Models\Server::where('status', 'online')->count();
@endphp
<x-status-pill :status="$fleetOnline > 0 ? 'online' : 'offline'">{{ __('shell.fleet_online', ['online' => $fleetOnline, 'total' => $fleetTotal]) }}</x-status-pill>
{{-- Live-polled island so the online count updates when a server goes up/down (not just on nav). --}}
<livewire:shell.fleet-status />
</div>
</header>

View File

@ -10,7 +10,8 @@
};
@endphp
<div class="space-y-5">
{{-- Poll so firing/resolved incidents appear live, matching the dashboard, without a manual refresh. --}}
<div class="space-y-5" wire:poll.10s>
{{-- Header --}}
<div>
<p class="font-mono text-[11px] uppercase tracking-[0.2em] text-accent-text">{{ __('alerts.eyebrow') }}</p>
@ -140,7 +141,7 @@
</div>
<button type="button" wire:click="toggleRule('{{ $rule->uuid }}')"
@class([
'inline-flex min-h-9 shrink-0 items-center gap-1.5 rounded-md border px-2.5 text-xs transition-colors',
'inline-flex h-8 shrink-0 items-center gap-1.5 rounded-md border px-2.5 text-xs transition-colors',
'border-online/20 bg-online/10 text-online' => $rule->enabled,
'border-line bg-inset text-ink-3 hover:bg-raised' => ! $rule->enabled,
])>

View File

@ -26,7 +26,7 @@
@foreach (['all', 'group', 'servers'] as $s)
<button type="button" wire:click="$set('scopeType', '{{ $s }}')"
@class([
'inline-flex min-h-9 items-center rounded-md border px-3 text-xs transition-colors',
'inline-flex h-8 items-center rounded-md border px-3 text-xs transition-colors',
'border-accent/25 bg-accent/10 text-ink' => $scopeType === $s,
'border-line bg-inset text-ink-2 hover:bg-raised hover:text-ink' => $scopeType !== $s,
])>{{ __('commands.scope_'.$s) }}</button>

View File

@ -1,4 +1,6 @@
<div x-data="{ open: false }" @click.outside="open = false" @keydown.escape="open = false" class="relative">
{{-- Poll so the active + fleet status dots reflect a server going up/down live (Livewire morph keeps
the Alpine `open` dropdown state across re-renders). --}}
<div x-data="{ open: false }" @click.outside="open = false" @keydown.escape="open = false" class="relative" wire:poll.10s>
<button type="button" @click="open = ! open"
class="flex min-h-11 w-full items-center gap-2.5 rounded-md border border-line bg-inset px-3 py-2 text-left transition-colors hover:border-line-strong"
:aria-expanded="open" aria-haspopup="listbox">

View File

@ -31,14 +31,14 @@
<div class="flex flex-wrap items-center gap-1.5" role="group" aria-label="{{ __('groups.filter_label') }}">
<button type="button" wire:click="$set('group', null)"
@class([
'inline-flex min-h-9 items-center rounded-md border px-3 text-xs transition-colors',
'inline-flex h-8 items-center rounded-md border px-3 text-xs transition-colors',
'border-accent/25 bg-accent/10 text-ink' => ! $activeGroup,
'border-line bg-inset text-ink-2 hover:bg-raised hover:text-ink' => $activeGroup,
])>{{ __('groups.filter_all') }}</button>
@foreach ($groups as $g)
<button type="button" wire:click="$set('group', '{{ $g->uuid }}')"
@class([
'inline-flex min-h-9 items-center gap-1.5 rounded-md border px-3 text-xs transition-colors',
'inline-flex h-8 items-center gap-1.5 rounded-md border px-3 text-xs transition-colors',
'border-accent/25 bg-accent/10 text-ink' => $activeGroup?->id === $g->id,
'border-line bg-inset text-ink-2 hover:bg-raised hover:text-ink' => $activeGroup?->id !== $g->id,
])>

View File

@ -35,7 +35,7 @@
</div>
<div class="flex items-center gap-2">
<button type="button" x-data @click="window.dispatchEvent(new CustomEvent('onboarding:start'))"
class="inline-flex min-h-9 items-center gap-1.5 rounded-md border border-line px-3 font-mono text-[11px] text-ink-2 transition-colors hover:border-accent/40 hover:text-accent-text">
class="inline-flex h-8 items-center gap-1.5 rounded-md border border-line px-3 font-mono text-[11px] text-ink-2 transition-colors hover:border-accent/40 hover:text-accent-text">
<x-icon name="help-circle" class="h-3.5 w-3.5" />{{ __('onboarding.relaunch') }}
</button>
<x-badge tone="neutral">{{ $u->role?->label() ?? __('settings.role_admin') }}</x-badge>

View File

@ -0,0 +1,3 @@
<div wire:poll.10s>
<x-status-pill :status="$online > 0 ? 'online' : 'offline'">{{ __('shell.fleet_online', ['online' => $online, 'total' => $total]) }}</x-status-pill>
</div>