{{ __('admin_settings.title') }}

{{ __('admin_settings.subtitle') }}

{{-- Version & update --}} @if ($canManageSite) {{-- Polls itself: an operator watching an update run should not have to reload the page to find out whether it finished. Fast while something is happening, slow when nothing is. wire:poll alone is not enough, because the thing being watched restarts the thing doing the watching. Mid-run the requests fail, and what answers afterwards is a NEW build being questioned by the old page's JavaScript — so the card sat on "läuft" until somebody reloaded. That gap is now covered by the full-screen overlay in layouts/admin.blade.php, which binds the same watcher once for every console page rather than here — this card no longer binds it itself, so the two do not double-poll while this page is open. wire:poll below is unrelated and stays: it is Livewire's own refresh of this card's own details (log tail, elapsed time) for as long as a Livewire connection exists. --}}

{{ __('admin_settings.update_title') }}

@if ($update['running']) {{ __('admin_settings.update_running') }} @elseif ($update['checking']) {{ __('admin_settings.update_checking') }} @elseif ($update['available']) {{-- The version, when it is known: "Version 1.1.0 verfügbar" is a thing an operator can decide about; "1 Aktualisierung verfügbar" is not. --}} @if ($update['target_release']) {{ __('admin_settings.update_available_release', ['version' => ltrim($update['target_release'], 'v')]) }} @else {{ __('admin_settings.update_available', ['n' => $update['behind']]) }} @endif @elseif ($update['behind'] === 0) {{ __('admin_settings.update_current') }} @else {{-- Never shown as "up to date": not knowing and being current are different answers. --}} {{ __('admin_settings.update_unknown') }} @endif
{{ __('admin_settings.update_version') }}:
{{ $update['version'] }}@if ($update['commit']) · {{ substr($update['commit'], 0, 7) }}@endif
@if ($update['source'])
{{ __('admin_settings.update_source') }}:
{{ $update['source'] }}
@endif @if ($update['deployed_at'])
{{ __('admin_settings.update_deployed') }}:
{{ $update['deployed_at']->diffForHumans() }}
@endif @if ($update['checked_at'])
{{ __('admin_settings.update_checked') }}:
{{ $update['checked_at']->diffForHumans() }}
@endif
{{-- Bound, not @disabled(): the directive compiles to inline PHP inside the component tag, and Blade then stops seeing a component. Two separate actions, always offered together: looking and applying are different intentions, and an operator who only wants to know whether anything is new must not be routed through a real deployment to find out. "Jetzt aktualisieren" now requires a released version to install. It used to be offered unconditionally, because `behind` was a reading taken up to a minute ago and a console that had not noticed the last commit yet would refuse to act on it. That reasoning belonged to counting commits on a branch, where the answer changes several times an hour. An update is a TAG now: it appears when somebody releases one, not when somebody pushes — and pressing the check button answers within a second, so a stale reading is no longer something to work around. Disabled for the cases where it would go nowhere: nothing released above the installed version, a run already in flight, no agent, or a check already pending (so a second click cannot collide with the first and be refused). --}}
@if ($update['running']) {{ __('admin_settings.update_running') }} @elseif (! $update['agent_seen']) {{ __('admin_settings.update_unknown') }} @else {{ $update['checking'] ? __('admin_settings.update_checking') : __('admin_settings.update_check') }} {{ __('admin_settings.update_now') }} @endif
@if (! $update['agent_seen']) {{ __('admin_settings.update_no_agent') }} @elseif ($update['last_error']) {{ $update['last_error'] }} @elseif (! $update['available'] && ! $update['running'] && ! $update['checking']) {{-- Why the button is grey. Without this the console looks broken to whoever just pushed to main and expects to see something: it is not broken, it is waiting for a release. --}}

{{ __('admin_settings.update_only_releases') }}

@endif {{-- Where it is. "Läuft gerade" alone is what sends an operator to the shell: a queued update does nothing visible until the agent picks it up, and while it runs the site is in maintenance mode and this page is unreachable. Both look identical to "wedged" without a time — hence the live countdown, replacing what used to be a static "spätestens um 17:19" that read as a promise nothing then visibly kept. There is no countdown any more, for either. It was wrong in fact — the target was recomputed on every poll, and where the interval the agent reported was shorter than the timer actually installed on that host it landed in the past every time and reset to a full minute, so it ran backwards a few seconds and jumped, forever. And it was wrong in kind: a check starts nothing, and an update now starts at once. A queued run says so in a sentence; a queued check says nothing beyond its badge. --}} @if ($update['running'])
@if ($update['phase'])

{{ __('admin_settings.update_step', ['step' => $update['phase']]) }}

@if ($update['started_at'])

{{ __('admin_settings.update_since', ['when' => $update['started_at']->diffForHumans()]) }}

@endif @else

{{ $update['instant'] ? __('admin_settings.update_starting') : __('admin_settings.update_starting_queued') }}

@endif

{{ __('admin_settings.update_offline_hint') }}

@endif @if ($update['last_state'] !== null && ! $update['running'])

{{ __('admin_settings.update_last_run', [ 'state' => __('admin_settings.update_state.'.$update['last_state']), 'when' => $update['last_finished_at']?->diffForHumans() ?? '—', ]) }} @if ($update['last_started_at'] && $update['last_finished_at']) · {{ __('admin_settings.update_took', ['duration' => $update['last_started_at']->diffForHumans($update['last_finished_at'], true)]) }} @endif {{-- Only on a failure: on a run that worked the last step is "left maintenance mode", which says nothing. --}} @if ($update['last_state'] === 'failed' && $update['last_phase']) · {{ __('admin_settings.update_failed_at', ['step' => $update['last_phase']]) }} @endif

@endif {{-- Open while it runs: "läuft gerade" on its own tells an operator nothing about whether it is progressing or wedged. --}} @if ($updateLog)
{{ __('admin_settings.update_log') }}
{{ $updateLog }}
@endif
@endif {{-- My account --}} @if ($canManageSite)

{{ __('admin_settings.site_title') }}

{{ $sitePublic ? __('admin_settings.site_public') : __('admin_settings.site_hidden') }}

{{ $sitePublic ? __('admin_settings.site_public_body') : __('admin_settings.site_hidden_body') }}

{{ __('admin_settings.site_your_view', ['ip' => request()->ip()]) }} {{ $viewerOnVpn ? __('admin_settings.site_via_vpn') : __('admin_settings.site_not_vpn') }}

{{ $sitePublic ? __('admin_settings.site_hide') : __('admin_settings.site_show') }}
@endif {{-- Who may reach this console --}} @if ($canManageSite)

{{ __('admin_settings.console_title') }}

{{ $consoleRestricted ? __('admin_settings.console_locked_badge') : __('admin_settings.console_open_badge') }}

{{ $consoleRestricted ? __('admin_settings.console_locked_body') : __('admin_settings.console_open_body') }}

{{ __('admin_settings.console_your_ip', ['ip' => $viewerIp]) }}

{{ $consoleRestricted ? __('admin_settings.console_unlock') : __('admin_settings.console_lock') }}

{{ __('admin_settings.console_always') }}

@foreach ($consoleVpnRanges as $range)
{{ $range }} {{ __('admin_settings.console_vpn_note') }}
@endforeach

{{ __('admin_settings.console_extra') }}

@forelse ($consoleIps as $ip)
{{ $ip }} {{ __('admin_settings.console_remove') }}
@empty

{{ __('admin_settings.console_none') }}

@endforelse {{-- The hint sits under the row, not inside it: as a sibling of the field it made the flex row as tall as field-plus-hint, and the button stretched to match. --}}
{{ __('admin_settings.console_add') }}

{{ __('admin_settings.console_ip_hint') }}

@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)

{{ __('admin_settings.two_factor_title') }}

{{ __('admin_settings.two_factor_body') }}

{{ __('admin_settings.save') }}
@error('requireTwoFactor')

{{ $message }}

@enderror
@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. --}}

{{ __('admin_settings.password_title') }}

{{ __('admin_settings.password_sub') }}

{{ __('admin_settings.password_save') }}

{{ __('admin_settings.account_title') }}

{{ __('admin_settings.save') }}
{{-- Staff & access (Owner-only) --}} @if ($canManageStaff)

{{ __('admin_settings.staff_title') }}

{{ __('admin_settings.staff_sub') }}

{{-- Invite --}}
@error('staffName')

{{ $message }}

@enderror
@error('staffEmail')

{{ $message }}

@enderror
@error('staffRole')

{{ $message }}

@enderror
{{ __('admin_settings.invite') }}

{{ __('admin_settings.invite_hint') }}

@if ($invitedPassword)

{{ __('admin_settings.temp_title') }}

{{ __('admin_settings.temp_hint') }}

{{ __('admin_settings.email') }}:
{{ $invitedEmail }}
{{ __('admin_settings.temp_password') }}:
{{ $invitedPassword }}
@endif {{-- Staff list --}}
@foreach ($staff as $s) @endforeach
{{ __('admin_settings.col_person') }} {{ __('admin_settings.role') }} {{ __('admin_settings.col_actions') }}

{{ $s['name'] }} @if ($s['self'])({{ __('admin_settings.you') }})@endif

{{ $s['email'] }}

@if ($s['self']) {{ __('admin_settings.role_'.\Illuminate\Support\Str::slug($s['role'])) }} @else @endif {{-- Your own row has no actions — you cannot revoke yourself — and an empty cell reads as a bug rather than as a rule. --}} @if ($s['self']) @endif @unless ($s['self']) @endunless
@endif