The Team drawer is where matter responsibility becomes visible.
The Matter Action Rail already opens a Team sheet. This story fills its body with the current roster, lead assignment controls, the admin-only disclosure, and a history tab for matter access and team audit events.
What counsel sees
The lead is shown with the existing lead chip. If the matter has no lead, a vacant state says so and eligible users can claim or assign it.
Members render through the existing add-matter member chip. Active eligible firm members can be added from the shared member combobox.
Controls stay visible but disabled when the user lacks the required permission. The drawer explains whether matter write access or Firm Admin access is required.
The second tab reads matter.viewed and team-audit events from the audit surface so reviewers can see who viewed or changed the team.
Permission matrix
| Control | Gate | Visible behavior when blocked |
|---|---|---|
| Add member | hasMattersWritePermission(me) |
The combobox remains in place but is disabled with a no-access prompt. |
| Remove member | hasMattersWritePermission(me) |
Member chips remain visible; remove affordances are disabled. |
| Claim vacant lead | hasMattersWritePermission(me) and the current user is eligible |
The vacant-lead action is shown but disabled if either condition fails. |
| Assign or make lead | isFirmAdmin(me), or current lead counsel for an on-roster transfer |
The lead action remains visible next to eligible rows but is disabled for users who cannot transfer or assign lead. |
The data path
| Drawer need | Generated hook | Internal route |
|---|---|---|
| Read roster | useAppGetMatterRoster |
/api/internal/matters/:id/roster |
| Add a member | useAppAddMatterTeamMember |
/api/internal/matters/:id/team |
| Remove a member | useAppRemoveMatterTeamMember |
/api/internal/matters/:id/team/:userId |
| Assign lead | useAppAssignMatterLead |
/api/internal/matters/:id/lead |
| Read history | useAppMatterAuditTimeline |
/api/internal/matters/:uuid/access-history |
Review path
| Storybook state | Expected proof |
|---|---|
| MatterDetail / Team / MatterTeamDrawer / Default | Lead, roster, add combobox, and admin disclosure render in the Team drawer body. |
| MatterDetail / Team / MatterTeamDrawer / ReadOnly | Roster stays visible; add, remove, and lead controls are disabled rather than hidden. |
| MatterDetail / Team / MatterTeamDrawer / VacantLead | The vacant-lead state offers claim or assignment according to eligibility. |
| MatterDetail / Team / MatterTeamDrawer / AccessHistory | The history tab shows matter.viewed and team-audit entries from the audit timeline. |
| MatterDetail / Team / MatterTeamDrawer / Empty | The no-team state still shows the add combobox and lead disclosure. |
What this story does not do
This story does not change the BaseSheet chrome or the Matter Action Rail icon. A2 already wired the open state; this story only supplies the body inside that sheet.
This story does not edit generated OpenAPI files or run codegen. The generated hooks and types already exist on app main and are consumed through browser-to-server proxy routes.
Files changed
29 file(s), +3248 / -74 lines
| Path | Role | + | - |
|---|---|---|---|
| messages/en.json | Config | +70 | -0 |
| messages/hi.json | Config | +70 | -0 |
| src/app/[locale]/(workspace)/matters/[matterId]/layout.tsx | App route | +14 | -3 |
| src/app/api/internal/matters/[matterId]/access-history/route.ts | App route | +47 | -0 |
| src/app/api/internal/matters/[matterId]/lead/route.ts | App route | +52 | -0 |
| src/app/api/internal/matters/[matterId]/roster/route.ts | App route | +37 | -0 |
| src/app/api/internal/matters/[matterId]/team/[userId]/route.ts | App route | +47 | -0 |
| src/app/api/internal/matters/[matterId]/team/route.ts | App route | +54 | -0 |
| src/components/add-matter/avatar.tsx | Component | +16 | -5 |
| src/components/add-matter/lead-counsel-chip.tsx | Component | +65 | -18 |
| src/components/add-matter/member-combobox-inline-picker.tsx | Component | +133 | -0 |
| src/components/add-matter/member-combobox-search-picker.tsx | Component | +4 | -1 |
| src/components/add-matter/member-combobox.tsx | Component | +44 | -11 |
| src/components/add-matter/team-member-chip.tsx | Component | +59 | -14 |
| src/components/matter-detail/team/matter-team-access-history.tsx | Component | +210 | -0 |
| src/components/matter-detail/team/matter-team-add-member-view.tsx | Component | +225 | -0 |
| src/components/matter-detail/team/matter-team-drawer.stories.tsx | Component | +209 | -0 |
| src/components/matter-detail/team/matter-team-drawer.tsx | Component | +430 | -0 |
| src/components/matter-detail/team/matter-team-fixtures.ts | Component | +320 | -0 |
| src/components/matter-detail/team/matter-team-roster.tsx | Component | +467 | -0 |
| src/components/matter-detail/team/matter-team-transfer-lead-dialog.tsx | Component | +145 | -0 |
| src/components/matter-detail/team/presentation.ts | Component | +170 | -0 |
| src/components/matters/base-sheet.tsx | Component | +35 | -12 |
| src/components/matters/matter-shell-chrome.tsx | Component | +45 | -3 |
| src/lib/kubb-client.ts | Library | +35 | -0 |
| src/lib/matters/gateway.ts | Library | +117 | -4 |
| src/lib/matters/types.ts | Library | +2 | -0 |
| tests/unit/matter-shell-chrome.test.tsx | Test | +25 | -3 |
| tests/unit/matter-team-proxy.test.ts | Test | +101 | -0 |