PR: #476: feat(matters): Hearings tab — Sessions + Orders & Directions [Story-1887] by @ankitmaheshwari-com
View PR →
Story-1887

Hearings tab: events, not documents

A matter's Hearings tab now opens on two read-only event streams for the matter's Main proceeding — Hearing Sessions and Orders & Directions — switched by a segmented control. The doctrine line: documents are nouns, hearings are events. Every document lives in Filings (Story-1886); Hearings holds only sessions and orders and merely references documents. There is no Exhibit Register here.

What shipped

Contract reality — what powers each subtab (and the gaps)

The brief was explicit: do not invent an endpoint. There is no dedicated hearing_session / order entity in the synced src/gen contract yet (it lands in v2). So v1 reads the closest truthful sources and names every gap:

SurfaceBacking contractMapping & gap
Hearing Sessions GET /app/matters/{n}/proceedings/{pid}/stages → ProceedingStageView[] Session = stage title; Type = stageKey token (em-dash if absent); Date = stageDate ?? occurredAt; Status = stage status (Active / Archived). Gap: Forum / Bench has no backing field on ProceedingStageView — it renders an em-dash. A dedicated session "type" is approximated by the stable stage key.
Orders & Directions GET /app/audit/matters/{uuid}/proceedings/{pid}?category=decisions → AuditEventView[] No dedicated order entity exists, so Orders & Directions is the proceeding's audit timeline scoped to the decisions category — orders, rulings and directions are decision events. Headline = subjectLabel ?? action; meta = occurredAt + actorDisplayName. Gap: first page only (limit 50, no cursor paging in v1); a first-class order entity replaces this in v2.

Note the two matter ids: the stages path keys the matter by its firm-scoped number, the audit-timeline path by its UUID. They are different ids by contract — the page passes numericMatterId to sessions and matter.id to orders.

Explicitly deferred (not faked)

Out of scope

Filings / registers (#1886, already merged), Annexure Index export (#1888), and the Blake review surface (#1889). This Story is only the Hearings tab's two event subtabs.

Empty / first-run state

The real deliverable is the zero-row first run — a brand-new matter whose main proceeding has no recorded sessions or orders yet. Both subtabs render an explicit, honest empty panel (never a broken-looking blank), plus loading and error states, all sharing the register chrome. Verified in Storybook (Matters/Hearings/States) and against the real src/gen types — imports from src/gen only; no pnpm codegen run (off current main after Chore-1885).

Trust boundary & surface registry

Verify

Files changed

19 file(s), +762 / -7 lines

Path Role + -
messages/en.jsonConfig+34-1
messages/hi.jsonConfig+34-1
src/app/[locale]/(workspace)/matters/[matterId]/hearings/page.tsxApp route+94-0
src/app/api/internal/audit/matters/[matterId]/proceedings/[proceedingId]/route.tsApp route+48-0
src/app/api/internal/matters/[matterId]/proceedings/[proceedingId]/stages/route.tsApp route+7-0
src/components/matters/hearings/hearings-fixtures.tsComponent+78-0
src/components/matters/hearings/hearings-states.stories.tsxComponent+55-0
src/components/matters/hearings/hearings-tab.tsxComponent+101-0
src/components/matters/hearings/orders-list.stories.tsxComponent+29-0
src/components/matters/hearings/orders-list.tsxComponent+46-0
src/components/matters/hearings/presentation.tsComponent+21-0
src/components/matters/hearings/sessions-table.stories.tsxComponent+30-0
src/components/matters/hearings/sessions-table.tsxComponent+77-0
src/components/matters/live-matter-shell.tsxComponent+6-0
src/components/matters/matter-subnav.tsxComponent+1-1
src/components/matters/matter-tabs.tsxComponent+1-1
src/lib/hearings/gateway.tsLibrary+73-0
src/lib/kubb-client.tsLibrary+15-1
src/lib/surface-registry/route-registry.tsLibrary+12-2