# Story 4.6: Timeline Update Notifications ## Epic Reference **Epic 4:** Case Timeline System ## User Story As a **client**, I want **to receive email notifications when my timeline is updated**, So that **I stay informed about my case progress without checking the portal**. ## Story Context ### Existing System Integration - **Integrates with:** timeline_updates creation, email system - **Technology:** Laravel Notifications, queued emails - **Follows pattern:** Event-driven notification pattern - **Touch points:** Timeline update creation ### Previous Story Context (Story 4.2) Story 4.2 established: - `TimelineUpdate` model with fields: `timeline_id`, `admin_id`, `update_text` - `TimelineUpdate` belongs to `Timeline` and `User` (admin) - `Timeline` has many `TimelineUpdate` records - Updates created via `addUpdate()` method in timeline management Volt component - Notification trigger point: after `$this->timeline->updates()->create([...])` ### Prerequisites The following must exist before implementing this story: - **User model requirements:** - `preferred_language` field (string, defaults to 'ar') - from Epic 2 user registration - `isActive()` method returning boolean (checks `status !== 'deactivated'`) - from Epic 2 - **Route requirement:** - `client.timelines.show` route must exist (from Story 4.5) - **Models available:** - `Timeline` model with `user()` relationship - `TimelineUpdate` model with `timeline()` relationship ### Implementation Files - **Notification:** `app/Notifications/TimelineUpdateNotification.php` - **Arabic Template:** `resources/views/emails/timeline/update/ar.blade.php` - **English Template:** `resources/views/emails/timeline/update/en.blade.php` - **Modify:** Story 4.2 Volt component to trigger notification ## Acceptance Criteria ### Notification Trigger - [ ] Email sent when new update added to timeline - [ ] Triggered automatically on TimelineUpdate creation - [ ] Queued for performance ### Email Content - [ ] Case name and reference - [ ] Update summary or full text - [ ] Date of update - [ ] Link to view timeline - [ ] Libra branding ### Language Support - [ ] Email in client's preferred language - [ ] Arabic template - [ ] English template ### Exclusions - [ ] No email for archived timeline reactivation - [ ] No email if client deactivated ### Quality Requirements - [ ] Professional email template - [ ] Tests for notification sending - [ ] Error handling for failed sends ## Technical Notes ### Notification Class ```php preferred_language ?? 'ar'; $timeline = $this->update->timeline; return (new MailMessage) ->subject($this->getSubject($locale, $timeline->case_name)) ->markdown('emails.timeline.update.' . $locale, [ 'update' => $this->update, 'timeline' => $timeline, 'user' => $notifiable, ]); } private function getSubject(string $locale, string $caseName): string { return $locale === 'ar' ? "تحديث جديد على قضيتك: {$caseName}" : "New update on your case: {$caseName}"; } } ``` ### Email Templates #### Arabic ```blade # تحديث جديد على قضيتك عزيزي {{ $user->name }}، تم إضافة تحديث جديد على قضيتك: **القضية:** {{ $timeline->case_name }} @if($timeline->case_reference) **الرقم المرجعي:** {{ $timeline->case_reference }} @endif **تاريخ التحديث:** {{ $update->created_at->translatedFormat('d M Y - g:i A') }} --- {!! $update->update_text !!} --- عرض التفاصيل الكاملة مع أطيب التحيات، مكتب ليبرا للمحاماة ``` #### English ```blade # New Update on Your Case Dear {{ $user->name }}, A new update has been added to your case: **Case:** {{ $timeline->case_name }} @if($timeline->case_reference) **Reference:** {{ $timeline->case_reference }} @endif **Update Date:** {{ $update->created_at->format('M d, Y - g:i A') }} --- {!! $update->update_text !!} --- View Full Details Best regards, Libra Law Firm ``` ### Trigger in Update Creation ```php // In Story 4.2 addUpdate method $update = $this->timeline->updates()->create([...]); // Check if user is active before notifying if ($this->timeline->user->isActive()) { $this->timeline->user->notify(new TimelineUpdateNotification($update)); } ``` ### Test Scenarios - [ ] Notification sent when timeline update created - [ ] No notification to deactivated user (`status === 'deactivated'`) - [ ] Arabic template renders with correct content (case name, update text, date) - [ ] English template renders with correct content - [ ] Uses client's `preferred_language` for template selection (defaults to 'ar') - [ ] Email is queued (uses `ShouldQueue` interface) - [ ] Email contains valid link to timeline view - [ ] Subject line includes case name in correct language ### Testing Examples ```php use App\Models\{User, Timeline, TimelineUpdate}; use App\Notifications\TimelineUpdateNotification; use Illuminate\Support\Facades\Notification; it('sends notification when timeline update created', function () { Notification::fake(); $user = User::factory()->create(['status' => 'active']); $timeline = Timeline::factory()->for($user)->create(); $update = TimelineUpdate::factory()->create(['timeline_id' => $timeline->id]); // Simulate the trigger from Story 4.2 if ($timeline->user->isActive()) { $timeline->user->notify(new TimelineUpdateNotification($update)); } Notification::assertSentTo($timeline->user, TimelineUpdateNotification::class); }); it('does not send notification to deactivated user', function () { Notification::fake(); $user = User::factory()->create(['status' => 'deactivated']); $timeline = Timeline::factory()->for($user)->create(); $update = TimelineUpdate::factory()->create(['timeline_id' => $timeline->id]); // Simulate the trigger from Story 4.2 - should NOT notify if ($timeline->user->isActive()) { $timeline->user->notify(new TimelineUpdateNotification($update)); } Notification::assertNotSentTo($user, TimelineUpdateNotification::class); }); it('uses arabic template for arabic-preferred user', function () { Notification::fake(); $user = User::factory()->create(['preferred_language' => 'ar']); $timeline = Timeline::factory()->for($user)->create(['case_name' => 'Test Case']); $update = TimelineUpdate::factory()->create(['timeline_id' => $timeline->id]); $user->notify(new TimelineUpdateNotification($update)); Notification::assertSentTo($user, TimelineUpdateNotification::class, function ($notification, $channels, $notifiable) { $mail = $notification->toMail($notifiable); return str_contains($mail->subject, 'تحديث جديد على قضيتك'); }); }); it('uses english template for english-preferred user', function () { Notification::fake(); $user = User::factory()->create(['preferred_language' => 'en']); $timeline = Timeline::factory()->for($user)->create(['case_name' => 'Test Case']); $update = TimelineUpdate::factory()->create(['timeline_id' => $timeline->id]); $user->notify(new TimelineUpdateNotification($update)); Notification::assertSentTo($user, TimelineUpdateNotification::class, function ($notification, $channels, $notifiable) { $mail = $notification->toMail($notifiable); return str_contains($mail->subject, 'New update on your case'); }); }); ``` ## Definition of Done - [ ] Email sent on new update - [ ] Arabic template works - [ ] English template works - [ ] Uses client's preferred language - [ ] Link to timeline works - [ ] Queued for performance - [ ] No email to deactivated users - [ ] Tests pass - [ ] Code formatted with Pint ## Dependencies - **Story 4.2:** Timeline updates management (REQUIRED - notification triggered from addUpdate method) - **Story 4.5:** Client timeline view (REQUIRED - provides `client.timelines.show` route used in email link) - **Epic 2:** User model with `preferred_language` field and `isActive()` method - **Epic 8:** Email infrastructure (SOFT DEPENDENCY - notification will queue but email delivery requires Epic 8) ## Estimation **Complexity:** Low-Medium **Estimated Effort:** 2-3 hours