mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-05-18 20:08:19 +00:00
Maint shortcuts (Unlock Agent / Force Ready) used to read agentId from the CC-agent's localStorage config — supervisors had no such config and the endpoint 400'd. New flow: after OTP passes, modal calls /api/maint/session-status and renders a two-bucket picker (Locked selectable / Free informational 'Already free'). Orphan locks surface with an explicit label. - use-maint-shortcuts: agentPickerEndpoint flag on forceReady + unlockAgent - maint-otp-modal: two-phase — OTP gate, then picker, then submit; OTP held in state across phases so the operator doesn't re-enter it AI chat panel: supervisor context now shows supervisor-appropriate quick actions (Agent performance / Call summary / Campaign stats / Who needs attention?) that map 1:1 to the supervisor tool set on the sidecar. Agent flow keeps the theme-token quick actions (doctors/clinics/packages).
105 lines
3.8 KiB
TypeScript
105 lines
3.8 KiB
TypeScript
import { useState, useEffect, useCallback } from 'react';
|
|
|
|
export type MaintAction = {
|
|
endpoint: string;
|
|
label: string;
|
|
description: string;
|
|
needsPreStep?: boolean;
|
|
// When set, after OTP passes the modal calls this endpoint to fetch
|
|
// `{ locked, free }` agent buckets and shows a picker. Confirm then
|
|
// POSTs to `endpoint` with { agentId } from the selection.
|
|
agentPickerEndpoint?: string;
|
|
clientSideHandler?: (payload: any) => Promise<{ status: string; message: string }>;
|
|
};
|
|
|
|
const MAINT_ACTIONS: Record<string, MaintAction> = {
|
|
forceReady: {
|
|
endpoint: 'force-ready',
|
|
label: 'Force Ready',
|
|
description: 'Logout and re-login the agent to force Ready state on Ozonetel.',
|
|
agentPickerEndpoint: 'session-status',
|
|
},
|
|
unlockAgent: {
|
|
endpoint: 'unlock-agent',
|
|
label: 'Unlock Agent',
|
|
description: 'Release the Redis session lock so the agent can log in again.',
|
|
agentPickerEndpoint: 'session-status',
|
|
},
|
|
backfill: {
|
|
endpoint: 'backfill-missed-calls',
|
|
label: 'Backfill Missed Calls',
|
|
description: 'Match existing missed calls with lead records by phone number.',
|
|
},
|
|
fixTimestamps: {
|
|
endpoint: 'fix-timestamps',
|
|
label: 'Fix Timestamps',
|
|
description: 'Correct call timestamps that were stored with IST double-offset.',
|
|
},
|
|
clearCampaignLeads: {
|
|
endpoint: 'clear-campaign-leads',
|
|
label: 'Clear Campaign Leads',
|
|
description: 'Delete all imported leads from a selected campaign. For testing only.',
|
|
needsPreStep: true,
|
|
},
|
|
clearAnalysisCache: {
|
|
endpoint: 'clear-analysis-cache',
|
|
label: 'Regenerate AI Analysis',
|
|
description: 'Clear all cached recording analyses. Next AI click will re-transcribe and re-analyze.',
|
|
},
|
|
};
|
|
|
|
export const useMaintShortcuts = () => {
|
|
const [activeAction, setActiveAction] = useState<MaintAction | null>(null);
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
|
|
const openAction = useCallback((action: MaintAction) => {
|
|
setActiveAction(action);
|
|
setIsOpen(true);
|
|
}, []);
|
|
|
|
const close = useCallback(() => {
|
|
setIsOpen(false);
|
|
setActiveAction(null);
|
|
}, []);
|
|
|
|
// Listen for programmatic triggers (e.g., long-press on AI button)
|
|
useEffect(() => {
|
|
const maintHandler = (e: CustomEvent<string>) => {
|
|
const action = MAINT_ACTIONS[e.detail];
|
|
if (action) openAction(action);
|
|
};
|
|
window.addEventListener('maint:trigger', maintHandler as EventListener);
|
|
return () => window.removeEventListener('maint:trigger', maintHandler as EventListener);
|
|
}, [openAction]);
|
|
|
|
useEffect(() => {
|
|
const handler = (e: KeyboardEvent) => {
|
|
if (e.ctrlKey && e.shiftKey && e.key === 'R') {
|
|
e.preventDefault();
|
|
openAction(MAINT_ACTIONS.forceReady);
|
|
}
|
|
if (e.ctrlKey && e.shiftKey && e.key === 'U') {
|
|
e.preventDefault();
|
|
openAction(MAINT_ACTIONS.unlockAgent);
|
|
}
|
|
if (e.ctrlKey && e.shiftKey && e.key === 'B') {
|
|
e.preventDefault();
|
|
openAction(MAINT_ACTIONS.backfill);
|
|
}
|
|
if (e.ctrlKey && e.shiftKey && e.key === 'T') {
|
|
e.preventDefault();
|
|
openAction(MAINT_ACTIONS.fixTimestamps);
|
|
}
|
|
if (e.ctrlKey && e.shiftKey && e.key === 'C') {
|
|
e.preventDefault();
|
|
openAction(MAINT_ACTIONS.clearCampaignLeads);
|
|
}
|
|
};
|
|
|
|
window.addEventListener('keydown', handler);
|
|
return () => window.removeEventListener('keydown', handler);
|
|
}, [openAction]);
|
|
|
|
return { isOpen, activeAction, close, openAction, actions: MAINT_ACTIONS };
|
|
};
|