Converted all notification emails to the branded layout by routing them through a shared Blade template and swapping

the MailMessage builders to use view(). This keeps the existing copy/labels but aligns the look with resources/views/
  emails/partials/layout.blade.php. I also switched the customer add‑on receipt notification to reuse the existing
  branded view and added missing translations for the upload pipeline alert.
This commit is contained in:
Codex Agent
2025-12-23 14:03:42 +01:00
parent 20ff3044e2
commit 207725d460
35 changed files with 1247 additions and 528 deletions

View File

@@ -30,18 +30,8 @@ class AddonPurchaseReceipt extends Notification implements ShouldQueue
return (new MailMessage)
->subject(__('emails.addons.receipt.subject', ['addon' => $label]))
->greeting(__('emails.addons.receipt.greeting', ['name' => $tenant?->name ?? __('emails.package_limits.team_fallback')]))
->line(__('emails.addons.receipt.body', [
'addon' => $label,
'event' => $event?->name['de'] ?? $event?->name['en'] ?? $event?->name ?? __('emails.package_limits.event_fallback'),
'amount' => $amount ? $amount.' '.$currency : __('emails.addons.receipt.unknown_amount'),
]))
->line(__('emails.addons.receipt.summary', [
'photos' => $this->addon->extra_photos,
'guests' => $this->addon->extra_guests,
'days' => $this->addon->extra_gallery_days,
]))
->action(__('emails.addons.receipt.action'), $url)
->line(__('emails.package_limits.footer'));
->view('emails.addons.receipt', [
'addon' => $this->addon,
]);
}
}

View File

@@ -27,20 +27,29 @@ class RefundReceipt extends Notification implements ShouldQueue
$tenant = $this->purchase->tenant;
$package = $this->purchase->package;
$amount = number_format((float) $this->purchase->price, 2);
$mail = (new MailMessage)
->subject(__('emails.refund.subject', ['package' => $package?->name ?? __('emails.package_limits.package_fallback')]))
->greeting(__('emails.refund.greeting', ['name' => $tenant?->name ?? __('emails.package_limits.team_fallback')]))
->line(__('emails.refund.body', [
$subject = __('emails.refund.subject', ['package' => $package?->name ?? __('emails.package_limits.package_fallback')]);
$greeting = __('emails.refund.greeting', ['name' => $tenant?->name ?? __('emails.package_limits.team_fallback')]);
$lines = [
__('emails.refund.body', [
'amount' => $amount,
'currency' => '€',
'provider_id' => $this->purchase->provider_id ?? '—',
]));
]),
];
if ($this->reason) {
$mail->line(__('emails.refund.reason', ['reason' => $this->reason]));
$lines[] = __('emails.refund.reason', ['reason' => $this->reason]);
}
return $mail->line(__('emails.refund.footer'));
return (new MailMessage)
->subject($subject)
->view('emails.notifications.basic', [
'title' => $subject,
'preheader' => $greeting,
'heroTitle' => $greeting,
'heroSubtitle' => $subject,
'lines' => $lines,
'footer' => __('emails.refund.footer'),
]);
}
}

View File

@@ -27,13 +27,25 @@ class InactiveTenantDeletionWarning extends Notification implements ShouldQueue
{
$locale = $this->tenant->user?->preferred_locale ?? app()->getLocale();
$formattedDate = $this->plannedDeletion->copy()->locale($locale)->translatedFormat('d. F Y');
$subject = __('profile.retention.warning_subject', [], $locale);
return (new MailMessage)
->locale($locale)
->subject(__('profile.retention.warning_subject', [], $locale))
->line(__('profile.retention.line1', ['name' => $this->tenant->name], $locale))
->line(__('profile.retention.line2', ['date' => $formattedDate], $locale))
->line(__('profile.retention.line3', [], $locale))
->action(__('profile.retention.action', [], $locale), url('/login'));
->subject($subject)
->view('emails.notifications.basic', [
'title' => $subject,
'preheader' => __('profile.retention.line1', ['name' => $this->tenant->name], $locale),
'heroTitle' => $subject,
'lines' => [
__('profile.retention.line1', ['name' => $this->tenant->name], $locale),
__('profile.retention.line2', ['date' => $formattedDate], $locale),
__('profile.retention.line3', [], $locale),
],
'cta' => [
[
'label' => __('profile.retention.action', [], $locale),
'url' => url('/login'),
],
],
]);
}
}

View File

@@ -26,18 +26,32 @@ class AddonPurchased extends Notification implements ShouldQueue
$label = $this->addon->metadata['label'] ?? $this->addon->addon_key;
$amount = $this->addon->amount ? number_format((float) $this->addon->amount, 2) : null;
$currency = $this->addon->currency ?? 'EUR';
$subject = __('emails.ops.addon.subject', ['addon' => $label]);
$greeting = __('emails.ops.addon.greeting');
$lines = [
__('emails.ops.addon.tenant', ['tenant' => $tenant?->name ?? __('emails.tenant_feedback.unknown_tenant')]),
__('emails.ops.addon.event', ['event' => $event?->name['de'] ?? $event?->name['en'] ?? $event?->name ?? __('emails.package_limits.event_fallback')]),
__('emails.ops.addon.addon', ['addon' => $label, 'quantity' => $this->addon->quantity]),
];
if ($amount) {
$lines[] = __('emails.ops.addon.amount', ['amount' => $amount, 'currency' => $currency]);
}
$lines[] = __('emails.ops.addon.provider', [
'checkout' => $this->addon->checkout_id ?? '—',
'transaction' => $this->addon->transaction_id ?? '—',
]);
return (new MailMessage)
->subject(__('emails.ops.addon.subject', ['addon' => $label]))
->greeting(__('emails.ops.addon.greeting'))
->line(__('emails.ops.addon.tenant', ['tenant' => $tenant?->name ?? __('emails.tenant_feedback.unknown_tenant')]))
->line(__('emails.ops.addon.event', ['event' => $event?->name['de'] ?? $event?->name['en'] ?? $event?->name ?? __('emails.package_limits.event_fallback')]))
->line(__('emails.ops.addon.addon', ['addon' => $label, 'quantity' => $this->addon->quantity]))
->when($amount, fn ($mail) => $mail->line(__('emails.ops.addon.amount', ['amount' => $amount, 'currency' => $currency])))
->line(__('emails.ops.addon.provider', [
'checkout' => $this->addon->checkout_id ?? '—',
'transaction' => $this->addon->transaction_id ?? '—',
]))
->line(__('emails.ops.addon.footer'));
->subject($subject)
->view('emails.notifications.basic', [
'title' => $subject,
'preheader' => $greeting,
'heroTitle' => $greeting,
'heroSubtitle' => $subject,
'lines' => $lines,
'footer' => __('emails.ops.addon.footer'),
]);
}
}

View File

@@ -25,19 +25,28 @@ class PurchaseCreated extends Notification implements ShouldQueue
$package = $this->purchase->package;
$amount = number_format((float) $this->purchase->price, 2);
$consents = $this->purchase->metadata['consents'] ?? [];
$subject = __('emails.ops.purchase.subject', ['package' => $package?->name ?? __('emails.package_limits.package_fallback')]);
$greeting = __('emails.ops.purchase.greeting');
return (new MailMessage)
->subject(__('emails.ops.purchase.subject', ['package' => $package?->name ?? __('emails.package_limits.package_fallback')]))
->greeting(__('emails.ops.purchase.greeting'))
->line(__('emails.ops.purchase.tenant', ['tenant' => $tenant?->name ?? __('emails.tenant_feedback.unknown_tenant')]))
->line(__('emails.ops.purchase.package', ['package' => $package?->name ?? __('emails.package_limits.package_fallback')]))
->line(__('emails.ops.purchase.amount', ['amount' => $amount, 'currency' => '€']))
->line(__('emails.ops.purchase.provider', ['provider' => $this->purchase->provider, 'id' => $this->purchase->provider_id ?? '—']))
->line(__('emails.ops.purchase.consents', [
'legal' => $consents['legal_version'] ?? 'n/a',
'terms' => $consents['accepted_terms_at'] ?? 'n/a',
'waiver' => $consents['digital_content_waiver_at'] ?? 'n/a',
]))
->line(__('emails.ops.purchase.footer'));
->subject($subject)
->view('emails.notifications.basic', [
'title' => $subject,
'preheader' => $greeting,
'heroTitle' => $greeting,
'heroSubtitle' => $subject,
'lines' => [
__('emails.ops.purchase.tenant', ['tenant' => $tenant?->name ?? __('emails.tenant_feedback.unknown_tenant')]),
__('emails.ops.purchase.package', ['package' => $package?->name ?? __('emails.package_limits.package_fallback')]),
__('emails.ops.purchase.amount', ['amount' => $amount, 'currency' => '']),
__('emails.ops.purchase.provider', ['provider' => $this->purchase->provider, 'id' => $this->purchase->provider_id ?? '—']),
__('emails.ops.purchase.consents', [
'legal' => $consents['legal_version'] ?? 'n/a',
'terms' => $consents['accepted_terms_at'] ?? 'n/a',
'waiver' => $consents['digital_content_waiver_at'] ?? 'n/a',
]),
],
'footer' => __('emails.ops.purchase.footer'),
]);
}
}

View File

@@ -29,24 +29,33 @@ class RefundProcessed extends Notification implements ShouldQueue
$tenant = $this->purchase->tenant;
$package = $this->purchase->package;
$amount = number_format((float) $this->purchase->price, 2);
$mail = (new MailMessage)
->subject(__('emails.ops.refund.subject', ['package' => $package?->name ?? __('emails.package_limits.package_fallback')]))
->greeting(__('emails.ops.refund.greeting'))
->line(__('emails.ops.refund.tenant', ['tenant' => $tenant?->name ?? __('emails.tenant_feedback.unknown_tenant')]))
->line(__('emails.ops.refund.package', ['package' => $package?->name ?? __('emails.package_limits.package_fallback')]))
->line(__('emails.ops.refund.amount', ['amount' => $amount, 'currency' => '']))
->line(__('emails.ops.refund.provider', ['provider' => $this->purchase->provider, 'id' => $this->purchase->provider_id ?? '—']))
->line($this->success ? __('emails.ops.refund.status_success') : __('emails.ops.refund.status_failed'));
$subject = __('emails.ops.refund.subject', ['package' => $package?->name ?? __('emails.package_limits.package_fallback')]);
$greeting = __('emails.ops.refund.greeting');
$lines = [
__('emails.ops.refund.tenant', ['tenant' => $tenant?->name ?? __('emails.tenant_feedback.unknown_tenant')]),
__('emails.ops.refund.package', ['package' => $package?->name ?? __('emails.package_limits.package_fallback')]),
__('emails.ops.refund.amount', ['amount' => $amount, 'currency' => '€']),
__('emails.ops.refund.provider', ['provider' => $this->purchase->provider, 'id' => $this->purchase->provider_id ?? '']),
$this->success ? __('emails.ops.refund.status_success') : __('emails.ops.refund.status_failed'),
];
if ($this->reason) {
$mail->line(__('emails.ops.refund.reason', ['reason' => $this->reason]));
$lines[] = __('emails.ops.refund.reason', ['reason' => $this->reason]);
}
if ($this->error) {
$mail->line(__('emails.ops.refund.error', ['error' => $this->error]));
$lines[] = __('emails.ops.refund.error', ['error' => $this->error]);
}
return $mail->line(__('emails.ops.refund.footer'));
return (new MailMessage)
->subject($subject)
->view('emails.notifications.basic', [
'title' => $subject,
'preheader' => $greeting,
'heroTitle' => $greeting,
'heroSubtitle' => $subject,
'lines' => $lines,
'footer' => __('emails.ops.refund.footer'),
]);
}
}

View File

@@ -27,20 +27,34 @@ class EventPackageGalleryExpiredNotification extends Notification implements Sho
$eventName = $event?->name['de'] ?? $event?->name['en'] ?? $event?->name ?? __('emails.package_limits.event_fallback');
$url = url('/tenant/events/'.($event?->slug ?? ''));
$subject = __('emails.package_limits.gallery_expired.subject', [
'event' => $eventName,
]);
$greeting = __('emails.package_limits.gallery_expired.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]);
return (new MailMessage)
->subject(__('emails.package_limits.gallery_expired.subject', [
'event' => $eventName,
]))
->greeting(__('emails.package_limits.gallery_expired.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]))
->line(__('emails.package_limits.gallery_expired.body', [
'event' => $eventName,
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'date' => optional($this->eventPackage->gallery_expires_at)->toFormattedDateString(),
]))
->action(__('emails.package_limits.gallery_expired.action'), $url)
->line(__('emails.package_limits.footer'));
->subject($subject)
->view('emails.notifications.basic', [
'title' => $subject,
'preheader' => $greeting,
'heroTitle' => $greeting,
'heroSubtitle' => $subject,
'lines' => [
__('emails.package_limits.gallery_expired.body', [
'event' => $eventName,
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'date' => optional($this->eventPackage->gallery_expires_at)->toFormattedDateString(),
]),
],
'cta' => [
[
'label' => __('emails.package_limits.gallery_expired.action'),
'url' => $url,
],
],
'footer' => __('emails.package_limits.footer'),
]);
}
}

View File

@@ -30,22 +30,36 @@ class EventPackageGalleryExpiringNotification extends Notification implements Sh
$eventName = $event?->name['de'] ?? $event?->name['en'] ?? $event?->name ?? __('emails.package_limits.event_fallback');
$url = url('/tenant/events/'.($event?->slug ?? ''));
$subject = trans_choice('emails.package_limits.gallery_warning.subject', $this->daysRemaining, [
'event' => $eventName,
'days' => $this->daysRemaining,
]);
$greeting = __('emails.package_limits.gallery_warning.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]);
return (new MailMessage)
->subject(trans_choice('emails.package_limits.gallery_warning.subject', $this->daysRemaining, [
'event' => $eventName,
'days' => $this->daysRemaining,
]))
->greeting(__('emails.package_limits.gallery_warning.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]))
->line(trans_choice('emails.package_limits.gallery_warning.body', $this->daysRemaining, [
'event' => $eventName,
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'days' => $this->daysRemaining,
'date' => optional($this->eventPackage->gallery_expires_at)->toFormattedDateString(),
]))
->action(__('emails.package_limits.gallery_warning.action'), $url)
->line(__('emails.package_limits.footer'));
->subject($subject)
->view('emails.notifications.basic', [
'title' => $subject,
'preheader' => $greeting,
'heroTitle' => $greeting,
'heroSubtitle' => $subject,
'lines' => [
trans_choice('emails.package_limits.gallery_warning.body', $this->daysRemaining, [
'event' => $eventName,
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'days' => $this->daysRemaining,
'date' => optional($this->eventPackage->gallery_expires_at)->toFormattedDateString(),
]),
],
'cta' => [
[
'label' => __('emails.package_limits.gallery_warning.action'),
'url' => $url,
],
],
'footer' => __('emails.package_limits.footer'),
]);
}
}

View File

@@ -30,22 +30,39 @@ class EventPackageGuestLimitNotification extends Notification implements ShouldQ
$eventName = $event?->name['de'] ?? $event?->name['en'] ?? $event?->name ?? __('emails.package_limits.event_fallback');
$url = url('/tenant/events/'.($event?->slug ?? ''));
$subject = __('emails.package_limits.guest_limit.subject', [
'event' => $eventName,
]);
$greeting = __('emails.package_limits.guest_limit.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]);
return (new MailMessage)
->subject(__('emails.package_limits.guest_limit.subject', [
'event' => $eventName,
]))
->greeting(__('emails.package_limits.guest_limit.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]))
->line(__('emails.package_limits.guest_limit.body', [
'event' => $eventName,
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'limit' => $this->limit,
]))
->line(__('emails.package_limits.guest_limit.cta_addon'))
->action(__('emails.package_limits.guest_limit.addon_action'), $url)
->action(__('emails.package_limits.guest_limit.action'), $url)
->line(__('emails.package_limits.footer'));
->subject($subject)
->view('emails.notifications.basic', [
'title' => $subject,
'preheader' => $greeting,
'heroTitle' => $greeting,
'heroSubtitle' => $subject,
'lines' => [
__('emails.package_limits.guest_limit.body', [
'event' => $eventName,
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'limit' => $this->limit,
]),
__('emails.package_limits.guest_limit.cta_addon'),
],
'cta' => [
[
'label' => __('emails.package_limits.guest_limit.addon_action'),
'url' => $url,
],
[
'label' => __('emails.package_limits.guest_limit.action'),
'url' => $url,
],
],
'footer' => __('emails.package_limits.footer'),
]);
}
}

View File

@@ -34,24 +34,38 @@ class EventPackageGuestThresholdNotification extends Notification implements Sho
$remaining = max(0, $this->limit - $this->used);
$eventName = $event?->name['de'] ?? $event?->name['en'] ?? $event?->name ?? __('emails.package_limits.event_fallback');
$url = url('/tenant/events/'.($event?->slug ?? ''));
$subject = __('emails.package_limits.guest_threshold.subject', [
'event' => $eventName,
'percentage' => $percentage,
]);
$greeting = __('emails.package_limits.guest_threshold.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]);
return (new MailMessage)
->subject(__('emails.package_limits.guest_threshold.subject', [
'event' => $eventName,
'percentage' => $percentage,
]))
->greeting(__('emails.package_limits.guest_threshold.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]))
->line(__('emails.package_limits.guest_threshold.body', [
'event' => $eventName,
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'percentage' => $percentage,
'used' => $this->used,
'limit' => $this->limit,
'remaining' => $remaining,
]))
->action(__('emails.package_limits.guest_threshold.action'), $url)
->line(__('emails.package_limits.footer'));
->subject($subject)
->view('emails.notifications.basic', [
'title' => $subject,
'preheader' => $greeting,
'heroTitle' => $greeting,
'heroSubtitle' => $subject,
'lines' => [
__('emails.package_limits.guest_threshold.body', [
'event' => $eventName,
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'percentage' => $percentage,
'used' => $this->used,
'limit' => $this->limit,
'remaining' => $remaining,
]),
],
'cta' => [
[
'label' => __('emails.package_limits.guest_threshold.action'),
'url' => $url,
],
],
'footer' => __('emails.package_limits.footer'),
]);
}
}

View File

@@ -30,22 +30,39 @@ class EventPackagePhotoLimitNotification extends Notification implements ShouldQ
$eventName = $event?->name['de'] ?? $event?->name['en'] ?? $event?->name ?? __('emails.package_limits.event_fallback');
$url = url('/tenant/events/'.($event?->slug ?? ''));
$subject = __('emails.package_limits.photo_limit.subject', [
'event' => $eventName,
]);
$greeting = __('emails.package_limits.photo_limit.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]);
return (new MailMessage)
->subject(__('emails.package_limits.photo_limit.subject', [
'event' => $eventName,
]))
->greeting(__('emails.package_limits.photo_limit.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]))
->line(__('emails.package_limits.photo_limit.body', [
'event' => $eventName,
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'limit' => $this->limit,
]))
->line(__('emails.package_limits.photo_limit.cta_addon'))
->action(__('emails.package_limits.photo_limit.addon_action'), $url)
->action(__('emails.package_limits.photo_limit.action'), $url)
->line(__('emails.package_limits.footer'));
->subject($subject)
->view('emails.notifications.basic', [
'title' => $subject,
'preheader' => $greeting,
'heroTitle' => $greeting,
'heroSubtitle' => $subject,
'lines' => [
__('emails.package_limits.photo_limit.body', [
'event' => $eventName,
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'limit' => $this->limit,
]),
__('emails.package_limits.photo_limit.cta_addon'),
],
'cta' => [
[
'label' => __('emails.package_limits.photo_limit.addon_action'),
'url' => $url,
],
[
'label' => __('emails.package_limits.photo_limit.action'),
'url' => $url,
],
],
'footer' => __('emails.package_limits.footer'),
]);
}
}

View File

@@ -35,24 +35,38 @@ class EventPackagePhotoThresholdNotification extends Notification implements Sho
$eventName = $event?->name['de'] ?? $event?->name['en'] ?? $event?->name ?? __('emails.package_limits.event_fallback');
$url = url('/tenant/events/'.($event?->slug ?? ''));
$subject = __('emails.package_limits.photo_threshold.subject', [
'event' => $eventName,
'percentage' => $percentage,
]);
$greeting = __('emails.package_limits.photo_threshold.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]);
return (new MailMessage)
->subject(__('emails.package_limits.photo_threshold.subject', [
'event' => $eventName,
'percentage' => $percentage,
]))
->greeting(__('emails.package_limits.photo_threshold.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]))
->line(__('emails.package_limits.photo_threshold.body', [
'event' => $eventName,
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'percentage' => $percentage,
'used' => $this->used,
'limit' => $this->limit,
'remaining' => $remaining,
]))
->action(__('emails.package_limits.photo_threshold.action'), $url)
->line(__('emails.package_limits.footer'));
->subject($subject)
->view('emails.notifications.basic', [
'title' => $subject,
'preheader' => $greeting,
'heroTitle' => $greeting,
'heroSubtitle' => $subject,
'lines' => [
__('emails.package_limits.photo_threshold.body', [
'event' => $eventName,
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'percentage' => $percentage,
'used' => $this->used,
'limit' => $this->limit,
'remaining' => $remaining,
]),
],
'cta' => [
[
'label' => __('emails.package_limits.photo_threshold.action'),
'url' => $url,
],
],
'footer' => __('emails.package_limits.footer'),
]);
}
}

View File

@@ -28,19 +28,33 @@ class TenantPackageEventLimitNotification extends Notification implements Should
$package = $this->tenantPackage->package;
$url = url('/tenant/billing');
$subject = __('emails.package_limits.event_limit.subject', [
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
]);
$greeting = __('emails.package_limits.event_limit.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]);
return (new MailMessage)
->subject(__('emails.package_limits.event_limit.subject', [
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
]))
->greeting(__('emails.package_limits.event_limit.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]))
->line(__('emails.package_limits.event_limit.body', [
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'limit' => $this->limit,
]))
->action(__('emails.package_limits.event_limit.action'), $url)
->line(__('emails.package_limits.footer'));
->subject($subject)
->view('emails.notifications.basic', [
'title' => $subject,
'preheader' => $greeting,
'heroTitle' => $greeting,
'heroSubtitle' => $subject,
'lines' => [
__('emails.package_limits.event_limit.body', [
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'limit' => $this->limit,
]),
],
'cta' => [
[
'label' => __('emails.package_limits.event_limit.action'),
'url' => $url,
],
],
'footer' => __('emails.package_limits.footer'),
]);
}
}

View File

@@ -33,23 +33,37 @@ class TenantPackageEventThresholdNotification extends Notification implements Sh
$remaining = max(0, $this->limit - $this->used);
$url = url('/tenant/billing');
$subject = __('emails.package_limits.event_threshold.subject', [
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'percentage' => $percentage,
]);
$greeting = __('emails.package_limits.event_threshold.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]);
return (new MailMessage)
->subject(__('emails.package_limits.event_threshold.subject', [
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'percentage' => $percentage,
]))
->greeting(__('emails.package_limits.event_threshold.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]))
->line(__('emails.package_limits.event_threshold.body', [
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'percentage' => $percentage,
'used' => $this->used,
'limit' => $this->limit,
'remaining' => $remaining,
]))
->action(__('emails.package_limits.event_threshold.action'), $url)
->line(__('emails.package_limits.footer'));
->subject($subject)
->view('emails.notifications.basic', [
'title' => $subject,
'preheader' => $greeting,
'heroTitle' => $greeting,
'heroSubtitle' => $subject,
'lines' => [
__('emails.package_limits.event_threshold.body', [
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'percentage' => $percentage,
'used' => $this->used,
'limit' => $this->limit,
'remaining' => $remaining,
]),
],
'cta' => [
[
'label' => __('emails.package_limits.event_threshold.action'),
'url' => $url,
],
],
'footer' => __('emails.package_limits.footer'),
]);
}
}

View File

@@ -25,19 +25,33 @@ class TenantPackageExpiredNotification extends Notification implements ShouldQue
$package = $this->tenantPackage->package;
$url = url('/tenant/billing');
$subject = __('emails.package_limits.package_expired.subject', [
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
]);
$greeting = __('emails.package_limits.package_expired.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]);
return (new MailMessage)
->subject(__('emails.package_limits.package_expired.subject', [
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
]))
->greeting(__('emails.package_limits.package_expired.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]))
->line(__('emails.package_limits.package_expired.body', [
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'date' => optional($this->tenantPackage->expires_at)->toFormattedDateString(),
]))
->action(__('emails.package_limits.package_expired.action'), $url)
->line(__('emails.package_limits.footer'));
->subject($subject)
->view('emails.notifications.basic', [
'title' => $subject,
'preheader' => $greeting,
'heroTitle' => $greeting,
'heroSubtitle' => $subject,
'lines' => [
__('emails.package_limits.package_expired.body', [
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'date' => optional($this->tenantPackage->expires_at)->toFormattedDateString(),
]),
],
'cta' => [
[
'label' => __('emails.package_limits.package_expired.action'),
'url' => $url,
],
],
'footer' => __('emails.package_limits.footer'),
]);
}
}

View File

@@ -28,21 +28,35 @@ class TenantPackageExpiringNotification extends Notification implements ShouldQu
$package = $this->tenantPackage->package;
$url = url('/tenant/billing');
$subject = trans_choice('emails.package_limits.package_expiring.subject', $this->daysRemaining, [
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'days' => $this->daysRemaining,
]);
$greeting = __('emails.package_limits.package_expiring.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]);
return (new MailMessage)
->subject(trans_choice('emails.package_limits.package_expiring.subject', $this->daysRemaining, [
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'days' => $this->daysRemaining,
]))
->greeting(__('emails.package_limits.package_expiring.greeting', [
'name' => $tenant?->name ?? __('emails.package_limits.team_fallback'),
]))
->line(trans_choice('emails.package_limits.package_expiring.body', $this->daysRemaining, [
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'days' => $this->daysRemaining,
'date' => optional($this->tenantPackage->expires_at)->toFormattedDateString(),
]))
->action(__('emails.package_limits.package_expiring.action'), $url)
->line(__('emails.package_limits.footer'));
->subject($subject)
->view('emails.notifications.basic', [
'title' => $subject,
'preheader' => $greeting,
'heroTitle' => $greeting,
'heroSubtitle' => $subject,
'lines' => [
trans_choice('emails.package_limits.package_expiring.body', $this->daysRemaining, [
'package' => $package?->getNameForLocale() ?? $package?->name ?? __('emails.package_limits.package_fallback'),
'days' => $this->daysRemaining,
'date' => optional($this->tenantPackage->expires_at)->toFormattedDateString(),
]),
],
'cta' => [
[
'label' => __('emails.package_limits.package_expiring.action'),
'url' => $url,
],
],
'footer' => __('emails.package_limits.footer'),
]);
}
}

View File

@@ -35,38 +35,45 @@ class TenantFeedbackSubmitted extends Notification implements ShouldQueue
'tenant' => $tenantName,
'sentiment' => $sentiment,
]);
$mail = (new MailMessage())
->subject($subject)
->line(__('emails.tenant_feedback.tenant', ['tenant' => $tenantName]))
->line(__('emails.tenant_feedback.category', ['category' => $this->feedback->category ? Str::headline($this->feedback->category) : '—']))
->line(__('emails.tenant_feedback.sentiment', ['sentiment' => $sentiment]));
$lines = [
__('emails.tenant_feedback.tenant', ['tenant' => $tenantName]),
__('emails.tenant_feedback.category', ['category' => $this->feedback->category ? Str::headline($this->feedback->category) : '—']),
__('emails.tenant_feedback.sentiment', ['sentiment' => $sentiment]),
];
if ($eventName) {
$mail->line(__('emails.tenant_feedback.event', ['event' => $eventName]));
$lines[] = __('emails.tenant_feedback.event', ['event' => $eventName]);
}
if ($rating) {
$mail->line(__('emails.tenant_feedback.rating', ['rating' => $rating]));
$lines[] = __('emails.tenant_feedback.rating', ['rating' => $rating]);
}
if ($this->feedback->title) {
$mail->line(__('emails.tenant_feedback.title', ['subject' => $this->feedback->title]));
$lines[] = __('emails.tenant_feedback.title', ['subject' => $this->feedback->title]);
}
if ($this->feedback->message) {
$mail->line(__('emails.tenant_feedback.message'))->line($this->feedback->message);
$lines[] = __('emails.tenant_feedback.message');
$lines[] = $this->feedback->message;
}
$url = TenantFeedbackResource::getUrl('view', ['record' => $this->feedback], panel: 'superadmin');
if ($url) {
$mail->action(__('emails.tenant_feedback.open'), $url);
}
$lines[] = __('emails.tenant_feedback.received_at', ['date' => $this->feedback->created_at?->toDayDateTimeString()]);
$mail->line(__('emails.tenant_feedback.received_at', ['date' => $this->feedback->created_at?->toDayDateTimeString()]));
return $mail;
return (new MailMessage)
->subject($subject)
->view('emails.notifications.basic', [
'title' => $subject,
'preheader' => $subject,
'heroTitle' => $subject,
'lines' => $lines,
'cta' => $url ? [[
'label' => __('emails.tenant_feedback.open'),
'url' => $url,
]] : [],
]);
}
protected function resolveName(mixed $name): ?string

View File

@@ -11,9 +11,7 @@ class UploadPipelineFailed extends Notification implements ShouldQueue
{
use Queueable;
public function __construct(private readonly array $context)
{
}
public function __construct(private readonly array $context) {}
public function via(object $notifiable): array
{
@@ -29,15 +27,29 @@ class UploadPipelineFailed extends Notification implements ShouldQueue
public function toMail(object $notifiable): MailMessage
{
$context = $this->context;
$job = $context['job'] ?? 'n/a';
$queue = $context['queue'] ?? 'n/a';
$eventId = $context['event_id'] ?? 'n/a';
$photoId = $context['photo_id'] ?? 'n/a';
$exception = $context['exception'] ?? 'n/a';
$timestamp = now()->toDateTimeString();
return (new MailMessage)
->subject('Upload-Pipeline Fehler: '.($context['job'] ?? 'Unbekannter Job'))
->line('In der Upload-Pipeline ist ein Fehler aufgetreten.')
->line('Job: '.($context['job'] ?? 'n/a'))
->line('Queue: '.($context['queue'] ?? 'n/a'))
->line('Event ID: '.($context['event_id'] ?? 'n/a'))
->line('Foto ID: '.($context['photo_id'] ?? 'n/a'))
->line('Exception: '.($context['exception'] ?? 'n/a'))
->line('Zeitpunkt: '.now()->toDateTimeString());
->subject(__('emails.upload_pipeline_failed.subject', ['job' => $job]))
->view('emails.notifications.basic', [
'title' => __('emails.upload_pipeline_failed.subject', ['job' => $job]),
'preheader' => __('emails.upload_pipeline_failed.preheader'),
'heroTitle' => __('emails.upload_pipeline_failed.hero_title'),
'heroSubtitle' => __('emails.upload_pipeline_failed.hero_subtitle'),
'lines' => [
__('emails.upload_pipeline_failed.line_job', ['job' => $job]),
__('emails.upload_pipeline_failed.line_queue', ['queue' => $queue]),
__('emails.upload_pipeline_failed.line_event', ['event' => $eventId]),
__('emails.upload_pipeline_failed.line_photo', ['photo' => $photoId]),
__('emails.upload_pipeline_failed.line_exception', ['exception' => $exception]),
__('emails.upload_pipeline_failed.line_time', ['time' => $timestamp]),
],
'footer' => __('emails.upload_pipeline_failed.footer'),
]);
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Notifications;
use Illuminate\Auth\Notifications\VerifyEmail;
use Illuminate\Notifications\Messages\MailMessage;
class VerifyEmailNotification extends VerifyEmail
{
public function toMail($notifiable): MailMessage
{
$verificationUrl = $this->verificationUrl($notifiable);
return (new MailMessage)
->subject(__('emails.verification.subject'))
->view('emails.verify-email', [
'user' => $notifiable,
'verificationUrl' => $verificationUrl,
'expiresIn' => (int) config('auth.verification.expire', 60),
]);
}
}