8.9 KiB
8.9 KiB
Story 8.3: Booking Submitted Confirmation
Epic Reference
Epic 8: Email Notification System
Dependencies
- Story 8.1: Email Infrastructure Setup (base templates, SMTP config, queue setup)
- Provides: Base Mailable layout with Libra branding (navy #0A1F44 / gold #D4AF37), logo header, footer with firm contact info, mobile-responsive design, and queue configuration
User Story
As a client, I want to receive confirmation when I submit a booking request, So that I know my request was received and what to expect next.
Acceptance Criteria
Trigger
- Sent immediately after successful consultation creation
- Consultation status: pending
- Email queued for async delivery
Content
- Subject line: "Your consultation request has been submitted" / "تم استلام طلب الاستشارة"
- Personalized greeting with client name
- "Your consultation request has been submitted" message
- Requested date and time (formatted per user's language preference)
- Problem summary preview (first 200 characters, with "..." if truncated)
- "Pending Review" status note with visual indicator
- Expected response timeframe: "We will review your request and respond within 1-2 business days"
- Contact information for questions
Language
- Email sent in client's
preferred_language(default: 'ar') - Arabic template for Arabic users
- English template for English users
Design
- Uses base email template from Story 8.1
- No action required message (informational only)
- Professional template with Libra branding
- Gold call-to-action style for "View My Bookings" link (optional)
Technical Notes
Files to Create
app/Mail/BookingSubmittedEmail.php
resources/views/emails/booking/submitted/ar.blade.php
resources/views/emails/booking/submitted/en.blade.php
Mailable Implementation
<?php
namespace App\Mail;
use App\Models\Consultation;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class BookingSubmittedEmail extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
public function __construct(
public Consultation $consultation
) {}
public function envelope(): Envelope
{
$locale = $this->consultation->user->preferred_language ?? 'ar';
return new Envelope(
subject: $locale === 'ar'
? 'تم استلام طلب الاستشارة'
: 'Your Consultation Request Has Been Submitted',
);
}
public function content(): Content
{
$locale = $this->consultation->user->preferred_language ?? 'ar';
return new Content(
markdown: "emails.booking.submitted.{$locale}",
with: [
'consultation' => $this->consultation,
'user' => $this->consultation->user,
'summaryPreview' => $this->getSummaryPreview(),
'formattedDate' => $this->getFormattedDate($locale),
'formattedTime' => $this->getFormattedTime($locale),
],
);
}
private function getSummaryPreview(): string
{
$summary = $this->consultation->problem_summary ?? '';
return strlen($summary) > 200
? substr($summary, 0, 200) . '...'
: $summary;
}
private function getFormattedDate(string $locale): string
{
$date = $this->consultation->booking_date;
return $locale === 'ar'
? $date->format('d/m/Y')
: $date->format('m/d/Y');
}
private function getFormattedTime(string $locale): string
{
return $this->consultation->booking_time->format('h:i A');
}
}
Dispatch Point
Send the email after successful consultation creation. Typical location:
// In app/Actions/Consultation/CreateConsultationAction.php
// OR in the controller handling booking submission
use App\Mail\BookingSubmittedEmail;
use Illuminate\Support\Facades\Mail;
// After consultation is created successfully:
Mail::to($consultation->user->email)
->send(new BookingSubmittedEmail($consultation));
Edge Cases
- If
preferred_languageis null, default to 'ar' (Arabic) - If
problem_summaryis null or empty, show "No summary provided" - Ensure consultation has valid
booking_dateandbooking_timebefore sending
Testing Requirements
Unit Tests
test('booking submitted email has correct subject in Arabic', function () {
$user = User::factory()->create(['preferred_language' => 'ar']);
$consultation = Consultation::factory()->create(['user_id' => $user->id]);
$mailable = new BookingSubmittedEmail($consultation);
expect($mailable->envelope()->subject)
->toBe('تم استلام طلب الاستشارة');
});
test('booking submitted email has correct subject in English', function () {
$user = User::factory()->create(['preferred_language' => 'en']);
$consultation = Consultation::factory()->create(['user_id' => $user->id]);
$mailable = new BookingSubmittedEmail($consultation);
expect($mailable->envelope()->subject)
->toBe('Your Consultation Request Has Been Submitted');
});
test('problem summary is truncated at 200 characters', function () {
$longSummary = str_repeat('a', 250);
$user = User::factory()->create();
$consultation = Consultation::factory()->create([
'user_id' => $user->id,
'problem_summary' => $longSummary,
]);
$mailable = new BookingSubmittedEmail($consultation);
$content = $mailable->content();
expect($content->with['summaryPreview'])
->toHaveLength(203) // 200 + '...'
->toEndWith('...');
});
test('date is formatted as d/m/Y for Arabic users', function () {
$user = User::factory()->create(['preferred_language' => 'ar']);
$consultation = Consultation::factory()->create([
'user_id' => $user->id,
'booking_date' => '2025-03-15',
]);
$mailable = new BookingSubmittedEmail($consultation);
$content = $mailable->content();
expect($content->with['formattedDate'])->toBe('15/03/2025');
});
test('date is formatted as m/d/Y for English users', function () {
$user = User::factory()->create(['preferred_language' => 'en']);
$consultation = Consultation::factory()->create([
'user_id' => $user->id,
'booking_date' => '2025-03-15',
]);
$mailable = new BookingSubmittedEmail($consultation);
$content = $mailable->content();
expect($content->with['formattedDate'])->toBe('03/15/2025');
});
test('defaults to Arabic when preferred_language is null', function () {
$user = User::factory()->create(['preferred_language' => null]);
$consultation = Consultation::factory()->create(['user_id' => $user->id]);
$mailable = new BookingSubmittedEmail($consultation);
expect($mailable->envelope()->subject)
->toBe('تم استلام طلب الاستشارة');
});
test('empty problem summary returns empty string', function () {
$user = User::factory()->create();
$consultation = Consultation::factory()->create([
'user_id' => $user->id,
'problem_summary' => '',
]);
$mailable = new BookingSubmittedEmail($consultation);
$content = $mailable->content();
expect($content->with['summaryPreview'])->toBe('');
});
Feature Tests
test('email is sent when consultation is created', function () {
Mail::fake();
$user = User::factory()->create();
// Trigger consultation creation...
Mail::assertSent(BookingSubmittedEmail::class, function ($mail) use ($user) {
return $mail->hasTo($user->email);
});
});
test('email is queued for async delivery', function () {
Mail::fake();
$user = User::factory()->create();
$consultation = Consultation::factory()->create(['user_id' => $user->id]);
Mail::to($user->email)->send(new BookingSubmittedEmail($consultation));
Mail::assertQueued(BookingSubmittedEmail::class);
});
References
- PRD Section 8.2: Email Templates - "Booking Confirmation - Request submitted successfully"
- PRD Section 5.4: Booking Flow - Step 2 "Request Status: Pending"
- Story 8.1: Base email template structure and branding
- Story 8.2: Welcome email pattern (similar Mailable structure)
Definition of Done
BookingSubmittedEmailMailable class created- Arabic template created and renders correctly
- English template created and renders correctly
- Email dispatched on consultation creation
- Email queued (implements ShouldQueue)
- Date/time formatted per user language
- Summary preview truncated at 200 chars
- Pending status clearly communicated
- Response timeframe included
- Unit tests pass
- Feature tests pass
Estimation
Complexity: Low | Effort: 2 hours