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
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.
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.
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.
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 is a direct, optimistic unarchive; the backend re-attaches the label everywhere it was offered and the exact counts reconcile on invalidate.
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.
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)
useAppListParticipantLabels (where-used split),
useAppGetParticipantLabelArchiveImpact (archive
preview), useAppListMatterTypes,
useAppListMatterTypeParticipantLabels (delete cascade).
useAppArchiveParticipantLabel,
useAppUnarchiveParticipantLabel,
useAppDeleteMatterType.
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.json | Config | +18 | -18 |
| messages/hi.json | Config | +18 | -18 |
| src/app/[locale]/(workspace)/settings/matter-configuration/participant-labels/page.tsx | App route | +7 | -2 |
| src/app/api/internal/participant-labels/[participantLabelId]/archive-impact/route.ts | App route | +40 | -0 |
| src/components/settings/matter-types/matter-type-catalog-delete-dialog.stories.tsx | Component | +94 | -0 |
| src/components/settings/matter-types/matter-type-catalog-delete-dialog.test.tsx | Component | +91 | -0 |
| src/components/settings/matter-types/matter-type-catalog-delete-dialog.tsx | Component | +61 | -4 |
| src/components/settings/matter-types/matter-type-catalog-list-page.tsx | Component | +3 | -3 |
| src/components/settings/matter-types/matter-type-catalog-row-actions.tsx | Component | +0 | -1 |
| src/components/settings/matter-types/matter-type-catalog-table-layout.ts | Component | +3 | -2 |
| src/components/settings/matter-types/matter-type-catalog-table-row.tsx | Component | +5 | -21 |
| src/components/settings/matter-types/matter-type-catalog-table.stories.tsx | Component | +70 | -14 |
| src/components/settings/matter-types/matter-type-catalog-table.tsx | Component | +1 | -7 |
| src/components/settings/matter-types/use-matter-type-catalog-actions.ts | Component | +60 | -29 |
| src/components/settings/participant-labels/participant-label-archive-dialog.stories.tsx | Component | +108 | -0 |
| src/components/settings/participant-labels/participant-label-archive-dialog.test.tsx | Component | +100 | -0 |
| src/components/settings/participant-labels/participant-label-archive-dialog.tsx | Component | +122 | -0 |
| src/components/settings/participant-labels/participant-label-delete-dialog.tsx | Component | +0 | -50 |
| src/components/settings/participant-labels/participant-label-row-actions.tsx | Component | +8 | -18 |
| src/components/settings/participant-labels/participant-label-table-row.tsx | Component | +26 | -23 |
| src/components/settings/participant-labels/participant-labels-list-page.tsx | Component | +20 | -12 |
| src/components/settings/participant-labels/participant-labels-table-layout.ts | Component | +2 | -1 |
| src/components/settings/participant-labels/participant-labels-table.stories.tsx | Component | +18 | -15 |
| src/components/settings/participant-labels/participant-labels-table.tsx | Component | +1 | -8 |
| src/components/settings/participant-labels/use-participant-label-actions.ts | Component | +113 | -113 |
| src/lib/matter-types/presentation.ts | Library | +0 | -9 |
| src/lib/participant-labels/archive-impact.test.ts | Library | +50 | -0 |
| src/lib/participant-labels/gateway.ts | Library | +25 | -0 |
| src/lib/participant-labels/presentation.ts | Library | +0 | -9 |
| src/lib/surface-registry/route-registry.ts | Library | +2 | -2 |