From 9cf0f69dde50e7837d954d7ca6b19158c695d852 Mon Sep 17 00:00:00 2001 From: saridsa2 Date: Thu, 16 Apr 2026 18:32:57 +0530 Subject: [PATCH] =?UTF-8?q?feat:=20SSE=20push=20for=20worklist=20updates?= =?UTF-8?q?=20=E2=80=94=20instant=20missed-call=20notifications?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New worklist SSE stream replaces the 30s frontend poll. When the missed-call webhook creates a Call record, it emits a worklist-updated event via the supervisor's worklistSubject. All connected agents receive the event immediately. - supervisor.service.ts: worklistSubject + emitWorklistUpdate() - supervisor.controller.ts: @Sse('worklist/stream') broadcast endpoint - missed-call-webhook.controller.ts: emits after createCall() with callerPhone + callerName for toast notification - worklist.module.ts: imports SupervisorModule (forwardRef) Co-Authored-By: Claude Opus 4.6 (1M context) --- src/supervisor/supervisor.controller.ts | 14 ++++++++++++++ src/supervisor/supervisor.service.ts | 9 +++++++++ src/worklist/missed-call-webhook.controller.ts | 13 ++++++++++++- src/worklist/worklist.module.ts | 3 ++- 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/supervisor/supervisor.controller.ts b/src/supervisor/supervisor.controller.ts index 81ddce2..67e993e 100644 --- a/src/supervisor/supervisor.controller.ts +++ b/src/supervisor/supervisor.controller.ts @@ -52,4 +52,18 @@ export class SupervisorController { } as MessageEvent)), ); } + + // Worklist SSE — broadcast to all connected agents. When a missed + // call is created by the webhook, this fires immediately so agents + // don't wait for the 30s worklist poll. The payload includes the + // caller's phone + name for a toast notification. + @Sse('worklist/stream') + streamWorklistUpdates(): Observable { + this.logger.log('[SSE] Worklist stream opened'); + return this.supervisor.worklistSubject.pipe( + map(event => ({ + data: JSON.stringify(event), + } as MessageEvent)), + ); + } } diff --git a/src/supervisor/supervisor.service.ts b/src/supervisor/supervisor.service.ts index c5a64f6..d08ac16 100644 --- a/src/supervisor/supervisor.service.ts +++ b/src/supervisor/supervisor.service.ts @@ -36,6 +36,15 @@ export class SupervisorService implements OnModuleInit { private readonly agentStates = new Map(); private readonly acwTimers = new Map(); readonly agentStateSubject = new Subject<{ agentId: string; state: AgentOzonetelState | string; timestamp: string }>(); + // Worklist update stream — emitted when a missed call is created or + // assigned. Frontend SSE listener triggers an immediate worklist + // refresh so agents see new missed calls without waiting for the 30s poll. + readonly worklistSubject = new Subject<{ type: string; callerPhone?: string; callerName?: string; callId?: string; timestamp: string }>(); + + emitWorklistUpdate(data: { type: string; callerPhone?: string; callerName?: string; callId?: string }) { + this.worklistSubject.next({ ...data, timestamp: new Date().toISOString() }); + this.logger.log(`[WORKLIST-SSE] ${data.type} phone=${data.callerPhone ?? '?'} name=${data.callerName ?? '?'}`); + } // Barge session tracking — key is agentId private readonly bargeSessions = new Map SupervisorService)) private readonly supervisor: SupervisorService, ) { this.apiKey = config.get('platform.apiKey') ?? ''; } @@ -126,6 +128,15 @@ export class MissedCallWebhookController { this.logger.log(`Created call record: ${callId} (${callStatus})${resolved.leadName ? ` linked to ${resolved.leadName}` : ''}`); + // Push worklist SSE so agents see new calls instantly + // instead of waiting for the 30s frontend poll. + this.supervisor.emitWorklistUpdate({ + type: callStatus === 'MISSED' ? 'missed-call' : 'inbound-call', + callerPhone: callerPhone, + callerName: resolved.leadName ?? undefined, + callId, + }); + // Step 3: Lead-side side-effects (activity log + contact stats) if (resolved.leadId) { const summary = callStatus === 'MISSED' diff --git a/src/worklist/worklist.module.ts b/src/worklist/worklist.module.ts index 58c351a..46843f2 100644 --- a/src/worklist/worklist.module.ts +++ b/src/worklist/worklist.module.ts @@ -4,6 +4,7 @@ import { OzonetelAgentModule } from '../ozonetel/ozonetel-agent.module'; import { AuthModule } from '../auth/auth.module'; import { RulesEngineModule } from '../rules-engine/rules-engine.module'; import { CallerResolutionModule } from '../caller/caller-resolution.module'; +import { SupervisorModule } from '../supervisor/supervisor.module'; import { TelephonyConfigService } from '../config/telephony-config.service'; import { WorklistController } from './worklist.controller'; import { WorklistService } from './worklist.service'; @@ -12,7 +13,7 @@ import { MissedCallWebhookController } from './missed-call-webhook.controller'; import { KookooCallbackController } from './kookoo-callback.controller'; @Module({ - imports: [PlatformModule, forwardRef(() => OzonetelAgentModule), forwardRef(() => AuthModule), RulesEngineModule, forwardRef(() => CallerResolutionModule)], + imports: [PlatformModule, forwardRef(() => OzonetelAgentModule), forwardRef(() => AuthModule), RulesEngineModule, forwardRef(() => CallerResolutionModule), forwardRef(() => SupervisorModule)], controllers: [WorklistController, MissedCallWebhookController, KookooCallbackController], providers: [WorklistService, MissedQueueService, TelephonyConfigService], exports: [MissedQueueService],