@memberjunction/ng-dashboards 6.2.0-edge.2 → 6.2.0-edge.3

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.
Files changed (69) hide show
  1. package/dist/ComponentStudio/components/form-override-dialog.component.d.ts +12 -1
  2. package/dist/ComponentStudio/components/form-override-dialog.component.d.ts.map +1 -1
  3. package/dist/ComponentStudio/components/form-override-dialog.component.js +128 -81
  4. package/dist/ComponentStudio/components/form-override-dialog.component.js.map +1 -1
  5. package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +4 -0
  6. package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
  7. package/dist/DataExplorer/data-explorer-dashboard.component.js +47 -13
  8. package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
  9. package/dist/TelephonyOperations/telephony-operations-agent-context.d.ts +107 -0
  10. package/dist/TelephonyOperations/telephony-operations-agent-context.d.ts.map +1 -0
  11. package/dist/TelephonyOperations/telephony-operations-agent-context.js +216 -0
  12. package/dist/TelephonyOperations/telephony-operations-agent-context.js.map +1 -0
  13. package/dist/TelephonyOperations/telephony-operations-dashboard.component.d.ts +57 -0
  14. package/dist/TelephonyOperations/telephony-operations-dashboard.component.d.ts.map +1 -0
  15. package/dist/TelephonyOperations/telephony-operations-dashboard.component.js +805 -0
  16. package/dist/TelephonyOperations/telephony-operations-dashboard.component.js.map +1 -0
  17. package/dist/WorkQueue/components/work-queue-bindings.component.d.ts +37 -0
  18. package/dist/WorkQueue/components/work-queue-bindings.component.d.ts.map +1 -0
  19. package/dist/WorkQueue/components/work-queue-bindings.component.js +210 -0
  20. package/dist/WorkQueue/components/work-queue-bindings.component.js.map +1 -0
  21. package/dist/WorkQueue/components/work-queue-dead-letters.component.d.ts +70 -0
  22. package/dist/WorkQueue/components/work-queue-dead-letters.component.d.ts.map +1 -0
  23. package/dist/WorkQueue/components/work-queue-dead-letters.component.js +496 -0
  24. package/dist/WorkQueue/components/work-queue-dead-letters.component.js.map +1 -0
  25. package/dist/WorkQueue/components/work-queue-discard-dialog.component.d.ts +22 -0
  26. package/dist/WorkQueue/components/work-queue-discard-dialog.component.d.ts.map +1 -0
  27. package/dist/WorkQueue/components/work-queue-discard-dialog.component.js +99 -0
  28. package/dist/WorkQueue/components/work-queue-discard-dialog.component.js.map +1 -0
  29. package/dist/WorkQueue/components/work-queue-overview.component.d.ts +29 -0
  30. package/dist/WorkQueue/components/work-queue-overview.component.d.ts.map +1 -0
  31. package/dist/WorkQueue/components/work-queue-overview.component.js +278 -0
  32. package/dist/WorkQueue/components/work-queue-overview.component.js.map +1 -0
  33. package/dist/WorkQueue/components/work-queue-partitions.component.d.ts +59 -0
  34. package/dist/WorkQueue/components/work-queue-partitions.component.d.ts.map +1 -0
  35. package/dist/WorkQueue/components/work-queue-partitions.component.js +359 -0
  36. package/dist/WorkQueue/components/work-queue-partitions.component.js.map +1 -0
  37. package/dist/WorkQueue/services/work-queue-operator.service.d.ts +22 -0
  38. package/dist/WorkQueue/services/work-queue-operator.service.d.ts.map +1 -0
  39. package/dist/WorkQueue/services/work-queue-operator.service.js +44 -0
  40. package/dist/WorkQueue/services/work-queue-operator.service.js.map +1 -0
  41. package/dist/WorkQueue/work-queue-agent-context.d.ts +67 -0
  42. package/dist/WorkQueue/work-queue-agent-context.d.ts.map +1 -0
  43. package/dist/WorkQueue/work-queue-agent-context.js +119 -0
  44. package/dist/WorkQueue/work-queue-agent-context.js.map +1 -0
  45. package/dist/WorkQueue/work-queue-dashboard.component.d.ts +94 -0
  46. package/dist/WorkQueue/work-queue-dashboard.component.d.ts.map +1 -0
  47. package/dist/WorkQueue/work-queue-dashboard.component.js +545 -0
  48. package/dist/WorkQueue/work-queue-dashboard.component.js.map +1 -0
  49. package/dist/WorkQueue/work-queue-types.d.ts +40 -0
  50. package/dist/WorkQueue/work-queue-types.d.ts.map +1 -0
  51. package/dist/WorkQueue/work-queue-types.js +2 -0
  52. package/dist/WorkQueue/work-queue-types.js.map +1 -0
  53. package/dist/core-dashboards.module.d.ts +29 -28
  54. package/dist/core-dashboards.module.d.ts.map +1 -1
  55. package/dist/core-dashboards.module.js +7 -0
  56. package/dist/core-dashboards.module.js.map +1 -1
  57. package/dist/module.d.ts +14 -13
  58. package/dist/module.d.ts.map +1 -1
  59. package/dist/module.js +7 -0
  60. package/dist/module.js.map +1 -1
  61. package/dist/public-api.d.ts +4 -0
  62. package/dist/public-api.d.ts.map +1 -1
  63. package/dist/public-api.js +6 -0
  64. package/dist/public-api.js.map +1 -1
  65. package/dist/work-queue-dashboards.module.d.ts +23 -0
  66. package/dist/work-queue-dashboards.module.d.ts.map +1 -0
  67. package/dist/work-queue-dashboards.module.js +92 -0
  68. package/dist/work-queue-dashboards.module.js.map +1 -0
  69. package/package.json +72 -66
@@ -0,0 +1,107 @@
1
+ import { type AgentToolResult } from '../shared/agent-tool-validation';
2
+ export type TimeRangeFilter = '24h' | '7d' | '30d' | 'all';
3
+ export type ChannelFilter = 'all' | 'Phone' | 'Web' | 'Meeting';
4
+ export declare const VALID_TIME_RANGES: readonly TimeRangeFilter[];
5
+ export declare const VALID_CHANNELS: readonly ChannelFilter[];
6
+ export interface ChannelMetrics {
7
+ Channel: 'Phone' | 'Web' | 'Meeting';
8
+ Count: number;
9
+ Percentage: number;
10
+ }
11
+ export interface DirectionMetrics {
12
+ Direction: 'Inbound' | 'Outbound' | 'Internal';
13
+ Count: number;
14
+ Percentage: number;
15
+ }
16
+ export interface CostByDay {
17
+ Date: string;
18
+ Cost: number;
19
+ Count: number;
20
+ }
21
+ export interface CostByNumber {
22
+ PhoneNumberID: string | null;
23
+ PhoneNumber: string;
24
+ Cost: number;
25
+ Count: number;
26
+ }
27
+ export interface LiveInteractionSummary {
28
+ ID: string;
29
+ Channel: 'Phone' | 'Web' | 'Meeting';
30
+ Direction: 'Inbound' | 'Outbound' | 'Internal';
31
+ Status: string;
32
+ RemoteAddress: string | null;
33
+ StartedAt: Date;
34
+ ElapsedSeconds: number;
35
+ ElapsedLabel: string;
36
+ }
37
+ export interface OperationsMetrics {
38
+ TotalVolume: number;
39
+ LiveCallsCount: number;
40
+ AnsweredCount: number;
41
+ AnswerRate: number;
42
+ AbandonedCount: number;
43
+ AbandonmentRate: number;
44
+ AverageHandleTimeSeconds: number;
45
+ AverageHandleTimeFormatted: string;
46
+ AverageSpeedToAnswerSeconds: number;
47
+ AverageSpeedToAnswerFormatted: string;
48
+ TransferCount: number;
49
+ EscalationCount: number;
50
+ TotalCost: number;
51
+ TotalCostFormatted: string;
52
+ VolumeByChannel: ChannelMetrics[];
53
+ VolumeByDirection: DirectionMetrics[];
54
+ CostByDay: CostByDay[];
55
+ CostByNumber: CostByNumber[];
56
+ LiveInteractions: LiveInteractionSummary[];
57
+ }
58
+ export interface InteractionRecordLike {
59
+ ID: string;
60
+ Channel: 'Meeting' | 'Phone' | 'Web' | string;
61
+ Direction: 'Inbound' | 'Internal' | 'Outbound' | string;
62
+ Status: 'Abandoned' | 'Active' | 'Ended' | 'Failed' | 'Queued' | string;
63
+ AgentSessionID: string | null;
64
+ RoomName: string | null;
65
+ PhoneNumberID: string | null;
66
+ RemoteAddress: string | null;
67
+ StartedAt: Date | string;
68
+ AnsweredAt: Date | string | null;
69
+ EndedAt: Date | string | null;
70
+ EndReason: string | null;
71
+ RecordingEnabled: boolean;
72
+ CostEstimate: number | null;
73
+ }
74
+ export interface InteractionEventRecordLike {
75
+ ID: string;
76
+ InteractionID: string;
77
+ EventType: 'Abandoned' | 'Accepted' | 'Answered' | 'Created' | 'Declined' | 'Ended' | 'Escalated' | 'Held' | 'Offered' | 'Queued' | 'RecordingStarted' | 'RecordingStopped' | 'Resumed' | 'Transferred' | string;
78
+ OccurredAt: Date | string;
79
+ ActorUserID: string | null;
80
+ ActorAgentID: string | null;
81
+ Details: string | null;
82
+ }
83
+ export interface PhoneNumberRecordLike {
84
+ ID: string;
85
+ PhoneNumber: string;
86
+ FriendlyName?: string | null;
87
+ }
88
+ export declare function FormatDuration(seconds: number): string;
89
+ export declare function FormatCurrency(amount: number): string;
90
+ export declare function ParseDate(val: Date | string | null | undefined): Date | null;
91
+ export declare function CalculateOperationsMetrics(interactions: readonly InteractionRecordLike[], events: readonly InteractionEventRecordLike[], phoneNumbers: readonly PhoneNumberRecordLike[], now?: Date): OperationsMetrics;
92
+ export declare function BuildTelephonyOperationsAgentContext(metrics: OperationsMetrics, timeRange: TimeRangeFilter, channelFilter: ChannelFilter): Record<string, unknown>;
93
+ export declare function ValidateTimeRangeParam(raw: unknown): {
94
+ ok: true;
95
+ value: TimeRangeFilter;
96
+ } | {
97
+ ok: false;
98
+ result: AgentToolResult;
99
+ };
100
+ export declare function ValidateChannelParam(raw: unknown): {
101
+ ok: true;
102
+ value: ChannelFilter;
103
+ } | {
104
+ ok: false;
105
+ result: AgentToolResult;
106
+ };
107
+ //# sourceMappingURL=telephony-operations-agent-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telephony-operations-agent-context.d.ts","sourceRoot":"","sources":["../../src/TelephonyOperations/telephony-operations-agent-context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,216 @@
1
+ import { BoundNameList, ValidateEnumParam } from '../shared/agent-tool-validation';
2
+ export const VALID_TIME_RANGES = ['24h', '7d', '30d', 'all'];
3
+ export const VALID_CHANNELS = ['all', 'Phone', 'Web', 'Meeting'];
4
+ export function FormatDuration(seconds) {
5
+ if (!Number.isFinite(seconds) || seconds <= 0) {
6
+ return '0s';
7
+ }
8
+ const rounded = Math.floor(seconds);
9
+ const mins = Math.floor(rounded / 60);
10
+ const secs = rounded % 60;
11
+ if (mins === 0) {
12
+ return `${secs}s`;
13
+ }
14
+ return `${mins}m ${secs.toString().padStart(2, '0')}s`;
15
+ }
16
+ export function FormatCurrency(amount) {
17
+ if (!Number.isFinite(amount) || amount <= 0) {
18
+ return '$0.00';
19
+ }
20
+ return new Intl.NumberFormat('en-US', {
21
+ style: 'currency',
22
+ currency: 'USD',
23
+ minimumFractionDigits: 2,
24
+ maximumFractionDigits: 2,
25
+ }).format(amount);
26
+ }
27
+ export function ParseDate(val) {
28
+ if (!val)
29
+ return null;
30
+ if (val instanceof Date)
31
+ return val;
32
+ const parsed = new Date(val);
33
+ return isNaN(parsed.getTime()) ? null : parsed;
34
+ }
35
+ export function CalculateOperationsMetrics(interactions, events, phoneNumbers, now = new Date()) {
36
+ const totalVolume = interactions.length;
37
+ const phoneNumberMap = new Map();
38
+ for (const pn of phoneNumbers) {
39
+ phoneNumberMap.set(pn.ID.toLowerCase(), pn.FriendlyName?.trim() || pn.PhoneNumber);
40
+ }
41
+ let answeredCount = 0;
42
+ let abandonedCount = 0;
43
+ let totalHandleTime = 0;
44
+ let completedHandledCount = 0;
45
+ let totalWaitTime = 0;
46
+ let speedToAnswerCount = 0;
47
+ let totalCost = 0;
48
+ const channelCounts = { Phone: 0, Web: 0, Meeting: 0 };
49
+ const directionCounts = { Inbound: 0, Outbound: 0, Internal: 0 };
50
+ const costByDayMap = new Map();
51
+ const costByNumberMap = new Map();
52
+ const liveInteractions = [];
53
+ // Group events by interaction id for quick checks
54
+ const eventsByInteraction = new Map();
55
+ let transferCount = 0;
56
+ let escalationCount = 0;
57
+ for (const evt of events) {
58
+ if (evt.EventType === 'Transferred')
59
+ transferCount++;
60
+ if (evt.EventType === 'Escalated')
61
+ escalationCount++;
62
+ const key = evt.InteractionID.toLowerCase();
63
+ const existing = eventsByInteraction.get(key);
64
+ if (existing) {
65
+ existing.push(evt);
66
+ }
67
+ else {
68
+ eventsByInteraction.set(key, [evt]);
69
+ }
70
+ }
71
+ for (const row of interactions) {
72
+ const started = ParseDate(row.StartedAt) ?? new Date();
73
+ const answered = ParseDate(row.AnsweredAt);
74
+ const ended = ParseDate(row.EndedAt);
75
+ const status = row.Status;
76
+ // Channel count
77
+ const ch = row.Channel || 'Phone';
78
+ if (ch in channelCounts) {
79
+ channelCounts[ch]++;
80
+ }
81
+ // Direction count
82
+ const dir = row.Direction || 'Inbound';
83
+ if (dir in directionCounts) {
84
+ directionCounts[dir]++;
85
+ }
86
+ // Cost accumulation
87
+ const cost = typeof row.CostEstimate === 'number' && Number.isFinite(row.CostEstimate) ? row.CostEstimate : 0;
88
+ totalCost += cost;
89
+ // Cost by day
90
+ const dayKey = started.toISOString().split('T')[0];
91
+ const dayEntry = costByDayMap.get(dayKey) ?? { cost: 0, count: 0 };
92
+ dayEntry.cost += cost;
93
+ dayEntry.count++;
94
+ costByDayMap.set(dayKey, dayEntry);
95
+ // Cost by number
96
+ const pnId = row.PhoneNumberID ? row.PhoneNumberID.toLowerCase() : null;
97
+ const pnLabel = pnId ? (phoneNumberMap.get(pnId) ?? row.RemoteAddress ?? 'Assigned Pool') : (row.RemoteAddress ?? 'Non-Phone');
98
+ const pnKey = pnId ?? pnLabel;
99
+ const numberEntry = costByNumberMap.get(pnKey) ?? { phoneNumber: pnLabel, cost: 0, count: 0 };
100
+ numberEntry.cost += cost;
101
+ numberEntry.count++;
102
+ costByNumberMap.set(pnKey, numberEntry);
103
+ // Live interactions
104
+ const isLive = (status === 'Active' || status === 'Queued') && ended === null;
105
+ if (isLive) {
106
+ const elapsed = Math.max(0, Math.floor((now.getTime() - started.getTime()) / 1000));
107
+ liveInteractions.push({
108
+ ID: row.ID,
109
+ Channel: ch,
110
+ Direction: dir,
111
+ Status: status,
112
+ RemoteAddress: row.RemoteAddress,
113
+ StartedAt: started,
114
+ ElapsedSeconds: elapsed,
115
+ ElapsedLabel: FormatDuration(elapsed),
116
+ });
117
+ }
118
+ // Answered check
119
+ const hasAnswerEvent = (eventsByInteraction.get(row.ID.toLowerCase()) ?? []).some((e) => e.EventType === 'Answered' || e.EventType === 'Accepted');
120
+ const isAnswered = answered !== null || hasAnswerEvent || status === 'Active' || status === 'Ended';
121
+ if (isAnswered) {
122
+ answeredCount++;
123
+ }
124
+ // Abandoned check
125
+ const isAbandoned = status === 'Abandoned' || (eventsByInteraction.get(row.ID.toLowerCase()) ?? []).some((e) => e.EventType === 'Abandoned');
126
+ if (isAbandoned) {
127
+ abandonedCount++;
128
+ }
129
+ // Handle time: for ended interactions
130
+ if (ended !== null) {
131
+ const effectiveStart = answered ?? started;
132
+ const durationSec = Math.max(0, Math.floor((ended.getTime() - effectiveStart.getTime()) / 1000));
133
+ totalHandleTime += durationSec;
134
+ completedHandledCount++;
135
+ }
136
+ // Speed to answer
137
+ if (answered !== null) {
138
+ const waitSec = Math.max(0, Math.floor((answered.getTime() - started.getTime()) / 1000));
139
+ totalWaitTime += waitSec;
140
+ speedToAnswerCount++;
141
+ }
142
+ }
143
+ const answerRate = totalVolume > 0 ? (answeredCount / totalVolume) * 100 : 0;
144
+ const abandonmentRate = totalVolume > 0 ? (abandonedCount / totalVolume) * 100 : 0;
145
+ const avgHandleTime = completedHandledCount > 0 ? totalHandleTime / completedHandledCount : 0;
146
+ const avgSpeedToAnswer = speedToAnswerCount > 0 ? totalWaitTime / speedToAnswerCount : 0;
147
+ const volumeByChannel = ['Phone', 'Web', 'Meeting'].map((channel) => ({
148
+ Channel: channel,
149
+ Count: channelCounts[channel],
150
+ Percentage: totalVolume > 0 ? Math.round((channelCounts[channel] / totalVolume) * 100) : 0,
151
+ }));
152
+ const volumeByDirection = ['Inbound', 'Outbound', 'Internal'].map((direction) => ({
153
+ Direction: direction,
154
+ Count: directionCounts[direction],
155
+ Percentage: totalVolume > 0 ? Math.round((directionCounts[direction] / totalVolume) * 100) : 0,
156
+ }));
157
+ const costByDay = Array.from(costByDayMap.entries())
158
+ .map(([date, data]) => ({ Date: date, Cost: data.cost, Count: data.count }))
159
+ .sort((a, b) => a.Date.localeCompare(b.Date));
160
+ const costByNumber = Array.from(costByNumberMap.entries())
161
+ .map(([id, data]) => ({
162
+ PhoneNumberID: id.includes('-') ? id : null,
163
+ PhoneNumber: data.phoneNumber,
164
+ Cost: data.cost,
165
+ Count: data.count,
166
+ }))
167
+ .sort((a, b) => b.Cost - a.Cost);
168
+ return {
169
+ TotalVolume: totalVolume,
170
+ LiveCallsCount: liveInteractions.length,
171
+ AnsweredCount: answeredCount,
172
+ AnswerRate: answerRate,
173
+ AbandonedCount: abandonedCount,
174
+ AbandonmentRate: abandonmentRate,
175
+ AverageHandleTimeSeconds: avgHandleTime,
176
+ AverageHandleTimeFormatted: FormatDuration(avgHandleTime),
177
+ AverageSpeedToAnswerSeconds: avgSpeedToAnswer,
178
+ AverageSpeedToAnswerFormatted: FormatDuration(avgSpeedToAnswer),
179
+ TransferCount: transferCount,
180
+ EscalationCount: escalationCount,
181
+ TotalCost: totalCost,
182
+ TotalCostFormatted: FormatCurrency(totalCost),
183
+ VolumeByChannel: volumeByChannel,
184
+ VolumeByDirection: volumeByDirection,
185
+ CostByDay: costByDay,
186
+ CostByNumber: costByNumber,
187
+ LiveInteractions: liveInteractions.sort((a, b) => b.ElapsedSeconds - a.ElapsedSeconds),
188
+ };
189
+ }
190
+ export function BuildTelephonyOperationsAgentContext(metrics, timeRange, channelFilter) {
191
+ return {
192
+ Surface: 'TelephonyOperationsDashboard',
193
+ TimeRange: timeRange,
194
+ ChannelFilter: channelFilter,
195
+ TotalVolume: metrics.TotalVolume,
196
+ LiveCallsCount: metrics.LiveCallsCount,
197
+ AnswerRatePercent: Math.round(metrics.AnswerRate * 10) / 10,
198
+ AbandonmentRatePercent: Math.round(metrics.AbandonmentRate * 10) / 10,
199
+ AverageHandleTime: metrics.AverageHandleTimeFormatted,
200
+ AverageSpeedToAnswer: metrics.AverageSpeedToAnswerFormatted,
201
+ TransferCount: metrics.TransferCount,
202
+ EscalationCount: metrics.EscalationCount,
203
+ TotalCostEstimate: metrics.TotalCostFormatted,
204
+ VolumeByChannel: metrics.VolumeByChannel.map((c) => `${c.Channel}: ${c.Count} (${c.Percentage}%)`),
205
+ VolumeByDirection: metrics.VolumeByDirection.map((d) => `${d.Direction}: ${d.Count} (${d.Percentage}%)`),
206
+ VisibleLiveCallAddresses: BoundNameList(metrics.LiveInteractions.map((i) => `${i.Channel} (${i.Direction}): ${i.RemoteAddress || i.ID} [${i.ElapsedLabel}]`)),
207
+ TopCostNumbers: BoundNameList(metrics.CostByNumber.map((n) => `${n.PhoneNumber}: $${n.Cost.toFixed(2)} (${n.Count} calls)`)),
208
+ };
209
+ }
210
+ export function ValidateTimeRangeParam(raw) {
211
+ return ValidateEnumParam(raw, VALID_TIME_RANGES, 'timeRange');
212
+ }
213
+ export function ValidateChannelParam(raw) {
214
+ return ValidateEnumParam(raw, VALID_CHANNELS, 'channel');
215
+ }
216
+ //# sourceMappingURL=telephony-operations-agent-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telephony-operations-agent-context.js","sourceRoot":"","sources":["../../src/TelephonyOperations/telephony-operations-agent-context.ts"],"names":[],"mappings":"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","sourcesContent":["import { BoundNameList, ValidateEnumParam, type AgentToolResult } from '../shared/agent-tool-validation';\n\nexport type TimeRangeFilter = '24h' | '7d' | '30d' | 'all';\nexport type ChannelFilter = 'all' | 'Phone' | 'Web' | 'Meeting';\n\nexport const VALID_TIME_RANGES: readonly TimeRangeFilter[] = ['24h', '7d', '30d', 'all'] as const;\nexport const VALID_CHANNELS: readonly ChannelFilter[] = ['all', 'Phone', 'Web', 'Meeting'] as const;\n\nexport interface ChannelMetrics {\n Channel: 'Phone' | 'Web' | 'Meeting';\n Count: number;\n Percentage: number;\n}\n\nexport interface DirectionMetrics {\n Direction: 'Inbound' | 'Outbound' | 'Internal';\n Count: number;\n Percentage: number;\n}\n\nexport interface CostByDay {\n Date: string;\n Cost: number;\n Count: number;\n}\n\nexport interface CostByNumber {\n PhoneNumberID: string | null;\n PhoneNumber: string;\n Cost: number;\n Count: number;\n}\n\nexport interface LiveInteractionSummary {\n ID: string;\n Channel: 'Phone' | 'Web' | 'Meeting';\n Direction: 'Inbound' | 'Outbound' | 'Internal';\n Status: string;\n RemoteAddress: string | null;\n StartedAt: Date;\n ElapsedSeconds: number;\n ElapsedLabel: string;\n}\n\nexport interface OperationsMetrics {\n TotalVolume: number;\n LiveCallsCount: number;\n AnsweredCount: number;\n AnswerRate: number;\n AbandonedCount: number;\n AbandonmentRate: number;\n AverageHandleTimeSeconds: number;\n AverageHandleTimeFormatted: string;\n AverageSpeedToAnswerSeconds: number;\n AverageSpeedToAnswerFormatted: string;\n TransferCount: number;\n EscalationCount: number;\n TotalCost: number;\n TotalCostFormatted: string;\n VolumeByChannel: ChannelMetrics[];\n VolumeByDirection: DirectionMetrics[];\n CostByDay: CostByDay[];\n CostByNumber: CostByNumber[];\n LiveInteractions: LiveInteractionSummary[];\n}\n\nexport interface InteractionRecordLike {\n ID: string;\n Channel: 'Meeting' | 'Phone' | 'Web' | string;\n Direction: 'Inbound' | 'Internal' | 'Outbound' | string;\n Status: 'Abandoned' | 'Active' | 'Ended' | 'Failed' | 'Queued' | string;\n AgentSessionID: string | null;\n RoomName: string | null;\n PhoneNumberID: string | null;\n RemoteAddress: string | null;\n StartedAt: Date | string;\n AnsweredAt: Date | string | null;\n EndedAt: Date | string | null;\n EndReason: string | null;\n RecordingEnabled: boolean;\n CostEstimate: number | null;\n}\n\nexport interface InteractionEventRecordLike {\n ID: string;\n InteractionID: string;\n EventType: 'Abandoned' | 'Accepted' | 'Answered' | 'Created' | 'Declined' | 'Ended' | 'Escalated' | 'Held' | 'Offered' | 'Queued' | 'RecordingStarted' | 'RecordingStopped' | 'Resumed' | 'Transferred' | string;\n OccurredAt: Date | string;\n ActorUserID: string | null;\n ActorAgentID: string | null;\n Details: string | null;\n}\n\nexport interface PhoneNumberRecordLike {\n ID: string;\n PhoneNumber: string;\n FriendlyName?: string | null;\n}\n\nexport function FormatDuration(seconds: number): string {\n if (!Number.isFinite(seconds) || seconds <= 0) {\n return '0s';\n }\n const rounded = Math.floor(seconds);\n const mins = Math.floor(rounded / 60);\n const secs = rounded % 60;\n if (mins === 0) {\n return `${secs}s`;\n }\n return `${mins}m ${secs.toString().padStart(2, '0')}s`;\n}\n\nexport function FormatCurrency(amount: number): string {\n if (!Number.isFinite(amount) || amount <= 0) {\n return '$0.00';\n }\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: 'USD',\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }).format(amount);\n}\n\nexport function ParseDate(val: Date | string | null | undefined): Date | null {\n if (!val) return null;\n if (val instanceof Date) return val;\n const parsed = new Date(val);\n return isNaN(parsed.getTime()) ? null : parsed;\n}\n\nexport function CalculateOperationsMetrics(\n interactions: readonly InteractionRecordLike[],\n events: readonly InteractionEventRecordLike[],\n phoneNumbers: readonly PhoneNumberRecordLike[],\n now: Date = new Date(),\n): OperationsMetrics {\n const totalVolume = interactions.length;\n const phoneNumberMap = new Map<string, string>();\n for (const pn of phoneNumbers) {\n phoneNumberMap.set(pn.ID.toLowerCase(), pn.FriendlyName?.trim() || pn.PhoneNumber);\n }\n\n let answeredCount = 0;\n let abandonedCount = 0;\n let totalHandleTime = 0;\n let completedHandledCount = 0;\n let totalWaitTime = 0;\n let speedToAnswerCount = 0;\n let totalCost = 0;\n\n const channelCounts: Record<'Phone' | 'Web' | 'Meeting', number> = { Phone: 0, Web: 0, Meeting: 0 };\n const directionCounts: Record<'Inbound' | 'Outbound' | 'Internal', number> = { Inbound: 0, Outbound: 0, Internal: 0 };\n const costByDayMap = new Map<string, { cost: number; count: number }>();\n const costByNumberMap = new Map<string, { phoneNumber: string; cost: number; count: number }>();\n const liveInteractions: LiveInteractionSummary[] = [];\n\n // Group events by interaction id for quick checks\n const eventsByInteraction = new Map<string, InteractionEventRecordLike[]>();\n let transferCount = 0;\n let escalationCount = 0;\n\n for (const evt of events) {\n if (evt.EventType === 'Transferred') transferCount++;\n if (evt.EventType === 'Escalated') escalationCount++;\n\n const key = evt.InteractionID.toLowerCase();\n const existing = eventsByInteraction.get(key);\n if (existing) {\n existing.push(evt);\n } else {\n eventsByInteraction.set(key, [evt]);\n }\n }\n\n for (const row of interactions) {\n const started = ParseDate(row.StartedAt) ?? new Date();\n const answered = ParseDate(row.AnsweredAt);\n const ended = ParseDate(row.EndedAt);\n const status = row.Status;\n\n // Channel count\n const ch = (row.Channel as 'Phone' | 'Web' | 'Meeting') || 'Phone';\n if (ch in channelCounts) {\n channelCounts[ch]++;\n }\n\n // Direction count\n const dir = (row.Direction as 'Inbound' | 'Outbound' | 'Internal') || 'Inbound';\n if (dir in directionCounts) {\n directionCounts[dir]++;\n }\n\n // Cost accumulation\n const cost = typeof row.CostEstimate === 'number' && Number.isFinite(row.CostEstimate) ? row.CostEstimate : 0;\n totalCost += cost;\n\n // Cost by day\n const dayKey = started.toISOString().split('T')[0];\n const dayEntry = costByDayMap.get(dayKey) ?? { cost: 0, count: 0 };\n dayEntry.cost += cost;\n dayEntry.count++;\n costByDayMap.set(dayKey, dayEntry);\n\n // Cost by number\n const pnId = row.PhoneNumberID ? row.PhoneNumberID.toLowerCase() : null;\n const pnLabel = pnId ? (phoneNumberMap.get(pnId) ?? row.RemoteAddress ?? 'Assigned Pool') : (row.RemoteAddress ?? 'Non-Phone');\n const pnKey = pnId ?? pnLabel;\n const numberEntry = costByNumberMap.get(pnKey) ?? { phoneNumber: pnLabel, cost: 0, count: 0 };\n numberEntry.cost += cost;\n numberEntry.count++;\n costByNumberMap.set(pnKey, numberEntry);\n\n // Live interactions\n const isLive = (status === 'Active' || status === 'Queued') && ended === null;\n if (isLive) {\n const elapsed = Math.max(0, Math.floor((now.getTime() - started.getTime()) / 1000));\n liveInteractions.push({\n ID: row.ID,\n Channel: ch,\n Direction: dir,\n Status: status,\n RemoteAddress: row.RemoteAddress,\n StartedAt: started,\n ElapsedSeconds: elapsed,\n ElapsedLabel: FormatDuration(elapsed),\n });\n }\n\n // Answered check\n const hasAnswerEvent = (eventsByInteraction.get(row.ID.toLowerCase()) ?? []).some(\n (e) => e.EventType === 'Answered' || e.EventType === 'Accepted',\n );\n const isAnswered = answered !== null || hasAnswerEvent || status === 'Active' || status === 'Ended';\n if (isAnswered) {\n answeredCount++;\n }\n\n // Abandoned check\n const isAbandoned = status === 'Abandoned' || (eventsByInteraction.get(row.ID.toLowerCase()) ?? []).some(\n (e) => e.EventType === 'Abandoned',\n );\n if (isAbandoned) {\n abandonedCount++;\n }\n\n // Handle time: for ended interactions\n if (ended !== null) {\n const effectiveStart = answered ?? started;\n const durationSec = Math.max(0, Math.floor((ended.getTime() - effectiveStart.getTime()) / 1000));\n totalHandleTime += durationSec;\n completedHandledCount++;\n }\n\n // Speed to answer\n if (answered !== null) {\n const waitSec = Math.max(0, Math.floor((answered.getTime() - started.getTime()) / 1000));\n totalWaitTime += waitSec;\n speedToAnswerCount++;\n }\n }\n\n const answerRate = totalVolume > 0 ? (answeredCount / totalVolume) * 100 : 0;\n const abandonmentRate = totalVolume > 0 ? (abandonedCount / totalVolume) * 100 : 0;\n const avgHandleTime = completedHandledCount > 0 ? totalHandleTime / completedHandledCount : 0;\n const avgSpeedToAnswer = speedToAnswerCount > 0 ? totalWaitTime / speedToAnswerCount : 0;\n\n const volumeByChannel: ChannelMetrics[] = (['Phone', 'Web', 'Meeting'] as const).map((channel) => ({\n Channel: channel,\n Count: channelCounts[channel],\n Percentage: totalVolume > 0 ? Math.round((channelCounts[channel] / totalVolume) * 100) : 0,\n }));\n\n const volumeByDirection: DirectionMetrics[] = (['Inbound', 'Outbound', 'Internal'] as const).map((direction) => ({\n Direction: direction,\n Count: directionCounts[direction],\n Percentage: totalVolume > 0 ? Math.round((directionCounts[direction] / totalVolume) * 100) : 0,\n }));\n\n const costByDay: CostByDay[] = Array.from(costByDayMap.entries())\n .map(([date, data]) => ({ Date: date, Cost: data.cost, Count: data.count }))\n .sort((a, b) => a.Date.localeCompare(b.Date));\n\n const costByNumber: CostByNumber[] = Array.from(costByNumberMap.entries())\n .map(([id, data]) => ({\n PhoneNumberID: id.includes('-') ? id : null,\n PhoneNumber: data.phoneNumber,\n Cost: data.cost,\n Count: data.count,\n }))\n .sort((a, b) => b.Cost - a.Cost);\n\n return {\n TotalVolume: totalVolume,\n LiveCallsCount: liveInteractions.length,\n AnsweredCount: answeredCount,\n AnswerRate: answerRate,\n AbandonedCount: abandonedCount,\n AbandonmentRate: abandonmentRate,\n AverageHandleTimeSeconds: avgHandleTime,\n AverageHandleTimeFormatted: FormatDuration(avgHandleTime),\n AverageSpeedToAnswerSeconds: avgSpeedToAnswer,\n AverageSpeedToAnswerFormatted: FormatDuration(avgSpeedToAnswer),\n TransferCount: transferCount,\n EscalationCount: escalationCount,\n TotalCost: totalCost,\n TotalCostFormatted: FormatCurrency(totalCost),\n VolumeByChannel: volumeByChannel,\n VolumeByDirection: volumeByDirection,\n CostByDay: costByDay,\n CostByNumber: costByNumber,\n LiveInteractions: liveInteractions.sort((a, b) => b.ElapsedSeconds - a.ElapsedSeconds),\n };\n}\n\nexport function BuildTelephonyOperationsAgentContext(\n metrics: OperationsMetrics,\n timeRange: TimeRangeFilter,\n channelFilter: ChannelFilter,\n): Record<string, unknown> {\n return {\n Surface: 'TelephonyOperationsDashboard',\n TimeRange: timeRange,\n ChannelFilter: channelFilter,\n TotalVolume: metrics.TotalVolume,\n LiveCallsCount: metrics.LiveCallsCount,\n AnswerRatePercent: Math.round(metrics.AnswerRate * 10) / 10,\n AbandonmentRatePercent: Math.round(metrics.AbandonmentRate * 10) / 10,\n AverageHandleTime: metrics.AverageHandleTimeFormatted,\n AverageSpeedToAnswer: metrics.AverageSpeedToAnswerFormatted,\n TransferCount: metrics.TransferCount,\n EscalationCount: metrics.EscalationCount,\n TotalCostEstimate: metrics.TotalCostFormatted,\n VolumeByChannel: metrics.VolumeByChannel.map((c) => `${c.Channel}: ${c.Count} (${c.Percentage}%)`),\n VolumeByDirection: metrics.VolumeByDirection.map((d) => `${d.Direction}: ${d.Count} (${d.Percentage}%)`),\n VisibleLiveCallAddresses: BoundNameList(\n metrics.LiveInteractions.map((i) => `${i.Channel} (${i.Direction}): ${i.RemoteAddress || i.ID} [${i.ElapsedLabel}]`),\n ),\n TopCostNumbers: BoundNameList(\n metrics.CostByNumber.map((n) => `${n.PhoneNumber}: $${n.Cost.toFixed(2)} (${n.Count} calls)`),\n ),\n };\n}\n\nexport function ValidateTimeRangeParam(raw: unknown): { ok: true; value: TimeRangeFilter } | { ok: false; result: AgentToolResult } {\n return ValidateEnumParam(raw, VALID_TIME_RANGES, 'timeRange');\n}\n\nexport function ValidateChannelParam(raw: unknown): { ok: true; value: ChannelFilter } | { ok: false; result: AgentToolResult } {\n return ValidateEnumParam(raw, VALID_CHANNELS, 'channel');\n}\n"]}
@@ -0,0 +1,57 @@
1
+ import { AfterViewInit, OnDestroy, OnInit } from '@angular/core';
2
+ import { BaseResourceComponent } from '@memberjunction/ng-shared';
3
+ import { ResourceData } from '@memberjunction/core-entities';
4
+ import { type ChannelFilter, type InteractionEventRecordLike, type InteractionRecordLike, type OperationsMetrics, type PhoneNumberRecordLike, type TimeRangeFilter } from './telephony-operations-agent-context';
5
+ import * as i0 from "@angular/core";
6
+ /**
7
+ * TELEPHONY & INTERACTION OPERATIONS DASHBOARD
8
+ *
9
+ * An Explorer L3 dashboard providing operational analytics across phone calls, web realtime
10
+ * sessions, and multi-party meetings.
11
+ *
12
+ * Metrics computed from `MJ: Interactions`, `MJ: Interaction Events`, and `MJ: Phone Numbers`:
13
+ * - Volume by channel (Phone, Web, Meeting) and direction (Inbound, Outbound, Internal)
14
+ * - Answer rate and abandonment rate
15
+ * - Average handle time (AHT) and speed to answer (ASA)
16
+ * - Transfer and escalation volumes
17
+ * - Cost estimates by day and by phone number
18
+ * - Active live calls / sessions in real time
19
+ *
20
+ * Data is loaded in a single batched `RunViews` call and aggregated in memory.
21
+ */
22
+ export declare class TelephonyOperationsDashboardComponent extends BaseResourceComponent implements OnInit, OnDestroy, AfterViewInit {
23
+ IsLoading: boolean;
24
+ ErrorMessage: string | null;
25
+ SelectedTimeRange: TimeRangeFilter;
26
+ SelectedChannelFilter: ChannelFilter;
27
+ SearchQuery: string;
28
+ RawInteractions: InteractionRecordLike[];
29
+ RawEvents: InteractionEventRecordLike[];
30
+ RawPhoneNumbers: PhoneNumberRecordLike[];
31
+ Metrics: OperationsMetrics;
32
+ private refreshTimer;
33
+ private readonly cdr;
34
+ GetResourceDisplayName(_data: ResourceData): Promise<string>;
35
+ GetResourceIconClass(_data: ResourceData): Promise<string>;
36
+ ngOnInit(): Promise<void>;
37
+ ngAfterViewInit(): void;
38
+ ngOnDestroy(): void;
39
+ LoadData(): Promise<void>;
40
+ SetTimeRange(range: TimeRangeFilter): void;
41
+ SetChannelFilter(channel: ChannelFilter): void;
42
+ OnSearchQueryChange(query: string): void;
43
+ get FilteredLiveInteractions(): OperationsMetrics['LiveInteractions'];
44
+ get FilteredRecentInteractions(): InteractionRecordLike[];
45
+ FormatCost(amount: number | null): string;
46
+ FormatTime(date: Date | string | null): string;
47
+ FormatDate(date: Date | string | null): string;
48
+ ComputeDuration(started: Date | string, ended: Date | string | null): string;
49
+ private get filteredRawInteractions();
50
+ private recalculateMetrics;
51
+ private getDateFilter;
52
+ private publishAgentContext;
53
+ private registerAgentClientTools;
54
+ static ɵfac: i0.ɵɵFactoryDeclaration<TelephonyOperationsDashboardComponent, never>;
55
+ static ɵcmp: i0.ɵɵComponentDeclaration<TelephonyOperationsDashboardComponent, "mj-telephony-operations-dashboard", never, {}, {}, never, never, false, never>;
56
+ }
57
+ //# sourceMappingURL=telephony-operations-dashboard.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telephony-operations-dashboard.component.d.ts","sourceRoot":"","sources":["../../src/TelephonyOperations/telephony-operations-dashboard.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAgC,SAAS,EAAE,MAAM,EAAU,MAAM,eAAe,CAAC;AAEvG,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAOL,KAAK,aAAa,EAClB,KAAK,0BAA0B,EAC/B,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACrB,MAAM,sCAAsC,CAAC;;AAE9C;;;;;;;;;;;;;;;GAeG;AACH,qBAQa,qCACX,SAAQ,qBACR,YAAW,MAAM,EAAE,SAAS,EAAE,aAAa;IAEpC,SAAS,UAAQ;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEnC,iBAAiB,EAAE,eAAe,CAAQ;IAC1C,qBAAqB,EAAE,aAAa,CAAS;IAC7C,WAAW,SAAM;IAEjB,eAAe,EAAE,qBAAqB,EAAE,CAAM;IAC9C,SAAS,EAAE,0BAA0B,EAAE,CAAM;IAC7C,eAAe,EAAE,qBAAqB,EAAE,CAAM;IAE9C,OAAO,EAAE,iBAAiB,CAA0C;IAE3E,OAAO,CAAC,YAAY,CAA+C;IACnE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA6B;IAElC,sBAAsB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAI5D,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAI1D,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAYxC,eAAe,IAAI,IAAI;IAKd,WAAW,IAAI,IAAI;IAQf,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IA6D/B,YAAY,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI;IAO1C,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAS9C,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK/C,IAAW,wBAAwB,IAAI,iBAAiB,CAAC,kBAAkB,CAAC,CAY3E;IAED,IAAW,0BAA0B,IAAI,qBAAqB,EAAE,CAc/D;IAEM,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM;IAIzC,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM;IAM9C,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM;IAM9C,eAAe,CAAC,OAAO,EAAE,IAAI,GAAG,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM;IASnF,OAAO,KAAK,uBAAuB,GAKlC;IAED,OAAO,CAAC,kBAAkB;IAQ1B,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,mBAAmB;IAU3B,OAAO,CAAC,wBAAwB;yCArOrB,qCAAqC;2CAArC,qCAAqC;CAkSjD"}