mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-04-11 18:28:15 +00:00
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>
This commit is contained in:
@@ -10,7 +10,8 @@ import {
|
||||
sipCallStartTimeAtom,
|
||||
sipCallUcidAtom,
|
||||
} from '@/state/sip-state';
|
||||
import { registerSipStateUpdater, connectSip, disconnectSip, getSipClient } from '@/state/sip-manager';
|
||||
import { registerSipStateUpdater, connectSip, disconnectSip, getSipClient, setOutboundPending } from '@/state/sip-manager';
|
||||
import { apiClient } from '@/lib/api-client';
|
||||
import type { SIPConfig } from '@/types/sip';
|
||||
|
||||
const getSipConfig = (): SIPConfig => {
|
||||
@@ -85,11 +86,14 @@ export const SipProvider = ({ children }: PropsWithChildren) => {
|
||||
// No auto-reset — the ActiveCallCard handles post-call flow (disposition → appointment → done)
|
||||
// and resets to idle via the "Back to Worklist" button
|
||||
|
||||
// Cleanup on page unload
|
||||
// Cleanup on unmount + page unload
|
||||
useEffect(() => {
|
||||
const handleUnload = () => disconnectSip();
|
||||
window.addEventListener('beforeunload', handleUnload);
|
||||
return () => window.removeEventListener('beforeunload', handleUnload);
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', handleUnload);
|
||||
disconnectSip();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return <>{children}</>;
|
||||
@@ -98,7 +102,7 @@ export const SipProvider = ({ children }: PropsWithChildren) => {
|
||||
// Hook for components to access SIP actions + state
|
||||
export const useSip = () => {
|
||||
const [connectionStatus] = useAtom(sipConnectionStatusAtom);
|
||||
const [callState] = useAtom(sipCallStateAtom);
|
||||
const [callState, setCallState] = useAtom(sipCallStateAtom);
|
||||
const [callerNumber, setCallerNumber] = useAtom(sipCallerNumberAtom);
|
||||
const [callUcid] = useAtom(sipCallUcidAtom);
|
||||
const [isMuted, setIsMuted] = useAtom(sipIsMutedAtom);
|
||||
@@ -110,6 +114,24 @@ export const useSip = () => {
|
||||
setCallerNumber(phoneNumber);
|
||||
}, [setCallerNumber]);
|
||||
|
||||
// Ozonetel outbound dial — single path for all outbound calls
|
||||
const dialOutbound = useCallback(async (phoneNumber: string): Promise<void> => {
|
||||
setCallState('ringing-out');
|
||||
setCallerNumber(phoneNumber);
|
||||
setOutboundPending(true);
|
||||
const safetyTimeout = setTimeout(() => setOutboundPending(false), 30000);
|
||||
|
||||
try {
|
||||
await apiClient.post('/api/ozonetel/dial', { phoneNumber });
|
||||
} catch {
|
||||
clearTimeout(safetyTimeout);
|
||||
setOutboundPending(false);
|
||||
setCallState('idle');
|
||||
setCallerNumber(null);
|
||||
throw new Error('Dial failed');
|
||||
}
|
||||
}, [setCallState, setCallerNumber]);
|
||||
|
||||
const answer = useCallback(() => getSipClient()?.answer(), []);
|
||||
const reject = useCallback(() => getSipClient()?.reject(), []);
|
||||
const hangup = useCallback(() => getSipClient()?.hangup(), []);
|
||||
@@ -147,6 +169,7 @@ export const useSip = () => {
|
||||
connect: () => connectSip(getSipConfig()),
|
||||
disconnect: disconnectSip,
|
||||
makeCall,
|
||||
dialOutbound,
|
||||
answer,
|
||||
reject,
|
||||
hangup,
|
||||
|
||||
Reference in New Issue
Block a user