link,[object Object]
Skip to content

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
  • Template data source (current): in-memory list via useEmailTemplates
    • Code: src/hooks/useEmailTemplates.ts:18-254
    • useUpdateEmailTemplate is a placeholder (logs and invalidates cache), no DB persistence yet: :266-280

Sending emails

  • Central service: src/lib/emailService.ts
    • Invokes send-email edge function with body { to, subject, html, emailType, templateName?, metadata? } (lines 14-21)
    • Provides helpers like sendNewMessageNotification(...) (lines 36-88) and sendListingApprovalNotification(...) (lines 90+)
  • 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
    • Listing approved β†’ sendListingApprovalNotification(recipientEmail, recipientId, listingTitle, listingUrl)
      • Code: src/lib/emailService.ts:90+
  • 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)
  • In-memory templates: src/hooks/useEmailTemplates.ts

    • new_message β€” new conversation message
    • listing_approved β€” seller notification listing approved
    • admin_notification β€” general admin message
    • admin_direct_message β€” direct admin β†’ user message
    • new_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.
  • Edit flow: open editor modal (EmailTemplateEditor) β†’ preview β†’ test send β†’ save.
    • Test send calls send-email with the edited subject/html and sample variables (EmailTemplateEditor.tsx:37-61).

Email logs

  • Table: email_logs stores 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

  1. Open Admin β†’ Templates (/admin/templates).
  2. Select a template (e.g., New Message, Listing Approved) and click Edit.
  3. Preview HTML; variables like {senderName} are replaced with sample data on test send.
  4. Click β€œSend Test” and provide an email address.
  5. Verify successful toast.
  6. Check email_logs in Admin β†’ Logs (/admin/logs) for a new entry (status sent/pending/failed).
  7. 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 matching EmailTemplate.
  • Edge send-email should record email_logs consistently 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 on language_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)

  1. Define template
    • Quick path (current): add entry in src/hooks/useEmailTemplates.ts REAL_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.
  2. UI overview
    • Add entry in AllTemplatesOverview.tsx under the correct category/function (source), with list of accepted variables.
  3. Sending path
    • Create helper in src/lib/emailService.ts or invoke send-email directly from the respective flow.
    • If it comes from an edge function (e.g., send-payment-email), define the appropriate payload and ensure variable mapping.
  4. Logging
    • send-email must write to email_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+)\}/g and a sample map (EmailTemplateEditor.tsx:46-57)
  • In production, supply the same variables via metadata or render fully on server before calling send-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 in send-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):

    ts
    import { emailService } from '@/lib/emailService'
    await emailService.sendNewMessageNotification('user@example.com', 'USER_UUID', 'John Smith', 'Sample SaaS')
  • Call edge function directly (custom payload):

    ts
    const { 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 pass metadata: { 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_message in email_logs and function logs in Supabase.