libra/docs/stories/story-8.1-email-infrastruct...

11 KiB

Story 8.1: Email Infrastructure Setup

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.php under retry_after
  • Failed jobs go to failed_jobs table for inspection
  • Monitor queue with php artisan queue:failed to 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.png when 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

✓ 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.