saridsa2 c1b636cb6d feat(onboarding/phase-2): settings hub, setup wizard shell, first-run redirect
Phase 2 of hospital onboarding & self-service plan
(docs/superpowers/plans/2026-04-06-hospital-onboarding-self-service.md
checked in here for the helix-engage repo).

Frontend foundations for the staff-portal Settings hub and 6-step setup
wizard. Backend was Phase 1 (helix-engage-server commit).

New shared components (src/components/setup/):
- wizard-shell.tsx — fullscreen layout with left step navigator, progress
  bar, and Skip-for-now affordance
- wizard-step.tsx — single-step wrapper with Mark Complete + Prev/Next/
  Finish navigation, completion badge
- section-card.tsx — Settings hub card with title/description/icon, links
  to a section page, optional status badge mirroring setup-state

New pages:
- pages/setup-wizard.tsx — top-level /setup route, fullscreen (no AppShell),
  loads setup-state from sidecar, renders the active step. Each step has a
  placeholder body for now; Phase 5 swaps placeholders for real form
  components from the matching settings pages. Already functional end-to-end:
  Mark Complete writes to PUT /api/config/setup-state/steps/<step>, Skip
  posts to /dismiss, Finish navigates to /.
- pages/team-settings.tsx — moved the existing workspace member listing out
  of the old monolithic settings.tsx into its own /settings/team route. No
  functional change; Phase 3 will add the invite form + role editor here.
- pages/settings-placeholder.tsx — generic "Coming in Phase X" stub used by
  routes for clinics, doctors, telephony, ai, widget until those pages land.

Modified pages:
- pages/settings.tsx — rewritten as the Settings hub (the new /settings
  route). Renders SectionCards in 3 groups (Hospital identity, Care
  delivery, Channels & automation) with completion badges sourced from
  /api/config/setup-state. The hub links to existing pages (/branding,
  /rules) and to placeholder pages for the not-yet-built sections.
- pages/login.tsx — after successful login, calls getSetupState() and
  redirects to /setup if wizardRequired. Failures fall through to / so an
  older sidecar without the setup-state endpoint still works.
- components/layout/sidebar.tsx — collapsed the Configuration group
  (Rules Engine + Branding standalone entries) into the single Settings
  entry that opens the hub. Removes the IconSlidersUp import that's no
  longer used.

New types and helpers (src/lib/setup-state.ts):
- SetupState / SetupStepName / SetupStepStatus types mirroring the sidecar
  shape
- SETUP_STEP_NAMES constant + SETUP_STEP_LABELS map (title + description
  per step) — single source of truth used by the wizard, hub, and any
  future surface that wants to render step metadata
- getSetupState / markSetupStepComplete / markSetupStepIncomplete /
  dismissSetupWizard / resetSetupState helpers wrapping the api-client

Other:
- lib/api-client.ts — added apiClient.put() helper for the setup-state
  step update mutations (PUT was the only verb missing from the existing
  get/post/graphql helpers)
- main.tsx — registered new routes:
    /setup                       (fullscreen, no AppShell)
    /settings                    (the hub, replaces old settings.tsx)
    /settings/team               (moved member listing)
    /settings/clinics            (placeholder, Phase 3)
    /settings/doctors            (placeholder, Phase 3)
    /settings/telephony          (placeholder, Phase 4)
    /settings/ai                 (placeholder, Phase 4)
    /settings/widget             (placeholder, Phase 4)

Tested via npx tsc --noEmit and npm run build (clean, only pre-existing
chunk-size and dynamic-import warnings unrelated to this change).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 07:13:25 +05:30

Helix Engage — Frontend

Call center CRM frontend for healthcare lead management. Built on the FortyTwo platform.

Owner: Mouli

Architecture

┌─────────────────────┐     ┌──────────────────────┐     ┌─────────────────────┐
│   helix-engage      │     │ helix-engage-server   │     │  FortyTwo Platform  │
│   (this repo)       │────▶│ (sidecar)             │────▶│  (backend)          │
│   React frontend    │     │ NestJS REST API       │     │  GraphQL API        │
│   Port 5173 (dev)   │     │ Port 4100             │     │  Port 4000          │
└─────────────────────┘     └──────────────────────┘     └─────────────────────┘
         │                           │
         │    SIP/WebRTC             │    Ozonetel CloudAgent APIs
         ▼                           ▼
   ┌───────────┐              ┌──────────────┐
   │ Ozonetel  │              │ Ozonetel     │
   │ SIP (444) │              │ REST APIs    │
   └───────────┘              └──────────────┘

Three repos:

Repo Purpose Owner
helix-engage (this) React frontend Mouli
helix-engage-server NestJS sidecar — Ozonetel + Platform bridge Karthik
helix-engage-app FortyTwo SDK app — entity schemas (Call, Lead, etc.) Shared

Getting Started

npm install
npm run dev       # http://localhost:5173
npm run build     # TypeScript check + production build

Environment Variables (set at build time or in .env)

Variable Purpose Dev Default Production
VITE_API_URL Platform GraphQL http://localhost:4000 https://engage-api.srv1477139.hstgr.cloud
VITE_SIDECAR_URL Sidecar REST API http://localhost:4100 https://engage-api.srv1477139.hstgr.cloud
VITE_SIP_URI Ozonetel SIP URI sip:523590@blr-pub-rtc4.ozonetel.com
VITE_SIP_PASSWORD SIP password 523590
VITE_SIP_WS_SERVER SIP WebSocket wss://blr-pub-rtc4.ozonetel.com:444

Production build command:

VITE_API_URL=https://engage-api.srv1477139.hstgr.cloud \
VITE_SIDECAR_URL=https://engage-api.srv1477139.hstgr.cloud \
VITE_SIP_URI=sip:523590@blr-pub-rtc4.ozonetel.com \
VITE_SIP_PASSWORD=523590 \
VITE_SIP_WS_SERVER=wss://blr-pub-rtc4.ozonetel.com:444 \
npm run build

Tech Stack

  • React 19 + TypeScript + Vite
  • Tailwind CSS 4 with semantic color tokens (text-primary, bg-brand-section — never raw colors like text-gray-900)
  • React Aria Components for accessibility (imports always prefixed Aria*)
  • Jotai for SIP/call state
  • React Context for auth, data, theme
  • FontAwesome Pro Duotone icons
  • Untitled UI component library (src/components/base/, src/components/application/)

Project Structure

src/
├── pages/                  # Route-level pages
│   ├── call-desk.tsx       # Main CC agent workspace — THE CORE PAGE
│   ├── login.tsx           # Auth page (centered card on blue bg)
│   ├── call-history.tsx    # CDR log viewer
│   ├── my-performance.tsx  # Agent KPI dashboard
│   ├── team-dashboard.tsx  # Supervisor overview
│   ├── all-leads.tsx       # Lead master table
│   └── campaigns.tsx       # Campaign listing
│
├── components/
│   ├── call-desk/          # ⚡ Call center components — WHERE MOST WORK HAPPENS
│   │   ├── active-call-card.tsx    # In-call UI + post-call disposition flow
│   │   ├── worklist-panel.tsx      # Agent task queue with tabs + sub-tabs
│   │   ├── context-panel.tsx       # AI assistant + Lead 360 sidebar
│   │   ├── disposition-form.tsx    # Post-call outcome selector
│   │   ├── appointment-form.tsx    # Book appointment during/after call
│   │   ├── agent-status-toggle.tsx # Ready/Break/Training/Offline toggle
│   │   ├── transfer-dialog.tsx     # Call transfer
│   │   ├── enquiry-form.tsx        # General enquiry capture
│   │   ├── live-transcript.tsx     # Real-time transcription (Deepgram)
│   │   └── phone-action-cell.tsx   # Click-to-call in table rows
│   ├── base/               # Untitled UI primitives (Button, Input, Select, Badge)
│   ├── application/        # Complex UI (Table, Modal, Tabs, DatePicker, Nav)
│   ├── layout/             # Sidebar — role-based navigation
│   └── dashboard/          # KPI cards, charts, missed queue widget
│
├── providers/
│   ├── sip-provider.tsx    # SIP WebRTC — call lifecycle management
│   ├── auth-provider.tsx   # User session, roles (executive/admin/cc-agent)
│   ├── data-provider.tsx   # Bulk entity loader (leads, campaigns, calls)
│   └── theme-provider.tsx  # Light/dark mode
│
├── hooks/
│   ├── use-worklist.ts     # Polls sidecar /api/worklist every 30s
│   ├── use-call-assist.ts  # Live transcript via Socket.IO
│   └── use-sip-phone.ts    # Low-level SIP.js wrapper
│
├── lib/
│   ├── api-client.ts       # REST + GraphQL client (auth, queries, sidecar calls)
│   ├── queries.ts          # Platform GraphQL query strings
│   └── format.ts           # Phone/date formatting
│
├── state/
│   └── sip-state.ts        # Jotai atoms (callState, callerNumber, isMuted, etc.)
│
└── types/
    └── entities.ts         # Lead, Patient, Call, Appointment, etc.

Troubleshooting Guide — Where to Look

"The call desk isn't working"

File: src/pages/call-desk.tsx This is the orchestrator. It uses useSip() for call state, useWorklist() for the task queue, and renders either ActiveCallCard (in-call) or WorklistPanel (idle). Start here, then drill into whichever child component is misbehaving.

"Calls aren't connecting / SIP errors"

File: src/providers/sip-provider.tsx + src/state/sip-state.ts Check VITE_SIP_* env vars. Ozonetel SIP WebSocket runs on port 444 — VPNs block it. If WebSocket hangs at "connecting", turn off VPN. Also check browser console for SIP.js registration errors.

"Worklist not loading / empty"

File: src/hooks/use-worklist.ts This polls GET /api/worklist on the sidecar every 30s. Open browser Network tab → filter for /api/worklist. Common causes: sidecar is down, auth token expired, or agent name doesn't match any assigned leads.

"Missed calls not appearing / sub-tabs empty"

File: src/components/call-desk/worklist-panel.tsx Missed calls come from the sidecar worklist response. The sub-tabs filter by callbackstatus field. If all sub-tabs are empty, the sidecar ingestion may not be running (check sidecar logs for MissedQueueService).

"Disposition / appointment not saving"

File: src/components/call-desk/active-call-card.tsxhandleDisposition() Posts to sidecar POST /api/ozonetel/dispose. Errors are caught silently (non-blocking). Check browser Network tab for the dispose request/response, then check sidecar logs.

"Login broken / Failed to fetch"

File: src/pages/login.tsx + src/lib/api-client.ts Login calls apiClient.login() → sidecar /auth/login → platform GraphQL. Most common cause: wrong VITE_API_URL (built with localhost instead of production URL). Always set env vars at build time.

"UI component looks wrong"

Files: src/components/base/ (primitives), src/components/application/ (complex) These come from the Untitled UI library. Design tokens are in src/styles/theme.css. Brand colors were rebuilt from logo blue rgb(32, 96, 160).

"Navigation / role-based access"

File: src/components/layout/sidebar.tsx Navigation groups are defined per role (admin, cc-agent, executive). Routes are registered in src/main.tsx.

Data Flow

User action
    │
    ▼
Component (e.g. ActiveCallCard)
    │
    ├──▶ Sidecar REST API (via apiClient.post/get)
    │       e.g. /api/ozonetel/dispose, /api/worklist
    │
    ├──▶ Platform GraphQL (via apiClient.graphql)
    │       e.g. leads, appointments, patients queries
    │
    └──▶ SIP.js (via useSip() hook)
            Call control: answer, hangup, mute, hold

Key pattern: The frontend talks to TWO backends:

  1. Sidecar (REST) — for Ozonetel telephony operations and worklist
  2. Platform (GraphQL) — for entity CRUD (leads, appointments, patients)

Conventions

  • File naming: kebab-case (worklist-panel.tsx)
  • Colors: Semantic tokens only (text-primary, bg-brand-section)
  • Icons: @fortawesome/pro-duotone-svg-icons + faIcon() wrapper in src/lib/icon-wrapper.ts
  • React Aria: Always prefix imports (Button as AriaButton)
  • Transitions: transition duration-100 ease-linear

Git Workflow

  • dev — active development
  • master — stable baseline
  • Always build with production env vars before deploying
Description
No description provided
Readme 3.4 MiB
Languages
TypeScript 96.1%
CSS 3.8%