mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-05-18 20:08:19 +00:00
fix: disable Book Appt/Enquiry until customer answers outbound call (#568)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
For outbound calls, SIP state transitions to 'active' when the agent's bridge connects — before the customer picks up. Ozonetel state stays 'calling' until customer answers, then goes to 'in-call'. Now reads ozonetelState from useAgentState and computes customerAnswered (callState=active AND ozonetelState!=calling). Action buttons (Book Appt, Enquiry, Transfer) disabled until customerAnswered is true. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -105,7 +105,11 @@ export const ActiveCallCard = ({ lead, callerPhone, missedCallId, onCallComplete
|
||||
|
||||
const agentConfig = localStorage.getItem('helix_agent_config');
|
||||
const agentIdForState = agentConfig ? (() => { try { return JSON.parse(agentConfig).ozonetelAgentId; } catch { return null; } })() : null;
|
||||
const { supervisorPresence } = useAgentState(agentIdForState);
|
||||
const { state: ozonetelState, supervisorPresence } = useAgentState(agentIdForState);
|
||||
// For outbound calls, SIP goes 'active' when the agent's bridge connects
|
||||
// (before customer answers). Ozonetel state stays 'calling' until customer
|
||||
// picks up, then transitions to 'in-call'. Use this to gate action buttons.
|
||||
const customerAnswered = callState === 'active' && ozonetelState !== 'calling';
|
||||
|
||||
const callDirectionRef = useRef(callState === 'ringing-out' ? 'OUTBOUND' : 'INBOUND');
|
||||
const wasAnsweredRef = useRef(callState === 'active');
|
||||
@@ -275,7 +279,7 @@ export const ActiveCallCard = ({ lead, callerPhone, missedCallId, onCallComplete
|
||||
|
||||
// Active call
|
||||
if (callState === 'active' || dispositionOpen) {
|
||||
wasAnsweredRef.current = true;
|
||||
if (customerAnswered) wasAnsweredRef.current = true;
|
||||
return (
|
||||
<>
|
||||
<div className={cx('flex flex-col rounded-xl border border-brand bg-primary overflow-hidden', (appointmentOpen || enquiryOpen || transferOpen) && 'flex-1 min-h-0')}>
|
||||
@@ -357,17 +361,17 @@ export const ActiveCallCard = ({ lead, callerPhone, missedCallId, onCallComplete
|
||||
|
||||
<Button size="sm" color={appointmentOpen ? 'primary' : 'secondary'}
|
||||
iconLeading={({ className, ...rest }: any) => <FontAwesomeIcon icon={faCalendarPlus} className={className} {...rest} />}
|
||||
isDisabled={!wasAnsweredRef.current}
|
||||
isDisabled={!customerAnswered}
|
||||
onClick={() => { setAppointmentOpen(!appointmentOpen); setEnquiryOpen(false); setTransferOpen(false); }}>
|
||||
{leadAppointments.length > 0 ? 'New / Reschedule Appt' : 'New Appt'}
|
||||
</Button>
|
||||
<Button size="sm" color={enquiryOpen ? 'primary' : 'secondary'}
|
||||
iconLeading={({ className, ...rest }: any) => <FontAwesomeIcon icon={faClipboardQuestion} className={className} {...rest} />}
|
||||
isDisabled={!wasAnsweredRef.current}
|
||||
isDisabled={!customerAnswered}
|
||||
onClick={() => { setEnquiryOpen(!enquiryOpen); setAppointmentOpen(false); setTransferOpen(false); }}>Enquiry</Button>
|
||||
<Button size="sm" color={transferOpen ? 'primary' : 'secondary'}
|
||||
iconLeading={({ className, ...rest }: any) => <FontAwesomeIcon icon={faPhoneArrowRight} className={className} {...rest} />}
|
||||
isDisabled={!wasAnsweredRef.current}
|
||||
isDisabled={!customerAnswered}
|
||||
onClick={() => { setTransferOpen(!transferOpen); setAppointmentOpen(false); setEnquiryOpen(false); }}>Transfer</Button>
|
||||
|
||||
<Button size="sm" color="primary-destructive" className="ml-auto"
|
||||
|
||||
Reference in New Issue
Block a user