PR: #750: feat(settings): archive & delete lifecycle surfaces [Story-2923] by @ayush-pramaan
View PR →
Story-2923

Archive & delete lifecycle surfaces

Settings → Matter configuration. Participant labels become archive-only with a where-used split and an impact-aware archive confirm; matter-type delete gains a cascade confirm and drops the duplicate in-use hint. Extends the Story-2920 catalogue surfaces.

What changed

Participant labels are archive-only

The row menu is Edit · Archive/Restore — no Delete (participant-label-row-actions.tsx). Durable references mean a label is retired by archiving, never destroyed.

Where-used split column

The catalogue table gains a Where it is used column showing the split as two prose lines — recorded on matters (durable references) and attached to matter types (detachable options) — read straight from the generated list item (recordedOnMatters / attachedToMatterTypes), never computed client-side.

Shared five-column matter-type table

Both tabs use one column set (AC #4, per the design): Matter type · Participant labels (pills +N) · In use · Reference · kebab. The In-use badge carries the count (amber when in use), and an uncurated type shows a Not set — all labels badge.

Archive confirm with impact

participant-label-archive-dialog.tsx reads useAppGetParticipantLabelArchiveImpact to name every matter type that stops offering the label and flag any last client-side option (intake then falls back to the firm catalogue).

Restore re-attaches, in order

Restore is a direct, optimistic unarchive; the backend re-attaches the label everywhere it was offered and the exact counts reconcile on invalidate.

Matter-type delete cascade

matter-type-catalog-delete-dialog.tsx lists the participant-label attachments that will be removed and states the labels themselves survive — only the attachment is removed.

Delete disabled, no duplicate hint

The kebab’s disabled Delete drops its tooltip (matter-type-catalog-row-actions.tsx); the reason a type can’t be deleted is carried by the In use column.

Optimistic, cross-catalog writes

Archive, restore, and delete reflect instantly and roll back on failure per the AGENTS.md non-negotiable. Because a label’s availability and a matter type’s attachment count are two sides of the same fact, each write patches both catalogues in one snapshot path and reconciles both (plus the per-type label lists that back the pills) on settle.

Action Instant cache patch Reconciled on settle
Archive label Label leaves Live tab, its attachedToMatterTypes zeroes; each affected matter type’s attachedParticipantLabels decrements. Participant-labels + matter-types lists + per-type label lists.
Restore label Label returns to the Live tab immediately. Same three collections — re-attached counts come back from the server.
Delete matter type Row drops; each attached label’s attachedToMatterTypes decrements. Matter-types + participant-labels lists + per-type label lists.

Scenario coverage

Scenario Surface Storybook story
PLM-26 — matter-type delete cascade + labels survive matter-type-catalog-delete-dialog.tsx Settings/MatterTypes/MatterTypeCatalogDeleteDialog → With attachments
PLM-27 — archive confirm names affected matter types participant-label-archive-dialog.tsx Settings/ParticipantLabels/ParticipantLabelArchiveDialog → With affected types
PLM-28 — last-attachment flag / catalogue fallback participant-label-archive-dialog.tsx … → Last option only · No impact
PLM-42 — where-used split column participant-labels-table.tsx Settings/ParticipantLabels/ParticipantLabelsTable → Where used (both lines)
PLM-43 — delete disabled when in use, no duplicate hint matter-type-catalog-row-actions.tsx Settings/MatterTypes/MatterTypeCatalogTable → In use (delete disabled)

Contracts (from src/gen)

Reads

useAppListParticipantLabels (where-used split), useAppGetParticipantLabelArchiveImpact (archive preview), useAppListMatterTypes, useAppListMatterTypeParticipantLabels (delete cascade).

Writes

useAppArchiveParticipantLabel, useAppUnarchiveParticipantLabel, useAppDeleteMatterType.

New trust boundary

Archive-impact gains a browser→server proxy (…/participant-labels/[participantLabelId]/archive-impact/route.ts) and a gateway method that parses upstream JSON through participantLabelArchiveImpactResponseSchema (HR-21). Round-trip covered by archive-impact.test.ts.

Files changed

30 file(s), +1066 / -379 lines

Path Role + -
messages/en.jsonConfig+18-18
messages/hi.jsonConfig+18-18
src/app/[locale]/(workspace)/settings/matter-configuration/participant-labels/page.tsxApp route+7-2
src/app/api/internal/participant-labels/[participantLabelId]/archive-impact/route.tsApp route+40-0
src/components/settings/matter-types/matter-type-catalog-delete-dialog.stories.tsxComponent+94-0
src/components/settings/matter-types/matter-type-catalog-delete-dialog.test.tsxComponent+91-0
src/components/settings/matter-types/matter-type-catalog-delete-dialog.tsxComponent+61-4
src/components/settings/matter-types/matter-type-catalog-list-page.tsxComponent+3-3
src/components/settings/matter-types/matter-type-catalog-row-actions.tsxComponent+0-1
src/components/settings/matter-types/matter-type-catalog-table-layout.tsComponent+3-2
src/components/settings/matter-types/matter-type-catalog-table-row.tsxComponent+5-21
src/components/settings/matter-types/matter-type-catalog-table.stories.tsxComponent+70-14
src/components/settings/matter-types/matter-type-catalog-table.tsxComponent+1-7
src/components/settings/matter-types/use-matter-type-catalog-actions.tsComponent+60-29
src/components/settings/participant-labels/participant-label-archive-dialog.stories.tsxComponent+108-0
src/components/settings/participant-labels/participant-label-archive-dialog.test.tsxComponent+100-0
src/components/settings/participant-labels/participant-label-archive-dialog.tsxComponent+122-0
src/components/settings/participant-labels/participant-label-delete-dialog.tsxComponent+0-50
src/components/settings/participant-labels/participant-label-row-actions.tsxComponent+8-18
src/components/settings/participant-labels/participant-label-table-row.tsxComponent+26-23
src/components/settings/participant-labels/participant-labels-list-page.tsxComponent+20-12
src/components/settings/participant-labels/participant-labels-table-layout.tsComponent+2-1
src/components/settings/participant-labels/participant-labels-table.stories.tsxComponent+18-15
src/components/settings/participant-labels/participant-labels-table.tsxComponent+1-8
src/components/settings/participant-labels/use-participant-label-actions.tsComponent+113-113
src/lib/matter-types/presentation.tsLibrary+0-9
src/lib/participant-labels/archive-impact.test.tsLibrary+50-0
src/lib/participant-labels/gateway.tsLibrary+25-0
src/lib/participant-labels/presentation.tsLibrary+0-9
src/lib/surface-registry/route-registry.tsLibrary+2-2