- Extended MaintAction with needsPreStep + clientSideHandler
- MaintOtpModal supports pre-step content before OTP (campaign selection)
- Removed standalone ClearCampaignLeadsModal — all maint actions go through one modal
- 4-step import wizard with Untitled UI Select for mapping
- DynamicTable className passthrough
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mapping bar with styled dropdowns sits above the DynamicTable.
Mapped columns show brand highlight, unmapped show gray.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- DynamicTable component: wraps Table for dynamic/unknown columns with headerRenderer support
- Import wizard preview now uses DynamicTable instead of plain HTML table
- Fixed modal height (80vh) to prevent jitter between wizard steps
- Campaign card shows actual linked lead count, not marketing metric
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ColumnToggle component with checkbox dropdown for column visibility
- useColumnVisibility hook for state management
- Campaign/Ad/FirstContact/Spam/Dups hidden by default (mostly empty)
- ResizableTableContainer wrapping Table for column resize support
- Column defaultWidth/minWidth props
- Removed non-functional Filter and Sort buttons
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
- SIP connection only for users with Agent entity (no env var fallback)
- Supervisor no longer intercepts CC agent calls
- Auth controller checks Agent entity for ALL roles, not just cc-agent
- Token refresh handles GraphQL UNAUTHENTICATED errors (200 with error body)
- Token refresh handles sidecar 400s from expired upstream tokens
- Network quality indicator in sidebar (offline/unstable/good)
- Ozonetel IDLE event mapped to ready state (fixes stuck calling after canceled call)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
- Logout is now async — awaits sidecar /auth/logout before clearing tokens
- confirmSignOut awaits logout() before navigate('/login')
- 5 second timeout on logout fetch to prevent indefinite hang
- Added console.warn on logout failure
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
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>
- 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>
- SIP provider reads credentials from agentConfig (login response)
- Auth logout calls sidecar to unlock Redis + Ozonetel logout
- AppShell heartbeat every 5 min for CC agents
- Login stores agentConfig in localStorage
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Appointment Master page with status tabs, search, PhoneActionCell
- Login calls DataProvider.refresh() to load data after auth
- Sidebar: appointments nav for CC agents + executives
- Multi-agent SIP + lockout spec and implementation plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
- 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>
- 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>
- 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>
- My Performance page with KPI cards, ECharts, DatePicker, time utilization
- Sidecar: agent summary + AHT + performance aggregation endpoint
- Logout confirmation modal
- Removed Patients from CC agent nav
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
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>
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>
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>