feat: implement email template system with vendor overrides
Add comprehensive email template management for both admin and vendors:
Admin Features:
- Email templates management page at /admin/email-templates
- Edit platform templates with language support (en, fr, de, lb)
- Preview templates with sample variables
- Send test emails
- View email logs per template
Vendor Features:
- Email templates customization page at /vendor/{code}/email-templates
- Override platform templates with vendor-specific versions
- Preview and test customized templates
- Revert to platform defaults
Technical Changes:
- Migration for vendor_email_templates table
- VendorEmailTemplate model with override management
- Enhanced EmailService with language resolution chain
(customer preferred -> vendor preferred -> platform default)
- Branding resolution (Wizamart default, removed for whitelabel)
- Platform-only template protection (billing templates)
- Admin and vendor API endpoints with full CRUD
- Updated seed script with billing and team templates
Files: 22 changed, ~3,900 lines added
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,107 +1,289 @@
|
||||
# Email Templates Architecture (Future)
|
||||
# Email System Enhancements - Implementation Plan
|
||||
|
||||
## Overview
|
||||
|
||||
Email templates follow a similar pattern to the CMS content pages system, with platform defaults that vendors can override.
|
||||
Enhance the email system to support:
|
||||
- Platform-level templates with vendor overrides
|
||||
- Wizamart branding by default (removed for Enterprise whitelabel tier)
|
||||
- Platform-only templates that cannot be overridden
|
||||
- Admin UI for editing platform templates
|
||||
- 4-language support (en, fr, de, lb)
|
||||
- Smart language resolution (customer → vendor → platform default)
|
||||
|
||||
## Architecture
|
||||
---
|
||||
|
||||
### Template Hierarchy
|
||||
## Phase 1: Database & Model Foundation
|
||||
|
||||
1. **Platform Default Templates** - Admin-managed base templates
|
||||
- Order confirmation
|
||||
- Shipping notification
|
||||
- Password reset
|
||||
- Welcome email
|
||||
- Invoice email
|
||||
- etc.
|
||||
### 1.1 Update EmailTemplate Model
|
||||
|
||||
2. **Vendor Overrides** - Optional customization
|
||||
- Vendors can override platform templates
|
||||
- Cannot create new template types (unlike CMS pages)
|
||||
- Must maintain required placeholders
|
||||
**File:** `models/database/email.py`
|
||||
|
||||
### Multi-language Support
|
||||
Add columns:
|
||||
- `is_platform_only: bool = False` - Cannot be overridden by vendors
|
||||
- `required_variables: JSON` - List of variables that must be provided
|
||||
|
||||
- Each template exists in all supported languages (FR, EN, DE, LB)
|
||||
- Vendor overrides are per-language
|
||||
- Falls back to platform default if no vendor override
|
||||
|
||||
### Key Differences from CMS Pages
|
||||
|
||||
| Feature | CMS Pages | Email Templates |
|
||||
|---------|-----------|-----------------|
|
||||
| Create new | Vendors can create | Vendors cannot create |
|
||||
| Template types | Unlimited | Fixed set by platform |
|
||||
| Tier limits | Number of pages per tier | No limits (all templates available) |
|
||||
| Override | Full content | Full content |
|
||||
|
||||
## Database Design (Proposed)
|
||||
|
||||
```sql
|
||||
-- Platform templates (admin-managed)
|
||||
CREATE TABLE email_templates (
|
||||
id SERIAL PRIMARY KEY,
|
||||
template_key VARCHAR(100) NOT NULL, -- e.g., 'order_confirmation'
|
||||
language VARCHAR(5) NOT NULL, -- e.g., 'fr', 'en', 'de'
|
||||
subject TEXT NOT NULL,
|
||||
html_body TEXT NOT NULL,
|
||||
text_body TEXT,
|
||||
placeholders JSONB, -- Required placeholders
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP,
|
||||
updated_at TIMESTAMP,
|
||||
UNIQUE(template_key, language)
|
||||
);
|
||||
|
||||
-- Vendor overrides
|
||||
CREATE TABLE vendor_email_templates (
|
||||
id SERIAL PRIMARY KEY,
|
||||
vendor_id INTEGER REFERENCES vendors(id),
|
||||
template_key VARCHAR(100) NOT NULL,
|
||||
language VARCHAR(5) NOT NULL,
|
||||
subject TEXT NOT NULL,
|
||||
html_body TEXT NOT NULL,
|
||||
text_body TEXT,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP,
|
||||
updated_at TIMESTAMP,
|
||||
UNIQUE(vendor_id, template_key, language)
|
||||
);
|
||||
```python
|
||||
is_platform_only = Column(Boolean, default=False, nullable=False)
|
||||
required_variables = Column(JSON, default=list)
|
||||
```
|
||||
|
||||
## Rendering Flow
|
||||
### 1.2 Create VendorEmailTemplate Model
|
||||
|
||||
1. Email service receives request (e.g., send order confirmation)
|
||||
2. Check for vendor override by `(vendor_id, template_key, language)`
|
||||
3. If no override, use platform default by `(template_key, language)`
|
||||
4. Render template with context variables
|
||||
5. Send via email service
|
||||
**File:** `models/database/vendor_email_template.py`
|
||||
|
||||
## API Endpoints (Proposed)
|
||||
```python
|
||||
class VendorEmailTemplate(Base):
|
||||
__tablename__ = "vendor_email_templates"
|
||||
|
||||
### Admin
|
||||
- `GET /admin/email-templates` - List all platform templates
|
||||
- `GET /admin/email-templates/{key}` - Get template by key
|
||||
- `PUT /admin/email-templates/{key}` - Update platform template
|
||||
- `POST /admin/email-templates/{key}/preview` - Preview template
|
||||
id: int
|
||||
vendor_id: int (FK → vendors.id)
|
||||
template_code: str # References EmailTemplate.code
|
||||
language: str # en, fr, de, lb
|
||||
subject: str
|
||||
body_html: str
|
||||
body_text: str
|
||||
is_active: bool = True
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
### Vendor
|
||||
- `GET /vendor/email-templates` - List available templates with override status
|
||||
- `GET /vendor/email-templates/{key}` - Get template (override or platform)
|
||||
- `PUT /vendor/email-templates/{key}` - Create/update override
|
||||
- `DELETE /vendor/email-templates/{key}` - Remove override (revert to platform)
|
||||
- `POST /vendor/email-templates/{key}/preview` - Preview with vendor context
|
||||
# Unique constraint: (vendor_id, template_code, language)
|
||||
```
|
||||
|
||||
## UI Considerations
|
||||
### 1.3 Migration
|
||||
|
||||
- Admin: Template editor with WYSIWYG or code view
|
||||
- Vendor: Simple override interface showing platform default as reference
|
||||
- Placeholder validation on save
|
||||
- Preview with sample data
|
||||
**File:** `alembic/versions/xxx_add_vendor_email_templates.py`
|
||||
|
||||
## Notes
|
||||
- Add columns to email_templates table
|
||||
- Create vendor_email_templates table
|
||||
- Add indexes for lookup performance
|
||||
|
||||
- Email templates feature is NOT part of the current settings page
|
||||
- Contact support messaging in settings directs vendors to admin
|
||||
- Full implementation to follow CMS pages pattern
|
||||
---
|
||||
|
||||
## Phase 2: Email Service Enhancement
|
||||
|
||||
### 2.1 Language Resolution
|
||||
|
||||
**Priority order for customer-facing emails:**
|
||||
1. `customer.preferred_language` (if customer exists)
|
||||
2. `vendor.storefront_language`
|
||||
3. Platform default (`en`)
|
||||
|
||||
**Priority order for vendor-facing emails:**
|
||||
1. `vendor.preferred_language`
|
||||
2. Platform default (`en`)
|
||||
|
||||
### 2.2 Template Resolution
|
||||
|
||||
**File:** `app/services/email_service.py`
|
||||
|
||||
```python
|
||||
def resolve_template(
|
||||
self,
|
||||
template_code: str,
|
||||
language: str,
|
||||
vendor_id: int | None = None
|
||||
) -> tuple[str, str, str]: # subject, body_html, body_text
|
||||
"""
|
||||
Resolve template with vendor override support.
|
||||
|
||||
1. If vendor_id provided and template not platform-only:
|
||||
- Look for VendorEmailTemplate override
|
||||
- Fall back to platform EmailTemplate
|
||||
2. If no vendor or platform-only:
|
||||
- Use platform EmailTemplate
|
||||
3. Language fallback: requested → en
|
||||
"""
|
||||
```
|
||||
|
||||
### 2.3 Branding Resolution
|
||||
|
||||
```python
|
||||
def get_branding(self, vendor_id: int | None) -> dict:
|
||||
"""
|
||||
Get branding variables for email.
|
||||
|
||||
Returns:
|
||||
- platform_name: "Wizamart" or vendor.name (if whitelabel)
|
||||
- platform_logo: Wizamart logo or vendor logo (if whitelabel)
|
||||
- support_email: platform or vendor support email
|
||||
- vendor_name: Always vendor.name
|
||||
- vendor_logo: Always vendor logo
|
||||
"""
|
||||
if vendor_id:
|
||||
vendor = self._get_vendor(vendor_id)
|
||||
if has_feature(vendor, "white_label"):
|
||||
return {
|
||||
"platform_name": vendor.name,
|
||||
"platform_logo": vendor.logo_url,
|
||||
...
|
||||
}
|
||||
return {
|
||||
"platform_name": "Wizamart",
|
||||
"platform_logo": PLATFORM_LOGO_URL,
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Admin API & UI
|
||||
|
||||
### 3.1 Admin API Endpoints
|
||||
|
||||
**File:** `app/api/v1/admin/email_templates.py`
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | `/email-templates` | List all platform templates |
|
||||
| GET | `/email-templates/{code}` | Get template (all languages) |
|
||||
| PUT | `/email-templates/{code}/{language}` | Update template |
|
||||
| POST | `/email-templates/{code}/preview` | Preview with sample data |
|
||||
| POST | `/email-templates/{code}/test` | Send test email |
|
||||
|
||||
### 3.2 Admin UI
|
||||
|
||||
**File:** `app/templates/admin/email-templates.html`
|
||||
|
||||
Features:
|
||||
- List view with template codes, categories, language badges
|
||||
- Edit view with:
|
||||
- Language tabs (en, fr, de, lb)
|
||||
- Subject field
|
||||
- Rich text editor for body_html
|
||||
- Plain text preview for body_text
|
||||
- Variable reference panel
|
||||
- Preview pane
|
||||
- Test send button
|
||||
- Platform-only badge/indicator
|
||||
- Required variables display
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Vendor API & UI
|
||||
|
||||
### 4.1 Vendor API Endpoints
|
||||
|
||||
**File:** `app/api/v1/vendor/email_templates.py`
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | `/email-templates` | List overridable templates |
|
||||
| GET | `/email-templates/{code}` | Get vendor override or default |
|
||||
| PUT | `/email-templates/{code}/{language}` | Create/update override |
|
||||
| DELETE | `/email-templates/{code}/{language}` | Reset to platform default |
|
||||
| POST | `/email-templates/{code}/preview` | Preview with vendor branding |
|
||||
| POST | `/email-templates/{code}/test` | Send test email |
|
||||
|
||||
### 4.2 Vendor UI
|
||||
|
||||
**File:** `app/templates/vendor/email-templates.html`
|
||||
|
||||
Features:
|
||||
- List view with templates, showing "Customized" vs "Using Default"
|
||||
- Edit view similar to admin but:
|
||||
- Can only edit non-platform-only templates
|
||||
- "Reset to Default" button
|
||||
- Shows platform default as reference
|
||||
- Tier gating: Only accessible for Business+ tiers
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Missing Templates
|
||||
|
||||
### Platform-Only Templates (Billing/Subscription)
|
||||
|
||||
| Code | Category | Description |
|
||||
|------|----------|-------------|
|
||||
| `tier_upgrade` | BILLING | Vendor upgraded subscription |
|
||||
| `tier_downgrade` | BILLING | Vendor downgraded subscription |
|
||||
| `payment_failed` | BILLING | Subscription payment failed |
|
||||
| `subscription_cancelled` | BILLING | Subscription cancelled |
|
||||
| `trial_ending` | BILLING | Trial period ending soon |
|
||||
| `usage_limit_warning` | BILLING | Approaching usage limits |
|
||||
|
||||
### Overridable Templates
|
||||
|
||||
| Code | Category | Description |
|
||||
|------|----------|-------------|
|
||||
| `team_invite` | AUTH | Team member invitation |
|
||||
| `order_shipped` | ORDERS | Order has shipped |
|
||||
| `shipping_update` | ORDERS | Shipping status update |
|
||||
| `low_stock_alert` | SYSTEM | Low inventory warning |
|
||||
|
||||
---
|
||||
|
||||
## Language Support Matrix
|
||||
|
||||
| Language | Code | Platform Default |
|
||||
|----------|------|------------------|
|
||||
| English | `en` | Yes (fallback) |
|
||||
| French | `fr` | No |
|
||||
| German | `de` | No |
|
||||
| Luxembourgish | `lb` | No |
|
||||
|
||||
**Fallback Logic:**
|
||||
```
|
||||
requested_language → en (if template not found)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Files to Create/Modify
|
||||
|
||||
### Phase 1 (Database)
|
||||
| File | Action |
|
||||
|------|--------|
|
||||
| `alembic/versions/xxx_add_vendor_email_templates.py` | Create |
|
||||
| `models/database/email.py` | Modify |
|
||||
| `models/database/vendor_email_template.py` | Create |
|
||||
| `models/database/__init__.py` | Modify |
|
||||
| `models/schema/email.py` | Create |
|
||||
|
||||
### Phase 2 (Service)
|
||||
| File | Action |
|
||||
|------|--------|
|
||||
| `app/services/email_service.py` | Modify |
|
||||
|
||||
### Phase 3 (Admin)
|
||||
| File | Action |
|
||||
|------|--------|
|
||||
| `app/api/v1/admin/email_templates.py` | Create |
|
||||
| `app/api/v1/admin/__init__.py` | Modify |
|
||||
| `app/templates/admin/email-templates.html` | Create |
|
||||
| `app/routes/admin_pages.py` | Modify |
|
||||
| `static/admin/js/email-templates.js` | Create |
|
||||
|
||||
### Phase 4 (Vendor)
|
||||
| File | Action |
|
||||
|------|--------|
|
||||
| `app/api/v1/vendor/email_templates.py` | Create |
|
||||
| `app/api/v1/vendor/__init__.py` | Modify |
|
||||
| `app/templates/vendor/email-templates.html` | Create |
|
||||
| `app/routes/vendor_pages.py` | Modify |
|
||||
| `static/vendor/js/email-templates.js` | Create |
|
||||
|
||||
### Phase 5 (Templates)
|
||||
| File | Action |
|
||||
|------|--------|
|
||||
| `scripts/seed_email_templates.py` | Modify |
|
||||
|
||||
---
|
||||
|
||||
## Execution Order
|
||||
|
||||
1. **Phase 1**: Database migration and models (~30 min)
|
||||
2. **Phase 2**: Email service enhancement (~45 min)
|
||||
3. **Phase 3**: Admin API and UI (~1-2 hours)
|
||||
4. **Phase 4**: Vendor API and UI (~1-2 hours)
|
||||
5. **Phase 5**: Add missing templates (~30 min)
|
||||
6. **Testing**: Integration tests for all phases
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **XSS Prevention**: Sanitize HTML in templates before storage
|
||||
2. **Access Control**: Vendors can only edit their own templates
|
||||
3. **Platform-only Protection**: API enforces is_platform_only flag
|
||||
4. **Template Validation**: Ensure required variables are present in template
|
||||
5. **Rate Limiting**: Test email sending limited to prevent abuse
|
||||
|
||||
Reference in New Issue
Block a user