{{ theme.skipToContentLabel || 'Skip to content' }}

Email Archive Feature Design

Date: 2026-02-09 Status: Approved

Problem

The "Sent" filter tab in the Email Schedules table and the separate "Logs" page show overlapping information. Completed one-time emails clutter the main list, making it harder to focus on actionable items.

Solution

  1. Remove the "Sent" filter tab — replaced by a new "Archived" tab
  2. Add Archive functionality — terminal-state one-time emails are auto-archived
  3. Keep the Logs page — serves as a detailed audit trail (separate concern)
  4. "All" tab excludes archived emails — main list stays clean

Design Decisions

DecisionChoiceRationale
What gets archived?Sent + failed + cancelled one-time emailsAll terminal-state one-time emails are "done"
Archive mechanismAuto-archive + manual unarchiveAuto keeps things tidy; unarchive provides safety net
Logs pageKeep as-is, remove "Sent" filter tabLogs = audit trail (different purpose from Archive)
Database implementationis_archived boolean columnPreserves original status (sent vs failed vs cancelled)

Data Model Changes

New column on email_schedules

sql
ALTER TABLE email_schedules
ADD COLUMN is_archived boolean NOT NULL DEFAULT false;

Auto-archive trigger

Fires when email_schedules.status changes to 'sent', 'failed', or 'cancelled' AND mode IN ('manual', 'scheduled') (one-time only). Sets is_archived = true.

Recurring emails are never auto-archived.

Migration steps

  1. Add is_archived column (default false)
  2. Backfill: set is_archived = true for existing terminal one-time emails
  3. Create trigger function for auto-archive on future status changes
  4. Add index on is_archived for efficient filtering

UI Changes

Filter tabs

Before: [All] [Pending] [Sent] [Failed] [Recurring]After: [All] [Pending] [Failed] [Recurring] [Archived]

TabFilter Logic
Allis_archived === false (excludes archived)
Pendingis_archived === false AND status IN (draft, scheduled, processing)
Failedis_archived === false AND status IN (failed, cancelled)
Recurringmode === 'recurring' (unaffected by archive)
Archivedis_archived === true

Row actions

  • Archived emails: Unarchive action (sets is_archived = false)
  • Non-archived terminal-state emails: Archive action (manual archive)

Stats cards

Replace "Sent" count with "Archived" count.

Service & Hook Changes

EmailScheduleService

typescript
// New method
static async setArchived(id: string, isArchived: boolean): Promise<void>

useEmailSchedules hook

typescript
// New actions
archiveSchedule(id: string): Promise<void>    // sets is_archived = true
unarchiveSchedule(id: string): Promise<void>  // sets is_archived = false

Client-side filtering

typescript
type FilterStatus = "all" | "pending" | "failed" | "recurring" | "archived";

Files to Modify

FileChange
supabase/migrations/YYYYMMDD_add_email_archive.sqlNew migration
src/features/email-schedules/types.tsAdd is_archived to EmailSchedule type
src/features/email-schedules/services/EmailScheduleService.tsAdd setArchived() method
src/features/email-schedules/hooks/useEmailSchedules.tsAdd archive/unarchive actions
src/features/email-schedules/components/EmailSchedulesTable.tsxUpdate filters, tabs, row actions
src/app/(admin)/(pages)/email-schedules/index.tsxUpdate stats cards
src/lib/supabaseTypes.tsRegenerate types