Docs / decisions/2026-08-01-chimti-web-merge.md

Decision — chimti-web: one web repo, OTLU theme, phased rebuild

**Date:** 1 Aug 2026 · **Decided by:** Jagjeet Singh · **Status:** In execution (Phase 0)

Decisions

1. **Merge.** `chimti-user-app` and `chimti-admin-app` are replaced by one repo,

**`otlugroup/chimti-web`**. `VITE_APP_VARIANT` (`user` | `admin`) picks the build.

The admin surface must never silently render as the user app; the variant

guard also blocks internal-role logins on the user build and vice versa.

Rationale (measured): 48 same-path files across the two repos — 22 identical,

26 drifted by 21,917 lines; every API change was being made twice.

2. **Theme.** The OTLU admin console design system is adopted **verbatim**

(tokens, radii, shadows, type scale, motion, component patterns) with one

change: the accent is Chimti blue (`#0a81d9` light / `#38adf8` dark), brand

constant `#18b9fe`. Spec lives in `chimti-web/src/styles/tokens.css`.

3. **Sections unchanged.** Screens migrate skin-only — same tabs, fields and

flows. Section redesign is a separate, later conversation.

4. **API untouched.** `chimti-api` remains its own repo/service; chimti-web is

a client, as are mobile and (later) the customer app.

5. **Test gate.** `npm test` (token lint + render smoke both variants, growing

per phase) must be green before anything reaches `production`. CI enforces it.

6. **Docs.** `chimti-docs` is canonical for product knowledge from today. The

admin-app copy is a pointer stub and stops receiving updates.

Plan

Phases 0–6 (foundation → orders → registers → bespoke → comms/AI → cutover →

mobile). Old app repos keep serving production until Phase 5 cutover, then get

archived. Full plan artifact lives with the founder; summary of gates:

| Phase | Done when |

| --- | --- |

| 0 | Both variants boot on the new shell, Orders list live, tests green |

| 1 | A real order runs intake → tag → print → status → payment on the new UI |

| 2 | Every register in both variants runs on the module engine |

| 3 | No screen imports the old App.css; the 18k-line stylesheet deletes |

| 4 | All comms modules usable end-to-end |

| 5 | chimti-user-app / chimti-admin-app domains serve chimti-web builds |

| 6 | Android + iOS store listings live |

Addendum — same day

`GET /v1/subscriptions` added to chimti-api (`src/routes/billing.js`) so the

chimti-web registers stop being placeholders. Additive; gated with the

existing `["payment.write","invoice.read"]` any-of pattern; old apps

unaffected.

client-side unified money feed (payments + customer wallet ledger),

mirroring the old "Transactions Feed". A dedicated ledger endpoint can

replace the union later without UI changes.

sections whose data sources land later (staff pulse, retention mix, risk

radar, heatmap, geography) are listed as pending on the page rather than

faked.

Addendum — Phase 4 (Comms & AI), 2026-08-01

Surfaces shipped in chimti-web: WhatsApp (user: Inbox/Templates/Logs/Credits/Setup — old 5 tabs verbatim),

WhatsApp inbox (admin, estate-wide), Email (old admin Emails structure: accounts, 9 folders, reader,

compose, sync — serves both variants), Calls, Internal chat, Chimti Genie (ai-chatbot), Photo pricing.

Recorded working assumptions (flag if wrong):

1. **User Email page upgraded mock → live.** Old chimti-user-app EmailPage ran on hardcoded demo mails;

the merged app now points the same list+reader structure at /v1/emails. If brand logins lack email

permissions, the page shows an honest empty state instead of demo data.

2. **Calls has no backend yet.** Old CallsPage was local demo rows; chimti-api has no calls route. The

new page ships the old Call Inbox structure honestly empty, pointing to Brand settings → Channel

Integrations → Call System. Call provider webhook + /v1/calls is future API work.

3. **Internal chat polls (10s) instead of the old SSE /internal-chat/stream.** Stream wiring is a

ride-along; API contract unchanged.

4. **Genie login uses the Codex device-login flow** from /v1/ai/account/login exactly as the old page.

5. **ai-pos ('AI POS assistant') remains a placeholder** — no old page existed; concept only in product

knowledge. Needs product definition before build.

Addendum — Phase 5 (Cutover prep), 2026-08-01

chimti-web now ships its own deploy surface, mirroring the old apps' pattern:

one Dockerfile (node:20-alpine build → nginx:1.27-alpine static) with build

args `VITE_APP_VARIANT` (user|admin) and `VITE_BACKEND_API_BASE_URL`

(default https://chimti-api.otlu.io) — two Coolify apps build the same repo.

Full order of operations, smoke checklist and rollback:

`deployment/chimti-web-cutover.md`. The stale PM2/VPS workflow inside

chimti-user-app (pre-API-split, in-repo backend/) is documented as dead.

Addendum — Phase 6 (Mobile: Android · iOS · tablet · phone), 2026-08-01

Strategy across form factors, decided and shipped in chimti-web:

nav folds into a hamburger drawer built from the SAME module registry (IA

unchanged). Registers scroll horizontally on phones; comms surfaces switch to

list ⇄ thread with a back button; safe-area utilities cover notches.

appName "Chimti", android/ + ios/ committed in chimti-web-app, scripts

`cap:sync` / `cap:android` / `cap:ios`, status bar follows the app theme.

Tablets enabled on both platforms (iOS device family 1,2). Runbook:

`chimti-web-app/docs/mobile.md` (signing, stores, icon generation).

remains the field-executive app** (pickup flows, barcode scanning, camera) —

a different audience from the owner/manager Capacitor app. Not merged.

the founder's Mac — engineering side is complete in the repo.

Addendum — Module activation sweep (post-Phase-4), 2026-08-01

All 9 remaining placeholders activated: Pickups & deliveries (Field Ops Queue

over live shipments), Delivery desk (My Pickup/Delivery Tasks + Rider Cash

Drawer from CASH payments), Customer experience (rollout board over

/v1/customer-experience), admin Clients register + ClientDetails (old 10 tabs;

clients grouped from portfolio client-side — no /v1/clients endpoint exists),

Modules (old Module Launch under its product name; catalog + LIVE/BETA/

INTERNAL/PLANNED/DEPRECATED statuses 1:1; GET/PATCH /v1/platform-module-settings),

Revenue engine (strategy board content ported 1:1), Drive files (tree +

bootstrap), admin Settings (Workspace Settings + Notifications → uiPreferences).

**AI POS Assistant removed from the user sidebar** — it is PLANNED in the

module launch catalog and product rules hide planned modules globally; it

stays managed from admin → Modules. Pending sub-surfaces unchanged (client/brand

onboarding tabs, permissions editor, per-brand module enablement).

Addendum — Worker layer shipped (all 5 waves), 2026-08-01

chimti-api now carries `src/worker.js` + `src/workers/` — 21 background jobs

across comms/revenue/ops/growth/hygiene, 25 unit tests green (`npm run

test:workers`), env-flag gated, advisory-locked, heartbeat-tracked. Runs as a

second Coolify process off the same image (`npm run worker`,

`WORKERS_ENABLED=1`). Details + deploy env:

`architecture/workers-automation-plan.md`. Schema-honest reductions: ads guard

report-only; AI status refresher deferred pending an export from routes/ai.js.

Addendum — Drop A: admin depth chain, 2026-08-01

Depth audit (all 40+ old pages, line/action level) exposed that top-level

structures were faithful but drill-down chains were shallow. Drop A closes the

admin chain end-to-end:

copied verbatim into chimti-web `src/lib/brandCatalog.js`; new shared

`CatalogEditor` renders the full model (enabled services, billing modes,

TAT, processing location, per-mode price rules OPEN/FIXED/RANGE + urgent,

per-item catalogue rules, reset-item-prices).

brand catalog ↔ PATCH /v1/workspace-preferences {brandId}; location override

↔ {brandId, storeId} (WorkspacePreferenceSetting is scopeKey-based, so store

records are native). "Reset to brand defaults" copies the brand record into

the store record. The brand's own user-app Preferences reads the same record.

launched modules ↔ brand-settings moduleVisibility), Work Queue (derived from

checklist), Checklist (12 old milestones, auto-verified from live data where

possible; manual milestones stay workstation-local like the old console),

Actions (Add Location → POST /v1/brands/:id/locations · Add User →

POST /v1/users · Record milestone · demo reset), Locations → per-location

panel with the old 5 store sub-tabs (Overview/Services/Team/Checklist/

Activity), Timeline (derived from subscription/locations/milestones/audits).

auto+manual), Work Queue, Features (per-brand rollup), Actions, Access Setup.

"Add Brand under client" stays visibly pending — no brand-create API exists.

Next: Drop B (user chain — LocationDetails 6 tabs, Preferences item-level

builder reusing CatalogEditor, OrderDetails missed actions), then Drop C

(register create/edit flows), Drop D (remaining specialty pages).

Addendum — Drop B: user chain depth, 2026-08-01

Services · Team · Orders & Activity · Shipments · Settings). Services shows

the EFFECTIVE catalog: store-scope workspace-preferences record when present

("Pricing Source: Location override") else the brand record ("Brand /

Workspace") — verified end-to-end: a store catalog saved from the admin

Brand details page is what the user's location page reads. Locations

register rows now open it (rowHref + /locations/:id route).

item-level rules (billing mode, OPEN/FIXED/RANGE, urgent), reset-item-prices,

same record the admin console edits. Replaces the earlier service-level-only

page; old page's auto-save became an explicit Save (bulk edits safer).

delivery partners from /v1/delivery-tracking sorted Available-first, saves

pickupAgent + pickupScheduleNotes via the order PATCH. Shown only when the

order has pickupRequired.

Addendum — Drop C: register CRUD flows (2026-08-01)

Depth audit ke chaar drops me se teesra. Registers ab sirf read-only tables nahi — old apps ke desks/actions wapas, real APIs par.

**10 naye/upgrade pages (explicit routes, `/:modulePath` se pehle):**

**Extra wiring:** leads module `rowHref` → detail page; render smoke me `/leads/test-lead-id`.

**Backend backlog (Drop C se confirm):** POST `/v1/shipments` (+ scan manifest persistence), leads write APIs (stage move/edit/notes). UI dono jagah honest "backend pending" dikhata hai, form/payload taiyaar.

**Verification:** npm test green (40 user + 29 admin routes render smoke, token lint, module contract); dono variants build; mock v10 par 17 Playwright screenshots — coupon create → list me aaya, route batch create → dispatch requests Assigned, claim status move, escalate, reconcile, scan OK/Unknown, wizard, confirm dialog; phone card-list pattern sab naye registers par bhi.

Addendum — Drop D: specialty pages & deep editors (2026-08-02)

Depth audit ka chautha (aakhri) drop. Pehle route-research kiya: old apps me kaunse specialty pages LIVE routed the aur kaunse pehle se parked (redirects). Fidelity dono taraf follow hui.

**Naye pages (old sources line-by-line padh kar, 1:1):**

**Redirect parity (old apps me ye paths pehle se parked the — same yahan):** `/module-launch`→`/modules`, `/brand-onboarding`→`/brands`, `/scale-ops`→`/reports`, `/data-control`→`/dashboard`. Route research receipts: old admin App.jsx:5524, old user App.jsx:7360/7506/7617. BrandOnboarding/ScaleOps/DataControl/user-AccessControl pages old app me dead code the (unroute); unka zinda kaam Brands/Reports/Dashboard/Users me pehle se absorbed hai.

**Platform layer:** `changeMyPassword` + `saveAccessRoles` (liveApi, real chimti-api routes verified in source), SessionContext `updateUser` (avatar/prefs persist), `src/lib/accessControl.js` (old permission meta catalog + persona map + role helpers shared by 3 pages).

**Backend backlog (Drop D se confirm):** `/v1/access-control/approval-requests` GET/PATCH abhi chimti-api me nahi (UI old sample + honest label ke saath chalti hai, API aate hi live).

**Verification:** npm test green — **47 user + 34 admin routes** render smoke, token lint, module contract; dono variants build; mock v11 par 17 Playwright screenshots — role editor me `audit.read` toggle → "Role setup saved to database" → PUT round-trip, approval Approve live, Add User Type modal, user Access save form, module detail + tags, plans matrix, help-center checklist, AI POS draft live signals (11 active orders → ₹4,287 quote), phone profile + phone AI console.

Addendum — Hotfix Drop E: production smoke blockers (2026-08-02)

Cutover live hone ke baad Codex ke smoke me 4 product-contract blockers mile. Charon fix, backend schemas se verify karke:

1. **Location detail → session logout (root cause mila):** `moduleApi` 401 **aur 403** dono par global `chimti:auth-expired` fire karta tha. Production me brand login ko `GET /v1/workspace-preferences?brandId&storeId` par **403 (Missing permission)** mila → poora session logout. Fix: sirf **401** (token invalid/expired) par logout; 403 ab sirf us call ki error hai — page brand-level pricing par fallback karke render hota hai. Mock me st2 par 403 simulate karke prove kiya: page rendered, session intact.

2. **Coupon create 400:** backend `couponCreateSchema` me `endsOn` **required datetime** hai (null invalid) aur `usageLimit` **int ≥ 1** ("0 = unlimited" backend me exist hi nahi karta). Fix: "Valid till" date field (default +30 din, `T23:59:59` ISO), usage limit min 1 (default 100), maxDiscount/minOrderValue int-round, brandId empty ho to omit.

3. **Module Save 400:** PATCH `/v1/platform-module-settings` `brandSettingsSchema` se validate hota hai — `brandName` (min 2) + `primaryColor` (min 4) top-level required. Fix: ModulesPage + ModuleDetailsPage ab GET snapshot rakh kar old-page jaisa **full record** PATCH karte hain `{brandName, primaryColor, logoDataUrl, logoFileName, integrations, moduleVisibility:{modules,launchStatus,menuTags,updatedAt,updatedBy}}`; GET parsing bhi production nesting (`settings.moduleVisibility.*`) ke hisaab se fix (pehle flat read se defaults dikh sakte the). BrandDetailsPage ke brand-module toggle par bhi same guard.

4. **Quick customer create 400:** backend `customerCreateSchema.addresses.min(1)` — har address me label(≥1)/line1(≥2)/pincode(≥4)/city(≥2)/state(≥2). Fix: New Order ke quick-create modal me Address label/line/pincode/city/state fields, `addresses:[{..., country:'India', isPrimary:true}]` payload, client-side validation.

**Verification:** mock v12 ab production contracts ENFORCE karta hai (customers/coupons/platform-module-settings/brand-settings par wahi 400s + st2 par 403) — purane payloads mock par bhi fail hote, naye pass: coupon FEST15 created, modules "Global modules setup saved", st2 location render + logged-in, quick-create address fields. npm test green (47 user + 34 admin routes).

**Deploy note:** sirf `chimti-web-app` badla — API/worker untouched. Push ke baad Coolify par `chimti-web-user` + `chimti-web-admin` redeploy kaafi hai.

**Positive shipment scan** blocker nahi tha — us workspace me live article tags 0 the (data), scan desk ka Unknown-path verify ho chuka hai.