zurich_kotak/README.md
Yashas 9210681488 Wire the console to the platform and make it deployable
The scaffold had the brand right and nothing behind it: the login was a
600ms setTimeout with a TODO, and three pieces of the frontgen deploy
contract were missing.

- Sign in against POST /usr/login (org_id as a STRING — a number is
  rejected by the gateway) with the session in a provider; surface the
  gateway's own message rather than a generic "invalid credentials",
  because a wrong password and a user without access to this app look
  identical from here and are not.
- Runtime config: the API URL is read from the config.js the server
  writes at placement, never compiled in, and requireConfigValue throws
  so a build with no config.js fails loudly instead of calling whichever
  backend built it.
- base: './' plus a router basename taken from <base href>, so one build
  serves any mount path.
- Move the fonts and logo from public/ into src/assets/ — Vite rewrites
  bundled asset URLs to be relative, while a public/ file referenced as
  "/fonts/..." stays absolute and 404s under the /zurich-kotak/ mount.
- API client for recordview / detailview / form-screens / start /
  activity, and the pipeline shell whose sidebar is the state machine in
  the order a lead moves.

Queue and lead-file data wait on the record and detail views.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 15:20:13 +05:30

104 lines
4.3 KiB
Markdown

# Zurich Kotak — Lead Desk
Operator console for the Zurich Kotak "Lead to Policy" demo. Leads arrive through
three channels — direct, bancassurance and agency — and run one state machine to
policy issuance. **The agents do the work; a person appears at one queue.**
Workflow config is seeded from `sm2/custom-apps/zurich-kotak/` (org **84**,
app **536**, workflow `zk_wf_lead`). Design doc:
`sm2/app-designs/zurich-kotak/design.html`. Workflow spec PDF:
`sm2/custom-apps/zurich-kotak/zurich-kotak-workflow.pdf`.
## Run it
```bash
npm install
npm run dev # http://localhost:5175
npm run build # → dist/
```
`.env` carries the API host **for local dev only**:
```
VITE_ZINO_API_URL=https://dev.getzino.in
```
Logins (all password `ZurichKotak@2026`, org `84`):
| Email | Role | Can do |
|---|---|---|
| `sanjay.uw@zurichkotak.example` | SME Underwriter | **Clear / Decline Referral** — the only human decision |
| `meera.rm@zurichkotak.example` | Bancassurance RM | Submit a **Partner Bank Lead** |
| `arjun.posp@zurichkotak.example` | POSP / Broker | Submit a **Partner Agent Lead** |
| `kavya.csr@zurichkotak.example` | Direct / Call centre | Submit a **Self-Serve Lead** |
| `deepa.ops@zurichkotak.example` | Operations | Cross-channel visibility |
Sign in as Sanjay to land in **Referred to Underwriting**, which is where the
demo happens.
## How it talks to the platform
Everything goes through `src/api/client.js`:
- `POST /usr/login` — auth. **`org_id` must be a string**; a number returns
`cannot unmarshal number into Go struct field LoginRequest.org_id`.
- `POST /app/536/view/recordview` — every queue is one record view
(`zk-rv-leads`) filtered server-side on `current_state_name`.
- `POST /app/536/view/form-screens` then `POST /app/536/activity` — forms are
read from the **live** activity schema and submitted straight back.
### Forms are not defined in this repo
Whatever `/view/form-screens` returns is what renders — labels, types, select
options, which fields are mandatory. Add a field to an activity in Studio,
redeploy, and it appears here with no frontend change. That is deliberate: the
workflow is the source of truth, and a hardcoded form would quietly diverge
from it.
### Permissions are never enforced here
The console does not hide a button to stop someone using it. Every permission
decision is the workflow's, made server-side on each submission — the platform
refuses and this app reports what it said. Two refusals worth knowing:
`/activity` answers **403**, `/start` answers **400**, and both carry
`permission denied`.
### The runtime-config contract
`VITE_ZINO_API_URL` is read at **runtime** from a `config.js` the server writes
when it places the build — never compiled in. One artifact is promoted between
environments unchanged, so a build-time URL would point every environment at
whichever backend happened to build it. `requireConfigValue` throws if it is
missing, so a production build with no `config.js` fails loudly rather than
calling the wrong backend.
`vite.config.js` uses `base: './'` and the router takes its basename from the
`<base href>` the server writes, so **one build serves any mount path**. Do not
reintroduce a build-time base.
**This is also why the fonts and logo live under `src/assets/` and not
`public/`.** Vite rewrites bundled asset URLs to be relative; a `public/` file
referenced as `/fonts/…` stays absolute and 404s under the `/zurich-kotak/`
mount path. The favicon is the one exception — it stays in `public/brand/` and
is referenced relatively from `index.html`.
## Deployment
Registered with frontgen by `sm2/custom-apps/zurich-kotak/05_frontgen_project.sql`
(slug `zurich-kotak`, repo_name `zurich_kotak`, org 84). A push to `main` builds
and serves at **https://preview-dev.getzino.in/zurich-kotak/**.
The webhook only fires on a **new** push — a push made before the frontgen
project row existed matched no project and did nothing.
## State of the build
| Piece | Status |
|---|---|
| Brand — Zurich Sans, palette, logo | done (kept from the original scaffold) |
| Sign-in against the real gateway | done |
| Runtime config, relative base, router | done |
| Pipeline sidebar (mirrors `tbl_wf_states`) | done |
| Queue lists | waiting on the `zk-rv-leads` record view |
| Lead file, activity forms, attribution panel | waiting on `zk-dv-lead` + form screens |