Files
WebAPP/resources/views/installer/steps/mail.blade.php
Rhino ee89141628 UI-Verbesserungen, PWA-Icons, Branding und Settings-Erweiterung
Invertiertes Logo für Admin-Navbar, neue PWA-Icons, Manifest-Updates,
Tailwind-Config-Extraktion, Farb-/Namenseinstellungen im Admin-Bereich
und diverse Layout-Optimierungen.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:49:12 +01:00

238 lines
14 KiB
PHP

<x-layouts.installer :currentStep="4">
<h2 class="text-lg font-semibold text-gray-900 mb-2">E-Mail-Konfiguration</h2>
<p class="text-sm text-gray-600 mb-5">
Damit Funktionen wie "Passwort vergessen" funktionieren, muss ein E-Mail-Versand konfiguriert werden.
Du kannst dies auch spaeter in den Admin-Einstellungen aendern.
</p>
<form method="POST" action="{{ route('install.mail.store') }}"
x-data="{
mailMode: '{{ old('mail_mode', 'smtp') }}',
editor: null,
testing: false,
testResult: false,
testSuccess: false,
testMessage: '',
syncEditor() {
if (this.editor) {
document.getElementById('input-pw-reset-de').value = this.editor.root.innerHTML;
}
},
async testSmtp() {
this.testing = true;
this.testResult = false;
try {
const res = await fetch('{{ route("install.test-mail") }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Accept': 'application/json',
},
body: JSON.stringify({
mail_host: document.getElementById('mail_host').value,
mail_port: document.getElementById('mail_port').value,
mail_username: document.getElementById('mail_username').value,
mail_password: document.getElementById('mail_password').value,
mail_encryption: document.getElementById('mail_encryption').value,
}),
});
const data = await res.json();
this.testSuccess = data.success;
this.testMessage = data.message;
} catch (e) {
this.testSuccess = false;
this.testMessage = 'Netzwerkfehler: ' + e.message;
}
this.testing = false;
this.testResult = true;
}
}"
@submit="syncEditor()">
@csrf
{{-- Mail-Modus --}}
<div class="mb-5">
<label class="block text-sm font-semibold text-gray-700 mb-3">Versandmethode</label>
<div class="space-y-2">
<label class="flex items-start gap-3 p-3 border rounded-md cursor-pointer transition"
:class="mailMode === 'smtp' ? 'border-blue-400 bg-blue-50' : 'border-gray-200 hover:bg-gray-50'">
<input type="radio" name="mail_mode" value="smtp" x-model="mailMode"
class="mt-0.5 text-blue-600 focus:ring-blue-500">
<div>
<span class="text-sm font-medium text-gray-800">SMTP-Server</span>
<p class="text-xs text-gray-500 mt-0.5">E-Mails werden ueber einen SMTP-Server versendet (empfohlen).</p>
</div>
</label>
<label class="flex items-start gap-3 p-3 border rounded-md cursor-pointer transition"
:class="mailMode === 'log' ? 'border-blue-400 bg-blue-50' : 'border-gray-200 hover:bg-gray-50'">
<input type="radio" name="mail_mode" value="log" x-model="mailMode"
class="mt-0.5 text-blue-600 focus:ring-blue-500">
<div>
<span class="text-sm font-medium text-gray-800">Kein Versand (Log)</span>
<p class="text-xs text-gray-500 mt-0.5">E-Mails werden nur ins Log geschrieben. Passwort-Reset funktioniert dann nicht.</p>
</div>
</label>
</div>
</div>
{{-- SMTP-Felder --}}
<div x-show="mailMode === 'smtp'" x-cloak class="space-y-4 mb-5 p-4 bg-gray-50 border border-gray-200 rounded-md">
<div class="grid grid-cols-2 gap-4">
<div>
<label for="mail_host" class="block text-sm font-medium text-gray-700 mb-1">SMTP-Host</label>
<input type="text" name="mail_host" id="mail_host" value="{{ old('mail_host') }}"
placeholder="z.B. smtp.strato.de"
class="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
@error('mail_host') <p class="text-xs text-red-600 mt-1">{{ $message }}</p> @enderror
</div>
<div>
<label for="mail_port" class="block text-sm font-medium text-gray-700 mb-1">Port</label>
<input type="number" name="mail_port" id="mail_port" value="{{ old('mail_port', '587') }}"
class="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
@error('mail_port') <p class="text-xs text-red-600 mt-1">{{ $message }}</p> @enderror
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label for="mail_username" class="block text-sm font-medium text-gray-700 mb-1">Benutzername</label>
<input type="text" name="mail_username" id="mail_username" value="{{ old('mail_username') }}"
placeholder="z.B. noreply@deinverein.de"
class="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
@error('mail_username') <p class="text-xs text-red-600 mt-1">{{ $message }}</p> @enderror
</div>
<div>
<label for="mail_password" class="block text-sm font-medium text-gray-700 mb-1">Passwort</label>
<input type="password" name="mail_password" id="mail_password" value="{{ old('mail_password') }}"
class="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
@error('mail_password') <p class="text-xs text-red-600 mt-1">{{ $message }}</p> @enderror
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label for="mail_from_address" class="block text-sm font-medium text-gray-700 mb-1">Absender-Adresse</label>
<input type="email" name="mail_from_address" id="mail_from_address" value="{{ old('mail_from_address') }}"
placeholder="z.B. noreply@deinverein.de"
class="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
@error('mail_from_address') <p class="text-xs text-red-600 mt-1">{{ $message }}</p> @enderror
</div>
<div>
<label for="mail_from_name" class="block text-sm font-medium text-gray-700 mb-1">Absender-Name <span class="text-gray-400 font-normal">(optional)</span></label>
<input type="text" name="mail_from_name" id="mail_from_name" value="{{ old('mail_from_name') }}"
placeholder="z.B. Mein Verein"
class="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
</div>
<div>
<label for="mail_encryption" class="block text-sm font-medium text-gray-700 mb-1">Verschluesselung</label>
<select name="mail_encryption" id="mail_encryption"
class="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<option value="tls" {{ old('mail_encryption', 'tls') === 'tls' ? 'selected' : '' }}>TLS (Port 587, empfohlen)</option>
<option value="ssl" {{ old('mail_encryption') === 'ssl' ? 'selected' : '' }}>SSL (Port 465)</option>
<option value="none" {{ old('mail_encryption') === 'none' ? 'selected' : '' }}>Keine</option>
</select>
</div>
{{-- SMTP-Test --}}
<div class="pt-2 border-t border-gray-200">
<button type="button" @click="testSmtp()"
:disabled="testing"
class="px-4 py-2 text-sm font-medium text-white bg-emerald-600 rounded-md hover:bg-emerald-700 transition disabled:opacity-50 disabled:cursor-wait inline-flex items-center gap-2">
<template x-if="testing">
<svg class="animate-spin h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
</svg>
</template>
<span x-text="testing ? 'Teste Verbindung...' : 'Verbindung testen'"></span>
</button>
<p x-show="testResult" x-cloak x-text="testMessage"
:class="testSuccess ? 'text-green-600' : 'text-red-600'"
class="text-sm mt-2"></p>
</div>
</div>
{{-- Passwort-Reset E-Mail Template --}}
<div class="mb-5">
<h3 class="text-sm font-semibold text-gray-700 mb-1">Passwort-Reset E-Mail (Deutsch)</h3>
<p class="text-xs text-gray-500 mb-3">
Dieser Text wird versendet, wenn ein Benutzer sein Passwort zuruecksetzen moechte.
Platzhalter: <code class="bg-gray-100 px-1 rounded">{name}</code>,
<code class="bg-gray-100 px-1 rounded">{app_name}</code>.
Der Reset-Link wird automatisch als Button angefuegt.
</p>
<div id="editor-pw-reset" class="bg-white border border-gray-300 rounded-md" style="min-height: 150px;">{!! $defaultPwResetDe !!}</div>
<input type="hidden" name="password_reset_email_de" id="input-pw-reset-de" value="{{ old('password_reset_email_de', $defaultPwResetDe) }}">
<p class="text-xs text-gray-400 mt-2">Texte fuer weitere Sprachen werden automatisch erstellt und koennen spaeter in den Einstellungen angepasst werden.</p>
</div>
@if ($errors->any())
<div class="mb-4 p-3 bg-red-50 border border-red-200 rounded-md">
<ul class="text-sm text-red-700 list-disc list-inside">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<div class="flex justify-between items-center">
<a href="{{ route('install.app') }}"
class="px-4 py-2 text-sm text-gray-600 bg-gray-100 rounded-md hover:bg-gray-200 transition">
Zurueck
</a>
<div class="flex items-center gap-3">
<button type="submit"
class="px-5 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700 transition">
Weiter
</button>
</div>
</div>
</form>
{{-- Ueberspringen-Link (separates Formular) --}}
<div class="text-center mt-3">
<form method="POST" action="{{ route('install.mail.store') }}" class="inline">
@csrf
<input type="hidden" name="mail_mode" value="log">
<button type="submit" class="text-xs text-gray-400 hover:text-gray-600 underline transition">
Schritt ueberspringen (kein E-Mail-Versand)
</button>
</form>
</div>
{{-- Quill Editor --}}
<link href="https://cdn.jsdelivr.net/npm/quill@1.3.7/dist/quill.snow.css" rel="stylesheet"
integrity="sha384-cPa8kzsYWhqpAfWOLWYIw3V0BhPi/m3lrd8tBTPxr2NrYCHRVZ7xy1cEoRGOM/03" crossorigin="anonymous">
<style>
#editor-pw-reset .ql-editor { min-height: 120px; }
.ql-toolbar.ql-snow { border-radius: 0.375rem 0.375rem 0 0; border-color: #d1d5db; }
.ql-container.ql-snow { border-radius: 0 0 0.375rem 0.375rem; border-color: #d1d5db; }
</style>
<script src="https://cdn.jsdelivr.net/npm/quill@1.3.7/dist/quill.min.js"
integrity="sha384-QUJ+ckWz1M+a7w0UfG1sEn4pPrbQwSxGm/1TIPyioqXBrwuT9l4f9gdHWLDLbVWI" crossorigin="anonymous"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const editorEl = document.getElementById('editor-pw-reset');
if (!editorEl) return;
const quill = new Quill('#editor-pw-reset', {
theme: 'snow',
modules: {
toolbar: [
[{ 'header': [2, 3, false] }],
['bold', 'italic', 'underline'],
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
['link'],
['clean']
]
}
});
// Speichere Referenz fuer Alpine.js syncEditor()
const component = document.querySelector('[x-data]').__x.$data;
component.editor = quill;
});
</script>
</x-layouts.installer>