@webex/contact-center 3.11.0-next.3 → 3.11.0-next.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/services/config/Util.js +1 -0
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/task/TaskManager.js +3 -3
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/types/services/config/types.d.ts +4 -0
- package/dist/webex.js +1 -1
- package/package.json +1 -1
- package/src/services/config/Util.ts +1 -0
- package/src/services/config/types.ts +4 -0
- package/src/services/task/TaskManager.ts +6 -3
- package/test/unit/spec/services/config/index.ts +2 -0
- package/test/unit/spec/services/task/TaskManager.ts +317 -2
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
package/package.json
CHANGED
|
@@ -204,6 +204,7 @@ function parseAgentConfigs(profileData: {
|
|
|
204
204
|
siteId: userData.siteId,
|
|
205
205
|
enterpriseId: orgInfoData.tenantId,
|
|
206
206
|
tenantTimezone: orgInfoData.timezone,
|
|
207
|
+
environment: orgInfoData.environment,
|
|
207
208
|
privacyShieldVisible: tenantData.privacyShieldVisible,
|
|
208
209
|
organizationIdleCodes: [], // TODO: for supervisor, getOrgFilteredIdleCodes(auxCodes, false),
|
|
209
210
|
idleCodesAccess: agentProfileData.accessIdleCode as 'ALL' | 'SPECIFIC',
|
|
@@ -580,6 +580,8 @@ export type OrgInfo = {
|
|
|
580
580
|
tenantId: string;
|
|
581
581
|
/** Organization timezone */
|
|
582
582
|
timezone: string;
|
|
583
|
+
/** Current environment (e.g., 'produs1', 'intgus1') */
|
|
584
|
+
environment: string;
|
|
583
585
|
};
|
|
584
586
|
|
|
585
587
|
/**
|
|
@@ -1031,6 +1033,8 @@ export type Profile = {
|
|
|
1031
1033
|
isAnalyzerEnabled?: boolean;
|
|
1032
1034
|
/** Tenant timezone */
|
|
1033
1035
|
tenantTimezone?: string;
|
|
1036
|
+
/** Current environment (e.g., 'produs1', 'intgus1') */
|
|
1037
|
+
environment?: string;
|
|
1034
1038
|
/** Available voice login options */
|
|
1035
1039
|
loginVoiceOptions?: LoginOption[];
|
|
1036
1040
|
/** Current login device type */
|
|
@@ -660,13 +660,16 @@ export default class TaskManager extends EventEmitter {
|
|
|
660
660
|
|
|
661
661
|
const isOutdial = task.data.interaction.outboundType === 'OUTDIAL';
|
|
662
662
|
const isNew = task.data.interaction.state === 'new';
|
|
663
|
-
const needsWrapUp = task.data.agentsPendingWrapUp?.
|
|
663
|
+
const needsWrapUp = task.data.agentsPendingWrapUp?.includes(this.agentId) ?? false;
|
|
664
664
|
|
|
665
665
|
// For OUTDIAL: only remove if NOT terminated (user-declined, no wrap-up follows)
|
|
666
|
-
// If terminated, keep task for wrap-up flow (CONTACT_ENDED → AGENT_WRAPUP)
|
|
667
666
|
// For non-OUTDIAL: remove if state is 'new'
|
|
668
667
|
// Always remove if secondary EpDn agent
|
|
669
|
-
if (
|
|
668
|
+
if (
|
|
669
|
+
(isNew && !(isOutdial && needsWrapUp)) ||
|
|
670
|
+
isSecondaryEpDnAgent(task.data.interaction) ||
|
|
671
|
+
(!needsWrapUp && isOutdial) // For outdial tasks, needs wrap-up is false and state is "WRAPUP". We need to just remove the task.
|
|
672
|
+
) {
|
|
670
673
|
this.removeTaskFromCollection(task);
|
|
671
674
|
}
|
|
672
675
|
}
|
|
@@ -718,6 +718,7 @@ describe('AgentConfigService', () => {
|
|
|
718
718
|
const mockOrgInfo = {
|
|
719
719
|
tenantId: 'tenant123',
|
|
720
720
|
timezone: 'GMT',
|
|
721
|
+
environment: 'produs1',
|
|
721
722
|
};
|
|
722
723
|
|
|
723
724
|
const mockOrgSettings = {
|
|
@@ -857,6 +858,7 @@ describe('AgentConfigService', () => {
|
|
|
857
858
|
const mockOrgInfo = {
|
|
858
859
|
tenantId: 'tenant123',
|
|
859
860
|
timezone: 'GMT',
|
|
861
|
+
environment: 'produs1',
|
|
860
862
|
};
|
|
861
863
|
|
|
862
864
|
const mockOrgSettings = {
|
|
@@ -896,6 +896,9 @@ describe('TaskManager', () => {
|
|
|
896
896
|
});
|
|
897
897
|
|
|
898
898
|
it('should NOT remove OUTDIAL task on CONTACT_ENDED when agentsPendingWrapUp exists', () => {
|
|
899
|
+
const agentId = '723a8ffb-a26e-496d-b14a-ff44fb83b64f';
|
|
900
|
+
taskManager.setAgentId(agentId);
|
|
901
|
+
|
|
899
902
|
const task = taskManager.getTask(taskId);
|
|
900
903
|
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
901
904
|
task.data = {
|
|
@@ -907,7 +910,7 @@ describe('TaskManager', () => {
|
|
|
907
910
|
state: 'new',
|
|
908
911
|
mediaType: 'telephony',
|
|
909
912
|
},
|
|
910
|
-
agentsPendingWrapUp: [
|
|
913
|
+
agentsPendingWrapUp: [agentId],
|
|
911
914
|
};
|
|
912
915
|
return task;
|
|
913
916
|
});
|
|
@@ -923,7 +926,7 @@ describe('TaskManager', () => {
|
|
|
923
926
|
state: 'new',
|
|
924
927
|
mediaType: 'telephony',
|
|
925
928
|
},
|
|
926
|
-
agentsPendingWrapUp: [
|
|
929
|
+
agentsPendingWrapUp: [agentId],
|
|
927
930
|
},
|
|
928
931
|
};
|
|
929
932
|
|
|
@@ -2530,6 +2533,318 @@ describe('TaskManager', () => {
|
|
|
2530
2533
|
});
|
|
2531
2534
|
});
|
|
2532
2535
|
|
|
2536
|
+
describe('handleTaskCleanup - stage changes', () => {
|
|
2537
|
+
const agentId = '723a8ffb-a26e-496d-b14a-ff44fb83b64f';
|
|
2538
|
+
|
|
2539
|
+
beforeEach(() => {
|
|
2540
|
+
taskManager.setAgentId(agentId);
|
|
2541
|
+
});
|
|
2542
|
+
|
|
2543
|
+
it('should remove OUTDIAL task on CONTACT_ENDED when current agent is NOT in agentsPendingWrapUp', () => {
|
|
2544
|
+
const task = taskManager.getTask(taskId);
|
|
2545
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
2546
|
+
task.data = {
|
|
2547
|
+
...task.data,
|
|
2548
|
+
...newData,
|
|
2549
|
+
interaction: {
|
|
2550
|
+
...task.data.interaction,
|
|
2551
|
+
outboundType: 'OUTDIAL',
|
|
2552
|
+
state: 'new',
|
|
2553
|
+
mediaType: 'telephony',
|
|
2554
|
+
},
|
|
2555
|
+
agentsPendingWrapUp: ['different-agent-123'], // Current agent not in the list
|
|
2556
|
+
};
|
|
2557
|
+
return task;
|
|
2558
|
+
});
|
|
2559
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2560
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2561
|
+
|
|
2562
|
+
const payload = {
|
|
2563
|
+
data: {
|
|
2564
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2565
|
+
interactionId: taskId,
|
|
2566
|
+
interaction: {
|
|
2567
|
+
outboundType: 'OUTDIAL',
|
|
2568
|
+
state: 'new',
|
|
2569
|
+
mediaType: 'telephony',
|
|
2570
|
+
},
|
|
2571
|
+
agentsPendingWrapUp: ['different-agent-123'], // Current agent not in the list
|
|
2572
|
+
},
|
|
2573
|
+
};
|
|
2574
|
+
|
|
2575
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2576
|
+
|
|
2577
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
2578
|
+
expect(taskManager.getTask(taskId)).toBeUndefined();
|
|
2579
|
+
});
|
|
2580
|
+
|
|
2581
|
+
it('should NOT remove OUTDIAL task on CONTACT_ENDED when current agent IS in agentsPendingWrapUp', () => {
|
|
2582
|
+
const task = taskManager.getTask(taskId);
|
|
2583
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
2584
|
+
task.data = {
|
|
2585
|
+
...task.data,
|
|
2586
|
+
...newData,
|
|
2587
|
+
interaction: {
|
|
2588
|
+
...task.data.interaction,
|
|
2589
|
+
outboundType: 'OUTDIAL',
|
|
2590
|
+
state: 'new',
|
|
2591
|
+
mediaType: 'telephony',
|
|
2592
|
+
},
|
|
2593
|
+
agentsPendingWrapUp: [agentId, 'other-agent-456'], // Current agent IS in the list
|
|
2594
|
+
};
|
|
2595
|
+
return task;
|
|
2596
|
+
});
|
|
2597
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2598
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2599
|
+
|
|
2600
|
+
const payload = {
|
|
2601
|
+
data: {
|
|
2602
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2603
|
+
interactionId: taskId,
|
|
2604
|
+
interaction: {
|
|
2605
|
+
outboundType: 'OUTDIAL',
|
|
2606
|
+
state: 'new',
|
|
2607
|
+
mediaType: 'telephony',
|
|
2608
|
+
},
|
|
2609
|
+
agentsPendingWrapUp: [agentId, 'other-agent-456'], // Current agent IS in the list
|
|
2610
|
+
},
|
|
2611
|
+
};
|
|
2612
|
+
|
|
2613
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2614
|
+
|
|
2615
|
+
expect(removeTaskSpy).not.toHaveBeenCalled();
|
|
2616
|
+
expect(taskManager.getTask(taskId)).toBeDefined();
|
|
2617
|
+
});
|
|
2618
|
+
|
|
2619
|
+
it('should remove OUTDIAL task when needsWrapUp is false and task is outdial', () => {
|
|
2620
|
+
const task = taskManager.getTask(taskId);
|
|
2621
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
2622
|
+
task.data = {
|
|
2623
|
+
...task.data,
|
|
2624
|
+
...newData,
|
|
2625
|
+
interaction: {
|
|
2626
|
+
...task.data.interaction,
|
|
2627
|
+
outboundType: 'OUTDIAL',
|
|
2628
|
+
state: 'WRAPUP', // Not 'new' state
|
|
2629
|
+
mediaType: 'telephony',
|
|
2630
|
+
},
|
|
2631
|
+
agentsPendingWrapUp: [], // No agents pending wrap-up
|
|
2632
|
+
};
|
|
2633
|
+
return task;
|
|
2634
|
+
});
|
|
2635
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2636
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2637
|
+
|
|
2638
|
+
const payload = {
|
|
2639
|
+
data: {
|
|
2640
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2641
|
+
interactionId: taskId,
|
|
2642
|
+
interaction: {
|
|
2643
|
+
outboundType: 'OUTDIAL',
|
|
2644
|
+
state: 'WRAPUP', // Not 'new' state
|
|
2645
|
+
mediaType: 'telephony',
|
|
2646
|
+
},
|
|
2647
|
+
agentsPendingWrapUp: [], // No agents pending wrap-up
|
|
2648
|
+
},
|
|
2649
|
+
};
|
|
2650
|
+
|
|
2651
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2652
|
+
|
|
2653
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
2654
|
+
expect(taskManager.getTask(taskId)).toBeUndefined();
|
|
2655
|
+
});
|
|
2656
|
+
|
|
2657
|
+
it('should remove OUTDIAL task when needsWrapUp is false (agentsPendingWrapUp is undefined)', () => {
|
|
2658
|
+
const task = taskManager.getTask(taskId);
|
|
2659
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
2660
|
+
task.data = {
|
|
2661
|
+
...task.data,
|
|
2662
|
+
...newData,
|
|
2663
|
+
interaction: {
|
|
2664
|
+
...task.data.interaction,
|
|
2665
|
+
outboundType: 'OUTDIAL',
|
|
2666
|
+
state: 'WRAPUP',
|
|
2667
|
+
mediaType: 'telephony',
|
|
2668
|
+
},
|
|
2669
|
+
agentsPendingWrapUp: undefined, // No agentsPendingWrapUp field
|
|
2670
|
+
};
|
|
2671
|
+
return task;
|
|
2672
|
+
});
|
|
2673
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2674
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2675
|
+
|
|
2676
|
+
const payload = {
|
|
2677
|
+
data: {
|
|
2678
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2679
|
+
interactionId: taskId,
|
|
2680
|
+
interaction: {
|
|
2681
|
+
outboundType: 'OUTDIAL',
|
|
2682
|
+
state: 'WRAPUP',
|
|
2683
|
+
mediaType: 'telephony',
|
|
2684
|
+
},
|
|
2685
|
+
// agentsPendingWrapUp not included
|
|
2686
|
+
},
|
|
2687
|
+
};
|
|
2688
|
+
|
|
2689
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2690
|
+
|
|
2691
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
2692
|
+
expect(taskManager.getTask(taskId)).toBeUndefined();
|
|
2693
|
+
});
|
|
2694
|
+
|
|
2695
|
+
it('should NOT remove OUTDIAL task when needsWrapUp is true (current agent in agentsPendingWrapUp) even if state is WRAPUP', () => {
|
|
2696
|
+
const task = taskManager.getTask(taskId);
|
|
2697
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
2698
|
+
task.data = {
|
|
2699
|
+
...task.data,
|
|
2700
|
+
...newData,
|
|
2701
|
+
interaction: {
|
|
2702
|
+
...task.data.interaction,
|
|
2703
|
+
outboundType: 'OUTDIAL',
|
|
2704
|
+
state: 'WRAPUP',
|
|
2705
|
+
mediaType: 'telephony',
|
|
2706
|
+
},
|
|
2707
|
+
agentsPendingWrapUp: [agentId], // Current agent needs wrap-up
|
|
2708
|
+
};
|
|
2709
|
+
return task;
|
|
2710
|
+
});
|
|
2711
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2712
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2713
|
+
|
|
2714
|
+
const payload = {
|
|
2715
|
+
data: {
|
|
2716
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2717
|
+
interactionId: taskId,
|
|
2718
|
+
interaction: {
|
|
2719
|
+
outboundType: 'OUTDIAL',
|
|
2720
|
+
state: 'WRAPUP',
|
|
2721
|
+
mediaType: 'telephony',
|
|
2722
|
+
},
|
|
2723
|
+
agentsPendingWrapUp: [agentId], // Current agent needs wrap-up
|
|
2724
|
+
},
|
|
2725
|
+
};
|
|
2726
|
+
|
|
2727
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2728
|
+
|
|
2729
|
+
expect(removeTaskSpy).not.toHaveBeenCalled();
|
|
2730
|
+
expect(taskManager.getTask(taskId)).toBeDefined();
|
|
2731
|
+
});
|
|
2732
|
+
|
|
2733
|
+
it('should remove non-OUTDIAL task when state is new regardless of agentsPendingWrapUp', () => {
|
|
2734
|
+
const task = taskManager.getTask(taskId);
|
|
2735
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
2736
|
+
task.data = {
|
|
2737
|
+
...task.data,
|
|
2738
|
+
...newData,
|
|
2739
|
+
interaction: {
|
|
2740
|
+
...task.data.interaction,
|
|
2741
|
+
outboundType: 'PREVIEW', // Not OUTDIAL
|
|
2742
|
+
state: 'new',
|
|
2743
|
+
mediaType: 'telephony',
|
|
2744
|
+
},
|
|
2745
|
+
agentsPendingWrapUp: [agentId],
|
|
2746
|
+
};
|
|
2747
|
+
return task;
|
|
2748
|
+
});
|
|
2749
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2750
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2751
|
+
|
|
2752
|
+
const payload = {
|
|
2753
|
+
data: {
|
|
2754
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2755
|
+
interactionId: taskId,
|
|
2756
|
+
interaction: {
|
|
2757
|
+
outboundType: 'PREVIEW',
|
|
2758
|
+
state: 'new',
|
|
2759
|
+
mediaType: 'telephony',
|
|
2760
|
+
},
|
|
2761
|
+
agentsPendingWrapUp: [agentId],
|
|
2762
|
+
},
|
|
2763
|
+
};
|
|
2764
|
+
|
|
2765
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2766
|
+
|
|
2767
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
2768
|
+
expect(taskManager.getTask(taskId)).toBeUndefined();
|
|
2769
|
+
});
|
|
2770
|
+
|
|
2771
|
+
it('should handle agentsPendingWrapUp with multiple agents correctly - remove if current agent not in list', () => {
|
|
2772
|
+
const task = taskManager.getTask(taskId);
|
|
2773
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
2774
|
+
task.data = {
|
|
2775
|
+
...task.data,
|
|
2776
|
+
...newData,
|
|
2777
|
+
interaction: {
|
|
2778
|
+
...task.data.interaction,
|
|
2779
|
+
outboundType: 'OUTDIAL',
|
|
2780
|
+
state: 'new',
|
|
2781
|
+
mediaType: 'telephony',
|
|
2782
|
+
},
|
|
2783
|
+
agentsPendingWrapUp: ['agent-1', 'agent-2', 'agent-3'], // Current agent not in the list
|
|
2784
|
+
};
|
|
2785
|
+
return task;
|
|
2786
|
+
});
|
|
2787
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2788
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2789
|
+
|
|
2790
|
+
const payload = {
|
|
2791
|
+
data: {
|
|
2792
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2793
|
+
interactionId: taskId,
|
|
2794
|
+
interaction: {
|
|
2795
|
+
outboundType: 'OUTDIAL',
|
|
2796
|
+
state: 'new',
|
|
2797
|
+
mediaType: 'telephony',
|
|
2798
|
+
},
|
|
2799
|
+
agentsPendingWrapUp: ['agent-1', 'agent-2', 'agent-3'],
|
|
2800
|
+
},
|
|
2801
|
+
};
|
|
2802
|
+
|
|
2803
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2804
|
+
|
|
2805
|
+
expect(removeTaskSpy).toHaveBeenCalled();
|
|
2806
|
+
expect(taskManager.getTask(taskId)).toBeUndefined();
|
|
2807
|
+
});
|
|
2808
|
+
|
|
2809
|
+
it('should handle agentsPendingWrapUp with multiple agents correctly - keep if current agent is in list', () => {
|
|
2810
|
+
const task = taskManager.getTask(taskId);
|
|
2811
|
+
task.updateTaskData = jest.fn().mockImplementation((newData) => {
|
|
2812
|
+
task.data = {
|
|
2813
|
+
...task.data,
|
|
2814
|
+
...newData,
|
|
2815
|
+
interaction: {
|
|
2816
|
+
...task.data.interaction,
|
|
2817
|
+
outboundType: 'OUTDIAL',
|
|
2818
|
+
state: 'new',
|
|
2819
|
+
mediaType: 'telephony',
|
|
2820
|
+
},
|
|
2821
|
+
agentsPendingWrapUp: ['agent-1', agentId, 'agent-3'], // Current agent IS in the list
|
|
2822
|
+
};
|
|
2823
|
+
return task;
|
|
2824
|
+
});
|
|
2825
|
+
task.unregisterWebCallListeners = jest.fn();
|
|
2826
|
+
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
2827
|
+
|
|
2828
|
+
const payload = {
|
|
2829
|
+
data: {
|
|
2830
|
+
type: CC_EVENTS.CONTACT_ENDED,
|
|
2831
|
+
interactionId: taskId,
|
|
2832
|
+
interaction: {
|
|
2833
|
+
outboundType: 'OUTDIAL',
|
|
2834
|
+
state: 'new',
|
|
2835
|
+
mediaType: 'telephony',
|
|
2836
|
+
},
|
|
2837
|
+
agentsPendingWrapUp: ['agent-1', agentId, 'agent-3'],
|
|
2838
|
+
},
|
|
2839
|
+
};
|
|
2840
|
+
|
|
2841
|
+
webSocketManagerMock.emit('message', JSON.stringify(payload));
|
|
2842
|
+
|
|
2843
|
+
expect(removeTaskSpy).not.toHaveBeenCalled();
|
|
2844
|
+
expect(taskManager.getTask(taskId)).toBeDefined();
|
|
2845
|
+
});
|
|
2846
|
+
});
|
|
2847
|
+
|
|
2533
2848
|
describe('CONTACT_MERGED event handling', () => {
|
|
2534
2849
|
let task;
|
|
2535
2850
|
let taskEmitSpy;
|