feat: SSE-driven worklist + agent call history split + remove SOURCE column

Worklist:
- SSE stream replaces 30s poll — EventSource on /api/supervisor/worklist/stream
  triggers immediate fetchWorklist() on missed-call events
- Toast notification: 'Missed Call — {name} — needs callback'
- No polling fallback — SSE is the source of truth

Call History split by role:
- Agent: 'My Call History' — own calls only (matched by agent relation
  or chain-parsed agentName), missed calls excluded (they belong on
  the Call Desk queue), no Agent/Recording/SLA columns, phone clickable
  via PhoneActionCell instead of separate Call button
- Supervisor: 'Call History' — all calls, Agent + Recording columns visible

Worklist panel:
- SOURCE/BRANCH column removed from display (data stays on row)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-16 18:33:17 +05:30
parent 5c9e70da20
commit df08bcfc19
3 changed files with 94 additions and 126 deletions

View File

@@ -178,34 +178,9 @@ const formatTimeAgo = (dateStr: string): string => {
const formatDisposition = (disposition: string): string =>
disposition.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
const formatSource = (source: string): string => {
const map: Record<string, string> = {
FACEBOOK_AD: 'Facebook',
GOOGLE_AD: 'Google',
WALK_IN: 'Walk-in',
REFERRAL: 'Referral',
WEBSITE: 'Website',
PHONE_INQUIRY: 'Phone',
PHONE: 'Phone',
OTHER: 'Other',
};
return map[source] ?? source.replace(/_/g, ' ');
};
// Resolve a DID (e.g. "918041763265") to a friendly branch/campaign name.
// The DID is the phone number the caller dialed — it identifies which
// hospital branch or campaign the call came through. Falls back to the
// last 10 digits if no mapping is found.
const formatDid = (did: string): string => {
if (!did) return '—';
// Known DIDs — loaded from the sidecar theme tokens at boot (via
// the ThemeTokenProvider). For now, hardcode nothing — strip country
// code and show the DID as a short number so it's at least readable.
const digits = did.replace(/\D/g, '');
// Strip leading 91 (India) for display
const short = digits.length > 10 && digits.startsWith('91') ? digits.slice(2) : digits;
return short;
};
// formatSource + formatDid kept for reference but no longer rendered
// in the table — SOURCE/BRANCH column removed from display per user
// request. Data stays on the row for future use.
const IconInbound = faIcon(faPhoneArrowDown);
const IconOutbound = faIcon(faPhoneArrowUp);
@@ -239,7 +214,7 @@ const buildRows = (missedCalls: MissedCall[], followUps: WorklistFollowUp[], lea
// Branch column: prefer the campaign name (e.g. "Cervical Cancer
// Screening Drive") over the raw DID. Falls back to formatted DID
// for organic calls with no campaign.
source: call.campaign?.campaignName ?? (call.callSourceNumber ? formatDid(call.callSourceNumber) : null),
source: call.campaign?.campaignName ?? call.callSourceNumber ?? null,
lastDisposition: call.disposition ?? null,
missedCallId: call.id,
});
@@ -497,7 +472,6 @@ export const WorklistPanel = ({ missedCalls, followUps, leads, loading, onSelect
<Table.Head id="priority" label="SCORE" className="w-20" isRowHeader allowsSorting />
<Table.Head id="name" label="PATIENT" allowsSorting />
<Table.Head label="PHONE" />
<Table.Head label={tab === 'missed' ? 'BRANCH' : 'SOURCE'} className="w-28" />
<Table.Head id="sla" label="SLA" className="w-24" allowsSorting />
</Table.Header>
<Table.Body items={pagedRows}>
@@ -578,15 +552,6 @@ export const WorklistPanel = ({ missedCalls, followUps, leads, loading, onSelect
<span className="text-xs text-quaternary italic">No phone</span>
)}
</Table.Cell>
<Table.Cell>
{row.source ? (
<span className="text-xs text-tertiary truncate block max-w-[100px]">
{formatSource(row.source)}
</span>
) : (
<span className="text-xs text-quaternary"></span>
)}
</Table.Cell>
<Table.Cell>
<Badge size="sm" color={sla.color} type="pill-color">
{sla.label}