redesign(system): compose Domain/TLS/Release-Kanal into a 2-column layout
The System page was just constrained to max-w-3xl — narrower, not designed. Rework it into an intentional composition at max-w-5xl: - Critical alerts (restart-required + bare-IP) are now full-width banners above the working area, not buried in stacked inset blocks. - A 2-column lg grid separates "do" from "know": left/main panel holds the actionable form (domain input + Speichern, TLS-mode selector); right/side panel is the status & recovery stack (access address, TLS state, SSH reset). - Release-Kanal stays a full-width band below. Everything stacks to one column on mobile. - Header HTTPS/Plaintext pill made more prominent; domain Save promoted to the primary action. All wire bindings (confirmDomain, confirmTlsMode, restartNow, restartPending/ restartRequested) and every __() key preserved; two new section-label keys (section_config, section_status) added with DE/EN parity. Tactical-Terminal tokens only, no inline styles, existing component kit reused. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>feat/v1-foundation
parent
4eeff4916a
commit
7e1d342844
|
|
@ -9,6 +9,10 @@ return [
|
|||
'https_active' => 'HTTPS aktiv',
|
||||
'https_plain' => 'Klartext-HTTP',
|
||||
|
||||
// Section labels (redesigned 2-column layout)
|
||||
'section_config' => 'Konfiguration',
|
||||
'section_status' => 'Status & Wiederherstellung',
|
||||
|
||||
// Domain & TLS panel
|
||||
'tls_title' => 'Domain & TLS',
|
||||
'tls_subtitle' => 'Erreichbarkeit des Panels und Let\'s-Encrypt-Zertifikat',
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@ return [
|
|||
'https_active' => 'HTTPS active',
|
||||
'https_plain' => 'Plaintext HTTP',
|
||||
|
||||
// Section labels (redesigned 2-column layout)
|
||||
'section_config' => 'Configuration',
|
||||
'section_status' => 'Status & recovery',
|
||||
|
||||
// Domain & TLS panel
|
||||
'tls_title' => 'Domain & TLS',
|
||||
'tls_subtitle' => 'Panel reachability and Let\'s Encrypt certificate',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<div class="mx-auto max-w-3xl space-y-5">
|
||||
<div class="mx-auto max-w-5xl space-y-5">
|
||||
{{-- Header --}}
|
||||
<div class="flex flex-wrap items-center gap-4 rounded-xl border border-line bg-surface p-5 shadow-panel">
|
||||
<span class="grid h-14 w-14 shrink-0 place-items-center rounded-xl border border-accent/25 bg-accent/10 text-accent">
|
||||
|
|
@ -10,82 +10,76 @@
|
|||
<p class="truncate font-mono text-[11px] text-ink-3">{{ __('system.subheading') }}</p>
|
||||
</div>
|
||||
<span @class([
|
||||
'inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 font-mono text-[11px]',
|
||||
'border-online/30 text-online' => $hasTls,
|
||||
'border-warning/30 text-warning' => ! $hasTls,
|
||||
'inline-flex items-center gap-1.5 rounded-full border px-3 py-1.5 font-mono text-xs',
|
||||
'border-online/30 bg-online/10 text-online' => $hasTls,
|
||||
'border-warning/30 bg-warning/10 text-warning' => ! $hasTls,
|
||||
])>
|
||||
<x-status-dot :status="$hasTls ? 'online' : 'warning'" />{{ $hasTls ? __('system.https_active') : __('system.https_plain') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{{-- Domain & TLS --}}
|
||||
<x-panel :title="__('system.tls_title')" :subtitle="__('system.tls_subtitle')">
|
||||
{{-- Current TLS state --}}
|
||||
@if ($hasTls)
|
||||
<div class="mb-4 flex items-start gap-3 rounded-md border border-online/25 bg-online/5 p-3">
|
||||
<x-icon name="shield" class="mt-0.5 h-4 w-4 shrink-0 text-online" />
|
||||
<div class="min-w-0">
|
||||
<p class="text-sm text-ink">{{ __('system.tls_active_title') }}</p>
|
||||
<p class="mt-0.5 font-mono text-[11px] text-ink-3">{{ __('system.tls_active_body_pre') }}
|
||||
<a href="{{ $panelUrl }}" class="break-all text-accent-text hover:underline">{{ $panelUrl }}</a> ·
|
||||
{{ __('system.tls_active_body_post') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div class="mb-4 flex items-start gap-3 rounded-md border border-warning/25 bg-warning/5 p-3">
|
||||
<x-icon name="alert" class="mt-0.5 h-4 w-4 shrink-0 text-warning" />
|
||||
<div class="min-w-0">
|
||||
<p class="text-sm text-ink">{{ __('system.tls_bare_title') }}</p>
|
||||
<p class="mt-0.5 font-mono text-[11px] text-ink-3">{{ __('system.tls_bare_body') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Last-resort recovery: SSH into the host and reset admin access. --}}
|
||||
<div class="mb-4 flex items-start gap-2.5 text-ink-4">
|
||||
<x-icon name="shield" class="mt-0.5 h-4 w-4 shrink-0" />
|
||||
<p class="font-mono text-[11px] leading-relaxed">{{ __('system.ssh_reset_hint') }}</p>
|
||||
</div>
|
||||
{{-- ── Critical alerts (full-width, above the grid) ─────────────────────────
|
||||
Restart-required and bare-IP notices stay prominent banners — never buried
|
||||
in a sidebar. --}}
|
||||
|
||||
{{-- Restart-required notice (shown after a successful save). One-click: the button
|
||||
writes a sentinel a HOST-side watcher reacts to and restarts the stack — the
|
||||
container never gets the Docker socket. No terminal needed. --}}
|
||||
@if ($restartPending)
|
||||
<div class="mb-4 flex items-start gap-3 rounded-md border border-warning/30 bg-warning/10 p-3">
|
||||
<x-icon name="alert" class="mt-0.5 h-4 w-4 shrink-0 text-warning" />
|
||||
<div class="flex items-start gap-3 rounded-xl border border-warning/30 bg-warning/10 p-4 shadow-panel">
|
||||
<x-icon name="alert" class="mt-0.5 h-5 w-5 shrink-0 text-warning" />
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-sm text-ink">{{ __('system.restart_title') }}</p>
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div class="min-w-0">
|
||||
<p class="font-display text-sm font-semibold text-ink">{{ __('system.restart_title') }}</p>
|
||||
<p class="mt-0.5 font-mono text-[11px] leading-relaxed text-ink-3">{{ __('system.restart_body') }}</p>
|
||||
|
||||
</div>
|
||||
@if ($restartRequested)
|
||||
<p class="mt-2 flex items-center gap-2 font-mono text-[11px] text-accent-text">
|
||||
<p class="flex shrink-0 items-center gap-2 font-mono text-[11px] text-accent-text">
|
||||
<x-icon name="rotate" class="h-3.5 w-3.5 shrink-0" />{{ __('system.restart_running') }}
|
||||
</p>
|
||||
@else
|
||||
<div class="mt-3">
|
||||
<x-btn variant="primary" wire:click="restartNow" wire:loading.attr="disabled" wire:target="restartNow">
|
||||
<x-btn variant="primary" wire:click="restartNow" wire:loading.attr="disabled" wire:target="restartNow" class="shrink-0">
|
||||
<x-icon name="rotate" class="h-3.5 w-3.5" /> {{ __('system.restart_now') }}
|
||||
</x-btn>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
<p class="mt-2 font-mono text-[11px] leading-relaxed text-ink-4">{{ __('system.restart_watcher_hint') }}</p>
|
||||
<p class="mt-2 font-mono text-[11px] leading-relaxed text-ink-4">{{ __('system.restart_lockout_hint') }}</p>
|
||||
<p class="mt-1 font-mono text-[11px] leading-relaxed text-ink-4">{{ __('system.restart_lockout_hint') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Bare-IP warning: no domain set → plaintext HTTP. Full-width call to action. --}}
|
||||
@unless ($hasTls)
|
||||
<div class="flex items-start gap-3 rounded-xl border border-warning/25 bg-warning/5 p-4 shadow-panel">
|
||||
<x-icon name="alert" class="mt-0.5 h-5 w-5 shrink-0 text-warning" />
|
||||
<div class="min-w-0">
|
||||
<p class="font-display text-sm font-semibold text-ink">{{ __('system.tls_bare_title') }}</p>
|
||||
<p class="mt-0.5 font-mono text-[11px] leading-relaxed text-ink-3">{{ __('system.tls_bare_body') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@endunless
|
||||
|
||||
{{-- ── 2-column working area ────────────────────────────────────────────────
|
||||
LEFT (main): the actionable form — domain + Speichern, TLS-mode selector.
|
||||
RIGHT (side): the status & recovery stack — access address, TLS state, SSH reset. --}}
|
||||
<div class="grid grid-cols-1 gap-5 lg:grid-cols-3">
|
||||
|
||||
{{-- LEFT / main column — "DO" ────────────────────────────────────────── --}}
|
||||
<x-panel :title="__('system.tls_title')" :subtitle="__('system.tls_subtitle')" class="lg:col-span-2">
|
||||
<div class="space-y-6">
|
||||
{{-- Editable panel domain. Saving persists it and asks for a restart; on restart
|
||||
app URL, the Reverb endpoint, Caddy's cert and the secure cookie re-derive from it. --}}
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<p class="mb-3 font-mono text-[11px] uppercase tracking-[0.18em] text-ink-4">{{ __('system.section_config') }}</p>
|
||||
<label for="domainInput" class="mb-1 block text-sm text-ink-2">{{ __('system.domain') }}</label>
|
||||
<div class="flex flex-col gap-2 sm:flex-row">
|
||||
<input wire:model="domainInput" id="domainInput" type="text" autocomplete="off"
|
||||
autocapitalize="off" spellcheck="false"
|
||||
placeholder="{{ __('system.domain_placeholder') }}"
|
||||
class="h-9 w-full rounded-md border border-line bg-inset px-3 font-mono text-sm text-ink placeholder:text-ink-4 focus:border-accent/40 focus:outline-none" />
|
||||
<x-btn variant="secondary" wire:click="confirmDomain" wire:loading.attr="disabled" class="shrink-0">
|
||||
<x-btn variant="primary" wire:click="confirmDomain" wire:loading.attr="disabled" class="shrink-0">
|
||||
<x-icon name="save" class="h-3.5 w-3.5" /> {{ __('common.save') }}
|
||||
</x-btn>
|
||||
</div>
|
||||
|
|
@ -95,27 +89,12 @@
|
|||
<p class="mt-1.5 font-mono text-[11px] leading-relaxed text-ink-4">{{ __('system.domain_help') }}</p>
|
||||
</div>
|
||||
|
||||
{{-- Current access address (read-only status) --}}
|
||||
<dl class="divide-y divide-line overflow-hidden rounded-md border border-line">
|
||||
<div class="flex items-center justify-between gap-3 px-4 py-2.5">
|
||||
<dt class="shrink-0 text-sm text-ink-2">{{ __('system.access_address') }}</dt>
|
||||
<dd class="min-w-0 truncate font-mono text-sm text-ink">
|
||||
@if ($panelUrl)
|
||||
<a href="{{ $panelUrl }}" class="text-accent-text hover:underline">{{ $panelUrl }}</a>
|
||||
@else
|
||||
{{ __('system.access_bare_ip') }}
|
||||
@endif
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{-- TLS-mode selector --}}
|
||||
<div class="mt-5 space-y-3">
|
||||
<div class="space-y-3 border-t border-line pt-5">
|
||||
<p class="text-sm font-medium text-ink-2">{{ __('system.tls_mode_title') }}</p>
|
||||
|
||||
{{-- Segmented control (mirrors the channel selector) --}}
|
||||
<div role="radiogroup" aria-label="{{ __('system.tls_mode_title') }}" class="inline-flex w-full max-w-md rounded-md border border-line bg-inset p-1">
|
||||
<div role="radiogroup" aria-label="{{ __('system.tls_mode_title') }}" class="flex w-full rounded-md border border-line bg-inset p-1">
|
||||
<button type="button"
|
||||
role="radio"
|
||||
aria-checked="{{ $tlsMode === 'caddy' ? 'true' : 'false' }}"
|
||||
|
|
@ -162,9 +141,55 @@
|
|||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</x-panel>
|
||||
|
||||
{{-- Release-Kanal --}}
|
||||
{{-- RIGHT / side column — "KNOW / RECOVER" ──────────────────────────── --}}
|
||||
<x-panel :title="__('system.section_status')" class="lg:col-span-1">
|
||||
<div class="space-y-4">
|
||||
{{-- Current access address (read-only status) --}}
|
||||
<div class="rounded-md border border-line bg-inset/60 p-3">
|
||||
<p class="font-mono text-[11px] uppercase tracking-wider text-ink-4">{{ __('system.access_address') }}</p>
|
||||
<p class="mt-1 min-w-0 break-all font-mono text-sm text-ink">
|
||||
@if ($panelUrl)
|
||||
<a href="{{ $panelUrl }}" class="text-accent-text hover:underline">{{ $panelUrl }}</a>
|
||||
@else
|
||||
{{ __('system.access_bare_ip') }}
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{{-- Current TLS state --}}
|
||||
@if ($hasTls)
|
||||
<div class="flex items-start gap-3 rounded-md border border-online/25 bg-online/5 p-3">
|
||||
<x-icon name="shield" class="mt-0.5 h-4 w-4 shrink-0 text-online" />
|
||||
<div class="min-w-0">
|
||||
<p class="text-sm text-ink">{{ __('system.tls_active_title') }}</p>
|
||||
<p class="mt-0.5 font-mono text-[11px] leading-relaxed text-ink-3">{{ __('system.tls_active_body_pre') }}
|
||||
<a href="{{ $panelUrl }}" class="break-all text-accent-text hover:underline">{{ $panelUrl }}</a> ·
|
||||
{{ __('system.tls_active_body_post') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div class="flex items-start gap-3 rounded-md border border-warning/25 bg-warning/5 p-3">
|
||||
<x-icon name="alert" class="mt-0.5 h-4 w-4 shrink-0 text-warning" />
|
||||
<div class="min-w-0">
|
||||
<p class="text-sm text-ink">{{ __('system.tls_bare_title') }}</p>
|
||||
<p class="mt-0.5 font-mono text-[11px] leading-relaxed text-ink-3">{{ __('system.tls_bare_body') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Last-resort recovery: SSH into the host and reset admin access. --}}
|
||||
<div class="flex items-start gap-2.5 border-t border-line pt-4 text-ink-4">
|
||||
<x-icon name="shield" class="mt-0.5 h-4 w-4 shrink-0" />
|
||||
<p class="font-mono text-[11px] leading-relaxed">{{ __('system.ssh_reset_hint') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</x-panel>
|
||||
</div>
|
||||
|
||||
{{-- ── Release-Kanal (full-width band, below the grid) ──────────────────── --}}
|
||||
<x-panel :title="__('system.channel_title')" :subtitle="__('system.channel_subtitle')">
|
||||
<div class="space-y-4">
|
||||
{{-- Segmented control --}}
|
||||
|
|
|
|||
Loading…
Reference in New Issue