Files
helix-engage/src/hooks/use-worklist.ts
saridsa2 42e23a52ec feat: call-desk refresh — disposition modal, active-call UI, worklist + perf updates
- Call-desk: active-call-card supervisor presence badges, incoming-call-card polish, transfer-dialog, call-log
- Disposition modal: auto-lock based on actions taken, not-interested split
- Forms: appointment-form + enquiry-form improvements (placeholder handling, phone format)
- Worklist-panel: pagination awareness, filter chips
- Pages: all-leads/patients/patient-360/missed-calls/team-performance/call-history/appointments polish
- SIP: sip-client reconnect, sip-provider + sip-manager state, agent-status-toggle spinner
- Hooks: use-agent-state supervisor SSE events, use-worklist, use-performance-alerts
- Types: entities.ts extended

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 06:49:36 +05:30

143 lines
4.6 KiB
TypeScript

import { useState, useEffect, useCallback } from 'react';
import { apiClient } from '@/lib/api-client';
type MissedCall = {
id: string;
createdAt: string;
callDirection: string | null;
callStatus: string | null;
callerNumber: { number: string; callingCode: string }[] | null;
agentName: string | null;
startedAt: string | null;
endedAt: string | null;
durationSeconds: number | null;
disposition: string | null;
callNotes: string | null;
leadId: string | null;
leadName: string | null;
callbackStatus: string | null;
callSourceNumber: string | null;
missedCallCount: number | null;
callbackAttemptedAt: string | null;
};
type WorklistFollowUp = {
id: string;
createdAt: string | null;
followUpType: string | null;
followUpStatus: string | null;
scheduledAt: string | null;
completedAt: string | null;
priority: string | null;
assignedAgent: string | null;
patientId: string | null;
callId: string | null;
patientName?: string;
patientPhone?: string;
};
type WorklistLead = {
id: string;
createdAt: string;
contactName: { firstName: string; lastName: string } | null;
contactPhone: { number: string; callingCode: string }[] | null;
contactEmail: { address: string }[] | null;
leadSource: string | null;
leadStatus: string | null;
interestedService: string | null;
assignedAgent: string | null;
campaignId: string | null;
adId: string | null;
contactAttempts: number | null;
spamScore: number | null;
isSpam: boolean | null;
aiSummary: string | null;
aiSuggestedAction: string | null;
lastContacted: string | null;
utmCampaign: string | null;
};
type WorklistData = {
missedCalls: MissedCall[];
followUps: WorklistFollowUp[];
marketingLeads: WorklistLead[];
totalPending: number;
};
type UseWorklistResult = WorklistData & {
loading: boolean;
error: string | null;
refresh: () => void;
};
const EMPTY_WORKLIST: WorklistData = {
missedCalls: [],
followUps: [],
marketingLeads: [],
totalPending: 0,
};
export const useWorklist = (): UseWorklistResult => {
const [data, setData] = useState<WorklistData>(EMPTY_WORKLIST);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const fetchWorklist = useCallback(async () => {
if (!apiClient.isAuthenticated()) {
setError('Not authenticated');
setLoading(false);
return;
}
try {
const json = await apiClient.get<any>('/api/worklist', { silent: true });
// Transform platform field shapes to frontend types
const transformed: WorklistData = {
...json,
marketingLeads: (json.marketingLeads ?? []).map((lead: any) => ({
...lead,
leadSource: lead.source ?? lead.leadSource,
leadStatus: lead.status ?? lead.leadStatus,
contactPhone: lead.contactPhone?.primaryPhoneNumber
? [{ number: lead.contactPhone.primaryPhoneNumber, callingCode: lead.contactPhone.primaryPhoneCallingCode ?? '+91' }]
: lead.contactPhone,
contactEmail: lead.contactEmail?.primaryEmail
? [{ address: lead.contactEmail.primaryEmail }]
: lead.contactEmail,
})),
missedCalls: (json.missedCalls ?? []).map((call: any) => ({
...call,
callDirection: call.direction ?? call.callDirection,
durationSeconds: call.durationSec ?? call.durationSeconds ?? 0,
callerNumber: call.callerNumber?.primaryPhoneNumber
? [{ number: call.callerNumber.primaryPhoneNumber, callingCode: '+91' }]
: call.callerNumber,
})),
followUps: (json.followUps ?? []).map((fu: any) => ({
...fu,
followUpType: fu.typeCustom ?? fu.followUpType,
followUpStatus: fu.status ?? fu.followUpStatus,
})),
};
setData(transformed);
setError(null);
} catch {
setError('Sidecar not reachable');
}
setLoading(false);
}, []);
useEffect(() => {
fetchWorklist();
// Refresh every 30 seconds
const interval = setInterval(fetchWorklist, 30000);
return () => clearInterval(interval);
}, [fetchWorklist]);
return { ...data, loading, error, refresh: fetchWorklist };
};