feat(dashboard): let staff edit the email on a request #1

Merged
thelion merged 1 commit from feat/edit-email into main 2026-08-09 17:15:56 +00:00
Owner

The address was captured at intake and never editable afterwards. A request
that arrived with a missing or wrong address could not be corrected from the
dashboard at all, which also blocked anything downstream that emails the user
-- verification resend, portal account creation, credentials.

Adds PUT /api/requests/{id}/email alongside the existing note endpoint, and
turns the email row in the request detail view into an inline editor. The
button reads "add" when there is no address yet and "edit" when replacing one.

EmailNormalized is recomputed with checks.Normalize on every save rather than
left as-is. Both risk.dedupe and FindByEmailNormalized key off that column, so
leaving a stale value there would quietly match the wrong account -- the
normalizer strips +tags and gmail dots, so it does not track the raw address on
its own.

Validation is shape-only, mirroring the email.malformed signal in checks, and
returns 400 rather than the 502 the note handler uses, because a bad address is
the caller's mistake and not an upstream failure. Deliverability is deliberately
not checked here; the existing verification flow is what proves an address
works. A rejected save keeps the editor open so the value can be fixed instead
of being discarded.

SaveEmail returns the previous address so the audit event can record the
transition, distinguishing a correction from a first-time fill-in.

Verified: go build ./... and go vet ./internal/... clean, svelte-check reports
0 errors with no new warnings in the touched files.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

The address was captured at intake and never editable afterwards. A request that arrived with a missing or wrong address could not be corrected from the dashboard at all, which also blocked anything downstream that emails the user -- verification resend, portal account creation, credentials. Adds PUT /api/requests/{id}/email alongside the existing note endpoint, and turns the email row in the request detail view into an inline editor. The button reads "add" when there is no address yet and "edit" when replacing one. EmailNormalized is recomputed with checks.Normalize on every save rather than left as-is. Both risk.dedupe and FindByEmailNormalized key off that column, so leaving a stale value there would quietly match the wrong account -- the normalizer strips +tags and gmail dots, so it does not track the raw address on its own. Validation is shape-only, mirroring the email.malformed signal in checks, and returns 400 rather than the 502 the note handler uses, because a bad address is the caller's mistake and not an upstream failure. Deliverability is deliberately not checked here; the existing verification flow is what proves an address works. A rejected save keeps the editor open so the value can be fixed instead of being discarded. SaveEmail returns the previous address so the audit event can record the transition, distinguishing a correction from a first-time fill-in. Verified: go build ./... and go vet ./internal/... clean, svelte-check reports 0 errors with no new warnings in the touched files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The address was captured at intake and never editable afterwards. A request
that arrived with a missing or wrong address could not be corrected from the
dashboard at all, which also blocked anything downstream that emails the user
-- verification resend, portal account creation, credentials.

Adds PUT /api/requests/{id}/email alongside the existing note endpoint, and
turns the email row in the request detail view into an inline editor. The
button reads "add" when there is no address yet and "edit" when replacing one.

EmailNormalized is recomputed with checks.Normalize on every save rather than
left as-is. Both risk.dedupe and FindByEmailNormalized key off that column, so
leaving a stale value there would quietly match the wrong account -- the
normalizer strips +tags and gmail dots, so it does not track the raw address on
its own.

Validation is shape-only, mirroring the email.malformed signal in checks, and
returns 400 rather than the 502 the note handler uses, because a bad address is
the caller's mistake and not an upstream failure. Deliverability is deliberately
not checked here; the existing verification flow is what proves an address
works. A rejected save keeps the editor open so the value can be fixed instead
of being discarded.

SaveEmail returns the previous address so the audit event can record the
transition, distinguishing a correction from a first-time fill-in.

Verified: go build ./... and go vet ./internal/... clean, svelte-check reports
0 errors with no new warnings in the touched files.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ircforever/managebot!1
No description provided.