diff --git a/app/resources/css/app.css b/app/resources/css/app.css index 6469768..a0fef1f 100644 --- a/app/resources/css/app.css +++ b/app/resources/css/app.css @@ -942,4 +942,63 @@ .clu-feed-body { flex: 1; min-width: 0; } .clu-feed-msg { font-size: 12.5px; color: var(--color-fg); } .clu-feed-meta { font-size: 10.5px; color: var(--color-muted); font-family: var(--font-mono); margin-top: 2px; } + + /* Topbar headings */ + .clu-page-title { font-size: 17px; font-weight: 600; letter-spacing: -0.01em; margin: 0; } + .clu-page-sub { font-size: 12px; color: var(--color-muted); font-family: var(--font-mono); margin-top: 2px; } + + /* Segmented button group */ + .clu-seg { + display: inline-flex; + background: rgba(255,255,255,0.4); + border: 1px solid var(--color-hairline); + border-radius: var(--radius-sm); + padding: 3px; + gap: 2px; + } + .clu-seg button { + padding: 6px 12px; + border-radius: 9px; + font-size: 12px; font-weight: 600; + color: var(--color-muted); + transition: background .15s, color .15s; + } + .clu-seg button:hover { color: var(--color-fg); } + .clu-seg button.active { + background: rgba(255,255,255,0.95); + color: var(--color-fg); + box-shadow: 0 1px 2px rgba(40,52,92,0.08); + } + .clu-seg button .count { opacity: 0.55; margin-left: 4px; font-family: var(--font-mono); } + + /* Ghost button */ + .clu-ghost-btn { + display: inline-flex; align-items: center; gap: 6px; + padding: 7px 12px; + background: rgba(255,255,255,0.5); + border: 1px solid var(--color-hairline); + border-radius: var(--radius-sm); + font-size: 12px; font-weight: 600; + color: var(--color-fg); + transition: background .15s, border-color .15s; + } + .clu-ghost-btn:hover { background: rgba(255,255,255,0.85); border-color: var(--color-glass-border); } + + /* Toolbar wrapper */ + .clu-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 12px 18px; border-bottom: 1px solid var(--color-hairline); } + + /* Pagination row */ + .clu-pager { display: flex; align-items: center; justify-content: space-between; padding: 12px 18px; border-top: 1px solid var(--color-hairline); } + .clu-pager-meta { font-size: 12px; color: var(--color-muted); font-family: var(--font-mono); } + + /* Row actions cluster */ + .clu-row-actions { display: inline-flex; gap: 4px; justify-content: flex-end; } + .clu-row-actions button { + width: 28px; height: 28px; + border-radius: 8px; + color: var(--color-muted); + display: grid; place-items: center; + transition: background .15s, color .15s; + } + .clu-row-actions button:hover { background: rgba(255,255,255,0.7); color: var(--color-fg); } } diff --git a/app/resources/views/livewire/pages/sites/index.blade.php b/app/resources/views/livewire/pages/sites/index.blade.php new file mode 100644 index 0000000..615202e --- /dev/null +++ b/app/resources/views/livewire/pages/sites/index.blade.php @@ -0,0 +1,231 @@ +resetPage(); } + public function updatingFilter(): void { $this->resetPage(); } + public function updatingServerFilter(): void { $this->resetPage(); } + public function updatingSort(): void { $this->resetPage(); } + + public function setFilter(string $f): void + { + $this->filter = $f; + $this->resetPage(); + } + + public function with(): array + { + $query = Site::query()->with('server'); + + if ($this->search !== '') { + $query->where('domain', 'like', '%' . $this->search . '%'); + } + + match ($this->filter) { + 'updates' => $query->where('updates_pending', '>', 0), + 'warnings' => $query->where('status', 'warn'), + 'critical' => $query->where('status', 'bad'), + default => null, + }; + + if ($this->serverFilter !== '') { + $query->where('server_id', $this->serverFilter); + } + + $query->orderBy(match ($this->sort) { + 'visitors' => 'visitors_24h', + 'response' => 'response_ms', + 'updates' => 'updates_pending', + default => 'domain', + }, $this->sort === 'name' ? 'asc' : 'desc'); + + return [ + 'sites' => $query->paginate(15), + 'servers' => Server::orderBy('name')->get(), + 'totalSites' => Site::count(), + 'updatesCount' => Site::where('updates_pending', '>', 0)->count(), + 'warnCount' => Site::where('status', 'warn')->count(), + 'criticalCount' => Site::where('status', 'bad')->count(), + 'avgPagespeed' => (int) Site::avg('pagespeed_score'), + 'totalVisitors' => Site::sum('visitors_24h'), + ]; + } +}; ?> + +
| Site | +Server | +WordPress | +PHP | +Besucher / 24h | +Response | +Status | ++ |
|---|---|---|---|---|---|---|---|
|
+
+
+ {{ mb_substr($site->domain, 0, 1) }}
+
+ |
+ {{ $site->server->name }} | +WP {{ $site->wp_version }} | +PHP {{ $site->php_version }} | +{{ number_format($site->visitors_24h, 0, ',', '.') }} | +{{ $site->response_ms }}ms | ++ + {{ match($site->status) { + 'ok' => $site->updates_pending > 0 ? $site->updates_pending . ' Updates' : 'Optimal', + 'warn' => 'Wartung nötig', + 'bad' => 'Kritisch', + default => $site->status, + } }} + + | +
+
+
+
+
+ |
+
| Keine Sites gefunden. | |||||||