libra/docs/stories/story-8.3-booking-submitted...

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_language is null, default to 'ar' (Arabic)
  • If problem_summary is null or empty, show "No summary provided"
  • Ensure consultation has valid booking_date and booking_time before 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

  • BookingSubmittedEmail Mailable 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