Appearance
Email & Templates Guide β
Purpose: Manage and test email templates and delivery. Audience: Admin, Developer, AI Agent Prerequisites: Supabase project linked; send-email function configured for delivery.
Where templates live
- UI components:
src/components/admin/templates/*- Editors:
EmailTemplateEditor.tsx,NotificationTemplateEditor.tsx - Overview:
AllTemplatesOverview.tsx
- Editors:
- Template data source (current): in-memory list via
useEmailTemplates- Code:
src/hooks/useEmailTemplates.ts:18-254 useUpdateEmailTemplateis a placeholder (logs and invalidates cache), no DB persistence yet::266-280
- Code:
Sending emails
- Central service:
src/lib/emailService.ts- Invokes
send-emailedge function with body{ to, subject, html, emailType, templateName?, metadata? }(lines 14-21) - Provides helpers like
sendNewMessageNotification(...)(lines 36-88) andsendListingApprovalNotification(...)(lines 90+)
- Invokes
- Other flows trigger emails directly via functions (e.g., seller verification):
src/hooks/useSellerVerification.ts:47-57,72-82
Template catalog (discovered)
EmailService helpers (ready-to-use):
- New message β
sendNewMessageNotification(recipientEmail, recipientId, senderName, listingTitle)- Code:
src/lib/emailService.ts:36-88
- Code:
- Listing approved β
sendListingApprovalNotification(recipientEmail, recipientId, listingTitle, listingUrl)- Code:
src/lib/emailService.ts:90+
- Code:
- New message β
Admin Templates Overview (function-sourced categories):
src/components/admin/templates/AllTemplatesOverview.tsx- Auth (source:
send-auth-email):auth_signup,auth_login,auth_recovery(vars:token,redirectTo) - Admin (source:
send-admin-listing-notification):admin_listing_notification,admin_listing_updated(vars:listingTitle,listingId,sellerName) - Sponsored (source:
send-sponsored-notifications):sponsored_activated,sponsored_expired,sponsored_scheduled(vars:listingTitle,sellerName,duration?,scheduledStart?) - Payments (source:
send-payment-email):payment_confirmation(vars:buyerName,dealId,listingTitle,amount,currency) - Admin Contact (source:
send-admin-contact-notification):admin_contact(vars:senderName,subject,message)
- Auth (source:
In-memory templates:
src/hooks/useEmailTemplates.tsnew_messageβ new conversation messagelisting_approvedβ seller notification listing approvedadmin_notificationβ general admin messageadmin_direct_messageβ direct admin β user messagenew_offerβ new offer notification (present in file; verify object id)
Admin Templates UI
- All templates view groups templates by source/function and category.
- Source examples (from
AllTemplatesOverview.tsx):send-auth-email,send-admin-listing-notification,send-sponsored-notifications,send-payment-email,send-admin-contact-notification.
- Source examples (from
- Edit flow: open editor modal (EmailTemplateEditor) β preview β test send β save.
- Test send calls
send-emailwith the edited subject/html and sample variables (EmailTemplateEditor.tsx:37-61).
- Test send calls
Email logs
- Table:
email_logsstores send attempts with status and metadata.- Columns include: recipient_email, recipient_id, email_type, subject, template_name, send_status ('pending'|'sent'|'failed'), resend_email_id, error_message, metadata, created_at, updated_at.
- DDL reference:
supabase/backups/remote-schema.sql:2390.
- Admin logs UI:
src/pages/admin/AdminLogs.tsxβ displays email logs with status badges and filters.
Testing steps
- Open Admin β Templates (
/admin/templates). - Select a template (e.g., New Message, Listing Approved) and click Edit.
- Preview HTML; variables like
{senderName}are replaced with sample data on test send. - Click βSend Testβ and provide an email address.
- Verify successful toast.
- Check
email_logsin Admin β Logs (/admin/logs) for a new entry (status sent/pending/failed). - Confirm email reception in inbox (in dev, use a test inbox or a mail catcher if configured).
Production considerations
- Move templates to a real table for persistence/versioning (e.g.,
email_templates) with fields matchingEmailTemplate. - Edge
send-emailshould recordemail_logsconsistently and render with the correct templateName and metadata placeholders. - Validate and sanitize template HTML.
- Prefer centralized variables (e.g., site URL, logo) to reduce duplication.
Internationalization
- Prefer separate templates per locale (subject/html) or resolve localized strings server-side before rendering the template. Current templates are predominantly EN/RO; recommendation: align with UI locales.
- For complete i18n, store per-language (e.g.,
subject_ro,subject_en,html_ro,html_en) or use an engine that loads strings based onlanguage_preference.
Security & accessibility
- Sanitize/validate HTML; don't interpolate untrusted content directly.
- Inline CSS compatible with email clients (Outlook, Gmail, iOS Mail etc.).
- Alt text for images and sufficient contrast.
- Include email purpose and return link to application.
Adding a new template (end-to-end)
- Define template
- Quick path (current): add entry in
src/hooks/useEmailTemplates.tsREAL_EMAIL_TEMPLATES (id,template_key,template_name,subject,html_content,category,description). - Recommended path (next): persistence in DB (e.g.,
email_templates) and loading via query hook.
- Quick path (current): add entry in
- UI overview
- Add entry in
AllTemplatesOverview.tsxunder the correct category/function (source), with list of accepted variables.
- Add entry in
- Sending path
- Create helper in
src/lib/emailService.tsor invokesend-emaildirectly from the respective flow. - If it comes from an edge function (e.g.,
send-payment-email), define the appropriate payload and ensure variable mapping.
- Create helper in
- Logging
send-emailmust write toemail_logs(email_type,template_name,metadata).- Verify in
/admin/logs.
Related
- Edge functions quick sheet:
docs/architecture/edge-functions.md - Notifications:
docs/features/notifications.md
Template variables syntax
- Variable placeholder format:
{variableName}(curly braces, alphanumeric/underscore) - Preview/Test replacement in editor uses regex
/\{(\w+)\}/gand a sample map (EmailTemplateEditor.tsx:46-57) - In production, supply the same variables via
metadataor render fully on server before callingsend-email
Variables by template (reference)
- New Message (emailService):
{senderName},{listingTitle} - Listing Approved (emailService):
{listingTitle},{listingUrl} - Admin Notification (in-memory template):
{message},{actionUrl} - Admin Direct Message (in-memory template):
{userName},{message},{adminName} - Auth Emails (function sourced):
{token},{redirectTo}(confirm insend-auth-email) - Sponsored Notifications (function sourced):
{listingTitle},{sellerName}, optional{duration},{scheduledStart} - Payment Confirmation (function sourced):
{buyerName},{dealId},{listingTitle},{amount},{currency}
Examples
Use EmailService helper (recommended):
tsimport { emailService } from '@/lib/emailService' await emailService.sendNewMessageNotification('user@example.com', 'USER_UUID', 'John Smith', 'Sample SaaS')Call edge function directly (custom payload):
tsconst { data, error } = await supabase.functions.invoke('send-email', { body: { to: 'user@example.com', subject: 'Hello', html: '<p>Hi {userName}</p>', emailType: 'custom', templateName: 'admin_direct_message', metadata: { userName: 'John' } } })
SQL quick checks
- Recent 50 emails:sql
select id, recipient_email, email_type, template_name, send_status, created_at from email_logs order by created_at desc limit 50; - Failed sends (last 24h):sql
select * from email_logs where send_status = 'failed' and created_at > now() - interval '24 hours' order by created_at desc;
FAQ
- How do I add a button URL? Use a placeholder like
{actionUrl}in HTML and passmetadata: { actionUrl: 'https://...' }. - Can I localize subjects? Yesβeither store separate fields per locale or pre-render the localized subject on the server before invoking
send-email. - Where do I see delivery errors? Check
error_messageinemail_logsand function logs in Supabase.