Uh oh!
There was an error while loading. Please reload this page.
feat(web): multi-org membership UI — org switcher, add-to-org form, member list - #1442
Merged
Conversation
…ember list Spec 021 plan 4 (MNG-1674): the user-facing layer over the curl-testable backend from plans 2-3 (web/ only). - Active-org switcher (sidebar) for non-superadmin multi-org members, backed by auth.listMyOrgs + auth.setActiveOrg; invalidates every query on switch so the dashboard refetches against the new active org. Single-org users get an inert org-name banner (spec AC #9). Superadmin cross-org switching via x-org-context is unchanged (spec AC #7). - Add-existing-account dialog on Settings → Users calling users.addExistingUserToOrg, surfacing the NOT_FOUND envelope inline; the complementary CONFLICT from users.create is already shown inline by the create dialog (spec AC #1). - Member list now renders per-org role alongside the account role plus a Guest badge for cross-home members (spec AC #5). Pure helpers (shouldShowOrgSwitcher, resolveActiveOrgName, formatAddToOrgSuccess, describeMemberRow) and SSR-safe presentational components (OrgSwitcherView, AddToOrgForm) are unit-tested; hook-wired containers are thin. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nhopeatall
approved these changes
Jun 25, 2026
nhopeatall
left a comment
Collaborator
There was a problem hiding this comment.
Summary
APPROVE — clean, well-scoped web/-only PR that consumes existing plan 2–3 tRPC endpoints. I verified the frontend/backend contracts, the org-switch resolution path, and ran the new tests + typecheck + lint locally (all green).
What I verified
- Switcher actually works for members.
computeEffectiveOrgId(src/api/context.ts) resolves a non-superadmin's effective org from the session'sactive_org_idand ignores thex-org-contextheader. The existingOrgProviderstill emitsx-org-context: <home org>for members, but since the backend ignores it for non-superadmins there is no conflict —auth.setActiveOrg+invalidateQueries()correctly re-scopes the whole dashboard. ConfirmedOrgSwitcheris never mounted for superadmins (theisSuperadminbranch insidebar.tsxalways returns first), preserving AC #7. - Data shapes match.
MyOrg {id,name,role}matcheslistOrgMembershipsForUser;AddToOrgResult {email,role,alreadyMember}matchesaddExistingUserToOrg's return;describeMemberRowmapsOrgMember {role,globalRole,isGuest}correctly; the NOT_FOUND copy matches the server envelope.colSpancorrectly bumped 5→6 for the new "Org role" column. - Tests/typecheck/lint. 23 new tests pass;
webtsc --noEmitclean;biome checkclean on all changed files; CI 7/7 green.
Minor (non-blocking)
- The hook-wired containers (
OrgSwitcher,AddToOrgDialog) are intentionally untested — only the pure helpers and SSR-safe views are covered. Reasonable tradeoff given the node test env, and the container logic (theorgId !== activeOrgIdguard, invalidation) is trivial. No action needed. - Defensive-only edge: if a multi-org member ever lacked a membership row for their home org (the "pre-backfill safety" path noted in
computeEffectiveOrgId),effectiveOrgIdwould be the home org butlistMyOrgswouldn't include it, so the<select value>wouldn't match an option. Not reachable post plan-1 backfill (andusers.createalways creates a home membership), so mentioning only for awareness.
LGTM.
🕵️ claude-code · claude-opus-4-8 · run details
Uh oh!
There was an error while loading. Please reload this page.
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 freeto 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.
Multi-org membership · 4/4 UI
Spec 021, plan 4 of 4 — the user-facing layer over the curl-testable backend shipped in plans 2–3 (touches
web/only).Issue: https://linear.app/mongrel/issue/MNG-1674/multi-org-membership-44-ui-org-switcher-add-to-org-form-member-list (MNG-1674)
What's in this PR
1. Active-org switcher (sidebar/nav)
OrgSwitcherrenders for non-superadmin members who belong to more than one org, backed byauth.listMyOrgs(the membership list) +auth.setActiveOrg(writes the session'sactive_org_idserver-side).queryClient.invalidateQueries()so every org-scoped query — andauth.me— refetches against the new active org (whichcomputeEffectiveOrgIdnow resolves from the session). The selected org is read fromauth.me.effectiveOrgId.OrgNameBanner). (spec AC fix: improve PostgreSQL startup reliability and agent documentation #9)x-org-contextheader, all orgs) is preserved unchanged. (spec AC ci: add GitHub Actions for CI and Fly.io deployment #7) Superadmins never mount the membership switcher (theiractive_org_idis intentionally ignored by the backend).2. "Add existing account to this org" form
AddToOrgDialogon Settings → Users (a secondary "Add existing account" button next to "New User") callsusers.addExistingUserToOrg.3. Membership-based member list
UsersTablenow renders the per-org membership role ("Org role" column) alongside the global Account role, and tags cross-home accounts with a Guest badge. Guests still get "Remove from this org" vs. "Delete account" for home-org members. (spec AC feat: add UpdateChecklistItem gadget and improve agent workflow #5)Spec AC mapping
[manual]— switcher visible for multi-org / hidden·inert for single-orgDesign notes
shouldShowOrgSwitcher,resolveActiveOrgName,formatAddToOrgSuccess,describeMemberRow,roleVariant) and SSR-safe presentational components (OrgSwitcherView,AddToOrgForm) built onNativeSelect, so they unit-test underrenderToStaticMarkupin the node test env. The hook-wired containers (OrgSwitcher,AddToOrgDialog) stay thin.Testing
tests/unit/web/org-switcher.test.ts— switcher decision + view (multi/single/zero org, pending, active-org marking)tests/unit/web/add-to-org-dialog.test.ts— success-message helper + form rendering (NOT_FOUND inline, success banner, pending)tests/unit/web/users-table-membership.test.ts—describeMemberRow+roleVariantbiome check .clean;test:fast(pre-push) green.🤖 Generated with Claude Code
🕵️ claude-code · claude-opus-4-8 · run details