Commit Graph

50 Commits

Author SHA1 Message Date
fb92da113e fix: setup wizard role guard, Doctor.clinic removal, dial sends agent config
Defect 1: Setup wizard (/setup) now guarded by AdminSetupGuard —
CC agents and other non-admin roles are redirected to / instead of
seeing the setup wizard they can't complete.

Defect 3: Removed all references to Doctor.clinic (relation was
replaced by DoctorVisitSlot entity). Updated queries.ts,
appointments.tsx, transforms.ts, doctors.tsx, appointment-form.tsx.

Defect 6 (frontend side): Dial request now sends agentId and
campaignName from localStorage agent config so the sidecar dials
with the correct per-agent credentials, not global defaults.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 14:29:32 +05:30
72012f099c fix: three-layer ACW protection — prevent agent stuck in wrapping-up
Root cause: when an agent refreshes the page during or after a call,
the React state (UCID, callState, disposition modal) is wiped. The
SIP BYE event fires but no component exists to trigger the disposition
modal → no POST to /api/ozonetel/dispose → agent stuck in ACW.

Layer 1 (beforeunload warning):
  Shows browser's native "Leave page?" dialog during active calls.
  Agent can cancel and stay.

Layer 2 (sendBeacon auto-dispose):
  UCID persisted to localStorage when call activates. On page unload,
  navigator.sendBeacon fires /api/ozonetel/dispose with
  CALLBACK_REQUESTED. Guaranteed delivery even during page death.
  Cleared from localStorage when disposition modal submits normally.

Layer 3 lives in helix-engage-server (separate commit).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 12:31:45 +05:30
f57fbc1f24 feat(onboarding/phase-6): setup wizard polish, seed script alignment, doctor visit slots
- Setup wizard: 3-pane layout with right-side live previews, resume
  banner, edit/copy icons on team step, AI prompt configuration
- Forms: employee-create replaces invite-member (no email invites),
  clinic form with address/hours/payment, doctor form with visit slots
- Seed script: aligned to current SDK schema — doctors created as
  workspace members (HelixEngage Manager role), visitingHours replaced
  by doctorVisitSlot entity, clinics seeded, portalUserId linked
  dynamically, SUB/ORIGIN/GQL configurable via env vars
- Pages: clinics + doctors CRUD updated for new schema, team settings
  with temp password + role assignment
- New components: time-picker, day-selector, wizard-right-panes,
  wizard-layout-context, resume-setup-banner
- Removed: invite-member-form (replaced by employee-create-form per
  no-email-invites rule)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 08:37:34 +05:30
efe67dc28b feat(call-desk): lock patient name field behind explicit edit + confirm
Fixes the long-standing bug where the Appointment and Enquiry forms
silently overwrote existing patients' names with whatever happened to
be in the form's patient-name input. Before this change, an agent who
accidentally typed over the pre-filled name (or deliberately typed a
different name while booking on behalf of a relative) would rename
the patient across the entire workspace on save. The corruption
cascaded into past appointments, lead history, the AI summary, and
the Redis caller-resolution cache. This was the root cause of the
"Priya Sharma shows as Satya Sharma" incident on staging.

Root cause: appointment-form.tsx:249-278 and enquiry-form.tsx:107-117
fired updatePatient + updateLead.contactName unconditionally on every
save. Nothing distinguished "stub patient with no name yet" from
"existing patient whose name just needs this appointment booked".

Fix — lock-by-default with explicit unlock:

- src/components/modals/edit-patient-confirm-modal.tsx (new):
  generic reusable confirmation modal for any destructive edit to a
  patient's record. Accepts title/description/confirmLabel with
  sensible defaults so the call-desk forms can pass a name-specific
  description, and any future page that needs a "are you sure you
  want to change this patient field?" confirm can reuse it without
  building its own modal. Styled to match the sign-out confirmation
  in sidebar.tsx — warning circle, primary-destructive confirm button.

- src/components/call-desk/appointment-form.tsx:
  - New state: isNameEditable (default false when leadName is
    non-empty; true for first-time callers with no prior name to
    protect) + editConfirmOpen.
  - Name input renders disabled + shows an Edit button next to it
    when locked.
  - Edit button opens EditPatientConfirmModal. Confirm unlocks the
    field for the rest of the form session.
  - Save logic gates updatePatient / updateLead.contactName behind
    `isNameEditable && trimmedName.length > 0 && trimmedName !==
    initialLeadName`. Empty / same-as-initial values never trigger
    the rename chain, even if the field was unlocked.
  - On a real rename, fires POST /api/lead/:id/enrich to regenerate
    the AI summary against the corrected identity (phone passed in
    the body so the sidecar also invalidates the caller-resolution
    cache). Non-rename saves just invalidate the cache via the
    existing /api/caller/invalidate endpoint so status +
    lastContacted updates propagate.
  - Bundled fix: renamed `leadStatus: 'APPOINTMENT_SET'` →
    `status: 'APPOINTMENT_SET'` and `lastContactedAt` →
    `lastContacted` in the updateLead payload. The old field names
    are rejected by the staging platform schema and were causing the
    "Query failed: Field leadStatus is not defined by type
    LeadUpdateInput" toast on every appointment save.

- src/components/call-desk/enquiry-form.tsx:
  - Same lock + Edit + modal pattern as the appointment form.
  - Added leadName prop (the form previously didn't receive one).
  - Gated updatePatient behind the nameChanged check.
  - Gated lead.contactName in updateLead behind the same check.
  - Hooks the enrich endpoint on rename; cache invalidate otherwise.
  - Status + interestedService + source still update on every save
    (those are genuinely about this enquiry, not identity).

- src/components/call-desk/active-call-card.tsx: passes
  leadName={fullName || null} to EnquiryForm so the form can
  pre-populate + lock by default.

Behavior summary:
- New caller, no prior name: field unlocked, agent types, save runs
  the full chain (correct — this IS the name).
- Existing caller, agent leaves name alone: field locked, Save
  creates appointment/enquiry + updates lead status/lastContacted +
  invalidates cache. Zero risk of patient/lead rename.
- Existing caller, agent clicks Edit, confirms modal, changes name,
  Save: full rename chain runs — updatePatient + updateLead +
  /api/lead/:id/enrich + cache invalidate. The only code path that
  can mutate a linked patient's name, and it requires two explicit
  clicks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 13:54:22 +05:30
8470dd03c7 fix: UI polish — nav labels, date picker, rules engine, error messages
- Sidebar: removed "Master" from nav labels (Leads, Patients, Appointments, Call Log)
- Appointment form: Dept + Doctor in 2-col row, Date below, disabled cascade
- DatePicker: placement="bottom start" + shouldFlip fixes popover positioning
- Team Performance: default to "Week", grid KPI cards, chart legend spacing
- Rules Engine: manual save (removed auto-debounce), Reset to Defaults uses
  DEFAULT_PRIORITY_CONFIG (no template endpoint), removed dead saveTimerRef
- Automation rules: 6 showcase cards with trigger/condition/action, replaced
  agent-specific rule with generic round-robin
- Recording analysis: friendly error message with retry instead of raw Deepgram error
- Sidebar active/hover: brand color reference for theming

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 16:55:16 +05:30
afd0829dc6 feat: design tokens — multi-hospital theming system
Backend (sidecar):
- ThemeService: read/write/backup/reset theme.json with versioning
- ThemeController: GET/PUT/POST /api/config/theme endpoints
- ConfigThemeModule registered in app

Frontend:
- ThemeTokenProvider: fetches theme, injects CSS variables on <html>
- Login page: logo, title, subtitle, Google/forgot toggles from tokens
- Sidebar: title, subtitle, active highlight from brand color scale
- AI chat: quick actions from tokens
- Branding settings page: 2-column layout, file upload for logo/favicon,
  single color picker with palette generation, font dropdowns, presets,
  pinned footer, versioning

Theme CSS:
- Sidebar active/hover text now references --color-brand-400

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 15:50:36 +05:30
c5d5e9c4f9 feat: supervisor AI tools — agent performance, campaign stats, call summary, SLA breaches
- AiChatPanel accepts context type, team dashboard passes { type: 'supervisor' }
- Supervisor system prompt: data-driven, no bias, threshold-based comparisons

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 13:05:20 +05:30
4598740efe feat: inline forms, transfer redesign, patient fixes, UI polish
- Appointment/enquiry forms reverted to inline rendering (not modals)
- Forms: flat scrollable section with pinned footer, no card wrapper
- Appointment form: DatePicker component, date prefilled, removed Returning Patient checkbox
- Enquiry form: removed disposition dropdown, lead status defaults to CONTACTED
- Transfer dialog: agent picker with live status, doctor list with department, select-then-connect flow
- Transfer: removed external number input, moved Cancel/Connect to pinned header row
- Button mutual exclusivity: Book Appt / Enquiry / Transfer close each other
- Patient name write-back: appointment + enquiry forms update patient fullName after save
- Caller cache invalidation: POST /api/caller/invalidate after name update
- Follow-up fix (#513): assignedAgent, patientId, date validation in createFollowUp
- Patients page: removed status filters + column, added pagination (15/page)
- Pending badge removed from call desk header
- Table resize handles visible (bg-tertiary pill)
- Sim call button: dev-only (import.meta.env.DEV)
- CallControlStrip component (reusable, not currently mounted)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 12:14:38 +05:30
442a581c8a fix: appointment/enquiry modals + team performance fallback
- Appointment form: converted from inline to modal dialog, removed Returning Patient checkbox
- Enquiry form: converted from inline to modal dialog
- Active call card: removed max-h-[50vh] scroll container, forms render as modals
- Team Performance: fallback agent list from call records when Ozonetel unavailable
- NPS/Time sections show placeholder when data unavailable

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 17:20:59 +05:30
b90740e009 feat: rules engine — priority config UI + worklist scoring
- Rules engine spec v2 (priority vs automation rules distinction)
- Priority Rules settings page with weight sliders, SLA config, campaign/source weights
- Collapsible config sections with dynamic headers
- Live worklist preview panel with client-side scoring
- AI assistant panel (collapsible) with rules-engine-specific system prompt
- Worklist panel: score display with SLA status dots, sort by score
- Scoring library (scoring.ts) for client-side preview computation
- Sidebar: Rules Engine nav item under Configuration

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 17:20:59 +05:30
c3c3f4b3d7 feat: worklist sorting, contextual disposition, context panel redesign, notifications
- Worklist default sort descending (newest first), sortable column headers (PRIORITY, PATIENT, SLA) via React Aria
- Contextual disposition: auto-selects based on in-call actions (appointment → APPOINTMENT_BOOKED, enquiry → INFO_PROVIDED, transfer → FOLLOW_UP_SCHEDULED)
- Context panel redesign: collapsible AI Insight, Upcoming (appointments + follow-ups + linked patient), Recent (calls + activities) sections; auto-collapse on AI chat start
- Appointments added to DataProvider with APPOINTMENTS_QUERY, Appointment type, transform
- Notification bell for admin/supervisor: performance alerts (idle time, NPS, conversion thresholds) with toast on load + bell dropdown with dismiss; demo alerts as fallback
- Slideout z-index fix: added z-50 to slideout ModalOverlay matching modal component

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:45:52 +05:30
0477064b3e wip: AI chat streaming endpoint + useChat integration (protocol mismatch)
- Server: POST /api/ai/stream using streamText with tools (lookup_patient, lookup_appointments, lookup_doctor)
- Frontend: AiChatPanel rewritten with @ai-sdk/react useChat hook
- Tool result cards: PatientCard, AppointmentCard, DoctorCard
- Streaming works server-side but useChat v1 doesn't parse AI SDK v6 toTextStreamResponse format
- Context panel layout needs redesign — context section fills entire panel, chat pushed below fold
- TODO: Fix streaming protocol, redesign panel layout with collapsible context

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:44:48 +05:30
48ed300094 feat: unified context panel, remove tabs, context-aware layout
- Merged AI Assistant + Lead 360 tabs into single context-aware panel
- Context section shows: lead profile, AI insight (live from event bus), appointments, activity
- "On call with" banner only shows during active calls (isInCall prop)
- AI Chat always available at bottom of panel
- Phase 1 only — AI Chat panel needs full redesign with Vercel AI SDK tool calling (Phase 2)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:44:48 +05:30
e6b2208077 feat: disposition modal, persistent top bar, pagination, QA fixes
- DispositionModal: single modal for all call endings. Dismissable (agent can resume call).
  Agent clicks End → modal → select reason → hangup + dispose. Caller disconnects → same modal.
- One call screen: CallWidget stripped to ringing notification + auto-redirect to Call Desk.
- Persistent top bar in AppShell: agent status toggle + network indicator on all pages.
- Network indicator always visible (Connected/Unstable/No connection).
- Pagination: Untitled UI PaginationCardDefault on Call History + Appointments (20/page).
- Pinned table headers/footers: sticky column headers, scrollable body, pinned pagination.
  Applied to Call Desk worklist, Call History, Appointments, Call Recordings, Missed Calls.
- "Patient" → "Caller" column label in Call History.
- Offline → Ready toggle enabled.
- Profile status dot reflects Ozonetel state.
- NavAccountCard: popover placement top, View Profile + Account Settings restored.
- WIP pages for /profile and /account-settings.
- Enquiry form PHONE_INQUIRY → PHONE enum fix.
- Force Ready / View Profile / Account Settings removed then restored properly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:44:48 +05:30
70e0f6fc3e feat: call recording analysis with Deepgram diarization + AI insights
- Deepgram pre-recorded API: transcription with diarization, sentiment, topics, summary
- OpenAI structured insights: call outcome, patient satisfaction, coaching notes, action items, compliance flags
- Slideout panel UI with audio player, speaker-labeled transcript, sentiment badge
- AI pill button in recordings table between Caller and Type columns
- Redis caching (7-day TTL) to avoid re-analyzing the same recording

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:44:48 +05:30
488f524f84 feat: SSE agent state, UCID fix, maint module, QA bug fixes
- Fix outbound disposition: store UCID from dial API response (root cause of silent disposition failure)
- SSE agent state: real-time Ozonetel state drives status toggle (ready/break/calling/in-call/acw)
- Maint module with OTP-protected endpoints (force-ready, unlock-agent, backfill, fix-timestamps)
- Maint OTP modal with PinInput component, keyboard shortcuts (Ctrl+Shift+R/U/B/T)
- Force-logout via SSE: admin unlock pushes force-logout to connected browsers
- Silence JsSIP debug flood, add structured lifecycle logging ([SIP], [DIAL], [DISPOSE], [AGENT-STATE])
- Centralize date formatting with IST-aware formatters across 11 files
- Fix call history: non-overlapping aggregates (completed/missed), correct timestamp display
- Auto-dismiss CallWidget ended/failed state after 3 seconds
- Remove floating "Helix Phone" idle badge from all pages
- Fix dead code in agent-state endpoint (auto-assign was unreachable after return)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:44:48 +05:30
710609dfee refactor: centralise outbound dial into useSip().dialOutbound()
- Single dialOutbound() in sip-provider handles all outbound state:
  callState, callerNumber, outboundPending, API call, error recovery
- ClickToCallButton, PhoneActionCell, Dialler all use dialOutbound()
- Removed direct Jotai atom manipulation from calling components
- Removed setOutboundPending imports from components
- SIP disconnects on provider unmount + auth logout
- Dialler input is now editable (type or numpad)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 18:49:10 +05:30
dbd8391f2c fix: UUID type mismatch, slot conflict, appt/enquiry tabs, dialler in header
- Changed $id: ID! to $id: UUID! in all update mutations (4 files)
- Removed redundant slot availability check (UI already disables booked slots)
- Book Appt and Enquiry act as toggle tabs — one closes the other
- Dialler moved from FAB to header dropdown next to status toggle

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 10:54:56 +05:30
1df40f14ff fix: disposition returns straight to worklist — no intermediate screens
Disposition is the last step. After submission, handleReset() clears
all state and returns to worklist immediately. Removed the "Call Completed"
card, post-disposition appointment form, and "Skip" button.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 09:55:37 +05:30
938f2a84d8 fix: enquiry in post-call, appointment skip button, AI scroll containment
- Enquiry button + form available during disposition stage (not just active call)
- Skip & Return to Worklist button on post-call appointment booking
- AI chat scroll uses parentElement.scrollTop instead of scrollIntoView

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 08:11:22 +05:30
5816cc0b5c fix: pinned header/chat input, numpad dialler, caller matching, appointment FK
- AppShell: h-screen + overflow-hidden for pinned header
- AI chat: input pinned to bottom, messages scroll independently
- Dialler: numpad grid (1-9,*,0,#) replaces text input
- Inbound calls: don't fall back to previously selected lead
- Appointment: use lead.patientId instead of leadId for FK
- Added .env.production for consistent builds

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 14:41:31 +05:30
727a0728ee feat: QA fixes — Patient 360 rewrite, token refresh, call flow, UI polish
- Patient 360 page queries Patient entity with appointments, calls, leads
- Patients added to CC agent sidebar navigation
- Auto token refresh on 401 (deduplicated concurrent refreshes)
- Call desk: callDismissed flag prevents SIP race on worklist return
- Missed calls skip disposition when never answered
- Callbacks tab renamed to Leads tab
- Branch column header on missed calls tab
- F0rty2.ai link on login footer

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 11:52:33 +05:30
744a91a1ff feat: Phase 2 — missed call queue, login redesign, button fix
- Missed call queue with FIFO auto-assignment, dedup, SLA tracking
- Status sub-tabs (Pending/Attempted/Completed/Invalid) in worklist
- missedCallId passed through disposition flow for callback tracking
- Login page redesigned: centered white card on blue background
- Disposition button changed to content-width
- NavAccountCard popover close fix on menu item click

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 09:16:53 +05:30
c3604377b9 feat: Phase 1 — agent status toggle, global search, enquiry form
- Agent status toggle: Ready/Break/Training/Offline with Ozonetel sync
- Global search: cross-entity search (leads + patients + appointments) via sidecar
- General enquiry form: capture caller questions during calls
- Button standard: icon-only for toggles, text+icon for primary actions
- Sidecar: agent-state endpoint, search module with platform queries

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 14:21:40 +05:30
5ccfa9bca8 refactor: patients page icon-only actions, remove Patients from CC agent nav
- Replace text buttons with icon-only (eye, SMS, WhatsApp, call)
- Remove Patients tab from CC agent sidebar (CC works with leads, not patients)
- Fix icon wrapper prop forwarding cleanup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 12:24:54 +05:30
631acf63dc fix: pass data-icon prop through FontAwesome icon wrappers
Replaced all bare `FC<{ className?: string }>` and `FC<HTMLAttributes<...>>`
wrappers that only forwarded `className` with `faIcon()` from
`src/lib/icon-wrapper.ts`, ensuring props like `data-icon` needed by the
Button component's CSS selector `*:data-icon:size-5` are correctly forwarded.
Also widened `NavItemBaseProps.icon` and `NavItemType.icon` prop types to
`FC<Record<string, any>>` to stay compatible with `faIcon()` return type.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 11:19:46 +05:30
2ace6efae5 refactor: clean up duplicate imports from icon migration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 11:09:17 +05:30
6f40b82579 refactor: migrate all icons from Untitled UI to FontAwesome Pro Duotone
Replace all @untitledui/icons imports across 55 files with equivalent
@fortawesome/pro-duotone-svg-icons icons, using FontAwesomeIcon wrappers
(FC<{ className?: string }>) for prop-based usage and inline replacements
for direct JSX usage. Drops unsupported Untitled UI-specific props
(strokeWidth, numeric size). TypeScript compiles clean with no errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 11:07:19 +05:30
3064eeb444 feat: CC agent features, live call assist, worklist redesign, brand tokens
CC Agent:
- Call transfer (CONFERENCE + KICK_CALL) with inline transfer dialog
- Recording pause/resume during active calls
- Missed calls API (Ozonetel abandonCalls)
- Call history API (Ozonetel fetchCDRDetails)

Live Call Assist:
- Deepgram Nova STT via raw WebSocket
- OpenAI suggestions every 10s with lead context
- LiveTranscript component in sidebar during calls
- Browser audio capture from remote WebRTC stream

Worklist:
- Redesigned table: clickable phones, context menu (Call/SMS/WhatsApp)
- Last interaction sub-line, source column, improved SLA
- Filtered out rows without phone numbers
- New missed call notifications

Brand:
- Logo on login page
- Blue scale rebuilt from logo blue rgb(32, 96, 160)
- FontAwesome duotone CSS variables set globally
- Profile menu icons switched to duotone

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 10:36:10 +05:30
99bca1e008 feat: telephony overhaul + appointment availability + Force Ready
Telephony:
- Track UCID from SIP headers and ManualDial response
- Submit disposition to Ozonetel via Set Disposition API (ends ACW)
- Fix outboundPending flag lifecycle to prevent inbound poisoning
- Fix render order: post-call UI takes priority over active state
- Pre-select disposition when appointment booked during call

Appointment form:
- Convert from slideout to inline collapsible below call card
- Fetch real doctors from platform, filter by department
- Show time slot availability grid (booked slots greyed + strikethrough)
- Double-check availability before booking
- Support edit and cancel existing appointments

UI:
- Add Force Ready button to profile menu (logout+login to clear ACW)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 20:24:58 +05:30
3a5bbc3f2a feat: send disposition to sidecar with UCID for Ozonetel ACW release
- Store UCID from outbound dial API response in sipCallUcidAtom
- Replace direct createCall GraphQL mutation with sidecar /api/ozonetel/dispose
- Remove agent-ready call from handleReset (no longer needed)
- Clear UCID on dial error and call reset

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 18:38:19 +05:30
e7c5c13e83 feat: switch outbound to direct SIP call from browser
Instead of Kookoo outbound → IVR → dial back to SIP (broken bridge),
make the call directly from JsSIP in the browser. The SIP INVITE goes
through Ozonetel's SIP server to the PSTN. No intermediary needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 06:52:24 +05:30
f454f2e682 fix: outbound call — End Call label, force active state after auto-answer
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 21:33:40 +05:30
26b9d93f32 feat: outbound call UI — immediate call card, auto-answer SIP bridge
- ClickToCallButton sets callState='ringing-out' immediately on click
- ActiveCallCard shows "Calling..." state for outbound
- SIP manager auto-answers incoming SIP when outbound is pending (Kookoo bridge)
- CallPrepCard shows lead context while dialing
- On error, resets state cleanly

Flow: Click Call → UI shows call card → Kookoo dials customer →
customer answers → SIP bridges → auto-answer → active call → disposition

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 21:29:26 +05:30
dd3b049253 fix: add isRowHeader to all tables — fixes React error #520 in production
All Table.Head components need at least one isRowHeader prop set.
Fixed in: worklist, agent-table, settings, patients, call-history, agent-detail.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 21:09:56 +05:30
a88cbecfbf fix: unused notes param in disposition handler
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 18:07:24 +05:30
edd2aa689d feat: post-call workflow — disposition, appointment booking, follow-up creation
ActiveCallCard now handles the full post-call flow:
- Call ends → Disposition form appears (6 options + notes)
- "Appointment Booked" → Opens appointment booking slideout
- "Follow-up Needed" → Auto-creates follow-up in platform
- Other dispositions → Logs call and returns to worklist
- "Book Appt" button available during active call too
- Creates Call record in platform on disposition submit
- Removed auto-reset to idle (ActiveCallCard manages lifecycle)
- "Back to Worklist" resets SIP state via Jotai atoms

Also fixes:
- All 7 GraphQL queries corrected (LINKS subfields, field renames)
- Campaign edit button moved to bottom-right
- Avg Response Time uses Math.abs for seed data edge case

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 17:24:46 +05:30
941b51731f fix: cleanup unused imports, restore campaigns, fix settings pagination syntax
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 16:42:04 +05:30
bb004744f4 feat: polish all pages — tooltips, sticky headers, roles, search, AI prompts
Dashboard KPI:
- Fix 1534m → 25h 34m (formatMinutes helper)
- Add info icon tooltips on all KPI and metric cards
- Pass role="admin" to AI panel for manager-specific prompts

Settings:
- Add search + pagination to employee table
- Infer roles from email convention (platform roles API returns null via API key)

AI Assistant:
- Role-specific quick prompts: manager sees "Agent performance", "Missed risks"
- Agent sees "Doctor availability", "Treatment packages"

Sticky headers:
- Add overflow-hidden to campaigns and all-leads pages

Misc:
- Fix free-brands-svg-icons → pro-duotone in integrations
- Remove Follow-ups from CC agent sidebar
- Remove global search from TopBar

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 16:23:10 +05:30
d9d98bce9c feat: dashboard restructure, integrations, settings, UI fixes
Dashboard:
- Split into components (kpi-cards, agent-table, missed-queue)
- Add collapsible AI panel on right (same pattern as Call Desk)
- Add tabs: Agent Performance | Missed Queue | Campaigns
- Date range filter in header

Integrations page:
- Ozonetel (connected), WhatsApp, Facebook, Google, Instagram, Website, Email
- Status badges, config details, webhook URL with copy button

Settings page:
- Employee table from workspaceMembers GraphQL query
- Name, email, roles, status, reset password action

Fixes:
- Fix CALLS_QUERY: callerNumber needs { primaryPhoneNumber }, recordingUrl → recording { primaryLinkUrl }
- Remove duplicate AI Assistant header
- Remove Follow-ups from CC agent sidebar (already in worklist tabs)
- Remove global search from TopBar (decorative, unused)
- Slim down TopBar height
- Fix search/table gap in worklist
- Add brand border to active nav item

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:58:31 +05:30
4c6cae9d65 feat: build all data pages — worklist table, call history, patients, dashboard, reports
Worklist (call-desk):
- Upgrade to Untitled UI Table with columns: Priority, Patient, Phone, Type, SLA, Actions
- Filter tabs: All Tasks / Missed Calls / Callbacks / Follow-ups with counts
- Search by name or phone
- SLA timer color-coded: green <15m, amber <30m, red >30m

Call History:
- Full table: Type (direction icon), Patient (matched from leads), Phone, Duration, Outcome, Agent, Recording (play/pause), Time
- Search + All/Inbound/Outbound/Missed filter
- Recording playback via native <audio>

Patients:
- New page with table: Patient (avatar+name+age), Contact, Type, Gender, Status, Actions
- Search + status filter
- Call + View Details actions
- Added patients to DataProvider + transforms + queries
- Route /patients added, sidebar nav updated for cc-agent + executive

Supervisor Dashboard:
- KPI cards: Total Calls, Inbound, Outbound, Missed
- Performance metrics: Avg Response Time, Callback Time, Conversion %
- Agent performance table with per-agent stats
- Missed Call Queue
- AI Assistant section
- Day/Week/Month filter

Reports:
- ECharts bar chart: Call Volume Trend (7-day, Inbound vs Outbound)
- ECharts donut chart: Call Outcomes (Booked, Follow-up, Info, Missed)
- KPI cards with trend indicators (+/-%)
- Route /reports, sidebar Analytics → /reports

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 12:26:13 +05:30
526ad18159 feat: call desk redesign — 2-panel layout, collapsible sidebar, inline AI, ringtone
- Collapsible sidebar with Jotai atom (icon-only mode, persisted to localStorage)
- 2-panel call desk: worklist (60%) + context panel (40%) with AI + Lead 360 tabs
- Inline AI call prep card — known lead summary or unknown caller script
- Active call card with compact Answer/Decline buttons
- Worklist panel with human-readable labels, priority badges, click-to-select
- Context panel auto-switches to Lead 360 when lead selected or call incoming
- Browser ringtone via Web Audio API on incoming calls
- Sonner + Untitled UI IconNotification for toast system
- apiClient pattern: centralized post/get/graphql with auto-toast on errors
- Remove duplicate avatar from top bar, hide floating widget on call desk
- Fix Link routing in collapsed sidebar (was using <a> causing full page reload)
- Fix GraphQL field names: adStatus→status, platformUrl needs subfield selection
- Silent mode for DataProvider queries to prevent toast spam

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 18:33:36 +05:30
bb48d07e61 feat: add worklist with missed calls, follow-ups, and leads sections 2026-03-18 11:26:16 +05:30
ffcaa79410 feat: add Patient 360 page, global search, appointment form — all using FontAwesome Pro duotone icons 2026-03-18 11:19:16 +05:30
9690ac416e feat: add appointment booking form slide-out during calls, wired to platform createAppointment mutation 2026-03-18 11:07:15 +05:30
66ad398b81 feat: wire real auth — login returns user profile with role from platform, remove mock users and role selector tabs 2026-03-18 10:42:54 +05:30
832fa31597 feat: wire call logging to platform — disposition creates Call record, updates Lead status, logs LeadActivity 2026-03-18 09:11:15 +05:30
aff383cb6d fix: session reset after call end, add reject/decline for incoming calls, extract caller ID from SIP headers (P-Asserted-Identity, Remote-Party-ID) 2026-03-17 21:31:53 +05:30
b2cc3d7012 feat: add floating call widget with SIP controls, click-to-call, and SIP provider
Introduce a shared SipProvider context so all components use the same SIP
connection. Add a floating CallWidget (idle pill, ringing, active with
disposition, ended states) visible for CC agents on every page. Add a
ClickToCallButton for the worklist. Wire SIP status badge and worklist
into the call-desk page.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 18:08:46 +05:30
5efa22a35a feat: build CC Agent Call Desk with CTI simulation, AI insights, disposition form, and call log
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 18:30:34 +05:30