feat: admin email hub — sent log, inbox, compose, audiences, delivery tracking

Add full email management at /admin/emails with:
- email_log table tracking all outgoing emails with resend_id + delivery events
- inbound_emails table for Resend webhook-received messages
- Resend webhook handler (/webhooks/resend) updating delivery status in real-time
- send_email() returns resend_id (str|None) instead of bool; all 9 worker
  handlers pass email_type= for per-type filtering
- Admin UI: sent log with HTMX filters, email detail with API-enriched HTML
  preview, inbox with unread badges + reply, compose with branded wrapping,
  audience management with contact list/remove
- Sidebar Email section with unread badge via blueprint context processor

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Deeman
2026-02-23 12:15:34 +01:00
parent cb1f00baf0
commit 454b362c88
20 changed files with 1049 additions and 21 deletions

View File

@@ -7,6 +7,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Unreleased]
### Added
- **Admin Email Hub** (`/admin/emails`) — full email management dashboard with:
sent log (filterable by type/event/search, HTMX partial updates), email detail
with Resend API enrichment for HTML preview, inbound inbox with unread badges
and inline reply, compose form with branded template wrapping, and Resend
audience management with contact list/remove
- **Email delivery tracking** — `email_log` table records every outgoing email
with resend_id; Resend webhook handler (`/webhooks/resend`) updates delivery
events (delivered, bounced, opened, clicked, complained) in real-time;
`inbound_emails` table stores received messages with full body
- **send_email() returns resend_id** — changed return type from `bool` to
`str | None` (backward-compatible: truthy string works like True); all 9
worker handlers now pass `email_type=` for per-type filtering in the log
- **Playtomic full data extraction** — expanded venue bounding boxes from 4 regions
(ES, UK, DE, FR) to 23 globally (Italy, Portugal, NL, BE, AT, CH, Nordics, Mexico,
Argentina, Middle East, USA); PAGE_SIZE increased from 20 to 100; availability