The phone app shipped with the API layer and the timeline copied from the
console but none of its dialogs, so several things it appeared to offer did
nothing at all:
- Tapping a WhatsApp exchange in the trail called an onOpenChat that was
never passed. The entry rendered, said "Open the conversation — all 7
messages", and there was no conversation to open.
- No screen had a New lead button. The entry activity, its permissions and
its form were all present and unreachable.
- An agent chip opened nothing; the call transcript and the recording were
unreachable; `call_transcript` was rendered as a field value, which is two
thousand characters cut at a hundred and fifty.
- The record showed its first three groups and truncated every long value
mid-sentence. There was no way to see the other sixty-odd fields.
- The action bar rendered only the stage's `do` actions, so Mark Lost, the
re-quote, the reminders and every recovery lever were unreachable — four
stages offered a partner agent one button and no way to drop a lead the
system was working.
- `nudgeFor` was called as nudgeFor(stage.uid, roles) rather than
nudgeFor(stage, lead), so `stage.uid` was undefined, the switch fell
through, and the way out of a stalled lead was never offered.
- The journey rail marked every step left of the current one as done, which
claims steps that never happened: a lead whose documents arrived from
Qualified skips Contacted, and a callback sends it backwards.
The dialogs are COPIED from the console rather than rewritten — the
conversation, the call, the agent card, the lead file, the new-lead form, and
the support desk — for the same reason api/ and Timeline.jsx already were: the
functionality has to be identical, and a second implementation is a second
account of the same lead.
What makes that work on a phone is one CSS block rather than six components.
They all render the console's prose__scrim / prose__dlg chrome, so app.css
re-points that chrome at phone width into a bottom sheet: full width, rounded
top, a grab handle via ::before so the copied markup is untouched, and
env(safe-area-inset-bottom) under the last row. Every dialog in the app becomes
phone-native at once, including the two ClampText was already opening. Above
700px the console's centred dialog is kept, which is right for a tablet.
Also here: the secondary actions get a sheet of their own, grouped by what they
ARE (optional, "lead not moving?", the way out) rather than listed as equals;
the record's long values fold instead of being cut, and the whole file opens
with a filter; the rail reads the audit for which steps this lead actually
entered; the overview grows the sign-off band for the role that owns it; and
the drawer grows Ask the desk for ops.
Verified: build clean, lint unchanged at the app's own baseline of 15, every
class the copied dialogs use resolves in the bundled CSS, and every dialog's
prop contract matches its call site. Not verified in a browser — no browser
tooling in this session.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|---|---|---|
| public | ||
| src | ||
| .env.example | ||
| .gitignore | ||
| eslint.config.js | ||
| index.html | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| vite.config.js | ||
Zurich Kotak — Lead Desk (phone)
The renewal desk on a phone. A separate app from the desktop console
(zurich_kotak), talking to the same platform with the same API client.
Why a second app rather than a breakpoint
The two answer different questions. The desktop console answers "what is the state of the book?" across a wide grid — six-column tables, a lead beside its record, a rail of queues always visible. A phone answers "what needs me, and what happened to this one?" in a column you scroll with a thumb.
Squeezing the first into 390px produced what it always produces: tables that scroll sideways, a menu that eats the first screenful, and a primary action somewhere below the fold. So the screens are written for the phone, and only the parts that must not diverge are shared.
What is shared, and what is not
Copied verbatim, because it is the contract with the platform — a divergence here would be two apps disagreeing about the same lead:
src/api/client.js every call
src/api/config.js stages, actions, doc slots, the workflow mirror
src/api/permissions.js who may perform what
src/api/portfolio.jsx the one record-view call the queues share
src/api/holder.js who is holding a lead
src/api/lead.js how to read a date, name a field, group the record
src/components/ActivityForm.jsx, FileField.jsx, Timeline.jsx
Timeline.jsx in particular is shared on purpose: its audit-row merging (one
submission writes three rows) is hard-won, and reimplementing it here would
mean two different accounts of the same history.
The dialogs are copied verbatim too — for the same reason, and because the functionality has to be identical rather than merely similar:
src/api/thread.js the WhatsApp thread, built once
src/components/Conversation.jsx the thread, with its gaps accounted for
src/components/CallTranscript.jsx
src/components/AgentCard.jsx what an agent is and what it can reach
src/components/LeadFileDialog.jsx the whole record, with a filter
src/components/NewLeadDialog.jsx filing a lead
src/components/AskDesk.jsx the support desk (ops only)
They are NOT restyled per file. They all render the console's prose__scrim /
prose__dlg chrome, and styles/app.css re-points that chrome at phone width
into a bottom sheet — full width, rounded top, a grab handle via ::before,
env(safe-area-inset-bottom) under the last row. One block, and every dialog
in the app is phone-native at once, including the two ClampText already opened.
Above 700px the console's centred dialog is kept, which is the right answer for
a tablet.
Add a dialog to the console and it can be copied here as-is. Restyle one of them here and the next copy silently reverts it.
Written fresh for the phone: every screen, the shell, and the stylesheet.
The colour rule
Unchanged from the console, and it is the whole product in four colours:
blue the AI is holding it
amber a person is holding it
teal the customer is holding it
red risk — and nothing else
A lead that is amber on a laptop must be amber on a phone.
Run it
npm install
npm run dev # http://localhost:5176
VITE_ZINO_API_URL in .env for local dev only. In a deployed build the API
host is read at RUNTIME from the config.js the server writes when it places
the build — never compiled in, so one artifact is promoted between
environments unchanged.
Installing it
It is a PWA: manifest.webmanifest, a standalone display mode, and the navy
theme colour, so Add to Home Screen gives a full-screen app with no browser
chrome. The layout pads for the notch and the home indicator through
env(safe-area-inset-*).