clupilot/app/resources/views/livewire/pages/servers/index.blade.php

176 lines
9.5 KiB
PHP

<?php
use App\Models\Server;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Url;
use Livewire\Volt\Component;
new #[Layout('layouts.app')] class extends Component {
#[Url(as: 'env')]
public string $envFilter = 'all'; // all | prod | edge | staging
#[Url(as: 'region')]
public string $regionFilter = '';
public function setEnv(string $env): void
{
$this->envFilter = $env;
}
public function with(): array
{
$query = Server::query()->withCount('sites');
if ($this->envFilter !== 'all') {
$query->where('env', $this->envFilter);
}
if ($this->regionFilter !== '') {
$query->where('region', $this->regionFilter);
}
return [
'servers' => $query->orderBy('name')->get(),
'totalServers' => Server::count(),
'okCount' => Server::where('status', 'ok')->count(),
'avgCpu' => (int) Server::avg('cpu_load_pct'),
'avgRam' => (int) Server::avg('ram_used_pct'),
'maintenance' => Server::where('status', 'maintenance')->count(),
'regions' => Server::query()->select('region')->distinct()->pluck('region'),
'prodCount' => Server::where('env', 'prod')->count(),
'edgeCount' => Server::where('env', 'edge')->count(),
'stagingCount' => Server::where('env', 'staging')->count(),
];
}
}; ?>
<div>
{{-- ───── Topbar ───── --}}
<div class="clu-topbar">
<x-clu.burger />
<div>
<h1 class="clu-page-title">Server</h1>
<div class="clu-page-sub">{{ $totalServers }} nodes · cluster acme</div>
</div>
<span class="clu-status-chip"><span class="clu-pulse-dot"></span> {{ $okCount }}/{{ $totalServers }} aktiv</span>
<div class="spacer"></div>
<button class="clu-btn-primary" style="height:36px;padding:0 14px;">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true">
<path d="M12 5v14M5 12h14"/>
</svg>
Cluster anlegen
</button>
</div>
{{-- ───── Metric tiles ───── --}}
<div class="grid gap-[14px] mb-[18px]" style="grid-template-columns:repeat(auto-fit,minmax(220px,1fr));">
<div class="clu-metric success">
<div class="clu-metric-row">
<span class="clu-metric-label">Aktive Nodes</span>
<span class="clu-metric-ico"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="7" rx="1.5"/><rect x="3" y="13" width="18" height="7" rx="1.5"/></svg></span>
</div>
<div class="clu-metric-value">{{ $okCount }}<span class="unit">/{{ $totalServers }}</span></div>
<div class="clu-metric-delta">{{ $maintenance }} im Maintenance</div>
</div>
<div class="clu-metric @if($avgCpu > 70) warning @endif">
<div class="clu-metric-row">
<span class="clu-metric-label">Ø CPU Load</span>
<span class="clu-metric-ico"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 12h4l3-9 4 18 3-9h4"/></svg></span>
</div>
<div class="clu-metric-value">{{ $avgCpu }}<span class="unit">%</span></div>
<div class="clu-metric-delta @if($avgCpu > 70) warn @endif">across {{ $totalServers }} nodes</div>
</div>
<div class="clu-metric @if($avgRam > 70) warning @endif">
<div class="clu-metric-row">
<span class="clu-metric-label">Ø RAM</span>
<span class="clu-metric-ico"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="7" width="18" height="10" rx="2"/><path d="M7 7v10M11 7v10M15 7v10M19 7v10"/></svg></span>
</div>
<div class="clu-metric-value">{{ $avgRam }}<span class="unit">%</span></div>
<div class="clu-metric-delta">used</div>
</div>
<div class="clu-metric">
<div class="clu-metric-row">
<span class="clu-metric-label">Maintenance</span>
<span class="clu-metric-ico"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M12 1v6M12 17v6M4.2 4.2l4.3 4.3M15.5 15.5l4.3 4.3M1 12h6M17 12h6M4.2 19.8l4.3-4.3M15.5 8.5l4.3-4.3"/></svg></span>
</div>
<div class="clu-metric-value">{{ $maintenance }}</div>
<div class="clu-metric-delta">geplant</div>
</div>
</div>
{{-- ───── Toolbar ───── --}}
<div class="clu-card mb-[18px]">
<div class="clu-toolbar">
<div class="clu-seg">
<button wire:click="setEnv('all')" class="@if($envFilter==='all') active @endif">Alle <span class="count">{{ $totalServers }}</span></button>
<button wire:click="setEnv('prod')" class="@if($envFilter==='prod') active @endif">Prod <span class="count">{{ $prodCount }}</span></button>
<button wire:click="setEnv('edge')" class="@if($envFilter==='edge') active @endif">Edge <span class="count">{{ $edgeCount }}</span></button>
<button wire:click="setEnv('staging')" class="@if($envFilter==='staging') active @endif">Staging <span class="count">{{ $stagingCount }}</span></button>
</div>
<div>
<select wire:model.live="regionFilter" class="clu-ghost-btn" style="appearance:auto;">
<option value="">Region: Alle</option>
@foreach ($regions as $r)
<option value="{{ $r }}">{{ $r }}</option>
@endforeach
</select>
</div>
</div>
</div>
{{-- ───── Server cards grid ───── --}}
<div class="grid gap-[14px] mb-[24px]" style="grid-template-columns:repeat(auto-fit,minmax(320px,1fr));">
@forelse ($servers as $server)
<div class="clu-server-card">
<div class="clu-server-head">
<div class="clu-server-flag">{{ $server->country_flag }}</div>
<div class="flex-1 min-w-0">
<div class="clu-server-name">{{ $server->name }}</div>
<div class="clu-server-meta">{{ $server->region }} · {{ $server->env }}</div>
</div>
<span class="clu-pill @if($server->status === 'ok') success @elseif($server->status === 'warn') warning @else danger @endif">{{ strtoupper($server->status) }}</span>
</div>
<div class="clu-server-spec">
<span>{{ $server->cpu_cores }} vCPU</span>
<span>·</span>
<span>{{ $server->ram_gb }} GB RAM</span>
<span>·</span>
<span>{{ $server->disk_gb }} GB SSD</span>
</div>
<div class="clu-gauge-row">
<div class="clu-gauge">
<span class="clu-gauge-label">CPU</span>
<div class="clu-gauge-bar"><span style="width:{{ $server->cpu_load_pct }}%;" class="@if($server->cpu_load_pct > 70) warn @endif @if($server->cpu_load_pct > 90) bad @endif"></span></div>
<span class="clu-gauge-val">{{ $server->cpu_load_pct }}%</span>
</div>
<div class="clu-gauge">
<span class="clu-gauge-label">RAM</span>
<div class="clu-gauge-bar"><span style="width:{{ $server->ram_used_pct }}%;" class="@if($server->ram_used_pct > 70) warn @endif @if($server->ram_used_pct > 90) bad @endif"></span></div>
<span class="clu-gauge-val">{{ $server->ram_used_pct }}%</span>
</div>
<div class="clu-gauge">
<span class="clu-gauge-label">DISK</span>
<div class="clu-gauge-bar"><span style="width:{{ $server->disk_used_pct }}%;" class="@if($server->disk_used_pct > 70) warn @endif @if($server->disk_used_pct > 90) bad @endif"></span></div>
<span class="clu-gauge-val">{{ $server->disk_used_pct }}%</span>
</div>
</div>
<div class="clu-server-foot">
<span class="clu-server-meta">{{ $server->sites_count }} sites</span>
<div class="clu-row-actions">
<button title="SSH" type="button">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 17l5-5-5-5M13 19h7"/></svg>
</button>
<button title="Mehr" type="button">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="5" cy="12" r="1.5"/><circle cx="12" cy="12" r="1.5"/><circle cx="19" cy="12" r="1.5"/></svg>
</button>
</div>
</div>
</div>
@empty
<div class="clu-card"><div class="clu-card-body" style="text-align:center;padding:40px;color:var(--color-muted);">Keine Server gefunden.</div></div>
@endforelse
</div>
</div>