/officer/portfolio — sectors first, then the enterprises inside one.
The existing enterprises screen is a flat ranked list. It answers 'who is
worst' but not 'where is the problem', and a sector is the unit a shock
actually arrives in: a feed price moves against poultry, a milk rate
against dairy. So the count of high-risk enterprises per sector is the
first thing on the page.
Icons come from lucide-react rather than hand-drawn paths — a supervisor
scans this grid before reading it, so the shape is doing the work. One
literal icon per sector: milk, egg, wheat, scissors, storefront.
Drill-down keeps the same shape as the rest of the console: band filter
chips, a ranked table with a six-month trend line, and a detail panel with
the score donut, the loan position and a link to the full risk file. Only
the trend's endpoint dot is coloured; a fully coloured line would read as
a severity gradient it does not mean.
Reads /officer/enterprises with the house demo fallback and a Demo data
pill. Verified against the live backend: 142 enterprises across five
sectors, bands and six-month trends all present.
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>