PR: #385: feat(documents): Filing By column + Side badges + group/slice [Story-1534] by @ankitmaheshwari-com
View PR →
Story 1534 · EPIC-1489 Documents & Intake · APP-1 Filing-By

The Documents Register learns whose filing it is

The Record is the complete case picture — our filings and the opponent's and the court's, sitting in one register. Until now it showed what a filing is (filing type) and its state (draft / filed) — but never whose it is. A "Reply Affidavit · Filed" row read the same whether we filed it or the claimant did. This Story adds the missing axis: a Filing By column (filer + label + a coloured Side badge), and the ability to group and slice the whole register by Side and by Participant.

🛰️ Satellite — why "whose" is a first-class question

The stakes: counsel reading the Record all day asks three questions of every filing — what is it, where is it in its life, and who put it on the table. We answered the first two and left the third to memory and filename archaeology. "Show me everything the opposing side filed" or "everything from Vega Capital" required eyeballing a column that does not exist. For an adversarial record that is not a nicety; it is the difference between reading our case and reading the dispute.

The naive fix bolts a free-text "filed by" string onto each document and calls it done. It detonates the moment you want to filter: free text does not bucket ("Vega", "Vega Capital", "Vega Cap." are three parties to a computer), and it has no notion of side — the one axis counsel actually pivots on. The real fix recognises two distinct things: a participant (a real party with a stable name) and a side (a fixed, four-value structural axis relative to our client). Both are already on the wire — this Story renders them and makes them pivotable.

✈️ Airplane — one cell, two axes, the engine you already have

Think of the register as a filing cabinet where every document carries a routing slip. The slip names the filer ("Vega Capital"), their role in this proceeding ("Claimant"), and a coloured tab for the side of the room they sit on. The Filing By column is that slip; the coloured tab is the Side badge:

SideBadgeMeaning (relative to our client)
oursOursOur own client's filing.
opposingOpposingThe adversary's filing.
courtCourt/TribunalThe neutral authority's filing (order, direction).
othersOthersAny other party (e.g. a regulator). The default until classified.

Crucially, the pivots are two axes, not one. "Side" buckets the whole record into Ours / Opposing / Court·Tribunal / Others; "Participant" buckets it by the individual filer. Both run through the exact view-state engine from Story-1510 (DOCS-VIEWENGINE) — the same grouping, slicing, counting, and sentinel machinery that already powers document-type and tag. This Story adds no new engine logic; it teaches the existing engine two new fields and renders them.

🔬 Microscope — the parts that earn their place

Why one display column but two group/slice axes

Counsel wants to see the filer, label, and side together in one cell — that is one column, filingBy. But they want to pivot on side or on participant independently. A single column maps to a single group/slice field, so the cell column cannot carry both pivots. The clean shape: filingBy is a normal display column (sortable by filer name, not directly groupable), and the two pivots live as axis fields — filingSide and filingParticipant — that surface only in the Group-by / Slice-by pickers and as group headers, never as their own table columns.

// columns.ts — axis fields are group/slice-only, not display columns
export const AXIS_FIELDS = [
  { key: "filingSide",        groupable: true, sliceable: true, sortable: false, ... },
  { key: "filingParticipant", groupable: true, sliceable: true, sortable: false, ... },
];
// they join GROUP_FIELDS / SLICE_FIELDS and getColumn(), but NOT REGISTER_COLUMNS

Why Side is the only value the engine must localize

Every other group/slice value the engine emits is already display-ready: a document type, a tag, a participant name are human strings from data. Side is different — it is a fixed enum (ours / opposing / court / others) that must read as Ours · Opposing · Court/Tribunal · Others, and correctly in Hindi. So the slice navigator and the group header special-case only the filingSide axis, mapping the token through DocumentsRegister.sideLabels.*. The participant axis needs no such treatment — its value is the name itself.

Why four distinct tokens, never a hex literal

Side is a structural axis, so it earns four distinct, token-backed hues: ours = green (our own), opposing = crimson (the adversary — deliberately not the red reserved for the failed status), court = the brand accent (neutral authority), others = neutral paper chrome. All live in presentation.ts as SIDE_TONE — no inline colour ever touches a component.

Why "filed by" is a read-time derive, never stored

The filing lifecycle lives on the Filing, never the document (ADR-125). FN-2 derives the filer from the linked Filing's filed_by_participant_id → participant name + per-proceeding label + side, and serves it on DocumentFilingLinkView.filedBy (FiledByView in src/gen). The adapter maps that straight onto the row; nothing about "whose" is stored on the document. A row with no link, or a filing that names no filer, simply renders "—" and falls into the No filer sentinel bucket.

Filings stay in the Record. This Story does not move filings out — it keeps the filingType and filingState columns exactly as they were and adds Filing By alongside. The Record is where you see everything; the Filings tab is where you edit. Both truths now coexist on the same row.

What shipped

FileChange
lib/documents/register/types.tsRegisterDocument gains filingParticipant / filingParticipantLabel / filingSide; FieldKey gains filingBy / filingSide / filingParticipant; new no-filer sentinel.
lib/documents/register/columns.tsfilingBy display column (after Filing state) + AXIS_FIELDS (Side, Participant) wired into GROUP_FIELDS / SLICE_FIELDS / getColumn / membership / sentinels.
lib/documents/register/adapter.tsMaps FN-2 filedBy (name + label + side) onto the row; null when unlinked / no filer.
components/.../filing-side-badge.tsxNew FilingSideBadge primitive — four token-backed hues, localized label.
components/.../register-cell.tsxfilingBy cell: filer name + label + Side badge ("—" when no filer).
register-slice-navigator.tsx / register-group-header.tsx / register-table.tsxSide-axis enum tokens localized in the slice rail and group headers (header receives the group field).
view-popover/icons.tsGlyphs for the three new fields (Stamp / Split / Users).
messages/{en,hi}.jsoncolumns.filingBy/filingSide/filingParticipant, sideLabels.*, sentinels.noFiler — both locales.
Stories & testsfiling-side-badge.stories (4 badges + all-sides), table "Grouped by Side / Participant", slice-nav "Slice by Side"; adapter + engine tests for the new derive and axes.

Mode-1 honoured: no AI naming/classification. Tokens only — no hex/rgb literals (verified). i18n en + hi at parity. Imports the generated FiledByView / FilingSide from src/gen; no codegen run (FN-1/FN-2 already merged + synced).

One-sentence recall

Filing By turns every Record row's routing slip — filer, label, and a coloured Side tab — into a first-class cell, and lets counsel pivot the whole record by Side or by Participant through the engine that already exists.

Files changed

18 file(s), +372 / -19 lines

Path Role + -
messages/en.jsonConfig+11-1
messages/hi.jsonConfig+11-1
src/components/documents/register/filing-side-badge.stories.tsxComponent+45-0
src/components/documents/register/filing-side-badge.tsxComponent+36-0
src/components/documents/register/presentation.tsComponent+36-0
src/components/documents/register/register-cell.tsxComponent+20-1
src/components/documents/register/register-group-header.tsxComponent+10-4
src/components/documents/register/register-slice-navigator.stories.tsxComponent+8-0
src/components/documents/register/register-slice-navigator.tsxComponent+11-4
src/components/documents/register/register-table.stories.tsxComponent+28-0
src/components/documents/register/register-table.tsxComponent+1-0
src/components/documents/register/view-popover/icons.tsComponent+6-0
src/lib/documents/register/adapter.test.tsLibrary+21-0
src/lib/documents/register/adapter.tsLibrary+5-0
src/lib/documents/register/columns.tsLibrary+59-7
src/lib/documents/register/fixtures.tsLibrary+16-0
src/lib/documents/register/types.tsLibrary+23-1
src/lib/documents/register/view-state.test.tsLibrary+25-0