LincPayandClaude Opus 5 e923daf825 feat(integration): DPD history tab — coverage, generation and backfill
A fourth tab on /officer/integration reading the loan book as a training
set. The verdict banner is the point of the screen: DPD history is the
model's label, and three thousand generated snapshots look identical on
a chart to three thousand real ones, so the split is stated in words at
the top rather than left to be inferred from a percentage.

Four tiles, and "unknown provenance" is one of them — rows written
before stamping existed are shown as their own bucket rather than
counted as real. Generation and purge sit beside a backfill-from-core
control, with the honest note that a backfill from a simulated adapter
produces simulated rows and only a bank's own core counts toward the
real total.

The demo fixture deliberately reports a book that is NOT trainable. A
fixture claiming a healthy training set would defeat the entire purpose
of the panel.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 18:58:14 +05:30

UdyamLinc — UI

Three surfaces in one Vite/React app, matching the three institutional tiers. Full project documentation, including the demo path and what is real versus seeded, lives in ../UdyamLinc-Backend/README.md.

npm install
npm run dev        # http://localhost:5182

The backend is expected at http://localhost:8082/api/v1/udyam. Override with VITE_UDYAM_API_BASE_URL in .env.local.


The three surfaces

Route Tier Shell
/nabard NABARD — aggregates only features/nabard
/officer Bank console — one institution's book features/officer
/app Enterprise PWA — offline, Hindi-first features/enterprise

Both staff tiers share one sign-in form and land in different portals; homeFor() picks the destination from the role. That routing is a convenience only — the backend re-derives the tier from the signed JWT and refuses the other surface with a 403, so a tampered session gains nothing.

The NABARD portal is deliberately a different chrome from the bank console. An apex user and a branch officer are not two ranks of one job, and a portal that looked identical would invite the assumption that the apex user is a bank user with more rights.


The house pattern

Every screen follows the same shape, and new ones should:

const { data, isError } = useQuery({ queryKey: [...], queryFn: () => get<T>('/path') })
const demo = isError
const rows = data ?? demoRows()      // in-file fallback from lib/demo/
…
{demo && <StatusPill status="Demo data" />}

A screen must render with the backend down — field devices are on patchy 2G — but it must say so. The "Demo data" pill means that screen's call failed, not that the whole app is a mock.

Demo fallbacks deliberately include failure states: the data-entry fallback contains rejected rows, and the NABARD fallback contains a bank below the suppression threshold. A demo where everything is clean teaches the wrong thing — a reviewer who only ever sees green rows will not know the rejection panel exists.


Screens that carry the argument

  • /officer/rules — every trip-wire with its outcome, including the ones that were skipped and why. This is the screen that shows the system refusing to fire on stale data.
  • /officer/feeds — provenance. Mandi rows carry a source: Agmarknet, or an amber 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 here.
  • /officer/data-entry — upload, preview, commit. Accepted, rejected and overwriting rows stay in three separate groups.
  • /officer/integration — core banking / LMS. The mapping table is the argument: each lender's core names the same eleven fields differently, and a per-bank mapping is what absorbs that. Simulated adapters are badged as simulated on every screen they appear on, and unmapped vendor columns are shown being dropped at the boundary.
  • /nabard — the India choropleth and the zone → state → district drill-down. States with no portfolio are drawn in a neutral grey that is nowhere on the stress ramp, so absence is never read as health.

Checks

npx tsc -b        # types
npx oxlint src    # lint
npm run build     # production build

All three are expected to pass before a commit.


Attribution

State boundaries by the DataMeet India community (datameet/maps), used under CC BY 4.0. Simplified for display and not an authoritative depiction of any boundary. The credit renders on the map itself, as the licence requires.

S
Description
No description provided
Readme
499 KiB
Languages
TypeScript 95.8%
CSS 3.5%
JavaScript 0.4%
HTML 0.2%
Dockerfile 0.1%