You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Parent: the email notifications epic. Blocked by #569 (delivery events).
Why
#547 records every send; #569 makes those records tell the truth about delivery. Nothing reads either — today you'd query the database by hand to answer "did the acceptance actually reach them?"
Lookup first. This is a tool for answering a question about one recipient, with failures surfaced prominently. It is not a metrics dashboard.
Scope
Admin-only page, following the /users pattern exactly: requireAdminOr404(), a data function in prisma/data/, a DataTable-based client table. Full-bleed width tier per DESIGN.md.
Search by recipient address — the primary interaction. Debounced, like the applications toolbar.
Filters for status and template, matching the existing toolbar idiom.
Columns: recipient, template, subject, status, and the relevant timestamp. A bounce shows its bounceType — Permanent and Transient mean very different things and the table must not flatten them.
A failure strip at the top: recent bounces, complaints and failed sends. One compact summary, not a chart.
Read-only. No resend button, no suppression management.
sent is not proof of receipt. The status vocabulary should make that legible rather than implying delivery — a sent row with no delivered event is genuinely unknown, not successful.
Empty state needed: a fresh environment has no rows at all.
Non-goals
No resend, no retry, no suppression list.
No email body preview — subject and metadata only.
No open or click tracking.
No charts or trend lines.
No retention or pruning policy. The table grows unbounded; that's a known follow-up.
Acceptance criteria
The page is admin-only; a manager and an applicant both get a 404.
Searching an address returns that recipient's sends, newest first.
Status and template filters work and participate in Clear filters.
Pagination round-trips through the URL like /applications.
A bounced row shows whether the bounce was permanent or transient.
The failure strip lists recent bounces, complaints and failures.
Loading, empty and error states all handled per ENGINEERING.md §4.
Nothing on the page can trigger a send.
No migration is added.
WORKFLOWS.md gains an admin entry for the page.
npm run prettier:check, eslint:check, tsc:check, test all pass.
Tests
tests/db/ — the query filters by recipient, status and template; pagination bounds are correct; a non-admin caller is refused.
tests/unit/ — the status-to-label mapping including bounce types; the failure-strip selection logic.
Parent: the email notifications epic. Blocked by #569 (delivery events).
Why
#547 records every send; #569 makes those records tell the truth about delivery. Nothing reads either — today you'd query the database by hand to answer "did the acceptance actually reach them?"
Lookup first. This is a tool for answering a question about one recipient, with failures surfaced prominently. It is not a metrics dashboard.
Scope
Admin-only page, following the
/userspattern exactly:requireAdminOr404(), a data function inprisma/data/, aDataTable-based client table. Full-bleed width tier perDESIGN.md.bounceType—PermanentandTransientmean very different things and the table must not flatten them.Notes
sentis not proof of receipt. The status vocabulary should make that legible rather than implying delivery — asentrow with nodeliveredevent is genuinely unknown, not successful.Non-goals
Acceptance criteria
/applications.ENGINEERING.md§4.WORKFLOWS.mdgains an admin entry for the page.npm run prettier:check,eslint:check,tsc:check,testall pass.Tests
tests/db/— the query filters by recipient, status and template; pagination bounds are correct; a non-admin caller is refused.tests/unit/— the status-to-label mapping including bounce types; the failure-strip selection logic.