diff --git a/app/resources/css/app.css b/app/resources/css/app.css index a0fef1f..f36fd09 100644 --- a/app/resources/css/app.css +++ b/app/resources/css/app.css @@ -1001,4 +1001,32 @@ transition: background .15s, color .15s; } .clu-row-actions button:hover { background: rgba(255,255,255,0.7); color: var(--color-fg); } + + /* Server card (Server.html) */ + .clu-server-card { + background: var(--color-glass-strong); + border: 1px solid var(--color-glass-border); + border-radius: var(--radius); + padding: 18px; + box-shadow: var(--shadow-glass), var(--shadow-glass-inset); + backdrop-filter: blur(20px) saturate(170%); + -webkit-backdrop-filter: blur(20px) saturate(170%); + display: flex; flex-direction: column; gap: 14px; + } + .clu-server-head { display: flex; align-items: center; gap: 12px; } + .clu-server-flag { font-size: 22px; line-height: 1; } + .clu-server-name { font-weight: 600; font-size: 14px; } + .clu-server-meta { font-size: 11.5px; color: var(--color-muted); font-family: var(--font-mono); } + .clu-gauge-row { display: flex; flex-direction: column; gap: 9px; } + .clu-gauge { + display: flex; align-items: center; gap: 10px; + } + .clu-gauge-label { font-size: 11px; color: var(--color-muted); width: 38px; flex: none; font-family: var(--font-mono); text-transform: uppercase; letter-spacing: 0.05em; } + .clu-gauge-bar { flex: 1; height: 6px; border-radius: 4px; background: rgba(30,35,60,0.10); overflow: hidden; } + .clu-gauge-bar > span { display: block; height: 100%; border-radius: 4px; background: var(--color-accent); } + .clu-gauge-bar > span.warn { background: var(--color-warning); } + .clu-gauge-bar > span.bad { background: var(--color-danger); } + .clu-gauge-val { font-family: var(--font-mono); font-size: 11px; color: var(--color-fg); width: 36px; text-align: right; } + .clu-server-spec { display: flex; gap: 8px; font-family: var(--font-mono); font-size: 11px; color: var(--color-muted); } + .clu-server-foot { display: flex; align-items: center; justify-content: space-between; padding-top: 10px; border-top: 1px solid var(--color-hairline); } } diff --git a/app/resources/views/livewire/pages/servers/index.blade.php b/app/resources/views/livewire/pages/servers/index.blade.php new file mode 100644 index 0000000..438dda7 --- /dev/null +++ b/app/resources/views/livewire/pages/servers/index.blade.php @@ -0,0 +1,174 @@ +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(), + ]; + } +}; ?> + +