Files
udyamlinc/src/lib/demo/apex.ts
T
LincPayandClaude Opus 5 38067c41f8 feat(nabard): supervisory portal — national view, banks, early warning
/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>
2026-08-06 18:10:51 +05:30

171 lines
5.5 KiB
TypeScript

import type { ApexCell, ApexDashboard, ApexDigest, BankScorecardRow, InstitutionRow } from '../api'
/**
* Demo fallback for the NABARD portal.
*
* <p>Mirrors the real shape closely, including a partner bank below the
* suppression threshold. A demo where every cell is publishable would hide the
* one behaviour this tier is judged on: that small cells are withheld rather
* than quietly rounded.
*/
export function demoApexDashboard(): ApexDashboard {
return {
partnerBanks: 3,
branches: 6,
enterprises: 142,
red: 34,
watch: 8,
standard: 100,
stressRatePct: 23.9,
portfolioValue: 32_320_000,
openAlerts: 75,
minCellSize: 5,
dataAgeDays: 0,
}
}
export function demoBankScorecard(): BankScorecardRow[] {
return [
{
institutionCode: 'MPGB',
institutionName: 'Madhya Pradesh Gramin Bank',
shortName: 'MP Gramin',
type: 'RRB',
branches: 2,
enterprises: 44,
suppressed: false,
red: 10,
watch: 3,
standard: 31,
stressRatePct: 22.7,
avgScore: 34.1,
alertsRaised: 24,
alertsActioned: 2,
responseRatePct: 8.3,
outstanding: 9_940_000,
},
{
institutionCode: 'SEHDCCB',
institutionName: 'Sehore District Central Cooperative Bank',
shortName: 'Sehore DCCB',
type: 'COOPERATIVE',
branches: 2,
enterprises: 39,
suppressed: false,
red: 7,
watch: 2,
standard: 30,
stressRatePct: 17.9,
avgScore: 30.6,
alertsRaised: 22,
alertsActioned: 8,
responseRatePct: 36.4,
outstanding: 8_610_000,
},
{
institutionCode: 'VSFB',
institutionName: 'Vindhya Small Finance Bank',
shortName: 'Vindhya SFB',
type: 'SFB',
branches: 2,
enterprises: 59,
suppressed: false,
red: 17,
watch: 3,
standard: 39,
stressRatePct: 28.8,
avgScore: 37.9,
alertsRaised: 34,
alertsActioned: 17,
responseRatePct: 50,
outstanding: 13_770_000,
},
{
institutionCode: 'NEWCOOP',
institutionName: 'Betul Cooperative Bank (onboarding)',
shortName: 'Betul Coop',
type: 'COOPERATIVE',
branches: 1,
enterprises: 3,
// Below the platform's minimum cell size — nothing else may be published.
suppressed: true,
},
]
}
export function demoDistricts(): ApexCell[] {
return [
{ districtName: 'Bhopal', enterprises: 35, suppressed: false, red: 9, watch: 2, standard: 24, stressRatePct: 25.7, avgScore: 35.2 },
{ districtName: 'Raisen', enterprises: 34, suppressed: false, red: 8, watch: 2, standard: 24, stressRatePct: 23.5, avgScore: 33.8 },
{ districtName: 'Sehore', enterprises: 37, suppressed: false, red: 9, watch: 2, standard: 26, stressRatePct: 24.3, avgScore: 34.5 },
{ districtName: 'Vidisha', enterprises: 36, suppressed: false, red: 8, watch: 2, standard: 26, stressRatePct: 22.2, avgScore: 33.1 },
]
}
export function demoSectors(): ApexCell[] {
return [
{ sectorName: 'Handicrafts', enterprises: 30, suppressed: false, red: 12, watch: 1, standard: 17, stressRatePct: 40, avgScore: 42.6 },
{ sectorName: 'Poultry', enterprises: 27, suppressed: false, red: 6, watch: 1, standard: 20, stressRatePct: 22.2, avgScore: 33.4 },
{ sectorName: 'Food processing', enterprises: 28, suppressed: false, red: 6, watch: 3, standard: 19, stressRatePct: 21.4, avgScore: 32.8 },
{ sectorName: 'Dairy', enterprises: 30, suppressed: false, red: 5, watch: 3, standard: 22, stressRatePct: 16.7, avgScore: 30.2 },
{ sectorName: 'Rural retail', enterprises: 27, suppressed: false, red: 5, watch: 0, standard: 22, stressRatePct: 18.5, avgScore: 31.1 },
]
}
export function demoDigest(): ApexDigest {
const banks = demoBankScorecard().filter((b) => !b.suppressed)
return {
asOf: '2026-08-06',
districtsUnderStress: demoDistricts().sort((a, b) => (b.red ?? 0) - (a.red ?? 0)),
sectorsUnderStress: demoSectors().sort((a, b) => (b.red ?? 0) - (a.red ?? 0)),
slowestResponders: banks.sort((a, b) => (a.responseRatePct ?? 0) - (b.responseRatePct ?? 0)),
}
}
export function demoInstitutions(): InstitutionRow[] {
return [
{
code: 'NABARD',
name: 'National Bank for Agriculture and Rural Development',
shortName: 'NABARD',
type: 'APEX',
state: 'Madhya Pradesh',
branches: [],
},
{
code: 'MPGB',
name: 'Madhya Pradesh Gramin Bank',
shortName: 'MP Gramin',
type: 'RRB',
state: 'Madhya Pradesh',
branches: [
{ code: 'SEH01', name: 'Sehore Main Branch', region: 'Bhopal Region', blocks: 'Sehore' },
{ code: 'VID01', name: 'Vidisha Main Branch', region: 'Bhopal Region', blocks: 'Vidisha' },
],
},
{
code: 'SEHDCCB',
name: 'Sehore District Central Cooperative Bank',
shortName: 'Sehore DCCB',
type: 'COOPERATIVE',
state: 'Madhya Pradesh',
branches: [
{ code: 'SEH02', name: 'Sehore Cooperative Branch', region: 'Sehore Region', blocks: 'Sehore' },
{ code: 'RAI01', name: 'Raisen Cooperative Branch', region: 'Sehore Region', blocks: 'Raisen' },
],
},
{
code: 'VSFB',
name: 'Vindhya Small Finance Bank',
shortName: 'Vindhya SFB',
type: 'SFB',
state: 'Madhya Pradesh',
branches: [
{ code: 'BPL01', name: 'Bhopal City Branch', region: 'Bhopal Region', blocks: 'Bhopal' },
{ code: 'RAI02', name: 'Raisen Branch', region: 'Bhopal Region', blocks: 'Raisen' },
],
},
]
}