Inline exhibit renumber + the two-identity row
Story-1886 shipped the Exhibit Register read-only, with two deferred
halves blocked on the backend contract. The Story-1891 contract is now
synced (codegen-sync #1894), so both are wired: counsel
single-clicks an exhibit number to renumber it in place,
and each row now shows the document's two identities together
— the drafter's annexure label and the tribunal exhibit number on one
row. No new contract fields invented; everything reads from the synced
DocumentExhibitMarkingView and the renumber PATCH.
What shipped
The exhibit-number cell is now an ExhibitLabelCell: click
the number → it becomes an input. Enter (or blur) saves via the
generated useAppRenumberExhibitMarking PATCH; Escape
cancels. On success the Exhibit Register query is invalidated so the row
reconciles with the server (the service may normalize the label).
A label that collides with another marking in the same party series
returns 409. The proxy passes the upstream ErrorResponse
straight through; the cell keeps the editor open and shows the message
inline under the field (role="alert"), so
counsel picks another number without losing context.
The Annexure column now renders marking.filingLabel (the
drafter's "Annexure A", JOINed onto the marking view in Story-1891)
beside the editable exhibit number. A standalone document with no
annexure label shows —.
Renumber is a counsel act: the route is matters:write gated
and the service stamps source='human'. There is no agent
path in the UI — an agent can never reach the PATCH. The edit is
audited server-side (exhibit_marking.renamed, old→new).
How the write threads through (BFF, HR-21, HR-22)
The browser only ever talks to /api/internal/*. The generated
client PATCHes the absolute /app/…/exhibit-register/{markingId}
path; toBrowserRoute in lib/kubb-client.ts maps it
onto a new BFF proxy at the matching /api/internal path. That
proxy parses the body with the generated
exhibitMarkingRenumberRequestSchema (HR-21 trust-boundary
parse), forwards through filingsGateway.renumberExhibitMarking
(which re-asserts the DocumentExhibitMarkingView response), and
passes a GatewayError — 409 included — straight back
with its status. All contract types come from src/gen (HR-22);
nothing hand-written.
Empty / zero first-run & privilege
A proceeding with no markings shows the existing empty state (no editable
cells, no crash). The privilege gate is unchanged: privileged documents are
dropped by the upstream JOIN (DR-2) and never reach the register, so the
renumber path can only ever touch a tenderable, non-privileged marking. The
read-only members case is enforced by the backend (matters:write
→ 403), surfaced inline like any other failure.
Files
components/matters/filings/exhibit-label-cell.tsx— new single-click inline editor: edit state, save/cancel, invalidate-on-success, inline 409 error.components/matters/filings/exhibit-register-table.tsx— rendersfilingLabelin the Annexure column + the editable cell; takesmatterId/proceedingId.components/matters/filings/filings-tab.tsx— passes ids down; hint copy flipped from "read-only / gated" to the live renumber hint.lib/filings/gateway.ts—renumberExhibitMarking(PATCH + response assert).app/api/internal/…/exhibit-register/[markingId]/route.ts— new PATCH BFF proxy.lib/kubb-client.ts—toBrowserRoutemapping for the per-marking PATCH path.messages/{en,hi}.json—Filings.renumberHint+Filings.exhibits.renumber*in both locales.- Fixtures + story:
filingLabel/parentDocumentIdon the exhibit fixtures; story wrapped in aQueryClientProviderfor the mutation.
Verify
pnpm typecheck (clean),
pnpm lint (0 errors),
pnpm build (green — new
/exhibit-register/[markingId] route registered),
pnpm test (full suite passing), and
pnpm check:routes (registry fresh). i18n parity
confirmed across en / hi; no raw color literals
(semantic tokens + token-backed Tailwind only). Types imported from
src/gen off current main — no codegen run.
Files changed
17 file(s), +483 / -31 lines
| Path | Role | + | - |
|---|---|---|---|
| messages/en.json | Config | +5 | -2 |
| messages/hi.json | Config | +5 | -2 |
| openapi/app-api.json | Config | +32 | -0 |
| src/app/api/internal/matters/[matterId]/proceedings/[proceedingId]/exhibit-register/[markingId]/route.ts | App route | +92 | -0 |
| src/components/matters/filings/exhibit-label-cell.tsx | Component | +147 | -0 |
| src/components/matters/filings/exhibit-register-table.stories.tsx | Component | +29 | -13 |
| src/components/matters/filings/exhibit-register-table.tsx | Component | +19 | -5 |
| src/components/matters/filings/filings-fixtures.ts | Component | +4 | -0 |
| src/components/matters/filings/filings-tab.tsx | Component | +10 | -5 |
| src/gen/schemas/documentListItem.json | Generated contract | +16 | -0 |
| src/gen/schemas/documentListResponse.json | Generated contract | +16 | -0 |
| src/gen/schemas/evidenceIndexManifest.json | Generated contract | +16 | -0 |
| src/gen/schemas/recordBootstrapResponse.json | Generated contract | +16 | -0 |
| src/gen/types/DocumentListItem.ts | Generated contract | +13 | -0 |
| src/gen/zod/documentListItemSchema.ts | Generated contract | +19 | -0 |
| src/lib/filings/gateway.ts | Library | +31 | -4 |
| src/lib/kubb-client.ts | Library | +13 | -0 |