8.9 KiB
8.9 KiB
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:
TimelineUpdatemodel with fields:timeline_id,admin_id,update_textTimelineUpdatebelongs toTimelineandUser(admin)Timelinehas manyTimelineUpdaterecords- 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_languagefield (string, defaults to 'ar') - from Epic 2 user registrationisActive()method returning boolean (checksstatus !== 'deactivated') - from Epic 2
- Route requirement:
client.timelines.showroute must exist (from Story 4.5)
- Models available:
Timelinemodel withuser()relationshipTimelineUpdatemodel withtimeline()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
namespace App\Notifications;
use App\Models\TimelineUpdate;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;
class TimelineUpdateNotification extends Notification
{
use Queueable;
public function __construct(
public TimelineUpdate $update
) {}
public function via(object $notifiable): array
{
return ['mail'];
}
public function toMail(object $notifiable): MailMessage
{
$locale = $notifiable->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
<x-mail::message>
# تحديث جديد على قضيتك
عزيزي {{ $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 !!}
---
<x-mail::button :url="route('client.timelines.show', $timeline)">
عرض التفاصيل الكاملة
</x-mail::button>
مع أطيب التحيات،
مكتب ليبرا للمحاماة
</x-mail::message>
English
<x-mail::message>
# 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 !!}
---
<x-mail::button :url="route('client.timelines.show', $timeline)">
View Full Details
</x-mail::button>
Best regards,
Libra Law Firm
</x-mail::message>
Trigger in Update Creation
// 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_languagefor template selection (defaults to 'ar') - Email is queued (uses
ShouldQueueinterface) - Email contains valid link to timeline view
- Subject line includes case name in correct language
Testing Examples
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.showroute used in email link) - Epic 2: User model with
preferred_languagefield andisActive()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