11 KiB
Story 8.1: Email Infrastructure Setup
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
Story Context
This is the foundational story for Epic 8. All subsequent email stories (8.2-8.10) depend on this infrastructure being complete. No other stories in Epic 8 can be implemented until this story is done.
User Story
As a developer, I want to configure email sending infrastructure and base templates, So that all emails have consistent branding and reliable delivery.
Acceptance Criteria
SMTP Configuration
- MAIL_MAILER configured via .env
- MAIL_HOST, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD
- MAIL_ENCRYPTION (TLS)
- MAIL_FROM_ADDRESS: no-reply@libra.ps
- MAIL_FROM_NAME: Libra Law Firm / مكتب ليبرا للمحاماة
Base Email Template
- Libra logo in header
- Navy blue (#0A1F44) and gold (#D4AF37) colors
- Professional typography
- Footer with firm contact info
- Mobile-responsive layout
Technical Setup
- Plain text fallback generation (auto-generated from HTML)
- Queue configuration for async sending (database driver)
- Email logging for debugging (log channel)
Implementation Steps
Step 1: Publish Laravel Mail Views
php artisan vendor:publish --tag=laravel-mail
This creates resources/views/vendor/mail/ with customizable templates.
Step 2: Create Base Mailable Class
Create app/Mail/BaseMailable.php:
<?php
namespace App\Mail;
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;
abstract class BaseMailable extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
public function envelope(): Envelope
{
return new Envelope(
from: new \Illuminate\Mail\Mailables\Address(
config('mail.from.address'),
$this->getFromName()
),
);
}
protected function getFromName(): string
{
$locale = $this->locale ?? app()->getLocale();
return $locale === 'ar' ? 'مكتب ليبرا للمحاماة' : 'Libra Law Firm';
}
}
Step 3: Configure Queue for Email
Ensure config/queue.php uses the database driver and run:
php artisan queue:table
php artisan migrate
Step 4: Update Environment Variables
Add to .env.example:
MAIL_MAILER=smtp
MAIL_HOST=
MAIL_PORT=587
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=no-reply@libra.ps
MAIL_FROM_NAME="Libra Law Firm"
QUEUE_CONNECTION=database
Step 5: Create Email Logo Asset
Place the email logo at public/images/logo-email.png (120px height recommended for email clients).
Step 6: Customize Mail Templates
Modify resources/views/vendor/mail/html/header.blade.php:
<tr>
<td class="header" style="background-color: #0A1F44; padding: 25px; text-align: center;">
<a href="{{ config('app.url') }}" style="display: inline-block;">
<img src="{{ asset('images/logo-email.png') }}" alt="Libra Law Firm" height="45" style="height: 45px;">
</a>
</td>
</tr>
Modify resources/views/vendor/mail/html/themes/default.css for brand colors:
- Primary background: #0A1F44 (navy)
- Accent/buttons: #D4AF37 (gold)
- Button text: #0A1F44 (navy on gold)
Step 7: Configure Email Logging
In config/logging.php, ensure a channel exists for mail debugging. Emails are automatically logged when using the log mail driver for local testing.
Error Handling
- SMTP Connection Failures: Queue will retry failed jobs automatically (3 attempts by default)
- Configure retry delay in
config/queue.phpunderretry_after - Failed jobs go to
failed_jobstable for inspection - Monitor queue with
php artisan queue:failedto see failed emails
Technical Notes
Files to Create/Modify
| File | Action |
|---|---|
app/Mail/BaseMailable.php |
Create |
resources/views/vendor/mail/html/header.blade.php |
Modify |
resources/views/vendor/mail/html/footer.blade.php |
Modify |
resources/views/vendor/mail/html/themes/default.css |
Modify |
public/images/logo-email.png |
Create/Add |
.env.example |
Update |
config/mail.php |
Verify defaults |
Queue Configuration
This project uses the database queue driver for reliability. Ensure queue worker runs in production:
php artisan queue:work --queue=default
Local Testing
For local development, use the log mail driver:
MAIL_MAILER=log
Emails will appear in storage/logs/laravel.log.
For visual testing, consider Mailpit or similar (optional):
MAIL_MAILER=smtp
MAIL_HOST=localhost
MAIL_PORT=1025
Testing Requirements
Test Scenarios
Create tests/Feature/Mail/BaseMailableTest.php:
- SMTP configuration validates - Verify mail config loads correctly
- Base template renders with branding - Logo, colors visible in HTML output
- Plain text fallback generates - HTML converts to readable plain text
- Emails queue successfully - Job dispatches to queue, not sent synchronously
- Arabic sender name works - "مكتب ليبرا للمحاماة" when locale is 'ar'
- English sender name works - "Libra Law Firm" when locale is 'en'
- Failed emails retry - Queue retries on temporary failure
Example Test Structure
<?php
use App\Mail\BaseMailable;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Queue;
test('emails are queued not sent synchronously', function () {
Queue::fake();
// Send a test email extending BaseMailable
// Assert job was pushed to queue
});
test('sender name is arabic when locale is ar', function () {
app()->setLocale('ar');
// Create mailable and check from name
// expect($mailable->getFromName())->toBe('مكتب ليبرا للمحاماة');
});
test('sender name is english when locale is en', function () {
app()->setLocale('en');
// Create mailable and check from name
// expect($mailable->getFromName())->toBe('Libra Law Firm');
});
Definition of Done
- SMTP sending works (verified with real credentials or log driver)
- Base template displays Libra branding (logo, navy/gold colors)
- Plain text fallback auto-generates from HTML
- Emails dispatch to queue (not sent synchronously)
- Queue worker processes emails successfully
- All tests pass
- Code formatted with Pint
Dependencies
- Requires: None (foundational story)
- Blocks: Stories 8.2-8.10 (all other email stories)
Estimation
Complexity: Medium | Effort: 3-4 hours
Dev Agent Record
Status
Ready for Review
Agent Model Used
Claude Opus 4.5 (claude-opus-4-5-20251101)
Completion Notes
- Published Laravel mail views via artisan command
- Created BaseMailable abstract class with locale-aware sender name
- Updated .env.example with SMTP configuration for production
- Updated phpunit.xml with mail configuration for testing
- Customized header.blade.php with navy (#0A1F44) background and logo placeholder
- Customized footer.blade.php with firm contact info (bilingual)
- Customized default.css with Libra brand colors (navy/gold)
- Created test mail template at resources/views/mail/test.blade.php
- All 8 tests passing
Debug Log References
None required - implementation completed without issues
File List
| File | Action |
|---|---|
app/Mail/BaseMailable.php |
Created |
resources/views/vendor/mail/html/header.blade.php |
Modified |
resources/views/vendor/mail/html/footer.blade.php |
Modified |
resources/views/vendor/mail/html/themes/default.css |
Modified |
resources/views/vendor/mail/html/button.blade.php |
Created (via publish) |
resources/views/vendor/mail/html/layout.blade.php |
Created (via publish) |
resources/views/vendor/mail/html/message.blade.php |
Created (via publish) |
resources/views/vendor/mail/html/panel.blade.php |
Created (via publish) |
resources/views/vendor/mail/html/subcopy.blade.php |
Created (via publish) |
resources/views/vendor/mail/html/table.blade.php |
Created (via publish) |
resources/views/vendor/mail/text/button.blade.php |
Created (via publish) |
resources/views/vendor/mail/text/footer.blade.php |
Created (via publish) |
resources/views/vendor/mail/text/header.blade.php |
Created (via publish) |
resources/views/vendor/mail/text/layout.blade.php |
Created (via publish) |
resources/views/vendor/mail/text/message.blade.php |
Created (via publish) |
resources/views/vendor/mail/text/panel.blade.php |
Created (via publish) |
resources/views/vendor/mail/text/subcopy.blade.php |
Created (via publish) |
resources/views/vendor/mail/text/table.blade.php |
Created (via publish) |
resources/views/mail/test.blade.php |
Created |
public/images/.gitkeep |
Created |
.env.example |
Modified |
phpunit.xml |
Modified |
tests/Feature/Mail/BaseMailableTest.php |
Created |
Change Log
| Date | Change |
|---|---|
| 2025-12-29 | Initial implementation of email infrastructure |
QA Results
Review Date: 2025-12-29
Reviewed By: Quinn (Test Architect)
Code Quality Assessment
Excellent implementation of the email infrastructure foundation. The code is clean, well-organized, and follows Laravel best practices. The BaseMailable abstract class provides a solid foundation for all future mailables with locale-aware sender names. Template customization properly applies Libra branding (navy #0A1F44 and gold #D4AF37 colors).
Refactoring Performed
None required - implementation is clean and follows established patterns.
Compliance Check
- Coding Standards: ✓ Pint passes, clean code
- Project Structure: ✓ Files in correct locations per Laravel conventions
- Testing Strategy: ✓ 8 tests covering all required scenarios
- All ACs Met: ✓ All acceptance criteria verified
Improvements Checklist
- SMTP configuration verified in .env.example
- Queue configuration verified (database driver)
- Base template branding verified (navy/gold colors)
- Footer with bilingual contact info verified
- Mobile-responsive layout verified
- Test coverage verified (8 tests, all passing)
- Add actual logo file to
public/images/logo-email.pngwhen asset is available (placeholder path exists)
Security Review
No security concerns. Email credentials properly externalized to environment variables. Uses config() helper instead of env() in application code per Laravel best practices.
Performance Considerations
Emails are properly queued using the database queue driver, preventing blocking of HTTP requests. Queue retry mechanism configured with 90-second retry_after.
Files Modified During Review
None - no modifications were necessary.
Gate Status
Gate: PASS → docs/qa/gates/8.1-email-infrastructure-setup.yml
Recommended Status
✓ Ready for Done
The implementation fully satisfies all acceptance criteria. The only outstanding item is the logo asset file, which is correctly referenced but awaiting the actual image file - this is expected for an infrastructure story and does not block functionality.