Step 2/5: Site Details sub-sidebar grouping (12 tabs → 5 categories)

User: 12 horizontal tabs unmanageable + Settings Workspace hardcoded.

Site Details:
- Replaced horizontal clu-dtabs with vertical clu-detail-nav grouped by
  category (Github/Linear/Stripe pattern).
- 5 categories: ÜBERBLICK, WORDPRESS, HOSTING, ZUGANG, BEOBACHTEN.
- 12 items distributed: overview / wp+plugins+cron+redirects /
  vhost+php+ssl+files+staging / access / logs.
- 220px sticky left nav, content fills remaining width.
- Mobile (<920px): nav collapses to top of stack (single column).
- Section titles uppercase muted, active item glass-highlighted with
  inset shadow + accent ico, count badges (Cronjobs 4).
- 'Datei-Manager' moved from header-of-content to HOSTING category.

Settings page:
- Workspace card now binds to auth user's currentWorkspace in mount()
  instead of hardcoded 'Acme Cluster'. Shows real name + slug + region.

CSS additions: clu-detail-layout (grid 220px + 1fr), clu-detail-nav
(sticky glass panel), clu-detail-section-title (caps muted), clu-
detail-item (with hover + active state + count badge).

5/5 SiteDetailsTest still green. 0 console errors. Browser-verified:
johnston.net loads, nav highlights Übersicht as active, sticky on
scroll, all sub-items navigate via wire:click setTab.
master
Boban Blaskovic 2026-06-01 01:19:05 +02:00
parent 555101c993
commit 43fb26e1ef
3 changed files with 130 additions and 17 deletions

View File

@ -1245,6 +1245,73 @@
}
.clu-btn-danger:hover { filter: brightness(1.05); }
/* ───── Site Details Sub-Sidebar Layout (vertical nav left, content right) ───── */
.clu-detail-layout {
display: grid;
grid-template-columns: 220px 1fr;
gap: 18px;
align-items: start;
}
@media (max-width: 920px) { .clu-detail-layout { grid-template-columns: 1fr; } }
.clu-detail-nav {
background: var(--color-glass-strong);
border: 1px solid var(--color-glass-border);
border-radius: var(--radius);
box-shadow: var(--shadow-glass), var(--shadow-glass-inset);
backdrop-filter: blur(28px) saturate(170%);
-webkit-backdrop-filter: blur(28px) saturate(170%);
padding: 10px 8px;
position: sticky;
top: 90px;
max-height: calc(100vh - 110px);
overflow-y: auto;
}
@media (max-width: 920px) { .clu-detail-nav { position: relative; top: auto; max-height: none; } }
.clu-detail-section-title {
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.09em;
color: var(--color-muted-2);
font-weight: 600;
padding: 12px 12px 6px;
}
.clu-detail-section-title:first-child { padding-top: 4px; }
.clu-detail-item {
display: flex; align-items: center; gap: 9px;
padding: 8px 11px;
border-radius: var(--radius-xs);
color: #3a3a47;
font-size: 13px; font-weight: 500;
transition: background .15s, color .15s, box-shadow .15s;
cursor: pointer;
text-decoration: none;
}
.clu-detail-item:hover {
background: rgba(255,255,255,0.55);
color: var(--color-fg);
text-decoration: none;
}
.clu-detail-item.active {
background: linear-gradient(180deg, rgba(255,255,255,0.92), rgba(255,255,255,0.66));
color: var(--color-fg);
box-shadow: 0 4px 12px -6px rgba(40,52,92,0.25), inset 0 1px 0 rgba(255,255,255,0.9);
}
.clu-detail-item .ico { width: 15px; height: 15px; color: var(--color-muted); flex: none; }
.clu-detail-item.active .ico { color: var(--color-accent); }
.clu-detail-item .count {
margin-left: auto;
font-size: 10px;
font-family: var(--font-mono);
color: var(--color-muted);
background: rgba(255,255,255,0.7);
padding: 1px 6px;
border-radius: 8px;
border: 1px solid var(--color-hairline);
}
/* ───── Site Details — exact template port (dtabs / fm / editor / console) ───── */
.clu-dtabs {
display: flex; gap: 3px; padding: 5px;

View File

@ -5,12 +5,22 @@ use Livewire\Attributes\Layout;
use Livewire\Volt\Component;
new #[Layout('layouts.app')] class extends Component {
public string $workspaceName = 'Acme Cluster';
public string $workspaceSlug = 'acme-cluster';
public string $description = 'WordPress fleet management for clients.';
public string $workspaceName = '';
public string $workspaceSlug = '';
public string $description = '';
public string $region = 'eu-central-1';
public string $timezone = 'Europe/Berlin';
public function mount(): void
{
$ws = auth()->user()?->currentWorkspace;
if ($ws) {
$this->workspaceName = $ws->name;
$this->workspaceSlug = $ws->slug;
$this->region = $ws->region ?? 'eu-central-1';
}
}
public bool $emailNotifications = true;
public bool $slackNotifications = false;
public bool $weeklyDigest = true;

View File

@ -467,20 +467,53 @@ new #[Layout('layouts.app')] class extends Component {
</button>
</div>
{{-- ── Tab bar ── --}}
<div class="clu-dtabs mb-[18px]" role="tablist">
@foreach (['overview' => ['Übersicht', '<rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/>'], 'wp' => ['WordPress', '<circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/>'], 'plugins' => ['Plugins', '<path d="M14 7V3H8v4M3 7h18v13H3zM12 11v6M9 14h6"/>'], 'redirects' => ['Redirects', '<path d="M3 12h14l-4-4M17 12l-4 4"/>'], 'staging' => ['Staging', '<path d="M3 12a9 9 0 1 0 9-9M3 4v5h5M21 12a9 9 0 1 0-9 9M21 20v-5h-5"/>'], 'vhost' => ['vHost', '<path d="M2 12h20M2 6h20M2 18h20"/>'], 'ssl' => ['SSL / TLS', '<rect x="5" y="11" width="14" height="9" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/>'], 'access' => ['SSH / FTP', '<path d="M4 17l5-5-5-5M13 19h7"/>'], 'php' => ['PHP / ini', '<path d="M4 7h16M4 12h16M4 17h10"/>'], 'files' => ['Datei-Manager', '<path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>'], 'cron' => ['Cronjobs', '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>'], 'logs' => ['Logs', '<rect x="3" y="3" width="18" height="18" rx="2"/><path d="M7 8h10M7 12h10M7 16h6"/>']] as $key => [$label, $svg])
<button type="button" role="tab"
wire:click="setTab('{{ $key }}')"
class="clu-dtab @if($tab === $key) active @endif">
{{-- ── Grouped Sub-Sidebar + Content ── --}}
@php
$sections = [
'ÜBERBLICK' => [
'overview' => ['Übersicht', '<rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/>'],
],
'WORDPRESS' => [
'wp' => ['Einstellungen','<circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/>'],
'plugins' => ['Plugins', '<path d="M14 7V3H8v4M3 7h18v13H3zM12 11v6M9 14h6"/>'],
'cron' => ['Cronjobs', '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>', $site->crons->count() ?: null],
'redirects' => ['Redirects', '<path d="M3 12h14l-4-4M17 12l-4 4"/>'],
],
'HOSTING' => [
'vhost' => ['vHost / nginx','<path d="M2 12h20M2 6h20M2 18h20"/>'],
'php' => ['PHP / ini', '<path d="M4 7h16M4 12h16M4 17h10"/>'],
'ssl' => ['SSL / TLS', '<rect x="5" y="11" width="14" height="9" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/>'],
'files' => ['Datei-Manager','<path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>'],
'staging' => ['Staging', '<path d="M3 12a9 9 0 1 0 9-9M3 4v5h5"/>'],
],
'ZUGANG' => [
'access' => ['SSH / FTP', '<path d="M4 17l5-5-5-5M13 19h7"/>'],
],
'BEOBACHTEN' => [
'logs' => ['Logs', '<rect x="3" y="3" width="18" height="18" rx="2"/><path d="M7 8h10M7 12h10M7 16h6"/>'],
],
];
@endphp
<div class="clu-detail-layout">
<nav class="clu-detail-nav">
@foreach ($sections as $sectionTitle => $items)
<div class="clu-detail-section-title">{{ $sectionTitle }}</div>
@foreach ($items as $key => $meta)
@php [$label, $svg, $count] = array_pad((array) $meta, 3, null); @endphp
<a href="?tab={{ $key }}" wire:click.prevent="setTab('{{ $key }}')"
class="clu-detail-item @if($tab === $key) active @endif">
<svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9">{!! $svg !!}</svg>
{{ $label }}
@if ($key === 'cron' && $site->crons->count())
<span class="count">{{ $site->crons->count() }}</span>
@if ($count)
<span class="count">{{ $count }}</span>
@endif
</button>
</a>
@endforeach
</div>
@endforeach
</nav>
<div class="clu-detail-content">
{{-- ────── OVERVIEW ────── --}}
@if ($tab === 'overview')
@ -1506,4 +1539,7 @@ new #[Layout('layouts.app')] class extends Component {
</div>
</div>
@endif
</div>{{-- /clu-detail-content --}}
</div>{{-- /clu-detail-layout --}}
</div>