PR: #494: feat(team): invite members by team role [Story-1962]
by @ayush-pramaan
View PR →
Story 1962
Invite by team role, not raw capability
The invite flow now captures the firm-authored team role designation
from the same catalog used by the Team roles page. The app still gives
Clerk the derived capability as the organization role, but the Clerk
invitation also carries publicMetadata.teamRoleId, which
pramaan-functions Story 1961 consumes on acceptance.
Invite sheet
-
InviteMemberFieldsnow rendersTeamRoleSelect, a searchable role picker backed byFirmRoleView[]from the existinguseAppListFirmRoles()query on the Team page. -
The form schema submits
teamRoleId, notrole. The default is blank, so validation blocks submit until a team role is explicitly selected. - The pending/member table role columns are untouched; that remains Story 1963 scope.
Clerk contract
-
createTeamInvitation()resolves the submittedteamRoleIdthroughfirmRolesGateway.list(). -
Clerk receives
role: toClerkRole(teamRole.capability). The invitation metadata includes bothpramaanRole: teamRole.capabilityfor compatibility andteamRoleId: teamRole.idfor the Story 1961 backend contract. -
A stale/deleted role id fails before Clerk creation with
pramaan.team_invitations.team_role_not_found.
Vocabulary cleanup
-
The local invitation schema now exports
capabilitySchema/Capabilityfor Clerk access values, freeing "team role" to mean the business designation. -
CapabilitySelectremains in place for Team roles catalog editing, where choosing the access a role grants is still the correct behavior.
Tests and Storybook
-
server.test.tsproves selected role id →publicMetadata.teamRoleIdand derived Clerk role. -
team-list-page.test.tsxproves the invite sheet blocks submit with no role and sends the selected role UUID in the POST body. -
New
TeamRoleSelectstories cover default, selected, disabled, and empty catalog states; invite field/sheet stories now render the role catalog.
Acceptance criteria
- Invite sheet shows a team-role picker. met
-
Invite creation writes
teamRoleIdmetadata and derives Clerk role from team-role capability. met -
New copy exists in
messages/en.jsonandmessages/hi.json. met -
No
pnpm codegenrun; committedsrc/genstayed untouched. met
Verification
-
pnpm lintpassed with one pre-existing warning insrc/components/add-matter/avatar.tsx. pnpm testpassed: 155 files, 976 tests.pnpm buildpassed.pnpm storybook:buildpassed.git diff --checkclean.- Diff-local hardcoded-copy and color-literal scans found no new violations.
Files changed
17 file(s), +449 / -59 lines
| Path | Role | + | - |
|---|---|---|---|
| messages/en.json | Config | +10 | -3 |
| messages/hi.json | Config | +10 | -3 |
| src/components/settings/team/capability-select.stories.tsx | Component | +2 | -2 |
| src/components/settings/team/capability-select.tsx | Component | +9 | -9 |
| src/components/settings/team/invite-member-fields.stories.tsx | Component | +11 | -2 |
| src/components/settings/team/invite-member-fields.tsx | Component | +16 | -4 |
| src/components/settings/team/invite-member-sheet.stories.tsx | Component | +2 | -0 |
| src/components/settings/team/invite-member-sheet.tsx | Component | +9 | -2 |
| src/components/settings/team/presentation.ts | Component | +5 | -5 |
| src/components/settings/team/team-list-page.test.tsx | Component | +83 | -0 |
| src/components/settings/team/team-list-page.tsx | Component | +1 | -0 |
| src/components/settings/team/team-role-select.stories.tsx | Component | +50 | -0 |
| src/components/settings/team/team-role-select.tsx | Component | +103 | -0 |
| src/components/settings/team/use-team-list-actions.ts | Component | +4 | -0 |
| src/lib/team-invitations/schema.ts | Library | +8 | -6 |
| src/lib/team-invitations/server.test.ts | Library | +95 | -11 |
| src/lib/team-invitations/server.ts | Library | +31 | -12 |