[Story-1196] Implement Team settings page

Story 1196Epic None84 files · +4222/−31

PR #308 · pramaan-io/pramaan-app · merged 2026-06-12

Why this matters

Who is in, who is out, who is still waiting?

Without one page, a firm cannot see, invite, or revoke its own people.

Two sources, one list, one accepted invite becomes a duplicate.

The fix — One page, one truth: members and invites live in their own lanes.

How it flows

sequenceDiagram
  autonumber
  participant ui as Team page UI
  participant proxy as Internal proxy routes
  participant server as Members gateway · Clerk invitations
  ui->>proxy: Page load fetches members and invitations
  proxy->>server: Gateway lists members, Clerk lists invitations
  server->>proxy: Active, deactivated, pending, expired rows
  proxy->>ui: Two clean tables rendered on one page
  ui->>ui: Invite · revoke · role · deactivate action
  ui-->>server: POST writes through proxy to Clerk and gateway

What changed, file by file

Internal member proxy for the Team page; returns firm members with role and status, paired with the Clerk invitation server.

New fields: params : AppListMembersQueryParams

Files changed (84)

File±
docs/walkthroughs/story-1196-walkthrough.html+185
messages/en.json+90
messages/hi.json+90
openapi/app-api.json+138 −4
src/app/[locale]/(workspace)/settings/firm/team/page.tsx+48
src/app/api/internal/_helpers.ts+49
src/app/api/internal/members/[userId]/deactivate/route.ts+43
src/app/api/internal/members/[userId]/reactivate/route.ts+43
src/app/api/internal/members/[userId]/role/route.ts+43
src/app/api/internal/members/route.ts+24
src/app/api/internal/seats-and-billing/route.ts+12
src/app/api/internal/team-invitations/[invitationId]/reset/route.ts+31
src/app/api/internal/team-invitations/[invitationId]/revoke/route.ts+31
src/app/api/internal/team-invitations/route.ts+53
src/components/settings/settings-sidebar.tsx+15 −1
src/components/settings/team/invite-member-fields.stories.tsx+34
src/components/settings/team/invite-member-fields.tsx+63
src/components/settings/team/invite-member-sheet.stories.tsx+30
src/components/settings/team/invite-member-sheet.tsx+93
src/components/settings/team/member-actions.stories.tsx+39
src/components/settings/team/member-actions.tsx+106
src/components/settings/team/member-identity-cell.stories.tsx+23
src/components/settings/team/member-identity-cell.tsx+40
src/components/settings/team/member-table-row.stories.tsx+34
src/components/settings/team/member-table-row.tsx+89
src/components/settings/team/members-table.stories.tsx+35
src/components/settings/team/members-table.tsx+76
src/components/settings/team/pending-invite-actions.stories.tsx+35
src/components/settings/team/pending-invite-actions.tsx+67
src/components/settings/team/pending-invite-row.stories.tsx+32
src/components/settings/team/pending-invite-row.tsx+86
src/components/settings/team/pending-invites-table.stories.tsx+31
src/components/settings/team/pending-invites-table.tsx+62
src/components/settings/team/presentation.ts+111
src/components/settings/team/revoke-invitation-dialog.stories.tsx+27
src/components/settings/team/revoke-invitation-dialog.tsx+51
src/components/settings/team/role-select.stories.tsx+29
src/components/settings/team/role-select.tsx+60
src/components/settings/team/seat-meter.stories.tsx+19
src/components/settings/team/seat-meter.tsx+49
src/components/settings/team/team-fixtures.ts+86
src/components/settings/team/team-list-content.stories.tsx+47
src/components/settings/team/team-list-content.tsx+91
src/components/settings/team/team-list-page.stories.tsx+25
src/components/settings/team/team-list-page.tsx+141
src/components/settings/team/team-list-toolbar.stories.tsx+29
src/components/settings/team/team-list-toolbar.tsx+61
src/components/settings/team/team-page-actions.stories.tsx+25
src/components/settings/team/team-page-actions.tsx+50
src/components/settings/team/team-storybook-helpers.tsx+85
src/components/settings/team/team-table-layout.ts+13
src/components/settings/team/use-team-list-actions.ts+127
src/components/settings/team/use-team-mutations.ts+53
src/components/shared/status-badge.stories.tsx+10 −4
src/components/shared/status-badge.tsx+9 −2
src/gen/client/agentsAtlasIntakeReview.ts+76
src/gen/client/index.ts+1
src/gen/hooks/index.ts+4
src/gen/hooks/useAgentsAtlasIntakeReview.ts ★+156
src/gen/index.ts+28
src/gen/schemas/appFirmMemberStatus.json+2 −2
src/gen/schemas/appFirmMemberStatusFilter.json+1 −1
src/gen/schemas/atlasReviewRequest.json+29
src/gen/schemas/firmMemberListItem.json+2 −2
src/gen/schemas/firmMemberListResponse.json+2 −2
src/gen/types/AgentsAtlasIntakeReview.ts+60
src/gen/types/AppFirmMemberStatus.ts+2 −2
src/gen/types/AppFirmMemberStatusFilter.ts+1 −5
src/gen/types/AtlasReviewRequest.ts+34
src/gen/types/FirmMemberListItem.ts+1 −1
src/gen/types/index.ts+12
src/gen/zod/agentsAtlasIntakeReviewSchema.ts+77
src/gen/zod/appFirmMemberStatusFilterSchema.ts+1 −1
src/gen/zod/appFirmMemberStatusSchema.ts+3 −3
src/gen/zod/atlasReviewRequestSchema.ts+23
src/gen/zod/firmMemberListItemSchema.ts+1 −1
src/gen/zod/index.ts+11
src/lib/kubb-client.ts+12
src/lib/members/gateway.ts ★+168
src/lib/members/query.ts+6
src/lib/team-invitations/hooks.ts+130
src/lib/team-invitations/query.ts+2
src/lib/team-invitations/schema.ts+56
src/lib/team-invitations/server.ts ★+253