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>
104 lines
4.3 KiB
Markdown
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 |
|