reviewed epic 8 stories
This commit is contained in:
@@ -3,6 +3,9 @@
|
||||
## 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**,
|
||||
@@ -25,52 +28,195 @@ So that **all emails have consistent branding and reliable delivery**.
|
||||
- [ ] Mobile-responsive layout
|
||||
|
||||
### Technical Setup
|
||||
- [ ] Plain text fallback generation
|
||||
- [ ] Queue configuration for async sending
|
||||
- [ ] Email logging for debugging
|
||||
- [ ] Plain text fallback generation (auto-generated from HTML)
|
||||
- [ ] Queue configuration for async sending (database driver)
|
||||
- [ ] Email logging for debugging (log channel)
|
||||
|
||||
## Technical Notes
|
||||
## Implementation Steps
|
||||
|
||||
### Step 1: Publish Laravel Mail Views
|
||||
```bash
|
||||
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
|
||||
// config/mail.php - from .env
|
||||
'from' => [
|
||||
'address' => env('MAIL_FROM_ADDRESS', 'no-reply@libra.ps'),
|
||||
'name' => env('MAIL_FROM_NAME', 'Libra Law Firm'),
|
||||
],
|
||||
<?php
|
||||
|
||||
// resources/views/vendor/mail/html/header.blade.php
|
||||
<tr>
|
||||
<td class="header" style="background-color: #0A1F44; padding: 25px;">
|
||||
<a href="{{ config('app.url') }}">
|
||||
<img src="{{ asset('images/logo-email.png') }}" alt="Libra Law Firm" height="45">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
namespace App\Mail;
|
||||
|
||||
// Base Mailable
|
||||
abstract class BaseMailable extends Mailable
|
||||
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 build()
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return $this->from(config('mail.from.address'), $this->getFromName());
|
||||
return new Envelope(
|
||||
from: new \Illuminate\Mail\Mailables\Address(
|
||||
config('mail.from.address'),
|
||||
$this->getFromName()
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
protected function getFromName(): string
|
||||
{
|
||||
$locale = $this->getLocale();
|
||||
$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:
|
||||
```bash
|
||||
php artisan queue:table
|
||||
php artisan migrate
|
||||
```
|
||||
|
||||
### Step 4: Update Environment Variables
|
||||
Add to `.env.example`:
|
||||
```env
|
||||
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`:
|
||||
```blade
|
||||
<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:
|
||||
```bash
|
||||
php artisan queue:work --queue=default
|
||||
```
|
||||
|
||||
### Local Testing
|
||||
For local development, use the `log` mail driver:
|
||||
```env
|
||||
MAIL_MAILER=log
|
||||
```
|
||||
Emails will appear in `storage/logs/laravel.log`.
|
||||
|
||||
For visual testing, consider Mailpit or similar (optional):
|
||||
```env
|
||||
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
|
||||
<?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
|
||||
- [ ] Base template with branding
|
||||
- [ ] Plain text fallback
|
||||
- [ ] Queued delivery works
|
||||
- [ ] Tests pass
|
||||
- [ ] 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
|
||||
|
||||
Reference in New Issue
Block a user