feat(web): sending-access status line in Settings - #1057
Merged
Merged
Conversation
Once an account is approved for external sending, the /sending-access banner on Inboxes disappears, and a declined account had no navigation back to the page. Add a one-line "External sending" status to Settings (reusing the account's sending_access status + latest request, and the existing lib/sendingAccess.ts helpers) that always links back to /sending-access: Restricted, Restricted — request under review, Restricted — request declined, Enabled — operator approved, or Enabled — paid plan. Renders no row on deployments without the sending_access object (self-host, feature disabled), and waits for both reads to settle before rendering to avoid a flash of stale state. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tVLxUHk3fqQuq8C3wqyHW
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Since v1.13.0 (#1052), a restricted account only learns about
/sending-accessfrom the Inboxes banner or a blocked send'srecovery_url. Once an operator approves the account (or a request is declined), that banner disappears and there's no navigation back to the page. This adds a one-line "External sending" status to the Settings page's account facts (dt/ddlist) that always links back to/sending-access, so the state is discoverable regardless of banner visibility.What
web/src/lib/sendingAccess.ts: two new pure helpers, reusing the existingisSendingRestrictedrule and the same route precedence assendingAccessEnabledRoute.sendingAccessEnabledReason(status)— short reason ("operator approved" / "paid plan") for the enabled case. A verified custom domain unlocks sending per-message (internal/sendingpolicy'sRouteCustomIdentity), never as an account-wide grant, so it has no field onsending_accessand never produces a reason.sendingAccessSettingsSummary(status, request)— the full one-line value:Restricted/Restricted — request under review/Restricted — request declined/Enabled/Enabled — <reason>. Returnsnullwhen the deployment has nosending_accessobject at all (self-host, feature disabled), so Settings renders no row rather than a "not restricted" line.web/src/app/(app)/settings/page.tsx: newSendingAccessRowcomponent in the Profile section's existingdl, matching the row style of the neighboring Email/User ID/Member since facts. Reads the account'ssending_access(via the existinguseSendingAccesshook) and the latest request (getSendingAccessRequest, same SWR key the/sending-accesspage uses), and waits for both to settle before rendering — no flash of "Restricted" before the request state (pending vs. declined) is known, and no row shown at all if the object is absent or a read errors.States rendered
sending_accessobject (self-host, feature off): no row at all.Each value is a link to
/sending-access.Test plan
describe("Settings — Sending access row", ...)toweb/src/app/(app)/settings/page.test.tsxcovering all 6 states above, mockingGET /v1/accountandGET /v1/account/sending-access/requestthe same waysending-access/page.test.tsxdoes.sendingAccessEnabledReason/sendingAccessSettingsSummarycases toweb/src/lib/sendingAccess.test.ts.page.profile-edit.test.tsxand an existing delete-account call-count assertion inpage.test.tsxfor the row's two new background reads on mount (switched both to the SWR-awaretest-utils/swrrender helper).cd web && npm run lint— cleancd web && npx tsc --noEmit— cleancd web && npm test -- --runTestsByPath src/app/\(app\)/settings/page.test.tsx src/app/\(app\)/settings/page.profile-edit.test.tsx src/lib/sendingAccess.test.ts src/app/\(app\)/sending-access/page.test.tsx— 4 suites, 87 tests passedcd web && npm test— full suite, 123 suites / 1094 tests passedcd web && npm run build— static export succeeds,/settingsand/sending-accessboth prerenderNo OpenAPI, SDK, MCP, or Go changes — this only consumes the existing
GET /v1/accountandGET /v1/account/sending-access/requestendpoints, so the "Client surface checklist" doesn't apply.🤖 Generated with Claude Code
https://claude.ai/code/session_018tVLxUHk3fqQuq8C3wqyHW