/nabard, reachable only by a NABARD role. Four screens: national view with
the India→zone→state→district drill-down, partner-bank scorecard that
expands into branches, early-warning digest, and the institution register.
Deliberately a different chrome from the bank console. A NABARD user and a
branch officer are not two ranks of one job; a portal that looked identical
would invite the assumption that the apex user is a bank user with more
rights. The sidebar states on every screen that this tier holds no borrower
identity, so a supervisor can tell a partner bank what it can see without
checking.
Response rate is the column the scorecard is built around. Stress rate is
mostly a fact about a bank's districts and sectors; response rate is a fact
about the bank, it is the one no bank self-reports, and low values are
coloured to draw the eye.
Suppressed cells keep their row and lose their numbers — dropping the row
reads as 'no enterprises here', a different and wrong claim, and the one a
reader would most easily act on.
Login routes by tier: both staff tiers share one sign-in and land in
different portals. RequireApex bounces a bank session to /officer rather
than to /login, because signed in as the wrong tier is not signed out. The
routing is a convenience only — the backend re-derives the tier from the
signed JWT and refuses the other surface with a 403.
The demo fallback includes a partner bank below the suppression threshold,
so the withholding behaviour is visible without a backend.
tsc, oxlint and npm run build clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
/officer/data-entry — upload, preview, commit, and the batch history, for
the feeds no API serves and for the loan book.
The screen is built around one rule: nothing is written until someone has
seen what will be. Accepted, rejected and overwriting rows are shown as
three separate groups, because merging them into one list of rows hides
the only two questions the person clicking Commit has — what will not
land, and what will land on top of something already there. An overwriting
row shows the value it replaces beside the one replacing it.
The screen states in plain words that the effective date is the date being
reported about rather than today, and that when we learned it is recorded
separately — the distinction is invisible in a spreadsheet and expensive
to get wrong. Selecting the loan book adds the no-borrower-identity rule.
On the feeds page, mandi rows now carry a source: Agmarknet, or a 'Hand'
chip for a price somebody typed. By the time a price reaches a feature the
two are indistinguishable, so the distinction has to be visible there or
it is visible nowhere.
The demo fallback deliberately contains rejections. A demo where every
upload is clean teaches the wrong thing — a reviewer who only sees green
rows will not know the rejection panel exists.
tsc, oxlint and npm run build all clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Vite/React 19 console and enterprise PWA. Officer console screens, offline
ledger and sync, data feeds and trip-wire rules screens with freshness chip.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>