280 lines
8.9 KiB
Markdown
280 lines
8.9 KiB
Markdown
# 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
|
|
<?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:
|
|
|
|
```php
|
|
// 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
|
|
```php
|
|
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
|
|
```php
|
|
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
|