10 KiB
📧 Royal Enfield Workflow - Email Templates
Professional, responsive, and dynamic email templates for the Royal Enfield Workflow System.
🎯 Key Features
✅ Dynamic & Adaptive - Templates adjust based on request data, priority, and workflow state
✅ Single Action Button - Only "View Request Details" button that redirects to request URL
✅ Fully Responsive - Mobile-friendly design that works across all devices
✅ Professional Design - Royal Enfield branded with color-coded scenarios
✅ Email Client Compatible - Table-based layout for maximum compatibility
✅ Placeholder-Based - Easy to integrate with backend templating engines
📁 Template Files (12 Templates)
Core Workflow Templates
- RequestCreated.html - Request submission confirmation
- ApprovalRequest.html - Single approver notification
- MultiApproverRequest.html - Multi-level approver notification with chain
- ApprovalConfirmation.html - Approval confirmation notification
- RejectionNotification.html - Rejection notification
TAT Management Templates
- TATReminder.html - TAT deadline reminder (80% threshold)
- TATBreached.html - TAT breach escalation
Workflow Control Templates
- WorkflowPaused.html - Workflow pause notification
- WorkflowResumed.html - Workflow resume notification
Participant Management Templates
- ParticipantAdded.html - New approver/spectator welcome
- ApproverSkipped.html - Approver skip notification
- RequestClosed.html - Request closure summary
🎨 Visual Design
Each template uses color-coded gradients to indicate the scenario:
| Template | Header Color | Purpose |
|---|---|---|
| RequestCreated | Purple (#667eea) | Information |
| ApprovalRequest | Purple (#667eea) | Action Required |
| MultiApproverRequest | Purple (#667eea) | Action Required |
| ApprovalConfirmation | Green (#28a745) | Success |
| RejectionNotification | Red (#dc3545) | Error/Rejection |
| TATReminder | Orange (#ff9800) | Warning |
| TATBreached | Red (#dc3545) | Critical |
| WorkflowPaused | Gray (#6c757d) | Neutral |
| WorkflowResumed | Green (#28a745) | Success |
| ParticipantAdded | Purple (#667eea) | Information |
| ApproverSkipped | Cyan (#17a2b8) | Information |
| RequestClosed | Purple (#6f42c1) | Complete |
🔗 View Details Button
All templates feature a single action button:
- Text: "View Request Details" / "Review Request Now" / "Take Action Now"
- Link Format:
{baseURL}/request/{requestNumber} - Example:
https://workflow.royalenfield.com/request/REQ-2025-12-0013
No approval/rejection buttons in emails - all actions happen within the application.
📋 How to Use Templates
1. Load Template File
const fs = require('fs');
const template = fs.readFileSync('./emailtemplates/ApprovalRequest.html', 'utf8');
2. Replace Placeholders
let emailContent = template
.replace(/\[ApproverName\]/g, approverName)
.replace(/\[InitiatorName\]/g, initiatorName)
.replace(/\[RequestId\]/g, requestId)
.replace(/\[ViewDetailsLink\]/g, `${baseURL}/request/${requestNumber}`)
.replace(/\[CompanyName\]/g, 'Royal Enfield');
3. Handle Dynamic Sections
// Priority Section Example
if (priority === 'HIGH' || priority === 'CRITICAL') {
const priorityHTML = `
<div style="padding: 20px; background-color: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px; margin-bottom: 30px;">
<h3 style="margin: 0 0 10px; color: #856404; font-size: 16px; font-weight: 600;">High Priority</h3>
<p style="margin: 0; color: #856404; font-size: 14px; line-height: 1.6;">
This request has been marked as ${priority} priority and requires prompt attention.
</p>
</div>
`;
emailContent = emailContent.replace('[PrioritySection]', priorityHTML);
} else {
emailContent = emailContent.replace('[PrioritySection]', '');
}
4. Send Email
await sendEmail({
to: recipientEmail,
subject: `[${requestId}] New Approval Request`,
html: emailContent
});
📖 Complete Documentation
See TEMPLATE_MAPPING.md for:
- Complete list of all placeholders for each template
- Dynamic section handling instructions
- Priority-based sending strategy
- Usage scenarios and triggers
- Security considerations
- Implementation examples
🚀 Integration Steps
Step 1: Email Service Setup
// services/email.service.ts
import nodemailer from 'nodemailer';
import fs from 'fs';
import path from 'path';
class EmailService {
private transporter;
constructor() {
this.transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: process.env.SMTP_PORT,
secure: process.env.SMTP_SECURE === 'true',
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASSWORD
}
});
}
async sendEmail(to: string, subject: string, html: string) {
return await this.transporter.sendMail({
from: process.env.EMAIL_FROM,
to,
subject,
html
});
}
loadTemplate(templateName: string): string {
const templatePath = path.join(__dirname, '../emailtemplates', `${templateName}.html`);
return fs.readFileSync(templatePath, 'utf8');
}
replaceplaceholders(template: string, data: Record<string, string>): string {
let result = template;
for (const [key, value] of Object.entries(data)) {
const regex = new RegExp(`\\[${key}\\]`, 'g');
result = result.replace(regex, value);
}
return result;
}
}
export const emailService = new EmailService();
Step 2: Create Email Handlers
// services/emailNotification.service.ts
export async function sendApprovalRequest(approverEmail: string, requestData: any) {
const template = emailService.loadTemplate('ApprovalRequest');
const placeholders = {
ApproverName: approverEmail.split('@')[0],
InitiatorName: requestData.initiatorName,
RequestId: requestData.requestNumber,
RequestDate: formatDate(requestData.createdAt),
RequestTime: formatTime(requestData.createdAt),
RequestType: requestData.requestType,
RequestDescription: requestData.description,
PrioritySection: getPrioritySection(requestData.priority),
ViewDetailsLink: `${process.env.BASE_URL}/request/${requestData.requestNumber}`,
CompanyName: 'Royal Enfield'
};
const html = emailService.replaceplaceholders(template, placeholders);
await emailService.sendEmail(
approverEmail,
`[${requestData.requestNumber}] New Approval Request`,
html
);
}
Step 3: Add to Workflow Events
// In workflow.service.ts
await sendApprovalRequest(approverEmail, requestData);
🔧 Environment Variables Required
Add to your .env file:
# SMTP Configuration
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-email@domain.com
SMTP_PASSWORD=your-app-password
# Email Settings
EMAIL_FROM=RE Workflow System <notifications@royalenfield.com>
BASE_URL=https://workflow.royalenfield.com
COMPANY_NAME=Royal Enfield
✅ Testing Templates
Preview in Browser
Open any .html file directly in a browser to see the design. Placeholders will be visible as [PlaceholderName].
Test Email Sending
// test/email.test.ts
import { emailService } from '../services/email.service';
describe('Email Templates', () => {
it('should send approval request email', async () => {
const template = emailService.loadTemplate('ApprovalRequest');
const testData = {
ApproverName: 'John Doe',
InitiatorName: 'Jane Smith',
RequestId: 'REQ-2025-12-0013',
// ... other test data
};
const html = emailService.replaceplaceholders(template, testData);
// Send to test email
await emailService.sendEmail('test@example.com', 'Test Email', html);
});
});
📊 Template Selection Logic
function getTemplateForScenario(scenario: string, hasMultipleApprovers: boolean): string {
switch(scenario) {
case 'request_created':
return 'RequestCreated';
case 'approval_required':
return hasMultipleApprovers ? 'MultiApproverRequest' : 'ApprovalRequest';
case 'request_approved':
return 'ApprovalConfirmation';
case 'request_rejected':
return 'RejectionNotification';
case 'tat_reminder':
return 'TATReminder';
case 'tat_breached':
return 'TATBreached';
case 'workflow_paused':
return 'WorkflowPaused';
case 'workflow_resumed':
return 'WorkflowResumed';
case 'participant_added':
return 'ParticipantAdded';
case 'approver_skipped':
return 'ApproverSkipped';
case 'request_closed':
return 'RequestClosed';
default:
throw new Error(`Unknown scenario: ${scenario}`);
}
}
🎯 Priority Queue Implementation
Use Bull/BullMQ for reliable email delivery:
import Queue from 'bull';
const emailQueue = new Queue('email-notifications', {
redis: {
host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT
}
});
// Add email to queue with priority
export async function queueEmail(emailData: any, priority: 'critical' | 'high' | 'medium' | 'low') {
const priorityMap = { critical: 1, high: 2, medium: 3, low: 4 };
await emailQueue.add('send-email', emailData, {
priority: priorityMap[priority],
attempts: 3,
backoff: {
type: 'exponential',
delay: 5000
}
});
}
// Process email queue
emailQueue.process('send-email', async (job) => {
const { to, subject, html } = job.data;
await emailService.sendEmail(to, subject, html);
});
📞 Support & Customization
For template customization or issues:
- Check TEMPLATE_MAPPING.md for complete documentation
- Test templates in email clients (Gmail, Outlook, Apple Mail)
- Validate HTML using online validators
- Use email testing services (Litmus, Email on Acid)
📝 Change Log
Version 2.0 (Dec 4, 2025)
- ✅ Removed all action buttons (Approve/Reject)
- ✅ Added single "View Request Details" button
- ✅ Made templates fully dynamic with conditional sections
- ✅ Added 12 templates for all scenarios
- ✅ Improved mobile responsiveness
- ✅ Professional design without emojis
- ✅ Added comprehensive documentation
Version 1.0 (Initial Release)
- Basic templates with action buttons
- 4 templates (Approval, Rejection, Multi-Approver, Confirmation)
Maintained by: Royal Enfield Development Team
Last Updated: December 4, 2025
Email Template Version: 2.0