mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-05-18 20:08:19 +00:00
feat: supervisor fixes — settings disabled cards, column toggle fix, hold SSE, campaign edit disabled
- SectionCard: added disabled prop (muted, non-clickable, no arrow) - Settings hub: Clinics, Doctors, Team, Telephony, AI, Widget cards disabled - Campaigns: edit button disabled - Missed calls + Call recordings: column toggle blank page fixed (key-based Table remount forces clean React Aria collection on column change) - Live monitor: replaced 5s polling with SSE stream for real-time active call updates (new/hold/unhold/disconnect reflected instantly) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -55,26 +55,48 @@ export const LiveMonitorPage = () => {
|
||||
const [contextLoading, setContextLoading] = useState(false);
|
||||
const { leads } = useData();
|
||||
|
||||
// Poll active calls every 5 seconds
|
||||
// Initial load + SSE stream for real-time active call updates
|
||||
useEffect(() => {
|
||||
const fetchCalls = () => {
|
||||
apiClient.get<ActiveCall[]>('/api/supervisor/active-calls', { silent: true })
|
||||
.then(calls => {
|
||||
setActiveCalls(calls);
|
||||
// If selected call ended, clear selection
|
||||
if (selectedCall && !calls.find(c => c.ucid === selectedCall.ucid)) {
|
||||
setSelectedCall(null);
|
||||
setCallerContext(null);
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => setLoading(false));
|
||||
};
|
||||
// Initial snapshot
|
||||
apiClient.get<ActiveCall[]>('/api/supervisor/active-calls', { silent: true })
|
||||
.then(setActiveCalls)
|
||||
.catch(() => {})
|
||||
.finally(() => setLoading(false));
|
||||
|
||||
fetchCalls();
|
||||
const interval = setInterval(fetchCalls, 5000);
|
||||
return () => clearInterval(interval);
|
||||
}, [selectedCall?.ucid]);
|
||||
// SSE stream — receives update/remove events in real-time
|
||||
const apiUrl = import.meta.env.VITE_API_URL ?? '';
|
||||
const es = new EventSource(`${apiUrl}/api/supervisor/active-calls/stream`);
|
||||
es.onmessage = (msg) => {
|
||||
try {
|
||||
const event = JSON.parse(msg.data) as { type: 'update' | 'remove'; call?: ActiveCall; ucid: string };
|
||||
setActiveCalls(prev => {
|
||||
if (event.type === 'remove') {
|
||||
return prev.filter(c => c.ucid !== event.ucid);
|
||||
}
|
||||
if (event.type === 'update' && event.call) {
|
||||
const exists = prev.find(c => c.ucid === event.ucid);
|
||||
if (exists) {
|
||||
return prev.map(c => c.ucid === event.ucid ? event.call! : c);
|
||||
}
|
||||
return [...prev, event.call];
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
} catch {}
|
||||
};
|
||||
es.onerror = () => {
|
||||
// SSE reconnects automatically; no-op
|
||||
};
|
||||
return () => es.close();
|
||||
}, []);
|
||||
|
||||
// Clear selection if the selected call ended
|
||||
useEffect(() => {
|
||||
if (selectedCall && !activeCalls.find(c => c.ucid === selectedCall.ucid)) {
|
||||
setSelectedCall(null);
|
||||
setCallerContext(null);
|
||||
}
|
||||
}, [activeCalls, selectedCall]);
|
||||
|
||||
// Tick every second for duration display
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user