Understanding how email notifications work in CHRIS.
Overview
CHRIS sends automated email notifications for key events:
- Leave request submissions
- Leave request approvals/rejections
- Welcome emails for new employees
- Password reset requests
Architecture
┌─────────────┐ ┌──────────────────┐ ┌─────────────┐
│ Frontend │────▶│ Edge Function │────▶│ SMTP Server │
│ │ │ (Deno Runtime) │ │ │
└─────────────┘ └────────┬─────────┘ └──────┬──────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────┐
│ company_settings│ │ Recipient │
│ (SMTP config) │ │ Inbox │
└─────────────────┘ └─────────────┘
Email Types
Leave Request Submitted
Trigger: Employee submits a new leave request
Recipients:
- Team leader (if assigned)
- HR managers
Content:
- Employee name and email
- Leave type (Annual, Sick, etc.)
- Date range
- Working days requested
- Reason (if provided)
Template Variables:
{{employee_name}}
{{employee_email}}
{{leave_type}}
{{start_date}}
{{end_date}}
{{working_days}}
{{reason}}
Leave Request Status Update
Trigger: Request is approved or rejected
Recipients:
- The employee who submitted the request
Content:
- Decision (Approved/Rejected)
- Approver's name
- Comments (if any)
- Original request details
Template Variables:
{{employee_name}}
{{status}}
{{approver_name}}
{{approver_comment}}
{{leave_type}}
{{start_date}}
{{end_date}}
{{working_days}}
Welcome Email
Trigger: New employee created
Recipients:
- The new employee
Content:
- Welcome message
- Login instructions
- Getting started guide
Template Variables:
{{employee_name}}
{{email}}
{{position}}
{{start_date}}
Password Reset
Trigger: Admin initiates password reset
Recipients:
- The employee
Content:
- Reset link (time-limited)
- Instructions
SMTP Configuration
Settings Location
SMTP settings are stored in company_settings:
| Field | Description |
|---|---|
| smtp_host | Server address (e.g., smtp.gmail.com) |
| smtp_port | Port number (usually 587) |
| smtp_username | Authentication email |
| smtp_password | SMTP password (encrypted) |
| smtp_from_email | Sender email address |
| smtp_from_name | Sender display name |
| smtp_use_tls | Enable TLS encryption |
| smtp_enabled | Master enable/disable |
Provider Examples
=== "Gmail"
```
Host: smtp.gmail.com
Port: 587
TLS: Enabled
Username: your-email@gmail.com
Password: App-specific password
```
=== "Outlook"
```
Host: smtp-mail.outlook.com
Port: 587
TLS: Enabled
Username: your-email@outlook.com
Password: App-specific password
```
=== "Custom SMTP"
Consult your provider's documentation for correct settings.
Email Flow
Sending an Email
// src/utils/emailNotifications.ts
export async function sendNotificationEmail({
to,
subject,
html,
text
}: EmailParams) {
const { data, error } = await supabase.functions.invoke(
'send-notification-email',
{
body: { to, subject, html, text }
}
);
if (error) {
console.error('Email failed:', error);
// Don't throw - email failure shouldn't block operations
}
return { success: !error };
}
Edge Function Processing
// supabase/functions/send-notification-email/index.ts
Deno.serve(async (req) => {
// 1. Verify JWT
const token = req.headers.get('Authorization')?.replace('Bearer ', '');
const { data: { user } } = await supabase.auth.getUser(token);
if (!user) return new Response('Unauthorized', { status: 401 });
// 2. Get request body
const { to, subject, html, text } = await req.json();
// 3. Get SMTP settings
const { data: settings } = await supabase
.from('company_settings')
.select('*')
.single();
if (!settings.smtp_enabled) {
return new Response(JSON.stringify({
success: false,
error: 'SMTP not enabled'
}));
}
// 4. Check for rerouting
let recipients = to;
if (settings.email_reroute_enabled) {
recipients = [settings.email_reroute_addresses];
}
// 5. Send via SMTP
await sendViaSMTP(settings, recipients, subject, html, text);
return new Response(JSON.stringify({ success: true }));
});
Email Templates
Template Storage
Templates stored in email_templates table:
CREATE TABLE email_templates (
id UUID PRIMARY KEY,
template_key TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
subject TEXT NOT NULL,
html_content TEXT NOT NULL,
description TEXT,
variables JSONB,
is_active BOOLEAN DEFAULT true
);
Template Example
<!-- leave_request_approved -->
<div style="font-family: Arial, sans-serif; max-width: 600px;">
<h2>Leave Request Approved</h2>
<p>Dear {{employee_name}},</p>
<p>Your leave request has been <strong>approved</strong>.</p>
<table style="border-collapse: collapse; width: 100%;">
<tr>
<td style="padding: 8px; border: 1px solid #ddd;">Leave Type</td>
<td style="padding: 8px; border: 1px solid #ddd;">{{leave_type}}</td>
</tr>
<tr>
<td style="padding: 8px; border: 1px solid #ddd;">Dates</td>
<td style="padding: 8px; border: 1px solid #ddd;">
{{start_date}} - {{end_date}}
</td>
</tr>
<tr>
<td style="padding: 8px; border: 1px solid #ddd;">Working Days</td>
<td style="padding: 8px; border: 1px solid #ddd;">{{working_days}}</td>
</tr>
</table>
<p>Approved by: {{approver_name}}</p>
{{#if approver_comment}}
<p>Comment: {{approver_comment}}</p>
{{/if}}
<p>Best regards,<br>HR Team</p>
</div>
Variable Substitution
function renderTemplate(
template: string,
variables: Record<string, string>
): string {
return template.replace(
/\{\{(\w+)\}\}/g,
(match, key) => variables[key] || match
);
}
Email Rerouting
Purpose
Redirect all emails to a test address during:
- Development
- Testing
- Training
Configuration
UPDATE company_settings SET
email_reroute_enabled = true,
email_reroute_addresses = 'testing@company.com';
Behavior
When enabled:
- Original recipients are ignored
- All emails go to reroute address
- Subject prefixed with "[REROUTED]"
- Body includes original recipients
Error Handling
Non-Blocking Design
Email failures don't block the main operation:
async function submitLeaveRequest(request: LeaveRequest) {
// 1. Save to database (critical)
const { data, error } = await supabase
.from('leave_requests')
.insert(request);
if (error) throw error; // This blocks
// 2. Send notification (non-critical)
try {
await sendLeaveRequestNotification(data);
} catch (emailError) {
console.error('Email notification failed:', emailError);
// Don't throw - request was saved successfully
}
return data;
}
Common Failures
| Error | Cause | Solution |
|---|---|---|
| Authentication failed | Wrong credentials | Check username/password |
| Connection timeout | Wrong host/port | Verify SMTP settings |
| TLS error | TLS mismatch | Toggle TLS setting |
| Rate limited | Too many emails | Wait or upgrade plan |
| Spam blocked | Content flagged | Review email content |
Monitoring
Edge Function Logs
View email logs in Supabase Dashboard:
- Go to Edge Functions
- Select send-notification-email
- Click Logs
Log Contents
{
"timestamp": "2025-01-15T10:00:00Z",
"status": "success",
"recipients": ["user@company.com"],
"subject": "Leave Request Approved",
"rerouted": false
}
Debugging
Enable verbose logging in Edge Function:
console.log('SMTP Config:', {
host: settings.smtp_host,
port: settings.smtp_port,
tls: settings.smtp_use_tls
});