15 KiB
Story 8.3: Booking Submitted Confirmation
Note: The color values in this story were implemented with the original Navy+Gold palette. These colors were updated in Epic 10 (Brand Color Refresh) to the new Charcoal+Warm Gray palette. See
docs/brand.mdfor current color specifications.
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
Dev Agent Record
Status
Ready for Review
Agent Model Used
Claude Opus 4.5 (claude-opus-4-5-20251101)
File List
| File | Action | Description |
|---|---|---|
app/Mail/BookingSubmittedMail.php |
Modified | Updated to implement ShouldQueue, add summary preview truncation, locale-based date/time formatting, and use separate language templates |
resources/views/emails/booking/submitted/ar.blade.php |
Created | Arabic email template with RTL support, pending status panel, and response timeframe |
resources/views/emails/booking/submitted/en.blade.php |
Created | English email template with pending status panel and response timeframe |
tests/Feature/Mail/BookingSubmittedMailTest.php |
Created | 16 tests covering subject lines, summary truncation, date formatting, template selection, and queue behavior |
Change Log
- Updated
BookingSubmittedMailto implementShouldQueueinterface for async delivery - Added
getSummaryPreview()method to truncate problem summary at 200 characters - Added
getFormattedDate()method for locale-based date formatting (d/m/Y for Arabic, m/d/Y for English) - Added
getFormattedTime()method for 12-hour time format - Changed from single view to separate markdown templates per language
- Created Arabic template with proper RTL direction and Arabic content
- Created English template with LTR content
- Both templates include pending status panel and 1-2 business days response timeframe
Completion Notes
- Used existing
BookingSubmittedMail.phpclass name (notBookingSubmittedEmail.phpas specified in story) to maintain compatibility with existing codebase and tests - Email dispatch already exists in
resources/views/livewire/client/consultations/book.blade.php:142-144 - Database constraints prevent null
preferred_languageandproblem_summary, so edge case tests were adjusted accordingly - All 16 BookingSubmittedMail tests pass
- All 21 BookingSubmission feature tests pass
Debug Log References
None - implementation completed without issues
QA Results
Review Date: 2026-01-02
Reviewed By: Quinn (Test Architect)
Code Quality Assessment
Overall: Excellent - The implementation is clean, well-structured, and follows Laravel best practices. The BookingSubmittedMail class properly implements the ShouldQueue interface for async delivery, uses Laravel's Mailable pattern correctly, and provides clear separation between Arabic and English templates.
Strengths:
- Clean class structure with public methods for helper functions (
getSummaryPreview,getFormattedDate,getFormattedTime) - Proper use of Carbon for time parsing
- Locale-aware date formatting (d/m/Y for Arabic, m/d/Y for English)
- Summary truncation with proper boundary handling (200 chars + "...")
- Email templates use the base email layout from Story 8.1 with Libra branding (navy header, gold accents, bilingual footer)
- RTL support properly implemented in Arabic template
Minor Observations:
- The
localeproperty is set in constructor but not used; envelope and content methods recalculate locale each time (minor redundancy, not a defect)
Refactoring Performed
None required - code quality is production-ready.
Compliance Check
- Coding Standards: ✓ Follows Laravel Mailable conventions, proper namespacing
- Project Structure: ✓ Files in correct locations (
app/Mail/,resources/views/emails/booking/submitted/) - Testing Strategy: ✓ 16 unit tests covering all acceptance criteria
- All ACs Met: ✓ All 10 acceptance criteria verified
Requirements Traceability
| AC | Requirement | Test Coverage |
|---|---|---|
| 1 | Sent immediately after consultation creation | BookingSubmissionTest::emails are sent to client and admin after submission |
| 2 | Consultation status: pending | BookingSubmissionTest::booking is created with pending status |
| 3 | Email queued for async delivery | BookingSubmittedMailTest::booking submitted email implements ShouldQueue, email is queued when sent |
| 4 | Correct Arabic subject line | BookingSubmittedMailTest::booking submitted email has correct subject in Arabic |
| 5 | Correct English subject line | BookingSubmittedMailTest::booking submitted email has correct subject in English |
| 6 | Summary preview truncated at 200 chars | BookingSubmittedMailTest::problem summary is truncated at 200 characters, summary exactly 200 characters is not truncated, summary under 200 characters is not truncated |
| 7 | Date formatted per locale | BookingSubmittedMailTest::date is formatted as d/m/Y for Arabic users, date is formatted as m/d/Y for English users |
| 8 | Time formatted as h:i A | BookingSubmittedMailTest::time is formatted as h:i A |
| 9 | Correct template per language | BookingSubmittedMailTest::uses correct Arabic template for Arabic users, uses correct English template for English users |
| 10 | All content data passed to template | BookingSubmittedMailTest::content includes all required data |
Improvements Checklist
- Implements ShouldQueue for async delivery
- Locale-based subject lines (Arabic/English)
- Summary preview truncation at 200 characters
- Date formatting per user language preference
- Time formatting in 12-hour format
- Separate Arabic and English templates
- Arabic template has RTL direction
- Pending status panel in both templates
- Response timeframe (1-2 business days) included
- Uses base email layout with Libra branding
Security Review
Status: PASS
- No security concerns identified
- Email content is properly escaped through Blade templates
- No user input directly concatenated into HTML
- Uses Laravel's built-in email sanitization
Performance Considerations
Status: PASS
- Email is queued (implements ShouldQueue) preventing blocking of HTTP requests
- Consultation model is serialized for queue, avoiding N+1 issues
- Carbon parsing is minimal and efficient
Files Modified During Review
None - no modifications required.
Gate Status
Gate: PASS → docs/qa/gates/8.3-booking-submitted-confirmation.yml
Recommended Status
✓ Ready for Done - All acceptance criteria met, comprehensive test coverage (37 tests passing), code quality is excellent, and follows project standards.