145 lines
7.8 KiB
PHP
145 lines
7.8 KiB
PHP
<div class="space-y-6">
|
|
<header class="animate-rise">
|
|
<p class="lbl">{{ __('proxy.eyebrow') }}</p>
|
|
<h1 class="mt-[7px] text-[23px] font-bold leading-[1.12] tracking-[-0.03em] text-ink min-[901px]:text-[30px]">{{ __('proxy.title') }}</h1>
|
|
<p class="mt-2 max-w-[76ch] text-sm leading-relaxed text-muted">{{ __('proxy.subtitle') }}</p>
|
|
</header>
|
|
|
|
@if ($notice)
|
|
<x-ui.alert variant="info" class="animate-rise">{{ $notice }}</x-ui.alert>
|
|
@endif
|
|
|
|
{{-- Die Übersicht in Zahlen, vor der Liste. Was ein Betreiber wissen will,
|
|
bevor er zwölf Zeilen liest: ist etwas kaputt, läuft etwas bald ab. --}}
|
|
<div class="grid gap-3 sm:grid-cols-2 lg:grid-cols-4 animate-rise [animation-delay:20ms]">
|
|
<x-ui.stat-tile :label="__('proxy.stat.total')" :value="$summary['total']" />
|
|
<x-ui.stat-tile :label="__('proxy.stat.valid')" :value="$summary['valid']" />
|
|
<x-ui.stat-tile :label="__('proxy.stat.soon')" :value="$summary['soon']"
|
|
:tone="$summary['soon'] > 0 ? 'warning' : null" />
|
|
<x-ui.stat-tile :label="__('proxy.stat.failing')" :value="$summary['failing']"
|
|
:tone="$summary['failing'] > 0 ? 'danger' : null" />
|
|
</div>
|
|
|
|
@if ($summary['unchecked'] > 0)
|
|
<x-ui.alert variant="info" class="animate-rise [animation-delay:30ms]">
|
|
{{ __('proxy.never_checked', ['count' => $summary['unchecked']]) }}
|
|
</x-ui.alert>
|
|
@endif
|
|
|
|
<section class="animate-rise [animation-delay:40ms]">
|
|
<div class="flex flex-wrap items-end justify-between gap-3">
|
|
<div>
|
|
<h2 class="text-md font-bold tracking-[-0.01em] text-ink">{{ __('proxy.list_title') }}</h2>
|
|
<p class="mt-1 max-w-[70ch] text-sm leading-relaxed text-muted">{{ __('proxy.list_sub') }}</p>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
<x-ui.button variant="secondary" size="sm" wire:click="checkCertificates" wire:loading.attr="disabled">
|
|
<x-slot:icon><x-ui.icon name="refresh" class="size-4" /></x-slot:icon>
|
|
{{ __('proxy.check') }}
|
|
</x-ui.button>
|
|
<x-ui.button variant="primary" size="sm" wire:click="apply" wire:loading.attr="disabled">
|
|
<x-slot:icon><x-ui.icon name="send" class="size-4" /></x-slot:icon>
|
|
{{ __('proxy.apply') }}
|
|
</x-ui.button>
|
|
</div>
|
|
</div>
|
|
|
|
<x-ui.panel class="mt-4">
|
|
@forelse ($hosts as $host)
|
|
<div class="grid gap-x-6 gap-y-2 px-6 py-4 sm:grid-cols-[minmax(0,1fr)_auto] sm:items-center">
|
|
<div class="min-w-0">
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<span class="truncate font-mono text-sm font-medium text-ink">{{ $host->hostname }}</span>
|
|
@if ($host->isConsole())
|
|
<x-ui.badge status="warning">{{ __('proxy.purpose.console') }}</x-ui.badge>
|
|
@endif
|
|
@if ($host->isFromConfig())
|
|
<span class="text-xs text-muted">{{ __('proxy.from_config') }}</span>
|
|
@endif
|
|
</div>
|
|
|
|
{{-- Der Zustand steht als Satz, nicht als Farbe: ein
|
|
Punkt in Grün oder Rot sagt nicht, wie lange noch,
|
|
und genau das ist hier die Frage. --}}
|
|
<p class="mt-1 text-xs leading-relaxed text-muted">
|
|
@if ($host->certificate_checked_at === null)
|
|
{{ __('proxy.cert.unknown') }}
|
|
@elseif ($host->certificate_error)
|
|
<span class="text-danger">{{ __('proxy.cert.error', ['error' => $host->certificate_error]) }}</span>
|
|
@elseif ($host->daysLeft() === null)
|
|
{{ __('proxy.cert.unknown') }}
|
|
@elseif ($host->isExpiringSoon())
|
|
<span class="text-danger">{{ __('proxy.cert.soon', ['days' => $host->daysLeft(), 'date' => $host->certificate_expires_at->local()->isoFormat('D. MMM YYYY')]) }}</span>
|
|
@else
|
|
{{ __('proxy.cert.valid', ['days' => $host->daysLeft(), 'date' => $host->certificate_expires_at->local()->isoFormat('D. MMM YYYY')]) }}
|
|
@endif
|
|
@if ($host->note)
|
|
· {{ $host->note }}
|
|
@endif
|
|
</p>
|
|
</div>
|
|
|
|
@unless ($host->isFromConfig())
|
|
<button type="button" wire:click="remove({{ $host->id }})"
|
|
class="justify-self-start text-xs font-medium text-muted transition hover:text-danger sm:justify-self-end">
|
|
{{ __('proxy.remove') }}
|
|
</button>
|
|
@endunless
|
|
</div>
|
|
@empty
|
|
<div class="px-6 py-8 text-center text-sm text-muted">{{ __('proxy.empty') }}</div>
|
|
@endforelse
|
|
</x-ui.panel>
|
|
|
|
<p class="mt-2 text-xs leading-relaxed text-muted">
|
|
@if ($summary['checkedAt'])
|
|
{{ __('proxy.last_checked', ['when' => $summary['checkedAt']->local()->isoFormat('D. MMM YYYY, HH:mm')]) }}
|
|
@endif
|
|
{{ __('proxy.apply_hint') }}
|
|
</p>
|
|
</section>
|
|
|
|
<form wire:submit="add" class="animate-rise [animation-delay:60ms]">
|
|
<h2 class="text-md font-bold tracking-[-0.01em] text-ink">{{ __('proxy.add_title') }}</h2>
|
|
<p class="mt-1 max-w-[70ch] text-sm leading-relaxed text-muted">{{ __('proxy.add_sub') }}</p>
|
|
|
|
<x-ui.panel class="mt-4">
|
|
<x-ui.row :label="__('proxy.field.hostname')" :hint="__('proxy.field.hostname_hint')" for="hostname">
|
|
<x-ui.input name="hostname" wire:model="hostname" placeholder="files.clupilot.com" />
|
|
</x-ui.row>
|
|
<x-ui.row :label="__('proxy.field.purpose')" :hint="__('proxy.field.purpose_hint')" for="purpose">
|
|
<select id="purpose" wire:model="purpose"
|
|
class="block w-full rounded border border-line bg-surface px-3 py-2 text-sm text-ink transition">
|
|
@foreach ($purposes as $option)
|
|
<option value="{{ $option }}">{{ __('proxy.purpose.'.$option) }}</option>
|
|
@endforeach
|
|
</select>
|
|
</x-ui.row>
|
|
<x-ui.row :label="__('proxy.field.note')" for="note">
|
|
<x-ui.input name="note" wire:model="note" />
|
|
</x-ui.row>
|
|
</x-ui.panel>
|
|
|
|
<div class="mt-4 flex justify-end">
|
|
<x-ui.button variant="primary" type="submit">{{ __('proxy.add') }}</x-ui.button>
|
|
</div>
|
|
</form>
|
|
|
|
<form wire:submit="saveAcmeEmail" class="animate-rise [animation-delay:80ms]">
|
|
<h2 class="text-md font-bold tracking-[-0.01em] text-ink">{{ __('proxy.acme_title') }}</h2>
|
|
<p class="mt-1 max-w-[70ch] text-sm leading-relaxed text-muted">{{ __('proxy.acme_sub') }}</p>
|
|
|
|
<x-ui.panel class="mt-4">
|
|
<x-ui.row :label="__('proxy.field.acme_email')"
|
|
:hint="$fallbackEmail ? __('proxy.field.acme_email_hint', ['email' => $fallbackEmail]) : __('proxy.field.acme_email_hint_none')"
|
|
for="acmeEmail">
|
|
<x-ui.input name="acmeEmail" wire:model="acmeEmail" type="email" :placeholder="$fallbackEmail" />
|
|
</x-ui.row>
|
|
</x-ui.panel>
|
|
|
|
<div class="mt-4 flex justify-end">
|
|
<x-ui.button variant="secondary" type="submit">{{ __('proxy.save') }}</x-ui.button>
|
|
</div>
|
|
</form>
|
|
</div>
|