Participant-label curation editor
Settings → Matter configuration → Matter types now shows which participant labels each matter type offers at intake, and lets a config member curate that set: attach existing labels, mint new ones (dedup-aware), and detach. Non-config members see the labels read-only.
What changed
The matter-types table gains a Participant labels column
(pills — show 2, then a +N overflow pill) and an
In use column. The kebab row action now reads
Edit · Participant labels · Archive · Delete, extending
the shipped matter-type-catalog-row-actions.tsx.
A right-side sheet (matter-type-label-editor-sheet.tsx)
renders attached labels two-per-row
(name · reference · detach), a search-adder, and inline mint.
Opened from the kebab or by clicking the labels cell.
The Add-Matter drawer's dedup rules were lifted into
src/lib/participant-labels/dedup.ts and are reused by
both the drawer and this editor — one implementation, no twin.
How to use it
- Open Settings → Matter configuration → Matter types.
-
Each row shows its participant-label pills (first two, then
+N) and how many matters use it. - Click the labels cell, or the kebab → Participant labels, to open the editor.
- In the editor: type to search and click a label to attach it; type a brand-new name and press Create & attach; type the name of an archived label and it offers Restore & attach instead of a duplicate.
- Remove an attached label with the ✕. Every change saves immediately and reflects instantly (optimistic), rolling back only if the server rejects it.
Optimistic write model
use-matter-type-label-editor.ts owns every write. Each
mutation updates the label-list cache in onMutate,
reconciles from the mutation's authoritative response (server order +
reference numbers), and rolls the snapshot back on error. The
matter-types catalogue list is invalidated on settle so the row's
pills and attached count refresh. These endpoints are
settings:write-scoped — the curation editor is the correct
caller (unlike the drawer, which stages + attaches under
matters:write at Create).
Scenarios covered
| Scenario | Where |
|---|---|
PLM-16 — pills with +N overflow |
participant-label-pills.tsx ·
ParticipantLabelPills story
|
| PLM-19 — attach an existing label |
matter-type-label-editor-adder.tsx results list
|
| PLM-20 — mint a new label (dedup-aware) |
adder mint row + lib/participant-labels/dedup.ts
|
| PLM-22 — attached labels two-per-row |
matter-type-label-editor-attached-list.tsx
|
| PLM-39 — archived name offers Restore, not a twin | adder Restore & attach affordance |
| PLM-44 — non-config members see labels read-only | editor canWrite=false path (no controls, no adder) |
Verification
Isolated states are verified in Storybook under
Settings/MatterTypes: ParticipantLabelPills
(empty · two · +3 overflow),
MatterTypeLabelEditorAttachedList (two-per-row · read-only
· empty), MatterTypeLabelEditorAdder (attach · restore ·
create · all-attached), and MatterTypeLabelEditorSheet
(config · read-only · empty). Gate: pnpm lint,
pnpm typecheck, pnpm test, and
pnpm build all pass; the four new
/api/internal/matter-types/[matterTypeId]/participant-labels/*
routes are registered in the build output.
Files changed
32 file(s), +1968 / -13 lines
| Path | Role | + | - |
|---|---|---|---|
| messages/en.json | Config | +30 | -0 |
| messages/hi.json | Config | +30 | -0 |
| src/app/api/internal/matter-types/[matterTypeId]/participant-labels/[participantLabelId]/route.ts | App route | +47 | -0 |
| src/app/api/internal/matter-types/[matterTypeId]/participant-labels/attach/route.ts | App route | +61 | -0 |
| src/app/api/internal/matter-types/[matterTypeId]/participant-labels/create-and-attach/route.ts | App route | +63 | -0 |
| src/components/add-matter/participant-label-picker.tsx | Component | +10 | -12 |
| src/components/settings/matter-types/matter-type-catalog-list-page.stories.tsx | Component | +31 | -0 |
| src/components/settings/matter-types/matter-type-catalog-list-page.tsx | Component | +11 | -0 |
| src/components/settings/matter-types/matter-type-catalog-row-actions.tsx | Component | +7 | -0 |
| src/components/settings/matter-types/matter-type-catalog-table-layout.ts | Component | +2 | -1 |
| src/components/settings/matter-types/matter-type-catalog-table-row.tsx | Component | +22 | -0 |
| src/components/settings/matter-types/matter-type-catalog-table.stories.tsx | Component | +2 | -0 |
| src/components/settings/matter-types/matter-type-catalog-table.tsx | Component | +5 | -0 |
| src/components/settings/matter-types/matter-type-label-editor-adder.stories.tsx | Component | +54 | -0 |
| src/components/settings/matter-types/matter-type-label-editor-adder.tsx | Component | +183 | -0 |
| src/components/settings/matter-types/matter-type-label-editor-attached-list.stories.tsx | Component | +44 | -0 |
| src/components/settings/matter-types/matter-type-label-editor-attached-list.tsx | Component | +65 | -0 |
| src/components/settings/matter-types/matter-type-label-editor-body.stories.tsx | Component | +76 | -0 |
| src/components/settings/matter-types/matter-type-label-editor-body.tsx | Component | +124 | -0 |
| src/components/settings/matter-types/matter-type-label-editor-sheet.stories.tsx | Component | +74 | -0 |
| src/components/settings/matter-types/matter-type-label-editor-sheet.tsx | Component | +37 | -0 |
| src/components/settings/matter-types/matter-type-label-fixtures.ts | Component | +75 | -0 |
| src/components/settings/matter-types/matter-type-label-pills-cell.stories.tsx | Component | +84 | -0 |
| src/components/settings/matter-types/matter-type-label-pills-cell.tsx | Component | +60 | -0 |
| src/components/settings/matter-types/participant-label-pills.stories.tsx | Component | +50 | -0 |
| src/components/settings/matter-types/participant-label-pills.tsx | Component | +68 | -0 |
| src/components/settings/matter-types/use-matter-type-label-editor.test.tsx | Component | +163 | -0 |
| src/components/settings/matter-types/use-matter-type-label-editor.ts | Component | +273 | -0 |
| src/lib/matter-types/gateway.ts | Library | +103 | -0 |
| src/lib/matter-types/types.ts | Library | +3 | -0 |
| src/lib/participant-labels/dedup.test.ts | Library | +59 | -0 |
| src/lib/participant-labels/dedup.ts | Library | +52 | -0 |