Faza 2 (din continuare): Email notifications

4 Mailables auto-trigger pe model events:
- WorkOrderReadyMail: la WO.status → 'ready', către client.email
  • Atașat PDF fișa lucru (via WorkOrderPdfService)
  • Total/achitat/rest, recomandări (warning box)
- PaymentReceivedMail: la Payment::created, confirmare cu sumă/metodă/ref
- AppointmentConfirmedMail: la Appointment::created status='scheduled'
- ServiceReminderMail: dispatch manual (vehicle, type=itp/oil/general, note)

Layout email branded (resources/views/emails/layout.blade.php):
- Header cu logo tenant + theme_color border-bottom
- Footer cu telefon/email/disclaimer
- Stiluri inline (compatibil tot mail client)

Settings page extins cu 4 toggle:
- 'Mașina e gata de ridicat'
- 'Confirmare plată primită'
- 'Programare confirmată'
- 'Reminder ITP / revizie'
Salvate în companies.settings.notify (JSON), default true.

NotificationDispatcher service centralizat:
- Verifică isEnabled() pe settings.notify[$key]
- Skip dacă client n-are email
- Try/catch + Log::warning pe eșec (nu crapă request-ul)

Mailables folosesc UsesTenantBranding trait pentru context unitar.
Test prin Mailpit: https://mailpit.service.mir.md (capturează toate).
This commit is contained in:
2026-05-07 13:20:19 +00:00
parent bfe58ed286
commit 09fd0bada2
15 changed files with 608 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="ro">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>{{ $title ?? 'AutoCRM' }}</title>
</head>
<body style="margin:0;padding:0;background:#f3f4f6;font-family:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;color:#1f2937;">
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f3f4f6;padding:24px 0;">
<tr><td align="center">
<table role="presentation" width="600" cellpadding="0" cellspacing="0" border="0" style="background:#fff;border-radius:10px;box-shadow:0 1px 3px rgba(0,0,0,.06);overflow:hidden;max-width:600px;width:100%;">
<tr><td style="padding:24px 28px;border-bottom:3px solid {{ $themeColor ?? '#3b82f6' }};">
@if (!empty($logoUrl))
<img src="{{ $logoUrl }}" alt="logo" style="max-height:42px;display:block;">
@else
<div style="font-size:20px;font-weight:700;color:{{ $themeColor ?? '#3b82f6' }};">{{ $companyName ?? 'AutoCRM' }}</div>
@endif
@if (!empty($city))
<div style="font-size:12px;color:#6b7280;margin-top:4px;">{{ $city }}</div>
@endif
</td></tr>
<tr><td style="padding:24px 28px;font-size:14px;line-height:1.6;">
{{ $slot }}
</td></tr>
<tr><td style="padding:16px 28px;border-top:1px solid #e5e7eb;background:#f9fafb;font-size:11px;color:#6b7280;">
<div>{{ $companyName ?? 'AutoCRM' }}</div>
@if (!empty($phone)) <div>📞 {{ $phone }}</div> @endif
@if (!empty($email)) <div> {{ $email }}</div> @endif
<div style="margin-top:8px;color:#9ca3af;">Acest email a fost generat automat. rugăm nu răspundeți.</div>
</td></tr>
</table>
</td></tr>
</table>
</body>
</html>