Skip to content

feat: email integration — IMAP sync, inbox, compose, contact emails, dashboard widget#20

Merged
Systemsaholic merged 90 commits intomainfrom
feature/email-integration
Mar 15, 2026
Merged

feat: email integration — IMAP sync, inbox, compose, contact emails, dashboard widget#20
Systemsaholic merged 90 commits intomainfrom
feature/email-integration

Conversation

@Systemsaholic
Copy link
Copy Markdown
Owner

@Systemsaholic Systemsaholic commented Mar 15, 2026

Summary

  • Full IMAP email integration: account setup, folder sync, send/receive
  • 3-column inbox page with folder sidebar, email list, and reader
  • Contact detail page: emails tab with unified agent + system emails, click-to-preview modal
  • Dashboard sidebar: recent emails widget with unread badge, compose/sync tools
  • Quick Create: "New Email" action
  • SMTP-first routing with Resend fallback, email compliance settings
  • Shared DOMPurify sanitizer, email preview dialog reusable across pages

Test plan

  • Verify IMAP account setup in user profile
  • Check inbox loads with folder sidebar, email list, and reader
  • Click email on contact page → preview modal opens with body
  • "Open in Inbox" from preview → navigates to inbox with email selected
  • Dashboard sidebar shows recent emails, clicking opens preview
  • Quick Create "New Email" opens compose on inbox page
  • Compose, reply, forward emails
  • Sync button refreshes IMAP

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Email integration: Configure email accounts, sync inbox, read/compose/send emails, and manage folders.
    • Email settings: Configure allowed domains and compliance footers for outbound emails.
    • Group bookings: Create, edit, and manage trip groups with documents and media.
    • Contact emails: View recent emails and full email history linked to contacts.
    • Email signature support: Personal email signatures automatically appended to outbound emails.
  • Enhancements

    • Unread email badge on navigation menu.
    • SMTP-first email sending with Resend fallback.

Systemsaholic and others added 30 commits February 16, 2026 08:46
… (Phase A)

Add contact_loyalty_programs table for managing cruise line, airline, and hotel
loyalty memberships. Add universal booking fields (net_price_cents,
non_refundable_deposit, cancellation_schedule_json) to activity_pricing, and
cruise-specific fields (reservation_number, stateroom_category_code,
onboard_credit_cents/currency) to custom_cruise_details. Wire import service
to extract new fields from Traveltek API responses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add loyalty programs API: controller, service, DTOs with PII access
  control, membership number normalization, and unique constraint handling
- Add loyalty programs UI: contact detail section with dialog-based CRUD,
  19 common provider suggestions, and tier level badges
- Add cruise form fields: reservation number, stateroom category code,
  onboard credit with currency selector
- Add cancellation schedule inline table editor on Booking & Pricing tab
- Add agency pricing card with net price and non-refundable deposit
- Wire audit system: entity type, sanitizer whitelist, Drizzle enum

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…migrations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
# Conflicts:
#	apps/api/src/cruise-booking/services/import-booking.service.ts
#	packages/database/src/migrations/meta/_journal.json
# Conflicts:
#	packages/database/src/schema/index.ts
Resolve merge conflicts: keep both ClientPortalModule and PortalModule,
use expanded imports in contacts.service, accept feature branch for all
client portal files (real Supabase auth replacing mock auth).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The AuthProvider context expected signInWithOtp/signOut/getAccessToken
but useAuth() returns { user, loading, logout }. Updated the interface
to match since useAuthContext() is not consumed anywhere.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Next.js 15 requires useSearchParams() to be inside a Suspense boundary.
Split LoginPage into wrapper + LoginPageContent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolve conflict in travelers/page.tsx — take feature branch's
profile editor over preview's PassengerInfoForm stub.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Systemsaholic and others added 19 commits March 14, 2026 12:33
New Email tab in Profile for IMAP/SMTP account configuration:
- Email account CRUD with connection test
- Display existing accounts with sync status
- Signature editing link points to Preferences tab (not duplicated)
- use-email-accounts.ts hook for TanStack Query

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ce footer

Admin settings page with:
- Tag-style allowed domains editor (empty = no restriction)
- HTML compliance footer with live preview
- Uses existing PATCH /agencies/:agencyId/settings endpoint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…r, and email reader

- Install DOMPurify for HTML email body sanitization
- Create use-emails hook (folders, emails list, detail, sync mutation)
- Convert /emails to redirect to /emails/inbox
- Create inbox page with folder sidebar, email list, and reader pane
- Create EmailReader with DOMPurify sanitization and attachment downloads
- Create ContactMatchBanner for linked contact display
- Fix unused import/variable in email-tab.tsx

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace "Coming Soon" placeholder with ContactEmailsSection that shows
emails matched to the contact via the contactId filter parameter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
P0: Make imapUid nullable for outbound emails, convert unique index to
partial (WHERE imap_uid IS NOT NULL) to prevent collision on SMTP sends.
P0: Fix TS strict-init errors on DTO classes (add ! assertions),
remove unused imports/vars across email-accounts module.
P1: Add unique partial index on email_attachments(email_id, imap_part_id)
for attachment metadata dedupe on re-sync.
P1: Stream attachment downloads with proper Content-Type/Disposition headers
using NestJS StreamableFile instead of returning raw object.
P1: Use @isemail() on send DTO address field for early validation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…navigation

Wrap email inbox page in DashboardLayout (was missing, causing top nav to disappear).
Add Zustand store for email view state (folder, selected email, search) so users
don't lose their position when navigating to other pages and returning.
Fix BigInt serialization error from ImapFlow by wrapping uidValidity/uidNext/uid/size
in Number() before storing in JSONB or integer columns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…compatible with ON CONFLICT

The partial unique index (WHERE imap_uid IS NOT NULL) could not be matched by
Drizzle's ON CONFLICT target, causing every email upsert to fail silently.
Changed to a regular unique index — PostgreSQL treats NULL as distinct, so
outbound emails with NULL imap_uid won't conflict.

Also added ImapFlow error event handler via createImapClient() helper to prevent
unhandled socket timeout errors from crashing the Node process.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rward, compose

- Backend: PATCH flags endpoint, DELETE email endpoint, batch flags endpoint
- Frontend: hover actions on email list (mark read/unread, star, delete)
- Auto-mark as read when email is opened
- Unread indicator dot on email list items
- Reply/Reply All/Forward open compose dialog with pre-filled fields
- New compose button in sidebar
- Create Contact button navigates to contacts with pre-filled data
- Compose dialog with To, Cc, Subject, Body fields

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously, when all recipients were filtered out by the non-production
domain allowlist, the code only logged a warning but continued to call
sendMail() with empty recipients. Now throws BadRequestException to
prevent any send attempt. Also adds kaponline.com to allowed domains.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Unread email count badge on "Emails" nav link (60s polling)
- Folder sidebar: create, rename, delete custom folders via IMAP
- Move-to-folder dropdown on email list hover actions and reader toolbar
- Backend: createFolder, renameFolder, deleteFolder, moveEmail endpoints
- Protected system folders (Inbox, Sent, Drafts, Trash, Junk) cannot be
  renamed or deleted

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When lastUid equals uidNext-1 (no new messages), fetching UID range
`lastUid+1:*` fails on some IMAP servers with "Command failed". Now
checks uidNext before attempting the fetch to avoid unnecessary errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Route transactional emails through the assigned agent's SMTP account
first, falling back to Resend on pre-delivery failures. This ensures
agent emails appear in their Sent folder and come from their real
address, improving deliverability and client experience.

Key changes:
- Add shared buildEmailBody() utility for unified signature/footer
- Add sendRaw() lightweight SMTP method to SmtpSendService
- Add findActiveAccountForUser() for deterministic account selection
- Implement agent resolution waterfall (contact → trip → createdBy)
- Wire EmailModule → EmailAccountsModule via forwardRef()
- Double-send prevention: post-delivery DB failures don't trigger Resend
- IMAP sync messageId dedup for SMTP-sent outbound emails
- Remove inline footers from hardcoded templates (now via buildEmailBody)
- Resend fallback uses verified domain for from, agent email as replyTo

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ents

Contact Communications tab now shows ALL emails (agent IMAP + system/automation)
merged into a unified timeline, with search, type filtering, and delivery status.
System emails display even when no IMAP account is configured.

Also includes inbox UI improvements: folder sidebar, contact match banner,
move-to-folder dropdown, drag-and-drop config, and email list refinements.

- Add useEmailLogs hook for system email logs
- Add category field to EmailLogResponse shared type
- Rewrite contact-emails-section with unified email view
- Improve inbox folder sidebar, email list, and contact matching UI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…emails

- Wrap ContactEmailsSection in p-6 div for consistent margins with other tabs
- Deduplicate emails in Communications tab: when an SMTP-sent email appears
  in both synced_emails and email_logs (matching messageId/providerMessageId),
  keep only the agent version (richer data with snippet, read status)
- Simplify email count display

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add email preview dialog that lazy-fetches body on demand for both
agent (IMAP) and system emails. Clickable email rows with keyboard
accessibility. "Open in Inbox" navigates to /emails/inbox with the
email pre-selected. Extract shared DOMPurify sanitizer. Add Recent
Emails sidebar widget on contact detail page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shows recent INBOX emails with unread badge, click-to-navigate to
inbox with email pre-selected. Quick tools: Compose, Open Inbox,
and Sync button. Widget hides when no email account is configured.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… emails

Quick Create now includes "New Email" which opens compose on the inbox
page. Clicking an email in the dashboard sidebar opens the preview
dialog (same as contact emails tab) instead of navigating away.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 15, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tailfire-client Building Building Preview, Comment Mar 15, 2026 5:28pm
tailfire-ota Building Building Preview, Comment Mar 15, 2026 5:28pm

Request Review

@Systemsaholic Systemsaholic merged commit a8a7d41 into main Mar 15, 2026
2 of 4 checks passed
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 15, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 85f49d3d-7310-4b55-a991-8eabc9a9c795

📥 Commits

Reviewing files that changed from the base of the PR and between 37d0eb2 and 8d5912f.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (96)
  • apps/admin/package.json
  • apps/admin/src/app/contacts/[id]/_components/contact-emails-section.tsx
  • apps/admin/src/app/contacts/[id]/_components/email-preview-dialog.tsx
  • apps/admin/src/app/contacts/[id]/_components/recent-emails-card.tsx
  • apps/admin/src/app/contacts/[id]/page.tsx
  • apps/admin/src/app/contacts/_components/quick-contact-dialog.tsx
  • apps/admin/src/app/dashboard/_components/dashboard-sidebar.tsx
  • apps/admin/src/app/dashboard/_components/sidebar-quick-create.tsx
  • apps/admin/src/app/dashboard/_components/sidebar-recent-emails.tsx
  • apps/admin/src/app/emails/inbox/_components/compose-email-dialog.tsx
  • apps/admin/src/app/emails/inbox/_components/contact-match-banner.tsx
  • apps/admin/src/app/emails/inbox/_components/email-list.tsx
  • apps/admin/src/app/emails/inbox/_components/email-reader.tsx
  • apps/admin/src/app/emails/inbox/_components/folder-sidebar.tsx
  • apps/admin/src/app/emails/inbox/_components/move-to-folder-dropdown.tsx
  • apps/admin/src/app/emails/inbox/page.tsx
  • apps/admin/src/app/emails/page.tsx
  • apps/admin/src/app/profile/_components/email-tab.tsx
  • apps/admin/src/app/profile/_components/profile-form-context.tsx
  • apps/admin/src/app/profile/page.tsx
  • apps/admin/src/app/settings/_components/settings-tabs-layout.tsx
  • apps/admin/src/app/settings/email/page.tsx
  • apps/admin/src/app/settings/page.tsx
  • apps/admin/src/app/trips/_components/group-form-dialog.tsx
  • apps/admin/src/app/trips/_components/groups-table.tsx
  • apps/admin/src/app/trips/_components/trip-form-dialog.tsx
  • apps/admin/src/app/trips/groups/[groupId]/_components/group-media-tab.tsx
  • apps/admin/src/app/trips/groups/[groupId]/page.tsx
  • apps/admin/src/app/trips/page.tsx
  • apps/admin/src/components/layout/top-nav.tsx
  • apps/admin/src/components/notes/NotesSection.tsx
  • apps/admin/src/components/trips/trips-view-switcher.tsx
  • apps/admin/src/hooks/use-email-accounts.ts
  • apps/admin/src/hooks/use-emails.ts
  • apps/admin/src/hooks/use-notes.ts
  • apps/admin/src/hooks/use-trips.ts
  • apps/admin/src/lib/dnd-config.ts
  • apps/admin/src/lib/sanitize-email-html.ts
  • apps/admin/src/lib/validation/trip-validation.ts
  • apps/admin/src/stores/email.store.ts
  • apps/api/package.json
  • apps/api/src/app.module.ts
  • apps/api/src/automation/automation.types.ts
  • apps/api/src/common/email/build-email-body.ts
  • apps/api/src/email-accounts/dto/create-email-account.dto.ts
  • apps/api/src/email-accounts/dto/email-filter.dto.ts
  • apps/api/src/email-accounts/dto/send-email.dto.ts
  • apps/api/src/email-accounts/dto/update-email-account.dto.ts
  • apps/api/src/email-accounts/email-accounts.controller.ts
  • apps/api/src/email-accounts/email-accounts.module.ts
  • apps/api/src/email-accounts/email-accounts.service.ts
  • apps/api/src/email-accounts/email-sync-scheduler.service.ts
  • apps/api/src/email-accounts/email-sync.processor.ts
  • apps/api/src/email-accounts/imap-sync.service.ts
  • apps/api/src/email-accounts/smtp-send.service.ts
  • apps/api/src/email/email.module.ts
  • apps/api/src/email/email.service.ts
  • apps/api/src/email/templates/client-portal-invite.template.ts
  • apps/api/src/email/templates/invite.template.ts
  • apps/api/src/email/templates/password-reset.template.ts
  • apps/api/src/email/templates/welcome.template.ts
  • apps/api/src/financials/stripe-connect.service.ts
  • apps/api/src/notes/dto/create-note.dto.ts
  • apps/api/src/notes/dto/note-filter.dto.ts
  • apps/api/src/notes/notes.controller.ts
  • apps/api/src/notes/notes.service.ts
  • apps/api/src/trips/dto/create-trip.dto.ts
  • apps/api/src/trips/trips.controller.ts
  • apps/api/src/trips/trips.service.ts
  • docs/plans/2026-03-08-group-bookings-design.md
  • docs/plans/2026-03-08-group-bookings-implementation.md
  • docs/superpowers/plans/2026-03-14-email-integration.md
  • docs/superpowers/plans/2026-03-14-smtp-priority-resend-fallback.md
  • docs/superpowers/specs/2026-03-14-smtp-priority-resend-fallback-design.md
  • packages/database/src/migrations/20260311120000_add_group_docs_media.sql
  • packages/database/src/migrations/20260314120000_create_email_accounts.sql
  • packages/database/src/migrations/20260314120100_create_synced_emails.sql
  • packages/database/src/migrations/20260314120200_create_email_attachments.sql
  • packages/database/src/migrations/20260314120300_add_email_settings_to_agency.sql
  • packages/database/src/migrations/20260314120400_add_contact_email_index.sql
  • packages/database/src/migrations/20260314120500_fix_email_schema_codex_review.sql
  • packages/database/src/migrations/meta/_journal.json
  • packages/database/src/schema/email-accounts.schema.ts
  • packages/database/src/schema/email-attachments.schema.ts
  • packages/database/src/schema/financials.schema.ts
  • packages/database/src/schema/index.ts
  • packages/database/src/schema/notes.schema.ts
  • packages/database/src/schema/synced-emails.schema.ts
  • packages/database/src/schema/trip-group-documents.schema.ts
  • packages/database/src/schema/trip-group-media.schema.ts
  • packages/shared-types/src/api/email-accounts.types.ts
  • packages/shared-types/src/api/email.types.ts
  • packages/shared-types/src/api/financials.types.ts
  • packages/shared-types/src/api/index.ts
  • packages/shared-types/src/api/notes.types.ts
  • packages/shared-types/src/api/trips.types.ts

📝 Walkthrough

Walkthrough

Comprehensive email integration with IMAP/SMTP account configuration, on-demand sync, per-folder management, and compose/reply/forward; extends group bookings with documents and media tables; integrates emails with contacts via unified email views and contact matching; adds profile email account setup and settings for compliance footers and allowed domains.

Changes

Cohort / File(s) Summary
Email Accounts Backend
apps/api/src/email-accounts/*, apps/api/src/email/email.module.ts, apps/api/src/email/email.service.ts
New EmailAccountsModule with controller, service, IMAP sync, SMTP send, and queue processor; SMTP-first routing in EmailService with Resend fallback; agent resolution and signature/footer injection.
Email Frontend - Inbox & Compose
apps/admin/src/app/emails/inbox/*, apps/admin/src/app/emails/page.tsx, apps/admin/src/stores/email.store.ts
Full email inbox with folder sidebar, searchable email list, email reader with threading, drag-drop folder moves, compose dialog with reply/forward, and centralized email view state store.
Contact-Email Integration
apps/admin/src/app/contacts/[id]/_components/*, apps/admin/src/app/contacts/_components/quick-contact-dialog.tsx
New ContactEmailsSection with search and filtering, RecentEmailsCard, EmailPreviewDialog, and enhanced QuickContactDialog to support pre-filling sender info and callbacks.
Email-Inbox Integration
apps/admin/src/app/emails/inbox/_components/contact-match-banner.tsx
ContactMatchBanner to link incoming emails to existing contacts or trigger quick contact creation with parsed sender details.
Group Bookings Enhancement
apps/admin/src/app/trips/*, apps/api/src/trips/*, packages/database/src/schema/trip-group-*.schema.ts
New GroupFormDialog and GroupsTable components; trip form inline group creation; group documents and media management; notes extended to support tripGroupId.
Email Settings & Profile
apps/admin/src/app/profile/_components/email-tab.tsx, apps/admin/src/app/settings/email/page.tsx, apps/admin/src/app/profile/page.tsx, apps/admin/src/app/settings/page.tsx
Email account CRUD form in profile; email settings page for allowed domains and compliance footer; profile and settings routing updated.
Database Migrations & Schema
packages/database/src/migrations/20260311*.sql, packages/database/src/migrations/20260314*.sql, packages/database/src/schema/email-*.schema.ts, packages/database/src/schema/trip-group-*.schema.ts
Seven migrations: group documents/media tables, email accounts, synced emails, attachments, agency settings, contact email index, schema fixes; corresponding Drizzle schema definitions with relations and indexes.
Hooks & Utilities
apps/admin/src/hooks/use-email-*.ts, apps/admin/src/hooks/use-notes.ts, apps/admin/src/lib/sanitize-email-html.ts, apps/admin/src/lib/dnd-config.ts, apps/admin/src/lib/validation/trip-validation.ts
New email account and email management hooks with React Query; email HTML sanitization utility; drag-drop data types; trip validation extended for tripGroupId; notes hook extended for tripGroupId.
Shared Types & Dependencies
packages/shared-types/src/api/email-*.types.ts, packages/shared-types/src/api/notes.types.ts, packages/shared-types/src/api/trips.types.ts, packages/shared-types/src/api/financials.types.ts, apps/admin/package.json, apps/api/package.json
New email accounts, synced emails, and attachment DTOs; notes and trips types extended with tripGroupId; agency settings types extended with email configuration; dompurify and email libraries added.
Email Template & Service Updates
apps/api/src/email/templates/*.ts, apps/api/src/common/email/build-email-body.ts, apps/api/src/automation/automation.types.ts, apps/api/src/financials/stripe-connect.service.ts, apps/admin/src/components/layout/top-nav.tsx, apps/admin/src/components/notes/NotesSection.tsx, apps/admin/src/components/trips/trips-view-switcher.tsx
Removed inline footers from templates; new buildEmailBody utility; EMAIL_SYNC queue and job types added; agency settings updated for email domains/footer; top nav unread badge for emails; trips view mode extended with groups; notes section tripGroupId support.
Documentation
docs/plans/2026-03-*.md, docs/superpowers/plans/2026-03-*.md, docs/superpowers/specs/2026-03-*.md
Comprehensive design and implementation plans for group bookings and email integration with IMAP/SMTP, agent resolution, signature/footer injection, and phased rollout strategy.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

🐰 Email hops into the warren with a mighty leap,
IMAP and SMTP in folders stacked deep,
From contacts to inboxes, messages now flow,
Groups gather documents and media to show,
Compliance footers signed, the work is complete! 📧✨

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/email-integration

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant