zurich_kotak/src/layout/Shell.jsx
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

72 lines
2.4 KiB
JavaScript

import { NavLink, Outlet } from 'react-router-dom'
import { useZino } from '../api/provider.jsx'
import { STAGES } from '../api/config.js'
import logo from '../assets/brand/zurich_logo.webp'
import './Shell.css'
/**
* The sidebar IS the pipeline, in the order a lead moves — so the shape of the
* process is legible before any record is opened. The one human queue is marked
* as such, because it is the only place the machine stops.
*/
export default function Shell() {
const { user, signOut } = useZino()
const work = STAGES.filter((s) => s.kind !== 'end')
const closed = STAGES.filter((s) => s.kind === 'end')
return (
<div className="shell">
<header className="shell__top">
<div className="shell__brand">
<img src={logo} alt="Zurich Kotak General Insurance" />
<div>
<strong>Lead Desk</strong>
<span>Lead to Policy</span>
</div>
</div>
<div className="shell__who">
<div className="shell__user">
<strong>{user?.name || user?.email || 'Signed in'}</strong>
<span>{user?.email}</span>
</div>
<button type="button" className="shell__signout" onClick={signOut}>
Sign out
</button>
</div>
</header>
<div className="shell__body">
<nav className="shell__nav" aria-label="Pipeline">
<p className="shell__navlabel">Pipeline</p>
{work.map((s) => (
<NavLink
key={s.uid}
to={`/stage/${s.uid}`}
className={({ isActive }) =>
'shell__stage' + (isActive ? ' is-active' : '') + (s.kind === 'human' ? ' is-human' : '')
}
>
<span className="shell__stagename">{s.name}</span>
{s.kind === 'human' ? <span className="shell__badge">you</span> : null}
</NavLink>
))}
<p className="shell__navlabel shell__navlabel--closed">Closed</p>
{closed.map((s) => (
<NavLink
key={s.uid}
to={`/stage/${s.uid}`}
className={({ isActive }) => 'shell__stage' + (isActive ? ' is-active' : '')}
>
<span className="shell__stagename">{s.name}</span>
</NavLink>
))}
</nav>
<main className="shell__main">
<Outlet />
</main>
</div>
</div>
)
}