Give the settings page an outline
Reported as cramped boxes with empty space either side, and it was: one column half the window wide, eight cards of identical weight stacked in an order nobody chose — the operator's own password sitting between two platform-wide switches — and every card taking a full row to hold three lines of text. The page is wider now and has three groups, each with a heading: what belongs to the installation, what belongs to the signed-in operator, and the team. Short cards sit side by side instead of one per row. The two-factor policy moved into the installation group, where it belongs: it is a rule for everybody who signs in, set by the owner, not a setting on one account — and it fills the space the short website card was leaving beside the long allow-list. Fields are sized to what goes in them. The three password boxes were laid out three across inside a narrow card; they stack in a half-width one. The window times are four characters and were as wide as their column. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>feature/betriebsmodus v1.3.14
parent
6c92aa5dd7
commit
da76a260fb
|
|
@ -190,4 +190,12 @@ return [
|
|||
'weekday' => [
|
||||
1 => 'Mo', 2 => 'Di', 3 => 'Mi', 4 => 'Do', 5 => 'Fr', 6 => 'Sa', 7 => 'So',
|
||||
],
|
||||
|
||||
// The page's outline. It had none — eight cards of equal weight in one
|
||||
// column, with the operator's own password between two platform switches.
|
||||
'group' => [
|
||||
'installation' => 'Installation',
|
||||
'account' => 'Mein Konto',
|
||||
'team' => 'Team & Zugriff',
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -189,4 +189,12 @@ return [
|
|||
'weekday' => [
|
||||
1 => 'Mon', 2 => 'Tue', 3 => 'Wed', 4 => 'Thu', 5 => 'Fri', 6 => 'Sat', 7 => 'Sun',
|
||||
],
|
||||
|
||||
// The page's outline. It had none — eight cards of equal weight in one
|
||||
// column, with the operator's own password between two platform switches.
|
||||
'group' => [
|
||||
'installation' => 'Installation',
|
||||
'account' => 'My account',
|
||||
'team' => 'Team & access',
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,9 +1,22 @@
|
|||
<div class="mx-auto max-w-3xl space-y-6">
|
||||
<div class="mx-auto max-w-[1120px] space-y-10">
|
||||
<div class="animate-rise">
|
||||
<h1 class="text-2xl font-bold tracking-tight text-ink">{{ __('admin_settings.title') }}</h1>
|
||||
<p class="mt-1 text-sm text-muted">{{ __('admin_settings.subtitle') }}</p>
|
||||
</div>
|
||||
|
||||
{{-- ── Die Installation ─────────────────────────────────────────────────
|
||||
Grouped and given headings because the page was one undifferentiated
|
||||
stack: eight cards of the same weight in a column half the window wide,
|
||||
with the operator's own password sitting between two platform-wide
|
||||
switches. What each card belongs to is now said out loud, and the short
|
||||
ones sit side by side instead of each taking a full row to hold three
|
||||
lines of text. --}}
|
||||
<section class="space-y-5">
|
||||
<div class="flex items-center gap-4">
|
||||
<h2 class="lbl">{{ __('admin_settings.group.installation') }}</h2>
|
||||
<span class="h-px flex-1 bg-line"></span>
|
||||
</div>
|
||||
|
||||
{{-- Version & update --}}
|
||||
@if ($canManageSite)
|
||||
{{-- Polls itself: an operator watching an update run should not have to
|
||||
|
|
@ -194,13 +207,13 @@
|
|||
<div>
|
||||
<label for="auto-from" class="mb-1.5 block text-xs font-semibold text-muted">{{ __('admin_settings.auto_from') }}</label>
|
||||
<input id="auto-from" type="time" wire:model="autoFrom" @disabled(! $autoUpdate)
|
||||
class="min-h-10 rounded border border-line bg-surface px-3 font-mono text-sm text-ink">
|
||||
class="min-h-10 w-32 rounded border border-line bg-surface px-3 font-mono text-sm text-ink">
|
||||
@error('autoFrom') <p class="mt-1 text-xs text-danger">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
<div>
|
||||
<label for="auto-to" class="mb-1.5 block text-xs font-semibold text-muted">{{ __('admin_settings.auto_to') }}</label>
|
||||
<input id="auto-to" type="time" wire:model="autoTo" @disabled(! $autoUpdate)
|
||||
class="min-h-10 rounded border border-line bg-surface px-3 font-mono text-sm text-ink">
|
||||
class="min-h-10 w-32 rounded border border-line bg-surface px-3 font-mono text-sm text-ink">
|
||||
@error('autoTo') <p class="mt-1 text-xs text-danger">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
<x-ui.button wire:click="saveUpdateWindow" variant="secondary" size="sm">{{ __('admin_settings.save') }}</x-ui.button>
|
||||
|
|
@ -248,6 +261,12 @@
|
|||
</div>
|
||||
@endif
|
||||
|
||||
<div class="grid gap-5 lg:grid-cols-2 lg:items-start">
|
||||
{{-- Website visibility and two-factor policy stack in the left
|
||||
column: both are short, and the console's allow-list beside
|
||||
them is long. Three cards of one row each left a column of
|
||||
white space down the middle of the page. --}}
|
||||
<div class="space-y-5">
|
||||
{{-- My account --}}
|
||||
@if ($canManageSite)
|
||||
<div class="rounded-lg border border-line bg-surface p-6 shadow-xs animate-rise">
|
||||
|
|
@ -279,6 +298,30 @@
|
|||
</div>
|
||||
@endif
|
||||
|
||||
|
||||
{{-- Two-factor policy — voluntary by default, the Owner can make it
|
||||
compulsory. The switch refuses to turn on while the operator flipping
|
||||
it has no confirmed two-factor themselves: the page that would turn it
|
||||
back off sits behind the switch. --}}
|
||||
@if ($canManageSite)
|
||||
<div class="rounded-lg border border-line bg-surface p-6 shadow-xs animate-rise [animation-delay:45ms]">
|
||||
<h2 class="font-semibold text-ink">{{ __('admin_settings.two_factor_title') }}</h2>
|
||||
<p class="mt-1 max-w-xl text-sm text-muted">{{ __('admin_settings.two_factor_body') }}</p>
|
||||
|
||||
<form wire:submit="saveTwoFactorPolicy" class="mt-4 flex flex-wrap items-center justify-between gap-3">
|
||||
<label class="flex items-center gap-2 text-sm text-body">
|
||||
<input type="checkbox" wire:model="requireTwoFactor" class="size-4 rounded border-line-strong text-accent" />
|
||||
{{ __('admin_settings.two_factor_require') }}
|
||||
</label>
|
||||
<x-ui.button type="submit" variant="primary" wire:loading.attr="disabled" wire:target="saveTwoFactorPolicy">
|
||||
{{ __('admin_settings.save') }}
|
||||
</x-ui.button>
|
||||
</form>
|
||||
@error('requireTwoFactor')<p class="mt-1.5 text-xs text-danger">{{ $message }}</p>@enderror
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Who may reach this console --}}
|
||||
@if ($canManageSite)
|
||||
<div class="rounded-lg border border-line bg-surface p-6 shadow-xs animate-rise [animation-delay:30ms]">
|
||||
|
|
@ -342,35 +385,29 @@
|
|||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Every operator sees their own, with no capability required: this is
|
||||
the account you are signed in as, and being unable to see where else
|
||||
that is true is the gap the feature exists to close. --}}
|
||||
<div class="animate-rise [animation-delay:40ms]">
|
||||
@livewire('admin.sessions')
|
||||
</div>
|
||||
|
||||
{{-- Two-factor policy — voluntary by default, the Owner can make it
|
||||
compulsory. The switch refuses to turn on while the operator flipping
|
||||
it has no confirmed two-factor themselves: the page that would turn it
|
||||
back off sits behind the switch. --}}
|
||||
@if ($canManageSite)
|
||||
<div class="rounded-lg border border-line bg-surface p-6 shadow-xs animate-rise [animation-delay:45ms]">
|
||||
<h2 class="font-semibold text-ink">{{ __('admin_settings.two_factor_title') }}</h2>
|
||||
<p class="mt-1 max-w-xl text-sm text-muted">{{ __('admin_settings.two_factor_body') }}</p>
|
||||
|
||||
<form wire:submit="saveTwoFactorPolicy" class="mt-4 flex flex-wrap items-center justify-between gap-3">
|
||||
<label class="flex items-center gap-2 text-sm text-body">
|
||||
<input type="checkbox" wire:model="requireTwoFactor" class="size-4 rounded border-line-strong text-accent" />
|
||||
{{ __('admin_settings.two_factor_require') }}
|
||||
</label>
|
||||
<x-ui.button type="submit" variant="primary" wire:loading.attr="disabled" wire:target="saveTwoFactorPolicy">
|
||||
{{ __('admin_settings.save') }}
|
||||
</x-ui.button>
|
||||
</section>
|
||||
|
||||
{{-- ── Das eigene Konto ────────────────────────────────────────────── --}}
|
||||
<section class="space-y-5">
|
||||
<div class="flex items-center gap-4">
|
||||
<h2 class="lbl">{{ __('admin_settings.group.account') }}</h2>
|
||||
<span class="h-px flex-1 bg-line"></span>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-5 lg:grid-cols-2 lg:items-start">
|
||||
<form wire:submit="saveAccount" class="space-y-4 rounded-lg border border-line bg-surface p-6 shadow-xs animate-rise [animation-delay:60ms]">
|
||||
<h2 class="font-semibold text-ink">{{ __('admin_settings.account_title') }}</h2>
|
||||
<div class="grid gap-4">
|
||||
<x-ui.input name="name" wire:model="name" :label="__('admin_settings.name')" />
|
||||
<x-ui.input name="email" wire:model="email" :label="__('admin_settings.email')" type="email" />
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<x-ui.button variant="primary" type="submit" wire:loading.attr="disabled" wire:target="saveAccount">{{ __('admin_settings.save') }}</x-ui.button>
|
||||
</div>
|
||||
</form>
|
||||
@error('requireTwoFactor')<p class="mt-1.5 text-xs text-danger">{{ $message }}</p>@enderror
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Own password. There was no way to change one at all: an account created
|
||||
with a generated password kept it until someone opened a shell. --}}
|
||||
|
|
@ -380,7 +417,7 @@
|
|||
<p class="mt-1 text-sm text-muted">{{ __('admin_settings.password_sub') }}</p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 sm:grid-cols-3">
|
||||
<div class="grid gap-4">
|
||||
<x-ui.input name="currentPassword" type="password" autocomplete="current-password"
|
||||
:label="__('admin_settings.password_current')" wire:model="currentPassword" />
|
||||
<x-ui.input name="newPassword" type="password" autocomplete="new-password"
|
||||
|
|
@ -393,20 +430,26 @@
|
|||
<x-ui.button type="submit" variant="primary" wire:loading.attr="disabled" wire:target="updateOwnPassword">{{ __('admin_settings.password_save') }}</x-ui.button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form wire:submit="saveAccount" class="space-y-4 rounded-lg border border-line bg-surface p-6 shadow-xs animate-rise [animation-delay:60ms]">
|
||||
<h2 class="font-semibold text-ink">{{ __('admin_settings.account_title') }}</h2>
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
<x-ui.input name="name" wire:model="name" :label="__('admin_settings.name')" />
|
||||
<x-ui.input name="email" wire:model="email" :label="__('admin_settings.email')" type="email" />
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<x-ui.button variant="primary" type="submit" wire:loading.attr="disabled" wire:target="saveAccount">{{ __('admin_settings.save') }}</x-ui.button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{{-- Staff & access (Owner-only) --}}
|
||||
|
||||
|
||||
{{-- Every operator sees their own, with no capability required: this is
|
||||
the account you are signed in as, and being unable to see where else
|
||||
that is true is the gap the feature exists to close. --}}
|
||||
<div class="animate-rise [animation-delay:40ms]">
|
||||
@livewire('admin.sessions')
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{-- ── Das Team ─────────────────────────────────────────────────────── --}}
|
||||
@if ($canManageStaff)
|
||||
<section class="space-y-5">
|
||||
<div class="flex items-center gap-4">
|
||||
<h2 class="lbl">{{ __('admin_settings.group.team') }}</h2>
|
||||
<span class="h-px flex-1 bg-line"></span>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4 rounded-lg border border-line bg-surface p-6 shadow-xs animate-rise [animation-delay:120ms]">
|
||||
<div>
|
||||
<h2 class="font-semibold text-ink">{{ __('admin_settings.staff_title') }}</h2>
|
||||
|
|
@ -504,5 +547,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue