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
parent
555101c993
commit
43fb26e1ef
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue