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
- **API additions approved:** read-only `GET /v1/invoices`, `GET /v1/plans`,
`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.
- **Transactions semantics:** the user-facing Transactions register is a
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.
- **Dashboard:** rebuilt on the new shell with the old page's section names;
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:
- **Responsive shell:** ≥1024px keeps the Odoo-style rail+sidebar; below that the
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.
- **Native app = Capacitor wrap of the user variant.** appId `io.chimti.app`,
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).
- **Admin stays web-only** — internal tooling, no store presence.
- **`chimti-user-mobile-app` (Expo, `io.chimti.user.mobile`) is untouched and
remains the field-executive app** (pickup flows, barcode scanning, camera) —
a different audience from the owner/manager Capacitor app. Not merged.
- Store submission (keystore, Play Console, Xcode signing/TestFlight) runs on
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:
- **Brand catalog/pricing engine ported** — `utils/brandCatalog.js` (616L)
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).
- **Persistence upgraded from the old console's localStorage to the real API**:
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.
- **BrandDetails now fully live in all 10 tabs**: Modules (per-brand toggles of
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).
- **ClientDetails 10/10 tabs live**: Activity, Checklist (8 readiness rows,
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
- **LocationDetailsPage (user)** built with the old 6 tabs verbatim (Overview ·
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).
- **PreferencesPage upgraded to the FULL builder** by reusing CatalogEditor —
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).
- **OrderDetailsPage: Assign/Reassign Pickup Boy** restored (old flow):
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):**
- `/staff` — Team Directory full CRUD: New employee + row-tap edit modal (position/team/registration/active) → POST/PATCH `/v1/staff`.
- `/users` — old "Complete User Onboarding Flow" 4-step wizard verbatim (Identity · Access & Scope · Setup Checklist 6 items · Review & Create) → POST `/v1/users`; brand/store scope tenant portfolio se.
- `/campaigns-loyalty` — 4 desks: Campaigns list · Coupon Code Management (Create Coupon modal + Pause/Activate → POST/PATCH coupons) · Loyalty Tiers + Assign Subscription Plan (POST subscriptions) · Customer Wallet credit/debit (POST wallet).
- `/support` — Support Queue tickets + Claims & Damage Workflow: per-claim status Select (Registered→…→Closed) → PATCH `/v1/support/claims/:code`.
- `/payments` — register + Payment Reconciliation desk: method rows (UPI/Cash/Card), Mark reconciled → POST `/v1/payments/method-settings/reconcile`.
- `/processing-queue` — register + SLA & Delay Engine: past-ETA detection (createdAt+etaMins), Escalate → POST `/v1/processing-queue/escalate`.
- `/delivery-tracking` — Route Planner (city+partner → POST route-batches, pending dispatch requests assign hote hain) · Simulate movement · Partner Feed · Route Batches status advance (Planned→Dispatched→In Progress→Completed via PATCH) · Dispatch Requests queue.
- `/shipments` — 4 tabs: All Shipments register · Create Shipment form (**submit disabled — POST /v1/shipments backend me nahi hai, documented backlog**) · Dispatch Scan · Receive Scan — scans LIVE orders ke article tags se verify hote hain (in-memory index; server-side manifest bhi shipments write API ke saath aayega).
- `/leads/:leadId` — old LeadDetails ke 5 tabs verbatim (Overview/Contact/Timeline/Demo/Notes); leads GET-only hai isliye stage-move nahi, notes workstation-local honest label ke saath (**lead write API = documented backlog**).
- `/demo-workspaces` — register + Reset Demo Data (ConfirmDialog, danger) → POST `/v1/demo-workspaces/reset`; nightly worker #19 ke saath complement.
**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):**
- `/profile` (dono apps) — old ProfilePage: avatar upload/remove (2 MB cap) via PATCH `/v1/auth/me/preferences` (uiPreferences), metric cards, Profile Details, Security: auto-logout toggle + change-password (min 6 / match validation) via **real PATCH `/v1/auth/me/password`**, Logout. Topbar avatar ab profile kholta hai (photo bhi wahan dikhti hai).
- `/permissions` (admin) — old AccessControlPage FULL rebuild: 3-section switch (Chimti Roles · Client Role Templates · Client Overrides + counts), role directory → editor, **Add User Type** modal (name→code auto-gen, scope/team/app-access/copy-from) → **real PUT `/v1/access-control/roles`**, Client Override Rules, Persona Map (14 personas), Approval Matrix (5 rows verbatim), **Pending Approvals** queue with Approve/Reject + status filter — approval-requests API na hone par old sample rows honest label ke saath (documented backlog).
- `/permissions/:roleKey` (admin) — old PermissionDetailsPage role editor 1:1: facets, Scope Rules, Role Setup (rename, permission search, add custom code, full-access `*` toggle, risk-tagged permission checklist), Mapped Personas, Enabled Modules; **Save = PUT roles (real, DB-backed), Reset = snapshot**.
- `/users/:userId` (dono) — old admin UserDetailsPage: 6 header facets, Overview/Access/Logs; Access form → **real PATCH `/v1/users/:id`**, Delete → real DELETE (self-delete blocked), role→permission preview live role map se; Logs = user-scoped audit rows + old fallback trail. Users register par row-tap ab detail kholta hai.
- `/modules/:moduleId` (admin) — old ModuleLaunchPage detail: facets, Launch Setup (status + **User App Tag** select — NEW/BETA/PILOT/AI/SOON/UPDATED, old options), Module Details (path/category/dependencies/description). Modules list me Tag column add hua; menuTags platform-module-settings record me save hote hain. Module catalog old descriptions + dependencies se enrich.
- `/plans` (admin) — old PlansPage depth: 3 blueprint cards (₹7,999/₹14,999/₹39,999 + setup fees + limits verbatim) with **live active-brands/MRR from subscriptions**, Feature Matrix (old 8-module clientFeatureModuleCatalog × plans, phone par card list), Billing Rules 4 policy cards, Inspect Plan selector + live plans register.
- `/help-center` (user) — old "How To Use Chimti" 1:1: role/task selectors, Task Assistant, Fast Recovery cards, Role-wise Daily Workflow checklist (localStorage old key `chimti.help-center.progress.v1`, progress %), Module SOP Library (8 SOPs verbatim) with search. User nav → Workspace › Help.
- `/ai-pos` … `/production-planner` (user, 9 routes) — old AiOpsModulesPage saare 9 AI consoles verbatim (AI POS, Damage Proof, Garment Tracking, Smart ETA, WhatsApp AI, Route Optimizer, Owner Brief, AI Upsell, Queue Planner): lane switcher, Operator Console (scenario + working input → draft output **LIVE workspace signals se** — orders/customers/catalog), Workflow, Readiness Inputs + %, Tools & Data, Implementation Queue.
**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.