System-Mails im pressekonto-CI: zentrales E-Mail-Layout fuer alle Transaktionsmails
- Neues Layout components/emails/layout (Wortmarke, 'Publisher - Hub'- Tagline, Brand-Familien-Fusszeile) + Button-Komponente; alle System- Mails (Magic-Link, Go-Live-Reset, Kontakt, PM published/rejected, Kuendigung, Widerruf) darauf umgestellt - Framework-Auth-Mails (Passwort-Reset, E-Mail-Verifizierung) via toMailUsing auf eigene Views im gleichen CI statt Laravel-Default - Anmeldelink-Betreff vereinheitlicht; EmailBrandingTest sichert das Branding ueber alle Mails ab Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
f0cd11aa57
commit
9832869596
16 changed files with 351 additions and 168 deletions
|
|
@ -22,7 +22,7 @@ class MagicLoginLink extends Mailable
|
||||||
public function envelope(): Envelope
|
public function envelope(): Envelope
|
||||||
{
|
{
|
||||||
return new Envelope(
|
return new Envelope(
|
||||||
subject: 'Ihr Login-Link fuer pressekonto'
|
subject: 'Ihr Anmeldelink für Ihr pressekonto'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,10 @@ use Illuminate\Auth\Events\Login;
|
||||||
use Illuminate\Auth\Events\Registered;
|
use Illuminate\Auth\Events\Registered;
|
||||||
use Illuminate\Auth\Events\Verified;
|
use Illuminate\Auth\Events\Verified;
|
||||||
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
||||||
|
use Illuminate\Auth\Notifications\ResetPassword;
|
||||||
|
use Illuminate\Auth\Notifications\VerifyEmail;
|
||||||
use Illuminate\Database\Events\QueryExecuted;
|
use Illuminate\Database\Events\QueryExecuted;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Event;
|
use Illuminate\Support\Facades\Event;
|
||||||
use Illuminate\Support\Facades\Gate;
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
|
@ -92,6 +95,33 @@ class AppServiceProvider extends ServiceProvider
|
||||||
Event::listen(Registered::class, SendEmailVerificationNotification::class);
|
Event::listen(Registered::class, SendEmailVerificationNotification::class);
|
||||||
Event::listen(Verified::class, ActivateUserAfterVerification::class);
|
Event::listen(Verified::class, ActivateUserAfterVerification::class);
|
||||||
|
|
||||||
|
// Framework-Auth-Mails (Passwort-Reset, E-Mail-Verifizierung) im
|
||||||
|
// pressekonto-CI rendern statt im Laravel-Standard-Markdown-Theme —
|
||||||
|
// gleiches Layout wie alle übrigen System-Mails (emails/layout).
|
||||||
|
ResetPassword::toMailUsing(function (User $user, string $token): MailMessage {
|
||||||
|
$resetUrl = url(route('password.reset', [
|
||||||
|
'token' => $token,
|
||||||
|
'email' => $user->getEmailForPasswordReset(),
|
||||||
|
], false));
|
||||||
|
|
||||||
|
return (new MailMessage)
|
||||||
|
->subject('Passwort zurücksetzen für Ihr pressekonto')
|
||||||
|
->view('emails.auth.reset-password', [
|
||||||
|
'user' => $user,
|
||||||
|
'resetUrl' => $resetUrl,
|
||||||
|
'expiresInMinutes' => (int) config('auth.passwords.'.config('auth.defaults.passwords').'.expire'),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
VerifyEmail::toMailUsing(function (User $user, string $verificationUrl): MailMessage {
|
||||||
|
return (new MailMessage)
|
||||||
|
->subject('E-Mail-Adresse bestätigen für Ihr pressekonto')
|
||||||
|
->view('emails.auth.verify-email', [
|
||||||
|
'user' => $user,
|
||||||
|
'verificationUrl' => $verificationUrl,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
// Bei jedem Login Firmen-/Kontakt-Zuordnungen (gleiche E-Mail) auffrischen.
|
// Bei jedem Login Firmen-/Kontakt-Zuordnungen (gleiche E-Mail) auffrischen.
|
||||||
Event::listen(Login::class, SyncCompanyMembershipsOnLogin::class);
|
Event::listen(Login::class, SyncCompanyMembershipsOnLogin::class);
|
||||||
|
|
||||||
|
|
|
||||||
12
resources/views/components/emails/button.blade.php
Normal file
12
resources/views/components/emails/button.blade.php
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
@props([
|
||||||
|
'url',
|
||||||
|
])
|
||||||
|
|
||||||
|
{{-- E-Mail-tauglicher Button im Hub-CI (Tabellen-Konstrukt für Outlook). --}}
|
||||||
|
<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="margin: 22px 0;">
|
||||||
|
<tr>
|
||||||
|
<td style="border-radius: 4px; background-color: #1A2540;">
|
||||||
|
<a href="{{ $url }}" style="display: inline-block; padding: 13px 30px; font-family: 'Inter Tight', 'Segoe UI', Arial, Helvetica, sans-serif; font-size: 14px; font-weight: bold; color: #ffffff; text-decoration: none; border-radius: 4px;">{{ $slot }}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
106
resources/views/components/emails/layout.blade.php
Normal file
106
resources/views/components/emails/layout.blade.php
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
@props([
|
||||||
|
'title' => null,
|
||||||
|
'preheader' => null,
|
||||||
|
])
|
||||||
|
|
||||||
|
@php
|
||||||
|
/**
|
||||||
|
* Zentrales CI-Layout für alle System-E-Mails (Auth, Pressemitteilungen,
|
||||||
|
* Kündigung/Widerruf, Kontakt). Absender-Marke ist immer pressekonto —
|
||||||
|
* der gemeinsame Publisher · Hub der Portale presseecho und
|
||||||
|
* businessportal24. Header und Footer erklären diese Zugehörigkeit,
|
||||||
|
* damit Empfänger die Mail sofort einordnen können.
|
||||||
|
*
|
||||||
|
* E-Mail-tauglich gebaut: Tabellen-Layout, Inline-Styles für alles
|
||||||
|
* Layout-Kritische, ergänzende Klassen im <style>-Block (werden von
|
||||||
|
* Gmail/Outlook.com/Apple Mail unterstützt; ohne sie bleibt die Mail
|
||||||
|
* trotzdem lesbar).
|
||||||
|
*/
|
||||||
|
$brand = config('domains.domains.pressekonto.brand', []);
|
||||||
|
$taglineShort = $brand['tagline_short'] ?? 'Publisher · Hub';
|
||||||
|
$footerLegal = str_replace(':year', (string) now()->year, $brand['footer_legal'] ?? '© '.now()->year.' pressekonto · Alle Rechte vorbehalten');
|
||||||
|
$presseechoUrl = config('domains.domain_presseecho_url');
|
||||||
|
$businessportalUrl = config('domains.domain_businessportal_url');
|
||||||
|
$fontStack = "'Inter Tight', 'Segoe UI', Arial, Helvetica, sans-serif";
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="color-scheme" content="light">
|
||||||
|
<title>{{ $title }}</title>
|
||||||
|
<style>
|
||||||
|
.email-body p { margin: 0 0 14px; }
|
||||||
|
.email-body .title { font-size: 17px; font-weight: bold; color: #1A2540; margin: 18px 0; padding-left: 14px; border-left: 3px solid #B07A3A; }
|
||||||
|
.email-body .meta { background: #f6f4ef; border-left: 3px solid #1A2540; padding: 14px 18px; margin: 18px 0; }
|
||||||
|
.email-body .meta-danger { background: #fdf2f0; border-left: 3px solid #b3401f; padding: 14px 18px; margin: 18px 0; }
|
||||||
|
.email-body .meta dt, .email-body .meta-danger dt { font-weight: bold; color: #1A2540; font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em; }
|
||||||
|
.email-body .meta dd, .email-body .meta-danger dd { margin: 2px 0 10px; }
|
||||||
|
.email-body .message { white-space: pre-line; background: #f6f4ef; border: 1px solid #e8e4da; padding: 16px 18px; margin: 18px 0; }
|
||||||
|
.email-body .note { font-size: 12.5px; color: #6b7280; border-top: 1px solid #e8e4da; padding-top: 14px; margin-top: 26px; }
|
||||||
|
.email-body .note p { margin: 0 0 8px; }
|
||||||
|
.email-body a { color: #1A2540; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="margin: 0; padding: 0; background-color: #e8e4da;">
|
||||||
|
|
||||||
|
@if ($preheader)
|
||||||
|
{{-- Vorschautext im Posteingang; im Mail-Body unsichtbar. --}}
|
||||||
|
<div style="display: none; max-height: 0; overflow: hidden; mso-hide: all;">{{ $preheader }}</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<table role="presentation" width="100%" border="0" cellpadding="0" cellspacing="0" style="background-color: #e8e4da;">
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="padding: 32px 16px;">
|
||||||
|
<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%; max-width: 600px;">
|
||||||
|
|
||||||
|
{{-- Bernstein-Akzentstreifen (CI: 3px-Streifen des Hubs) --}}
|
||||||
|
<tr>
|
||||||
|
<td style="height: 3px; background-color: #B07A3A; font-size: 0; line-height: 0;"> </td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{{-- Header: Wortmarke + Publisher · Hub in Hub-Blau --}}
|
||||||
|
<tr>
|
||||||
|
<td style="background-color: #1A2540; padding: 26px 36px;">
|
||||||
|
<span style="font-family: {{ $fontStack }}; font-size: 22px; font-weight: bold; letter-spacing: -0.4px; line-height: 1;"><span style="color: #ffffff;">{{ $brand['name'] ?? 'presse' }}</span><span style="color: #B07A3A;">{{ $brand['accent'] ?? 'konto' }}</span></span>
|
||||||
|
<div style="font-family: {{ $fontStack }}; font-size: 10px; font-weight: bold; letter-spacing: 0.22em; text-transform: uppercase; color: rgba(255, 255, 255, 0.65); margin-top: 8px;">
|
||||||
|
{{ $taglineShort }}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{{-- Inhalt --}}
|
||||||
|
<tr>
|
||||||
|
<td class="email-body" style="background-color: #ffffff; padding: 32px 36px 28px; font-family: {{ $fontStack }}; font-size: 14.5px; line-height: 1.65; color: #2a3038;">
|
||||||
|
{{ $slot }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{{-- Brand-Familie: Zugehörigkeit von presseecho + businessportal24 --}}
|
||||||
|
<tr>
|
||||||
|
<td style="background-color: #ffffff; border-top: 1px solid #e8e4da; padding: 20px 36px 24px; font-family: {{ $fontStack }}; font-size: 12px; line-height: 1.7; color: #6b7280;">
|
||||||
|
<span style="font-weight: bold; color: #1A2540;">presse<span style="color: #B07A3A;">konto</span></span>
|
||||||
|
ist der gemeinsame Publisher · Hub für
|
||||||
|
<a href="{{ $presseechoUrl }}" style="font-weight: bold; text-decoration: none;"><span style="color: #1B2417;">presse</span><span style="color: #345636;">echo</span></a>
|
||||||
|
und
|
||||||
|
<a href="{{ $businessportalUrl }}" style="font-weight: bold; text-decoration: none;"><span style="color: #232A33;">businessportal</span><span style="color: #C84A1E;">24</span></a>
|
||||||
|
— ein Konto, redaktionell geprüft, auf beiden Portalen veröffentlichen.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{{-- Rechtliches unterhalb der Karte --}}
|
||||||
|
<table role="presentation" border="0" cellpadding="0" cellspacing="0" style="width: 100%; max-width: 600px;">
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="padding: 18px 8px 6px; font-family: {{ $fontStack }}; font-size: 11.5px; color: #8a8f96;">
|
||||||
|
{{ $footerLegal }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -1,15 +1,4 @@
|
||||||
<!DOCTYPE html>
|
<x-emails.layout title="Bitte Passwort setzen – {{ config('app.name') }}" preheader="Ihr Account wurde übertragen – bitte einmalig ein neues Passwort setzen.">
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Bitte Passwort setzen – {{ config('app.name') }}</title>
|
|
||||||
<style>
|
|
||||||
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
||||||
.btn { display: inline-block; background: #2563eb; color: #fff; padding: 12px 28px; border-radius: 6px; text-decoration: none; font-weight: bold; margin: 16px 0; }
|
|
||||||
.note { font-size: 13px; color: #666; border-top: 1px solid #eee; padding-top: 12px; margin-top: 24px; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<p>Hallo {{ $user->name }},</p>
|
<p>Hallo {{ $user->name }},</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
@ -18,9 +7,9 @@
|
||||||
um sich künftig einzuloggen.
|
um sich künftig einzuloggen.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>Klicken Sie auf den folgenden Link, um Ihr Passwort festzulegen:</p>
|
<p>Klicken Sie auf den folgenden Button, um Ihr Passwort festzulegen:</p>
|
||||||
|
|
||||||
<p><a class="btn" href="{{ $resetUrl }}">Passwort jetzt setzen</a></p>
|
<x-emails.button :url="$resetUrl">Passwort jetzt setzen</x-emails.button>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Alternativ können Sie diesen Link in Ihren Browser kopieren:<br>
|
Alternativ können Sie diesen Link in Ihren Browser kopieren:<br>
|
||||||
|
|
@ -35,5 +24,4 @@
|
||||||
</p>
|
</p>
|
||||||
<p>Bei Fragen erreichen Sie uns unter {{ config('mail.from.address') }}.</p>
|
<p>Bei Fragen erreichen Sie uns unter {{ config('mail.from.address') }}.</p>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</x-emails.layout>
|
||||||
</html>
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,13 @@
|
||||||
<!DOCTYPE html>
|
<x-emails.layout title="Ihr Anmeldelink" preheader="Ihr Einmal-Link für den Login in Ihr pressekonto.">
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Ihr Login-Link</title>
|
|
||||||
</head>
|
|
||||||
<body style="font-family: Arial, sans-serif; line-height: 1.5;">
|
|
||||||
<p>Hallo {{ $user->name }},</p>
|
<p>Hallo {{ $user->name }},</p>
|
||||||
|
|
||||||
<p>hier ist Ihr Einmal-Link fuer den Login in Ihr Kundenkonto.</p>
|
<p>hier ist Ihr Einmal-Link für den Login in Ihr Kundenkonto.</p>
|
||||||
|
|
||||||
<p>
|
<x-emails.button :url="$loginUrl">Jetzt einloggen</x-emails.button>
|
||||||
<a href="{{ $loginUrl }}">Jetzt einloggen</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>Der Link ist gueltig bis {{ $expiresAt }} Uhr und kann nur einmal verwendet werden.</p>
|
<p>Der Link ist gültig bis {{ $expiresAt }} Uhr und kann nur einmal verwendet werden.</p>
|
||||||
|
|
||||||
<p>Falls Sie den Login nicht angefordert haben, ignorieren Sie diese E-Mail bitte.</p>
|
<div class="note">
|
||||||
</body>
|
<p>Falls Sie den Login nicht angefordert haben, ignorieren Sie diese E-Mail bitte.</p>
|
||||||
</html>
|
</div>
|
||||||
|
</x-emails.layout>
|
||||||
|
|
|
||||||
23
resources/views/emails/auth/reset-password.blade.php
Normal file
23
resources/views/emails/auth/reset-password.blade.php
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<x-emails.layout title="Passwort zurücksetzen" preheader="Mit diesem Link können Sie ein neues Passwort für Ihr pressekonto vergeben.">
|
||||||
|
<p>Hallo {{ $user->name }},</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Sie erhalten diese E-Mail, weil für Ihr Konto ein neues Passwort angefordert wurde.
|
||||||
|
Klicken Sie auf den folgenden Button, um ein neues Passwort zu vergeben:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<x-emails.button :url="$resetUrl">Neues Passwort vergeben</x-emails.button>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Alternativ können Sie diesen Link in Ihren Browser kopieren:<br>
|
||||||
|
<small>{{ $resetUrl }}</small>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="note">
|
||||||
|
<p>Der Link ist <strong>{{ $expiresInMinutes }} Minuten</strong> gültig.</p>
|
||||||
|
<p>
|
||||||
|
Falls Sie kein neues Passwort angefordert haben, ignorieren Sie diese E-Mail bitte —
|
||||||
|
Ihr Passwort bleibt unverändert.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</x-emails.layout>
|
||||||
19
resources/views/emails/auth/verify-email.blade.php
Normal file
19
resources/views/emails/auth/verify-email.blade.php
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<x-emails.layout title="E-Mail-Adresse bestätigen" preheader="Bitte bestätigen Sie Ihre E-Mail-Adresse, um Ihr pressekonto zu aktivieren.">
|
||||||
|
<p>Hallo {{ $user->name }},</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
vielen Dank für Ihre Registrierung. Bitte bestätigen Sie Ihre E-Mail-Adresse,
|
||||||
|
um Ihr Konto zu aktivieren:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<x-emails.button :url="$verificationUrl">E-Mail-Adresse bestätigen</x-emails.button>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Alternativ können Sie diesen Link in Ihren Browser kopieren:<br>
|
||||||
|
<small>{{ $verificationUrl }}</small>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="note">
|
||||||
|
<p>Falls Sie kein Konto erstellt haben, ignorieren Sie diese E-Mail bitte.</p>
|
||||||
|
</div>
|
||||||
|
</x-emails.layout>
|
||||||
|
|
@ -1,18 +1,4 @@
|
||||||
<!DOCTYPE html>
|
<x-emails.layout title="Neue Kontaktanfrage" preheader="Über das Kontaktformular ist eine neue Anfrage eingegangen.">
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Neue Kontaktanfrage</title>
|
|
||||||
<style>
|
|
||||||
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
||||||
.meta { background: #f8fafc; border-left: 4px solid #2563eb; padding: 12px 16px; margin: 16px 0; }
|
|
||||||
.meta dt { font-weight: bold; color: #111; }
|
|
||||||
.meta dd { margin: 0 0 8px; }
|
|
||||||
.message { white-space: pre-line; border: 1px solid #eee; padding: 16px; }
|
|
||||||
.note { font-size: 13px; color: #666; border-top: 1px solid #eee; padding-top: 12px; margin-top: 24px; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<p>Über das Kontaktformular auf <strong>{{ $host }}</strong> ist eine neue Anfrage eingegangen:</p>
|
<p>Über das Kontaktformular auf <strong>{{ $host }}</strong> ist eine neue Anfrage eingegangen:</p>
|
||||||
|
|
||||||
<dl class="meta">
|
<dl class="meta">
|
||||||
|
|
@ -24,6 +10,7 @@
|
||||||
|
|
||||||
<div class="message">{{ $messageText }}</div>
|
<div class="message">{{ $messageText }}</div>
|
||||||
|
|
||||||
<p class="note">Antworten gehen direkt an die Absenderadresse (Reply-To).</p>
|
<div class="note">
|
||||||
</body>
|
<p>Antworten gehen direkt an die Absenderadresse (Reply-To).</p>
|
||||||
</html>
|
</div>
|
||||||
|
</x-emails.layout>
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,11 @@
|
||||||
<!DOCTYPE html>
|
<x-emails.layout title="Ihre Pressemitteilung wurde veröffentlicht" preheader="Ihre Pressemitteilung wurde geprüft und veröffentlicht.">
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Ihre Pressemitteilung wurde veröffentlicht</title>
|
|
||||||
<style>
|
|
||||||
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
||||||
.btn { display: inline-block; background: #16a34a; color: #fff; padding: 12px 28px; border-radius: 6px; text-decoration: none; font-weight: bold; margin: 16px 0; }
|
|
||||||
.title { font-size: 18px; font-weight: bold; margin: 12px 0; color: #111; }
|
|
||||||
.note { font-size: 13px; color: #666; border-top: 1px solid #eee; padding-top: 12px; margin-top: 24px; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<p>Hallo {{ $user->name }},</p>
|
<p>Hallo {{ $user->name }},</p>
|
||||||
|
|
||||||
<p>Ihre Pressemitteilung wurde geprüft und erfolgreich veröffentlicht.</p>
|
<p>Ihre Pressemitteilung wurde geprüft und erfolgreich veröffentlicht.</p>
|
||||||
|
|
||||||
<p class="title">{{ $pressRelease->title }}</p>
|
<p class="title">{{ $pressRelease->title }}</p>
|
||||||
|
|
||||||
<p><a class="btn" href="{{ $showUrl }}">Pressemitteilung ansehen</a></p>
|
<x-emails.button :url="$showUrl">Pressemitteilung ansehen</x-emails.button>
|
||||||
|
|
||||||
@if($pressRelease->published_at)
|
@if($pressRelease->published_at)
|
||||||
<p>Veröffentlicht am: {{ $pressRelease->published_at->format('d.m.Y H:i') }} Uhr</p>
|
<p>Veröffentlicht am: {{ $pressRelease->published_at->format('d.m.Y H:i') }} Uhr</p>
|
||||||
|
|
@ -26,5 +14,4 @@
|
||||||
<div class="note">
|
<div class="note">
|
||||||
<p>Bei Fragen erreichen Sie uns unter {{ config('mail.from.address') }}.</p>
|
<p>Bei Fragen erreichen Sie uns unter {{ config('mail.from.address') }}.</p>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</x-emails.layout>
|
||||||
</html>
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,4 @@
|
||||||
<!DOCTYPE html>
|
<x-emails.layout title="Ihre Pressemitteilung wurde abgelehnt" preheader="Ihre eingereichte Pressemitteilung konnte nicht veröffentlicht werden.">
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Ihre Pressemitteilung wurde abgelehnt</title>
|
|
||||||
<style>
|
|
||||||
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
||||||
.btn { display: inline-block; background: #2563eb; color: #fff; padding: 12px 28px; border-radius: 6px; text-decoration: none; font-weight: bold; margin: 16px 0; }
|
|
||||||
.title { font-size: 18px; font-weight: bold; margin: 12px 0; color: #111; }
|
|
||||||
.reason { background: #fef2f2; border-left: 4px solid #dc2626; padding: 12px 16px; margin: 16px 0; color: #7f1d1d; }
|
|
||||||
.note { font-size: 13px; color: #666; border-top: 1px solid #eee; padding-top: 12px; margin-top: 24px; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<p>Hallo {{ $user->name }},</p>
|
<p>Hallo {{ $user->name }},</p>
|
||||||
|
|
||||||
<p>Ihre eingereichte Pressemitteilung konnte leider nicht veröffentlicht werden.</p>
|
<p>Ihre eingereichte Pressemitteilung konnte leider nicht veröffentlicht werden.</p>
|
||||||
|
|
@ -19,7 +6,7 @@
|
||||||
<p class="title">{{ $pressRelease->title }}</p>
|
<p class="title">{{ $pressRelease->title }}</p>
|
||||||
|
|
||||||
@if($reason)
|
@if($reason)
|
||||||
<div class="reason">
|
<div class="meta-danger">
|
||||||
<strong>Ablehnungsgrund:</strong><br>
|
<strong>Ablehnungsgrund:</strong><br>
|
||||||
{{ $reason }}
|
{{ $reason }}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -27,10 +14,9 @@
|
||||||
<p>Bitte überarbeiten Sie den Inhalt und reichen Sie die Pressemitteilung erneut ein.</p>
|
<p>Bitte überarbeiten Sie den Inhalt und reichen Sie die Pressemitteilung erneut ein.</p>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<p><a class="btn" href="{{ $editUrl }}">Pressemitteilung bearbeiten</a></p>
|
<x-emails.button :url="$editUrl">Pressemitteilung bearbeiten</x-emails.button>
|
||||||
|
|
||||||
<div class="note">
|
<div class="note">
|
||||||
<p>Bei Fragen erreichen Sie uns unter {{ config('mail.from.address') }}.</p>
|
<p>Bei Fragen erreichen Sie uns unter {{ config('mail.from.address') }}.</p>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</x-emails.layout>
|
||||||
</html>
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,4 @@
|
||||||
<!DOCTYPE html>
|
<x-emails.layout title="Eingangsbestätigung Ihrer Kündigung" preheader="Wir bestätigen den Eingang Ihrer Kündigungserklärung.">
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Eingangsbestätigung Ihrer Kündigung</title>
|
|
||||||
<style>
|
|
||||||
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
||||||
.meta { background: #f8fafc; border-left: 4px solid #2563eb; padding: 12px 16px; margin: 16px 0; }
|
|
||||||
.meta dt { font-weight: bold; color: #111; }
|
|
||||||
.meta dd { margin: 0 0 8px; }
|
|
||||||
.note { font-size: 13px; color: #666; border-top: 1px solid #eee; padding-top: 12px; margin-top: 24px; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<p>Guten Tag {{ $terminationRequest->name }},</p>
|
<p>Guten Tag {{ $terminationRequest->name }},</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
@ -46,10 +33,11 @@
|
||||||
schnellstmöglich den Zeitpunkt, zu dem das Vertragsverhältnis endet.
|
schnellstmöglich den Zeitpunkt, zu dem das Vertragsverhältnis endet.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="note">
|
<div class="note">
|
||||||
Diese Bestätigung wurde automatisch erstellt und dokumentiert Inhalt sowie Datum
|
<p>
|
||||||
und Uhrzeit des Zugangs Ihrer Kündigungserklärung (§ 312k BGB).
|
Diese Bestätigung wurde automatisch erstellt und dokumentiert Inhalt sowie Datum
|
||||||
Bitte bewahren Sie sie auf.
|
und Uhrzeit des Zugangs Ihrer Kündigungserklärung (§ 312k BGB).
|
||||||
</p>
|
Bitte bewahren Sie sie auf.
|
||||||
</body>
|
</p>
|
||||||
</html>
|
</div>
|
||||||
|
</x-emails.layout>
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,7 @@
|
||||||
<!DOCTYPE html>
|
<x-emails.layout title="Neue Online-Kündigung" preheader="Über den Kündigungsbutton ist eine neue Kündigung eingegangen.">
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Neue Online-Kündigung</title>
|
|
||||||
<style>
|
|
||||||
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
||||||
.meta { background: #fef2f2; border-left: 4px solid #dc2626; padding: 12px 16px; margin: 16px 0; }
|
|
||||||
.meta dt { font-weight: bold; color: #111; }
|
|
||||||
.meta dd { margin: 0 0 8px; }
|
|
||||||
.note { font-size: 13px; color: #666; border-top: 1px solid #eee; padding-top: 12px; margin-top: 24px; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<p>Über den Kündigungsbutton auf <strong>{{ $terminationRequest->host }}</strong> ist eine neue Kündigung eingegangen:</p>
|
<p>Über den Kündigungsbutton auf <strong>{{ $terminationRequest->host }}</strong> ist eine neue Kündigung eingegangen:</p>
|
||||||
|
|
||||||
<dl class="meta">
|
<dl class="meta-danger">
|
||||||
<dt>Vorgangsnummer</dt>
|
<dt>Vorgangsnummer</dt>
|
||||||
<dd>K-{{ $terminationRequest->id }}</dd>
|
<dd>K-{{ $terminationRequest->id }}</dd>
|
||||||
<dt>Art der Kündigung</dt>
|
<dt>Art der Kündigung</dt>
|
||||||
|
|
@ -35,9 +22,10 @@
|
||||||
<dd>{{ $terminationRequest->created_at->format('d.m.Y H:i') }} Uhr</dd>
|
<dd>{{ $terminationRequest->created_at->format('d.m.Y H:i') }} Uhr</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
<p class="note">
|
<div class="note">
|
||||||
Die Zugangsbestätigung an den Verbraucher wurde automatisch versendet (§ 312k Abs. 4 BGB).
|
<p>
|
||||||
Bitte den Vorgang dem Vertrag zuordnen und dem Kunden den Beendigungszeitpunkt bestätigen.
|
Die Zugangsbestätigung an den Verbraucher wurde automatisch versendet (§ 312k Abs. 4 BGB).
|
||||||
</p>
|
Bitte den Vorgang dem Vertrag zuordnen und dem Kunden den Beendigungszeitpunkt bestätigen.
|
||||||
</body>
|
</p>
|
||||||
</html>
|
</div>
|
||||||
|
</x-emails.layout>
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,4 @@
|
||||||
<!DOCTYPE html>
|
<x-emails.layout title="Eingangsbestätigung Ihres Widerrufs" preheader="Wir bestätigen den Eingang Ihres Widerrufs.">
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Eingangsbestätigung Ihres Widerrufs</title>
|
|
||||||
<style>
|
|
||||||
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
||||||
.meta { background: #f8fafc; border-left: 4px solid #2563eb; padding: 12px 16px; margin: 16px 0; }
|
|
||||||
.meta dt { font-weight: bold; color: #111; }
|
|
||||||
.meta dd { margin: 0 0 8px; }
|
|
||||||
.note { font-size: 13px; color: #666; border-top: 1px solid #eee; padding-top: 12px; margin-top: 24px; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<p>Guten Tag {{ $withdrawalRequest->name }},</p>
|
<p>Guten Tag {{ $withdrawalRequest->name }},</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|
@ -38,9 +25,10 @@
|
||||||
schnellstmöglich mit den weiteren Schritten (z. B. zur Rückabwicklung) bei Ihnen.
|
schnellstmöglich mit den weiteren Schritten (z. B. zur Rückabwicklung) bei Ihnen.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="note">
|
<div class="note">
|
||||||
Diese Bestätigung wurde automatisch erstellt und dokumentiert den Zeitpunkt
|
<p>
|
||||||
des Eingangs Ihrer Widerrufserklärung. Bitte bewahren Sie sie auf.
|
Diese Bestätigung wurde automatisch erstellt und dokumentiert den Zeitpunkt
|
||||||
</p>
|
des Eingangs Ihrer Widerrufserklärung. Bitte bewahren Sie sie auf.
|
||||||
</body>
|
</p>
|
||||||
</html>
|
</div>
|
||||||
|
</x-emails.layout>
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,7 @@
|
||||||
<!DOCTYPE html>
|
<x-emails.layout title="Neuer Online-Widerruf" preheader="Über den Widerrufsbutton ist ein neuer Widerruf eingegangen.">
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Neuer Online-Widerruf</title>
|
|
||||||
<style>
|
|
||||||
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
||||||
.meta { background: #fef2f2; border-left: 4px solid #dc2626; padding: 12px 16px; margin: 16px 0; }
|
|
||||||
.meta dt { font-weight: bold; color: #111; }
|
|
||||||
.meta dd { margin: 0 0 8px; }
|
|
||||||
.note { font-size: 13px; color: #666; border-top: 1px solid #eee; padding-top: 12px; margin-top: 24px; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<p>Über den Widerrufsbutton auf <strong>{{ $withdrawalRequest->host }}</strong> ist ein neuer Widerruf eingegangen:</p>
|
<p>Über den Widerrufsbutton auf <strong>{{ $withdrawalRequest->host }}</strong> ist ein neuer Widerruf eingegangen:</p>
|
||||||
|
|
||||||
<dl class="meta">
|
<dl class="meta-danger">
|
||||||
<dt>Vorgangsnummer</dt>
|
<dt>Vorgangsnummer</dt>
|
||||||
<dd>W-{{ $withdrawalRequest->id }}</dd>
|
<dd>W-{{ $withdrawalRequest->id }}</dd>
|
||||||
<dt>Name</dt>
|
<dt>Name</dt>
|
||||||
|
|
@ -27,10 +14,11 @@
|
||||||
<dd>{{ $withdrawalRequest->created_at->format('d.m.Y H:i') }} Uhr</dd>
|
<dd>{{ $withdrawalRequest->created_at->format('d.m.Y H:i') }} Uhr</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
<p class="note">
|
<div class="note">
|
||||||
Die Eingangsbestätigung an den Verbraucher wurde automatisch versendet.
|
<p>
|
||||||
Bitte den Vorgang dem Vertrag zuordnen und die Rückabwicklung anstoßen
|
Die Eingangsbestätigung an den Verbraucher wurde automatisch versendet.
|
||||||
(gesetzliche Frist beachten).
|
Bitte den Vorgang dem Vertrag zuordnen und die Rückabwicklung anstoßen
|
||||||
</p>
|
(gesetzliche Frist beachten).
|
||||||
</body>
|
</p>
|
||||||
</html>
|
</div>
|
||||||
|
</x-emails.layout>
|
||||||
|
|
|
||||||
100
tests/Feature/Mail/EmailBrandingTest.php
Normal file
100
tests/Feature/Mail/EmailBrandingTest.php
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Mail\ContactRequestSubmitted;
|
||||||
|
use App\Mail\GoLivePasswordReset;
|
||||||
|
use App\Mail\MagicLoginLink;
|
||||||
|
use App\Mail\PressReleasePublished;
|
||||||
|
use App\Mail\PressReleaseRejected;
|
||||||
|
use App\Mail\TerminationRequestReceived;
|
||||||
|
use App\Mail\TerminationRequestSubmitted;
|
||||||
|
use App\Mail\WithdrawalRequestReceived;
|
||||||
|
use App\Mail\WithdrawalRequestSubmitted;
|
||||||
|
use App\Models\PressRelease;
|
||||||
|
use App\Models\TerminationRequest;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\WithdrawalRequest;
|
||||||
|
use Illuminate\Auth\Notifications\ResetPassword;
|
||||||
|
use Illuminate\Auth\Notifications\VerifyEmail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alle System-Mails müssen im pressekonto-CI erscheinen: Wortmarke,
|
||||||
|
* „Publisher · Hub"-Tagline und die Zugehörigkeits-Erklärung zur
|
||||||
|
* Brand-Familie (presseecho + businessportal24) aus dem zentralen
|
||||||
|
* E-Mail-Layout (components/emails/layout).
|
||||||
|
*/
|
||||||
|
function assertHubEmailBranding(string $html): void
|
||||||
|
{
|
||||||
|
expect($html)
|
||||||
|
->toContain('Publisher · Hub')
|
||||||
|
->toContain('presseecho')
|
||||||
|
->toContain('businessportal24')
|
||||||
|
->toContain('ist der gemeinsame Publisher')
|
||||||
|
->toContain('#1A2540')
|
||||||
|
->toContain('#B07A3A');
|
||||||
|
}
|
||||||
|
|
||||||
|
test('the magic login link mail uses the hub ci layout', function () {
|
||||||
|
$user = User::factory()->make();
|
||||||
|
|
||||||
|
assertHubEmailBranding((new MagicLoginLink($user, 'https://pressekonto.test/magic-login/abc', '12.07.2026 10:00'))->render());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the go-live password reset mail uses the hub ci layout', function () {
|
||||||
|
$user = User::factory()->make();
|
||||||
|
|
||||||
|
assertHubEmailBranding((new GoLivePasswordReset($user, 'https://pressekonto.test/reset-password/abc'))->render());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the press release published and rejected mails use the hub ci layout', function () {
|
||||||
|
$pressRelease = PressRelease::factory()->published()->create();
|
||||||
|
$user = $pressRelease->user;
|
||||||
|
|
||||||
|
assertHubEmailBranding((new PressReleasePublished($user, $pressRelease))->render());
|
||||||
|
assertHubEmailBranding((new PressReleaseRejected($user, $pressRelease, 'Bitte Quellen ergänzen.'))->render());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the termination mails use the hub ci layout', function () {
|
||||||
|
$terminationRequest = TerminationRequest::factory()->create();
|
||||||
|
|
||||||
|
assertHubEmailBranding((new TerminationRequestReceived($terminationRequest))->render());
|
||||||
|
assertHubEmailBranding((new TerminationRequestSubmitted($terminationRequest))->render());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the withdrawal mails use the hub ci layout', function () {
|
||||||
|
$withdrawalRequest = WithdrawalRequest::factory()->create();
|
||||||
|
|
||||||
|
assertHubEmailBranding((new WithdrawalRequestReceived($withdrawalRequest))->render());
|
||||||
|
assertHubEmailBranding((new WithdrawalRequestSubmitted($withdrawalRequest))->render());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the contact request mail uses the hub ci layout', function () {
|
||||||
|
assertHubEmailBranding(
|
||||||
|
(new ContactRequestSubmitted('Paula Presse', 'paula@example.test', 'Eine Frage zur Plattform.', 'pressekonto.test'))->render()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the password reset notification renders in the hub ci layout', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
$mailMessage = (new ResetPassword('test-token'))->toMail($user);
|
||||||
|
|
||||||
|
expect($mailMessage->subject)->toBe('Passwort zurücksetzen für Ihr pressekonto');
|
||||||
|
|
||||||
|
$html = (string) $mailMessage->render();
|
||||||
|
|
||||||
|
assertHubEmailBranding($html);
|
||||||
|
expect($html)->toContain('/reset-password/test-token');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('the email verification notification renders in the hub ci layout', function () {
|
||||||
|
$user = User::factory()->unverified()->create();
|
||||||
|
|
||||||
|
$mailMessage = (new VerifyEmail)->toMail($user);
|
||||||
|
|
||||||
|
expect($mailMessage->subject)->toBe('E-Mail-Adresse bestätigen für Ihr pressekonto');
|
||||||
|
|
||||||
|
$html = (string) $mailMessage->render();
|
||||||
|
|
||||||
|
assertHubEmailBranding($html);
|
||||||
|
expect($html)->toContain('/verify-email/');
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue