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
@@ -0,0 +1,36 @@
@component('emails.layout', [
'companyName' => $companyName,
'themeColor' => $themeColor,
'phone' => $phone ?? null,
'email' => $email ?? null,
'city' => $city ?? null,
'logoUrl' => $logoUrl ?? null,
'title' => 'Plată confirmată',
])
<h2 style="margin:0 0 12px;font-size:20px;color:{{ $themeColor }};">💳 Plată confirmată</h2>
<p>Bună ziua{{ $client?->name ? ', ' . $client->name : '' }},</p>
<p>Confirmăm primirea plății dvs.:</p>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:16px 0;background:#f9fafb;border-radius:8px;">
<tr><td style="padding:14px 18px;font-size:13px;color:#374151;">Sumă achitată</td>
<td style="padding:14px 18px;font-size:18px;text-align:right;font-weight:700;color:#059669;">
{{ number_format((float) $payment->amount, 2, '.', ' ') }} {{ $currency }}
</td></tr>
<tr><td style="padding:8px 18px;font-size:12px;color:#6b7280;">Metoda</td>
<td style="padding:8px 18px;font-size:12px;text-align:right;">
{{ \App\Models\Tenant\Payment::METHODS[$payment->method] ?? $payment->method }}
</td></tr>
<tr><td style="padding:8px 18px;font-size:12px;color:#6b7280;">Data</td>
<td style="padding:8px 18px;font-size:12px;text-align:right;">{{ $payment->paid_at?->format('d.m.Y') }}</td></tr>
@if ($payment->reference)
<tr><td style="padding:8px 18px;font-size:12px;color:#6b7280;">Referință</td>
<td style="padding:8px 18px;font-size:12px;text-align:right;font-family:monospace;">{{ $payment->reference }}</td></tr>
@endif
@if ($workOrder)
<tr><td style="padding:8px 18px;font-size:12px;color:#6b7280;">Fișă lucru</td>
<td style="padding:8px 18px;font-size:12px;text-align:right;font-weight:600;">{{ $workOrder->number }}</td></tr>
@endif
</table>
<p> mulțumim!</p>
@endcomponent