159 lines
8.5 KiB
PHP
159 lines
8.5 KiB
PHP
<div class="mx-auto max-w-3xl space-y-6">
|
|
<div class="animate-rise">
|
|
<h1 class="text-2xl font-bold tracking-tight text-ink">{{ __('mail_settings.title') }}</h1>
|
|
<p class="mt-1 text-sm text-muted">{{ __('mail_settings.subtitle') }}</p>
|
|
</div>
|
|
|
|
{{-- The one thing that makes every mailbox on this page decorative. Said
|
|
here as well as on the front page, because this is where somebody comes
|
|
to find out why no mail arrived — and the test button below reports
|
|
success regardless, since MailboxTester builds its own transport on
|
|
purpose (a check that honoured MAIL_MAILER=log would report success
|
|
while writing to a file). --}}
|
|
@if (in_array(config('mail.default'), ['log', 'array', null], true))
|
|
<x-ui.alert variant="warning">
|
|
{{ __('admin.notice.mail_not_delivering', ['mailer' => (string) (config('mail.default') ?? 'null')]) }}
|
|
</x-ui.alert>
|
|
@endif
|
|
|
|
|
|
@if (! $usable)
|
|
<x-ui.alert variant="warning">{{ __('mail_settings.no_key') }}</x-ui.alert>
|
|
@endif
|
|
|
|
{{-- 1. The server. One of it, so one card. --}}
|
|
<x-ui.card>
|
|
<h2 class="text-lg font-semibold text-ink">{{ __('mail_settings.server_title') }}</h2>
|
|
<p class="mt-1 text-sm text-muted">{{ __('mail_settings.server_hint') }}</p>
|
|
|
|
<div class="mt-5 grid gap-4 sm:grid-cols-3">
|
|
<x-ui.input name="host" wire:model="host" :label="__('mail_settings.host')" />
|
|
<x-ui.input name="port" type="number" wire:model="port" :label="__('mail_settings.port')" />
|
|
<div>
|
|
<label for="encryption" class="text-sm font-medium text-ink">{{ __('mail_settings.encryption') }}</label>
|
|
<select id="encryption" wire:model="encryption"
|
|
class="mt-1.5 w-full rounded-md border border-line-strong bg-surface px-3 py-2 text-sm text-body">
|
|
<option value="tls">TLS</option>
|
|
<option value="ssl">SSL</option>
|
|
<option value="none">{{ __('mail_settings.encryption_none') }}</option>
|
|
</select>
|
|
@error('encryption')<p class="mt-1 text-xs text-danger">{{ $message }}</p>@enderror
|
|
</div>
|
|
</div>
|
|
|
|
@if (! $passwordConfirmed)
|
|
{{-- The second gate. $host is the platform's outbound relay for
|
|
every purpose at once — being signed in is not enough to
|
|
repoint it, the same reasoning as the credentials page. --}}
|
|
<form wire:submit="confirmPassword" class="mt-5 rounded-lg border border-line bg-surface-2 p-4">
|
|
<p class="text-sm text-body">{{ __('mail_settings.confirm_first') }}</p>
|
|
<div class="mt-3 flex flex-wrap items-start gap-2">
|
|
<div class="min-w-56 flex-1">
|
|
<x-ui.input name="confirmablePassword" type="password" autocomplete="current-password"
|
|
:label="__('admin_settings.password_current')" wire:model="confirmablePassword" />
|
|
</div>
|
|
<x-ui.button type="submit" variant="secondary" class="mt-7" wire:loading.attr="disabled" wire:target="confirmPassword">
|
|
{{ __('mail_settings.confirm_button') }}
|
|
</x-ui.button>
|
|
</div>
|
|
</form>
|
|
@else
|
|
<x-ui.button wire:click="saveServer" variant="primary" size="md" class="mt-5">
|
|
{{ __('mail_settings.save') }}
|
|
</x-ui.button>
|
|
@endif
|
|
</x-ui.card>
|
|
|
|
{{-- 2. The mailboxes. NO input field inside a <td> — R20. --}}
|
|
<x-ui.card>
|
|
<h2 class="text-lg font-semibold text-ink">{{ __('mail_settings.boxes_title') }}</h2>
|
|
|
|
{{-- One shared recipient field for every row's test button below — a
|
|
field ABOVE the table, not one growing inside a <td> (R20). --}}
|
|
<div class="mt-4 max-w-sm">
|
|
<x-ui.input name="testRecipient" type="email" wire:model="testRecipient" :label="__('mail_settings.test_recipient')" />
|
|
<p class="mt-2 text-sm text-muted">{{ __('mail_settings.test_hint') }}</p>
|
|
</div>
|
|
|
|
@if ($testResult)
|
|
<x-ui.alert :variant="$testResult['ok'] ? 'success' : 'danger'" class="mt-4">
|
|
<span class="font-medium">{{ $testedKey }}:</span>
|
|
@if ($testResult['ok'])
|
|
{{ __('mail_settings.test_ok') }}
|
|
@else
|
|
{{ __('mail_settings.test_failed') }} {{ $testResult['error'] }}
|
|
@endif
|
|
</x-ui.alert>
|
|
@endif
|
|
|
|
<table class="mt-5 w-full text-sm">
|
|
<thead>
|
|
<tr class="border-b border-line text-left">
|
|
<th class="lbl pb-2">{{ __('mail_settings.address') }}</th>
|
|
<th class="lbl pb-2">{{ __('mail_settings.last_verified') }}</th>
|
|
<th class="lbl pb-2"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach ($mailboxes as $box)
|
|
<tr wire:key="mailbox-{{ $box->uuid }}" class="border-b border-line last:border-0">
|
|
<td class="py-3">
|
|
<span class="font-medium text-ink">{{ $box->address }}</span>
|
|
<span class="ml-2 text-muted">{{ $box->key }}</span>
|
|
</td>
|
|
<td class="py-3 text-muted">
|
|
{{ $box->last_verified_at?->local()->isoFormat('DD.MM.YYYY HH:mm') ?? __('mail_settings.never_verified') }}
|
|
</td>
|
|
<td class="py-3 text-right space-x-2">
|
|
{{-- R18: icon beside its text, single line, size-4.
|
|
Two spans swapped by wire:loading, same
|
|
pattern as admin/host-create.blade.php's save
|
|
button — the disabled attribute alone left an
|
|
operator staring at a greyed-out button with
|
|
no sign anything was happening for up to the
|
|
full connect timeout. --}}
|
|
<x-ui.button size="sm" variant="ghost" wire:click="test('{{ $box->uuid }}')"
|
|
wire:loading.attr="disabled" wire:target="test('{{ $box->uuid }}')">
|
|
<x-ui.icon name="send" class="size-4" />
|
|
<span wire:loading.remove wire:target="test('{{ $box->uuid }}')">{{ __('mail_settings.test') }}</span>
|
|
<span wire:loading wire:target="test('{{ $box->uuid }}')">{{ __('mail_settings.testing') }}</span>
|
|
</x-ui.button>
|
|
<x-ui.button size="sm" variant="ghost"
|
|
x-on:click="$dispatch('openModal', { component: 'edit-mailbox', arguments: { uuid: '{{ $box->uuid }}' } })">
|
|
<x-ui.icon name="pencil" class="size-4" />{{ __('mail_settings.edit') }}
|
|
</x-ui.button>
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</x-ui.card>
|
|
|
|
{{-- 3. The mapping. One <select> per row — R20's stated exception. --}}
|
|
<x-ui.card>
|
|
<h2 class="text-lg font-semibold text-ink">{{ __('mail_settings.purposes_title') }}</h2>
|
|
<p class="mt-1 text-sm text-muted">{{ __('mail_settings.purposes_hint') }}</p>
|
|
|
|
<div class="mt-5 space-y-3">
|
|
@foreach ($purposeList as $purpose)
|
|
<div class="flex items-center justify-between gap-6 border-b border-line py-2">
|
|
<span class="text-sm text-ink">{{ __('mail_settings.purpose.'.$purpose) }}</span>
|
|
<select wire:model="purposes.{{ $purpose }}"
|
|
class="w-64 rounded-md border border-line-strong bg-surface px-3 py-2 text-sm text-body">
|
|
<option value="">—</option>
|
|
@foreach ($mailboxes as $box)
|
|
<option value="{{ $box->key }}">{{ $box->address }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
|
|
@error('purposes.system')<p class="mt-3 text-xs text-danger">{{ $message }}</p>@enderror
|
|
|
|
<x-ui.button wire:click="savePurposes" variant="primary" size="md" class="mt-5">
|
|
{{ __('mail_settings.save') }}
|
|
</x-ui.button>
|
|
</x-ui.card>
|
|
</div>
|