@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.
- package/dist/ComponentStudio/components/form-override-dialog.component.d.ts +12 -1
- package/dist/ComponentStudio/components/form-override-dialog.component.d.ts.map +1 -1
- package/dist/ComponentStudio/components/form-override-dialog.component.js +128 -81
- package/dist/ComponentStudio/components/form-override-dialog.component.js.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +4 -0
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.js +47 -13
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
- package/dist/TelephonyOperations/telephony-operations-agent-context.d.ts +107 -0
- package/dist/TelephonyOperations/telephony-operations-agent-context.d.ts.map +1 -0
- package/dist/TelephonyOperations/telephony-operations-agent-context.js +216 -0
- package/dist/TelephonyOperations/telephony-operations-agent-context.js.map +1 -0
- package/dist/TelephonyOperations/telephony-operations-dashboard.component.d.ts +57 -0
- package/dist/TelephonyOperations/telephony-operations-dashboard.component.d.ts.map +1 -0
- package/dist/TelephonyOperations/telephony-operations-dashboard.component.js +805 -0
- package/dist/TelephonyOperations/telephony-operations-dashboard.component.js.map +1 -0
- package/dist/WorkQueue/components/work-queue-bindings.component.d.ts +37 -0
- package/dist/WorkQueue/components/work-queue-bindings.component.d.ts.map +1 -0
- package/dist/WorkQueue/components/work-queue-bindings.component.js +210 -0
- package/dist/WorkQueue/components/work-queue-bindings.component.js.map +1 -0
- package/dist/WorkQueue/components/work-queue-dead-letters.component.d.ts +70 -0
- package/dist/WorkQueue/components/work-queue-dead-letters.component.d.ts.map +1 -0
- package/dist/WorkQueue/components/work-queue-dead-letters.component.js +496 -0
- package/dist/WorkQueue/components/work-queue-dead-letters.component.js.map +1 -0
- package/dist/WorkQueue/components/work-queue-discard-dialog.component.d.ts +22 -0
- package/dist/WorkQueue/components/work-queue-discard-dialog.component.d.ts.map +1 -0
- package/dist/WorkQueue/components/work-queue-discard-dialog.component.js +99 -0
- package/dist/WorkQueue/components/work-queue-discard-dialog.component.js.map +1 -0
- package/dist/WorkQueue/components/work-queue-overview.component.d.ts +29 -0
- package/dist/WorkQueue/components/work-queue-overview.component.d.ts.map +1 -0
- package/dist/WorkQueue/components/work-queue-overview.component.js +278 -0
- package/dist/WorkQueue/components/work-queue-overview.component.js.map +1 -0
- package/dist/WorkQueue/components/work-queue-partitions.component.d.ts +59 -0
- package/dist/WorkQueue/components/work-queue-partitions.component.d.ts.map +1 -0
- package/dist/WorkQueue/components/work-queue-partitions.component.js +359 -0
- package/dist/WorkQueue/components/work-queue-partitions.component.js.map +1 -0
- package/dist/WorkQueue/services/work-queue-operator.service.d.ts +22 -0
- package/dist/WorkQueue/services/work-queue-operator.service.d.ts.map +1 -0
- package/dist/WorkQueue/services/work-queue-operator.service.js +44 -0
- package/dist/WorkQueue/services/work-queue-operator.service.js.map +1 -0
- package/dist/WorkQueue/work-queue-agent-context.d.ts +67 -0
- package/dist/WorkQueue/work-queue-agent-context.d.ts.map +1 -0
- package/dist/WorkQueue/work-queue-agent-context.js +119 -0
- package/dist/WorkQueue/work-queue-agent-context.js.map +1 -0
- package/dist/WorkQueue/work-queue-dashboard.component.d.ts +94 -0
- package/dist/WorkQueue/work-queue-dashboard.component.d.ts.map +1 -0
- package/dist/WorkQueue/work-queue-dashboard.component.js +545 -0
- package/dist/WorkQueue/work-queue-dashboard.component.js.map +1 -0
- package/dist/WorkQueue/work-queue-types.d.ts +40 -0
- package/dist/WorkQueue/work-queue-types.d.ts.map +1 -0
- package/dist/WorkQueue/work-queue-types.js +2 -0
- package/dist/WorkQueue/work-queue-types.js.map +1 -0
- package/dist/core-dashboards.module.d.ts +29 -28
- package/dist/core-dashboards.module.d.ts.map +1 -1
- package/dist/core-dashboards.module.js +7 -0
- package/dist/core-dashboards.module.js.map +1 -1
- package/dist/module.d.ts +14 -13
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +7 -0
- package/dist/module.js.map +1 -1
- package/dist/public-api.d.ts +4 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +6 -0
- package/dist/public-api.js.map +1 -1
- package/dist/work-queue-dashboards.module.d.ts +23 -0
- package/dist/work-queue-dashboards.module.d.ts.map +1 -0
- package/dist/work-queue-dashboards.module.js +92 -0
- package/dist/work-queue-dashboards.module.js.map +1 -0
- package/package.json +72 -66
|
@@ -0,0 +1,805 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { ChangeDetectorRef, Component, inject } from '@angular/core';
|
|
8
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
9
|
+
import { BaseResourceComponent } from '@memberjunction/ng-shared';
|
|
10
|
+
import { RunView } from '@memberjunction/core';
|
|
11
|
+
import { BuildTelephonyOperationsAgentContext, CalculateOperationsMetrics, FormatCurrency, FormatDuration, ValidateChannelParam, ValidateTimeRangeParam, } from './telephony-operations-agent-context';
|
|
12
|
+
import * as i0 from "@angular/core";
|
|
13
|
+
import * as i1 from "@memberjunction/ng-ui-components";
|
|
14
|
+
import * as i2 from "@angular/common";
|
|
15
|
+
const _forTrack0 = ($index, $item) => $item.Channel;
|
|
16
|
+
const _forTrack1 = ($index, $item) => $item.Direction;
|
|
17
|
+
const _forTrack2 = ($index, $item) => $item.ID;
|
|
18
|
+
const _forTrack3 = ($index, $item) => $item.PhoneNumber;
|
|
19
|
+
function TelephonyOperationsDashboardComponent_Conditional_31_Template(rf, ctx) { if (rf & 1) {
|
|
20
|
+
i0.ɵɵelementStart(0, "div", 15);
|
|
21
|
+
i0.ɵɵelement(1, "i", 56);
|
|
22
|
+
i0.ɵɵelementStart(2, "span");
|
|
23
|
+
i0.ɵɵtext(3);
|
|
24
|
+
i0.ɵɵelementEnd()();
|
|
25
|
+
} if (rf & 2) {
|
|
26
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
27
|
+
i0.ɵɵadvance(3);
|
|
28
|
+
i0.ɵɵtextInterpolate(ctx_r0.ErrorMessage);
|
|
29
|
+
} }
|
|
30
|
+
function TelephonyOperationsDashboardComponent_Conditional_114_Template(rf, ctx) { if (rf & 1) {
|
|
31
|
+
i0.ɵɵelementStart(0, "div", 41);
|
|
32
|
+
i0.ɵɵelement(1, "i", 57);
|
|
33
|
+
i0.ɵɵelementStart(2, "p");
|
|
34
|
+
i0.ɵɵtext(3, "No active live interactions at this moment.");
|
|
35
|
+
i0.ɵɵelementEnd()();
|
|
36
|
+
} }
|
|
37
|
+
function TelephonyOperationsDashboardComponent_Conditional_115_For_18_Template(rf, ctx) { if (rf & 1) {
|
|
38
|
+
i0.ɵɵelementStart(0, "tr", 59)(1, "td", 60);
|
|
39
|
+
i0.ɵɵelement(2, "i", 61);
|
|
40
|
+
i0.ɵɵelementStart(3, "span", 62);
|
|
41
|
+
i0.ɵɵtext(4);
|
|
42
|
+
i0.ɵɵelementEnd()();
|
|
43
|
+
i0.ɵɵelementStart(5, "td")(6, "span");
|
|
44
|
+
i0.ɵɵtext(7);
|
|
45
|
+
i0.ɵɵelementEnd()();
|
|
46
|
+
i0.ɵɵelementStart(8, "td")(9, "span");
|
|
47
|
+
i0.ɵɵtext(10);
|
|
48
|
+
i0.ɵɵelementEnd()();
|
|
49
|
+
i0.ɵɵelementStart(11, "td")(12, "span", 63);
|
|
50
|
+
i0.ɵɵelement(13, "span", 64);
|
|
51
|
+
i0.ɵɵtext(14);
|
|
52
|
+
i0.ɵɵelementEnd()();
|
|
53
|
+
i0.ɵɵelementStart(15, "td");
|
|
54
|
+
i0.ɵɵtext(16);
|
|
55
|
+
i0.ɵɵelementEnd();
|
|
56
|
+
i0.ɵɵelementStart(17, "td", 65)(18, "span", 66);
|
|
57
|
+
i0.ɵɵtext(19);
|
|
58
|
+
i0.ɵɵelementEnd()()();
|
|
59
|
+
} if (rf & 2) {
|
|
60
|
+
const live_r2 = ctx.$implicit;
|
|
61
|
+
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
62
|
+
i0.ɵɵadvance(4);
|
|
63
|
+
i0.ɵɵtextInterpolate(live_r2.RemoteAddress || "Unknown");
|
|
64
|
+
i0.ɵɵadvance(2);
|
|
65
|
+
i0.ɵɵclassMap(i0.ɵɵinterpolate1("badge channel-badge ", live_r2.Channel.toLowerCase()));
|
|
66
|
+
i0.ɵɵadvance();
|
|
67
|
+
i0.ɵɵtextInterpolate1(" ", live_r2.Channel, " ");
|
|
68
|
+
i0.ɵɵadvance(2);
|
|
69
|
+
i0.ɵɵclassMap(i0.ɵɵinterpolate1("badge direction-badge ", live_r2.Direction.toLowerCase()));
|
|
70
|
+
i0.ɵɵadvance();
|
|
71
|
+
i0.ɵɵtextInterpolate1(" ", live_r2.Direction, " ");
|
|
72
|
+
i0.ɵɵadvance(4);
|
|
73
|
+
i0.ɵɵtextInterpolate1(" ", live_r2.Status, " ");
|
|
74
|
+
i0.ɵɵadvance(2);
|
|
75
|
+
i0.ɵɵtextInterpolate(ctx_r0.FormatTime(live_r2.StartedAt));
|
|
76
|
+
i0.ɵɵadvance(3);
|
|
77
|
+
i0.ɵɵtextInterpolate(live_r2.ElapsedLabel);
|
|
78
|
+
} }
|
|
79
|
+
function TelephonyOperationsDashboardComponent_Conditional_115_Template(rf, ctx) { if (rf & 1) {
|
|
80
|
+
i0.ɵɵelementStart(0, "div", 42)(1, "table", 58)(2, "thead")(3, "tr")(4, "th");
|
|
81
|
+
i0.ɵɵtext(5, "Remote Address");
|
|
82
|
+
i0.ɵɵelementEnd();
|
|
83
|
+
i0.ɵɵelementStart(6, "th");
|
|
84
|
+
i0.ɵɵtext(7, "Channel");
|
|
85
|
+
i0.ɵɵelementEnd();
|
|
86
|
+
i0.ɵɵelementStart(8, "th");
|
|
87
|
+
i0.ɵɵtext(9, "Direction");
|
|
88
|
+
i0.ɵɵelementEnd();
|
|
89
|
+
i0.ɵɵelementStart(10, "th");
|
|
90
|
+
i0.ɵɵtext(11, "Status");
|
|
91
|
+
i0.ɵɵelementEnd();
|
|
92
|
+
i0.ɵɵelementStart(12, "th");
|
|
93
|
+
i0.ɵɵtext(13, "Started");
|
|
94
|
+
i0.ɵɵelementEnd();
|
|
95
|
+
i0.ɵɵelementStart(14, "th");
|
|
96
|
+
i0.ɵɵtext(15, "Elapsed");
|
|
97
|
+
i0.ɵɵelementEnd()()();
|
|
98
|
+
i0.ɵɵelementStart(16, "tbody");
|
|
99
|
+
i0.ɵɵrepeaterCreate(17, TelephonyOperationsDashboardComponent_Conditional_115_For_18_Template, 20, 12, "tr", 59, _forTrack2);
|
|
100
|
+
i0.ɵɵelementEnd()()();
|
|
101
|
+
} if (rf & 2) {
|
|
102
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
103
|
+
i0.ɵɵadvance(17);
|
|
104
|
+
i0.ɵɵrepeater(ctx_r0.FilteredLiveInteractions);
|
|
105
|
+
} }
|
|
106
|
+
function TelephonyOperationsDashboardComponent_For_128_Template(rf, ctx) { if (rf & 1) {
|
|
107
|
+
i0.ɵɵelementStart(0, "div", 50)(1, "div", 67)(2, "span", 68);
|
|
108
|
+
i0.ɵɵtext(3);
|
|
109
|
+
i0.ɵɵelementEnd();
|
|
110
|
+
i0.ɵɵelementStart(4, "span", 69);
|
|
111
|
+
i0.ɵɵtext(5);
|
|
112
|
+
i0.ɵɵelementEnd()();
|
|
113
|
+
i0.ɵɵelementStart(6, "div", 70);
|
|
114
|
+
i0.ɵɵelement(7, "div");
|
|
115
|
+
i0.ɵɵelementEnd()();
|
|
116
|
+
} if (rf & 2) {
|
|
117
|
+
const item_r3 = ctx.$implicit;
|
|
118
|
+
i0.ɵɵadvance(3);
|
|
119
|
+
i0.ɵɵtextInterpolate(item_r3.Channel);
|
|
120
|
+
i0.ɵɵadvance(2);
|
|
121
|
+
i0.ɵɵtextInterpolate2("", item_r3.Count, " (", item_r3.Percentage, "%)");
|
|
122
|
+
i0.ɵɵadvance(2);
|
|
123
|
+
i0.ɵɵclassMap(i0.ɵɵinterpolate1("bar-fill channel-", item_r3.Channel.toLowerCase()));
|
|
124
|
+
i0.ɵɵstyleProp("width", item_r3.Percentage, "%");
|
|
125
|
+
} }
|
|
126
|
+
function TelephonyOperationsDashboardComponent_For_134_Template(rf, ctx) { if (rf & 1) {
|
|
127
|
+
i0.ɵɵelementStart(0, "div", 50)(1, "div", 67)(2, "span", 68);
|
|
128
|
+
i0.ɵɵtext(3);
|
|
129
|
+
i0.ɵɵelementEnd();
|
|
130
|
+
i0.ɵɵelementStart(4, "span", 69);
|
|
131
|
+
i0.ɵɵtext(5);
|
|
132
|
+
i0.ɵɵelementEnd()();
|
|
133
|
+
i0.ɵɵelementStart(6, "div", 70);
|
|
134
|
+
i0.ɵɵelement(7, "div");
|
|
135
|
+
i0.ɵɵelementEnd()();
|
|
136
|
+
} if (rf & 2) {
|
|
137
|
+
const item_r4 = ctx.$implicit;
|
|
138
|
+
i0.ɵɵadvance(3);
|
|
139
|
+
i0.ɵɵtextInterpolate(item_r4.Direction);
|
|
140
|
+
i0.ɵɵadvance(2);
|
|
141
|
+
i0.ɵɵtextInterpolate2("", item_r4.Count, " (", item_r4.Percentage, "%)");
|
|
142
|
+
i0.ɵɵadvance(2);
|
|
143
|
+
i0.ɵɵclassMap(i0.ɵɵinterpolate1("bar-fill direction-", item_r4.Direction.toLowerCase()));
|
|
144
|
+
i0.ɵɵstyleProp("width", item_r4.Percentage, "%");
|
|
145
|
+
} }
|
|
146
|
+
function TelephonyOperationsDashboardComponent_Conditional_141_Template(rf, ctx) { if (rf & 1) {
|
|
147
|
+
i0.ɵɵelementStart(0, "div", 41)(1, "p");
|
|
148
|
+
i0.ɵɵtext(2, "No phone number cost records in this window.");
|
|
149
|
+
i0.ɵɵelementEnd()();
|
|
150
|
+
} }
|
|
151
|
+
function TelephonyOperationsDashboardComponent_Conditional_142_For_12_Template(rf, ctx) { if (rf & 1) {
|
|
152
|
+
i0.ɵɵelementStart(0, "tr")(1, "td", 73);
|
|
153
|
+
i0.ɵɵtext(2);
|
|
154
|
+
i0.ɵɵelementEnd();
|
|
155
|
+
i0.ɵɵelementStart(3, "td");
|
|
156
|
+
i0.ɵɵtext(4);
|
|
157
|
+
i0.ɵɵelementEnd();
|
|
158
|
+
i0.ɵɵelementStart(5, "td", 74);
|
|
159
|
+
i0.ɵɵtext(6);
|
|
160
|
+
i0.ɵɵelementEnd()();
|
|
161
|
+
} if (rf & 2) {
|
|
162
|
+
const num_r5 = ctx.$implicit;
|
|
163
|
+
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
164
|
+
i0.ɵɵadvance(2);
|
|
165
|
+
i0.ɵɵtextInterpolate(num_r5.PhoneNumber);
|
|
166
|
+
i0.ɵɵadvance(2);
|
|
167
|
+
i0.ɵɵtextInterpolate(num_r5.Count);
|
|
168
|
+
i0.ɵɵadvance(2);
|
|
169
|
+
i0.ɵɵtextInterpolate(ctx_r0.FormatCost(num_r5.Cost));
|
|
170
|
+
} }
|
|
171
|
+
function TelephonyOperationsDashboardComponent_Conditional_142_Template(rf, ctx) { if (rf & 1) {
|
|
172
|
+
i0.ɵɵelementStart(0, "div", 52)(1, "table", 71)(2, "thead")(3, "tr")(4, "th");
|
|
173
|
+
i0.ɵɵtext(5, "Phone Number / Target");
|
|
174
|
+
i0.ɵɵelementEnd();
|
|
175
|
+
i0.ɵɵelementStart(6, "th");
|
|
176
|
+
i0.ɵɵtext(7, "Calls");
|
|
177
|
+
i0.ɵɵelementEnd();
|
|
178
|
+
i0.ɵɵelementStart(8, "th", 72);
|
|
179
|
+
i0.ɵɵtext(9, "Est. Cost");
|
|
180
|
+
i0.ɵɵelementEnd()()();
|
|
181
|
+
i0.ɵɵelementStart(10, "tbody");
|
|
182
|
+
i0.ɵɵrepeaterCreate(11, TelephonyOperationsDashboardComponent_Conditional_142_For_12_Template, 7, 3, "tr", null, _forTrack3);
|
|
183
|
+
i0.ɵɵelementEnd()()();
|
|
184
|
+
} if (rf & 2) {
|
|
185
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
186
|
+
i0.ɵɵadvance(11);
|
|
187
|
+
i0.ɵɵrepeater(ctx_r0.Metrics.CostByNumber);
|
|
188
|
+
} }
|
|
189
|
+
function TelephonyOperationsDashboardComponent_Conditional_151_Template(rf, ctx) { if (rf & 1) {
|
|
190
|
+
i0.ɵɵelementStart(0, "div", 41)(1, "p");
|
|
191
|
+
i0.ɵɵtext(2, "No interactions found matching criteria.");
|
|
192
|
+
i0.ɵɵelementEnd()();
|
|
193
|
+
} }
|
|
194
|
+
function TelephonyOperationsDashboardComponent_Conditional_152_For_22_Template(rf, ctx) { if (rf & 1) {
|
|
195
|
+
i0.ɵɵelementStart(0, "tr")(1, "td")(2, "div", 75)(3, "span", 76);
|
|
196
|
+
i0.ɵɵtext(4);
|
|
197
|
+
i0.ɵɵelementEnd();
|
|
198
|
+
i0.ɵɵelementStart(5, "span", 77);
|
|
199
|
+
i0.ɵɵtext(6);
|
|
200
|
+
i0.ɵɵelementEnd()()();
|
|
201
|
+
i0.ɵɵelementStart(7, "td", 60)(8, "span");
|
|
202
|
+
i0.ɵɵtext(9);
|
|
203
|
+
i0.ɵɵelementEnd()();
|
|
204
|
+
i0.ɵɵelementStart(10, "td")(11, "span");
|
|
205
|
+
i0.ɵɵtext(12);
|
|
206
|
+
i0.ɵɵelementEnd()();
|
|
207
|
+
i0.ɵɵelementStart(13, "td")(14, "span");
|
|
208
|
+
i0.ɵɵtext(15);
|
|
209
|
+
i0.ɵɵelementEnd()();
|
|
210
|
+
i0.ɵɵelementStart(16, "td")(17, "span");
|
|
211
|
+
i0.ɵɵelement(18, "span", 64);
|
|
212
|
+
i0.ɵɵtext(19);
|
|
213
|
+
i0.ɵɵelementEnd()();
|
|
214
|
+
i0.ɵɵelementStart(20, "td");
|
|
215
|
+
i0.ɵɵtext(21);
|
|
216
|
+
i0.ɵɵelementEnd();
|
|
217
|
+
i0.ɵɵelementStart(22, "td", 78);
|
|
218
|
+
i0.ɵɵtext(23);
|
|
219
|
+
i0.ɵɵelementEnd();
|
|
220
|
+
i0.ɵɵelementStart(24, "td", 72);
|
|
221
|
+
i0.ɵɵtext(25);
|
|
222
|
+
i0.ɵɵelementEnd()();
|
|
223
|
+
} if (rf & 2) {
|
|
224
|
+
const row_r6 = ctx.$implicit;
|
|
225
|
+
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
226
|
+
i0.ɵɵadvance(4);
|
|
227
|
+
i0.ɵɵtextInterpolate(ctx_r0.FormatDate(row_r6.StartedAt));
|
|
228
|
+
i0.ɵɵadvance(2);
|
|
229
|
+
i0.ɵɵtextInterpolate(ctx_r0.FormatTime(row_r6.StartedAt));
|
|
230
|
+
i0.ɵɵadvance(3);
|
|
231
|
+
i0.ɵɵtextInterpolate(row_r6.RemoteAddress || "Unknown");
|
|
232
|
+
i0.ɵɵadvance(2);
|
|
233
|
+
i0.ɵɵclassMap(i0.ɵɵinterpolate1("badge channel-badge ", row_r6.Channel.toLowerCase()));
|
|
234
|
+
i0.ɵɵadvance();
|
|
235
|
+
i0.ɵɵtextInterpolate1(" ", row_r6.Channel, " ");
|
|
236
|
+
i0.ɵɵadvance(2);
|
|
237
|
+
i0.ɵɵclassMap(i0.ɵɵinterpolate1("badge direction-badge ", row_r6.Direction.toLowerCase()));
|
|
238
|
+
i0.ɵɵadvance();
|
|
239
|
+
i0.ɵɵtextInterpolate1(" ", row_r6.Direction, " ");
|
|
240
|
+
i0.ɵɵadvance(2);
|
|
241
|
+
i0.ɵɵclassMap(i0.ɵɵinterpolate1("status-indicator ", row_r6.Status.toLowerCase()));
|
|
242
|
+
i0.ɵɵadvance(2);
|
|
243
|
+
i0.ɵɵtextInterpolate1(" ", row_r6.Status, " ");
|
|
244
|
+
i0.ɵɵadvance(2);
|
|
245
|
+
i0.ɵɵtextInterpolate(ctx_r0.ComputeDuration(row_r6.StartedAt, row_r6.EndedAt));
|
|
246
|
+
i0.ɵɵadvance(2);
|
|
247
|
+
i0.ɵɵtextInterpolate(row_r6.EndReason || "-");
|
|
248
|
+
i0.ɵɵadvance(2);
|
|
249
|
+
i0.ɵɵtextInterpolate(ctx_r0.FormatCost(row_r6.CostEstimate));
|
|
250
|
+
} }
|
|
251
|
+
function TelephonyOperationsDashboardComponent_Conditional_152_Template(rf, ctx) { if (rf & 1) {
|
|
252
|
+
i0.ɵɵelementStart(0, "div", 55)(1, "table", 58)(2, "thead")(3, "tr")(4, "th");
|
|
253
|
+
i0.ɵɵtext(5, "Date / Time");
|
|
254
|
+
i0.ɵɵelementEnd();
|
|
255
|
+
i0.ɵɵelementStart(6, "th");
|
|
256
|
+
i0.ɵɵtext(7, "Remote Party");
|
|
257
|
+
i0.ɵɵelementEnd();
|
|
258
|
+
i0.ɵɵelementStart(8, "th");
|
|
259
|
+
i0.ɵɵtext(9, "Channel");
|
|
260
|
+
i0.ɵɵelementEnd();
|
|
261
|
+
i0.ɵɵelementStart(10, "th");
|
|
262
|
+
i0.ɵɵtext(11, "Direction");
|
|
263
|
+
i0.ɵɵelementEnd();
|
|
264
|
+
i0.ɵɵelementStart(12, "th");
|
|
265
|
+
i0.ɵɵtext(13, "Status");
|
|
266
|
+
i0.ɵɵelementEnd();
|
|
267
|
+
i0.ɵɵelementStart(14, "th");
|
|
268
|
+
i0.ɵɵtext(15, "Duration");
|
|
269
|
+
i0.ɵɵelementEnd();
|
|
270
|
+
i0.ɵɵelementStart(16, "th");
|
|
271
|
+
i0.ɵɵtext(17, "End Reason");
|
|
272
|
+
i0.ɵɵelementEnd();
|
|
273
|
+
i0.ɵɵelementStart(18, "th", 72);
|
|
274
|
+
i0.ɵɵtext(19, "Est. Cost");
|
|
275
|
+
i0.ɵɵelementEnd()()();
|
|
276
|
+
i0.ɵɵelementStart(20, "tbody");
|
|
277
|
+
i0.ɵɵrepeaterCreate(21, TelephonyOperationsDashboardComponent_Conditional_152_For_22_Template, 26, 18, "tr", null, _forTrack2);
|
|
278
|
+
i0.ɵɵelementEnd()()();
|
|
279
|
+
} if (rf & 2) {
|
|
280
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
281
|
+
i0.ɵɵadvance(21);
|
|
282
|
+
i0.ɵɵrepeater(ctx_r0.FilteredRecentInteractions);
|
|
283
|
+
} }
|
|
284
|
+
/**
|
|
285
|
+
* TELEPHONY & INTERACTION OPERATIONS DASHBOARD
|
|
286
|
+
*
|
|
287
|
+
* An Explorer L3 dashboard providing operational analytics across phone calls, web realtime
|
|
288
|
+
* sessions, and multi-party meetings.
|
|
289
|
+
*
|
|
290
|
+
* Metrics computed from `MJ: Interactions`, `MJ: Interaction Events`, and `MJ: Phone Numbers`:
|
|
291
|
+
* - Volume by channel (Phone, Web, Meeting) and direction (Inbound, Outbound, Internal)
|
|
292
|
+
* - Answer rate and abandonment rate
|
|
293
|
+
* - Average handle time (AHT) and speed to answer (ASA)
|
|
294
|
+
* - Transfer and escalation volumes
|
|
295
|
+
* - Cost estimates by day and by phone number
|
|
296
|
+
* - Active live calls / sessions in real time
|
|
297
|
+
*
|
|
298
|
+
* Data is loaded in a single batched `RunViews` call and aggregated in memory.
|
|
299
|
+
*/
|
|
300
|
+
let TelephonyOperationsDashboardComponent = class TelephonyOperationsDashboardComponent extends BaseResourceComponent {
|
|
301
|
+
IsLoading = true;
|
|
302
|
+
ErrorMessage = null;
|
|
303
|
+
SelectedTimeRange = '7d';
|
|
304
|
+
SelectedChannelFilter = 'all';
|
|
305
|
+
SearchQuery = '';
|
|
306
|
+
RawInteractions = [];
|
|
307
|
+
RawEvents = [];
|
|
308
|
+
RawPhoneNumbers = [];
|
|
309
|
+
Metrics = CalculateOperationsMetrics([], [], []);
|
|
310
|
+
refreshTimer = null;
|
|
311
|
+
cdr = inject(ChangeDetectorRef);
|
|
312
|
+
async GetResourceDisplayName(_data) {
|
|
313
|
+
return 'Telephony & Interaction Operations';
|
|
314
|
+
}
|
|
315
|
+
async GetResourceIconClass(_data) {
|
|
316
|
+
return 'fa-solid fa-chart-line';
|
|
317
|
+
}
|
|
318
|
+
async ngOnInit() {
|
|
319
|
+
super.ngOnInit();
|
|
320
|
+
await this.LoadData();
|
|
321
|
+
this.NotifyLoadComplete();
|
|
322
|
+
// Auto-refresh live status every 15 seconds
|
|
323
|
+
this.refreshTimer = setInterval(() => {
|
|
324
|
+
this.recalculateMetrics();
|
|
325
|
+
this.cdr.markForCheck();
|
|
326
|
+
}, 15000);
|
|
327
|
+
}
|
|
328
|
+
ngAfterViewInit() {
|
|
329
|
+
this.publishAgentContext();
|
|
330
|
+
this.registerAgentClientTools();
|
|
331
|
+
}
|
|
332
|
+
ngOnDestroy() {
|
|
333
|
+
super.ngOnDestroy();
|
|
334
|
+
if (this.refreshTimer) {
|
|
335
|
+
clearInterval(this.refreshTimer);
|
|
336
|
+
this.refreshTimer = null;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
async LoadData() {
|
|
340
|
+
this.IsLoading = true;
|
|
341
|
+
this.ErrorMessage = null;
|
|
342
|
+
this.cdr.markForCheck();
|
|
343
|
+
try {
|
|
344
|
+
const dateFilterInteractions = this.getDateFilter('StartedAt');
|
|
345
|
+
const dateFilterEvents = this.getDateFilter('OccurredAt');
|
|
346
|
+
const rv = RunView.FromMetadataProvider(this.ProviderToUse);
|
|
347
|
+
const [interactionsResult, eventsResult, phoneNumbersResult] = await rv.RunViews([
|
|
348
|
+
{
|
|
349
|
+
EntityName: 'MJ: Interactions',
|
|
350
|
+
ResultType: 'simple',
|
|
351
|
+
ExtraFilter: dateFilterInteractions,
|
|
352
|
+
OrderBy: 'StartedAt DESC',
|
|
353
|
+
MaxRows: 1000,
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
EntityName: 'MJ: Interaction Events',
|
|
357
|
+
ResultType: 'simple',
|
|
358
|
+
ExtraFilter: dateFilterEvents,
|
|
359
|
+
OrderBy: 'OccurredAt DESC',
|
|
360
|
+
MaxRows: 2500,
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
EntityName: 'MJ: Phone Numbers',
|
|
364
|
+
ResultType: 'simple',
|
|
365
|
+
ExtraFilter: "Status = 'Active'",
|
|
366
|
+
MaxRows: 200,
|
|
367
|
+
},
|
|
368
|
+
]);
|
|
369
|
+
if (interactionsResult.Success && Array.isArray(interactionsResult.Results)) {
|
|
370
|
+
this.RawInteractions = interactionsResult.Results;
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
this.RawInteractions = [];
|
|
374
|
+
}
|
|
375
|
+
if (eventsResult.Success && Array.isArray(eventsResult.Results)) {
|
|
376
|
+
this.RawEvents = eventsResult.Results;
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
this.RawEvents = [];
|
|
380
|
+
}
|
|
381
|
+
if (phoneNumbersResult.Success && Array.isArray(phoneNumbersResult.Results)) {
|
|
382
|
+
this.RawPhoneNumbers = phoneNumbersResult.Results;
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
this.RawPhoneNumbers = [];
|
|
386
|
+
}
|
|
387
|
+
this.recalculateMetrics();
|
|
388
|
+
}
|
|
389
|
+
catch (err) {
|
|
390
|
+
this.ErrorMessage = err instanceof Error ? err.message : String(err);
|
|
391
|
+
}
|
|
392
|
+
finally {
|
|
393
|
+
this.IsLoading = false;
|
|
394
|
+
this.cdr.markForCheck();
|
|
395
|
+
this.publishAgentContext();
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
SetTimeRange(range) {
|
|
399
|
+
if (this.SelectedTimeRange !== range) {
|
|
400
|
+
this.SelectedTimeRange = range;
|
|
401
|
+
void this.LoadData();
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
SetChannelFilter(channel) {
|
|
405
|
+
if (this.SelectedChannelFilter !== channel) {
|
|
406
|
+
this.SelectedChannelFilter = channel;
|
|
407
|
+
this.recalculateMetrics();
|
|
408
|
+
this.cdr.markForCheck();
|
|
409
|
+
this.publishAgentContext();
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
OnSearchQueryChange(query) {
|
|
413
|
+
this.SearchQuery = query;
|
|
414
|
+
this.cdr.markForCheck();
|
|
415
|
+
}
|
|
416
|
+
get FilteredLiveInteractions() {
|
|
417
|
+
if (!this.SearchQuery.trim()) {
|
|
418
|
+
return this.Metrics.LiveInteractions;
|
|
419
|
+
}
|
|
420
|
+
const q = this.SearchQuery.toLowerCase();
|
|
421
|
+
return this.Metrics.LiveInteractions.filter((i) => (i.RemoteAddress && i.RemoteAddress.toLowerCase().includes(q)) ||
|
|
422
|
+
i.Channel.toLowerCase().includes(q) ||
|
|
423
|
+
i.Direction.toLowerCase().includes(q) ||
|
|
424
|
+
i.ID.toLowerCase().includes(q));
|
|
425
|
+
}
|
|
426
|
+
get FilteredRecentInteractions() {
|
|
427
|
+
let rows = this.filteredRawInteractions;
|
|
428
|
+
if (this.SearchQuery.trim()) {
|
|
429
|
+
const q = this.SearchQuery.toLowerCase();
|
|
430
|
+
rows = rows.filter((r) => (r.RemoteAddress && r.RemoteAddress.toLowerCase().includes(q)) ||
|
|
431
|
+
(r.EndReason && r.EndReason.toLowerCase().includes(q)) ||
|
|
432
|
+
r.Channel.toLowerCase().includes(q) ||
|
|
433
|
+
r.Direction.toLowerCase().includes(q) ||
|
|
434
|
+
r.ID.toLowerCase().includes(q));
|
|
435
|
+
}
|
|
436
|
+
return rows.slice(0, 15);
|
|
437
|
+
}
|
|
438
|
+
FormatCost(amount) {
|
|
439
|
+
return FormatCurrency(amount ?? 0);
|
|
440
|
+
}
|
|
441
|
+
FormatTime(date) {
|
|
442
|
+
if (!date)
|
|
443
|
+
return '-';
|
|
444
|
+
const d = date instanceof Date ? date : new Date(date);
|
|
445
|
+
return isNaN(d.getTime()) ? '-' : d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
446
|
+
}
|
|
447
|
+
FormatDate(date) {
|
|
448
|
+
if (!date)
|
|
449
|
+
return '-';
|
|
450
|
+
const d = date instanceof Date ? date : new Date(date);
|
|
451
|
+
return isNaN(d.getTime()) ? '-' : d.toLocaleDateString([], { month: 'short', day: 'numeric' });
|
|
452
|
+
}
|
|
453
|
+
ComputeDuration(started, ended) {
|
|
454
|
+
const s = started instanceof Date ? started : new Date(started);
|
|
455
|
+
if (isNaN(s.getTime()))
|
|
456
|
+
return '-';
|
|
457
|
+
const e = ended ? (ended instanceof Date ? ended : new Date(ended)) : new Date();
|
|
458
|
+
if (isNaN(e.getTime()))
|
|
459
|
+
return '-';
|
|
460
|
+
const sec = Math.max(0, Math.floor((e.getTime() - s.getTime()) / 1000));
|
|
461
|
+
return FormatDuration(sec);
|
|
462
|
+
}
|
|
463
|
+
get filteredRawInteractions() {
|
|
464
|
+
if (this.SelectedChannelFilter === 'all') {
|
|
465
|
+
return this.RawInteractions;
|
|
466
|
+
}
|
|
467
|
+
return this.RawInteractions.filter((i) => i.Channel === this.SelectedChannelFilter);
|
|
468
|
+
}
|
|
469
|
+
recalculateMetrics() {
|
|
470
|
+
const filtered = this.filteredRawInteractions;
|
|
471
|
+
const activeIds = new Set(filtered.map((i) => i.ID.toLowerCase()));
|
|
472
|
+
const relevantEvents = this.RawEvents.filter((e) => activeIds.has(e.InteractionID.toLowerCase()));
|
|
473
|
+
this.Metrics = CalculateOperationsMetrics(filtered, relevantEvents, this.RawPhoneNumbers);
|
|
474
|
+
}
|
|
475
|
+
getDateFilter(field) {
|
|
476
|
+
switch (this.SelectedTimeRange) {
|
|
477
|
+
case '24h':
|
|
478
|
+
return `${field} >= DATEADD(hour, -24, GETUTCDATE())`;
|
|
479
|
+
case '7d':
|
|
480
|
+
return `${field} >= DATEADD(day, -7, GETUTCDATE())`;
|
|
481
|
+
case '30d':
|
|
482
|
+
return `${field} >= DATEADD(day, -30, GETUTCDATE())`;
|
|
483
|
+
case 'all':
|
|
484
|
+
default:
|
|
485
|
+
return '1=1';
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
publishAgentContext() {
|
|
489
|
+
if (!this.navigationService)
|
|
490
|
+
return;
|
|
491
|
+
const snapshot = BuildTelephonyOperationsAgentContext(this.Metrics, this.SelectedTimeRange, this.SelectedChannelFilter);
|
|
492
|
+
this.navigationService.SetAgentContext(this, snapshot);
|
|
493
|
+
}
|
|
494
|
+
registerAgentClientTools() {
|
|
495
|
+
if (!this.navigationService)
|
|
496
|
+
return;
|
|
497
|
+
this.navigationService.SetAgentClientTools(this, [
|
|
498
|
+
{
|
|
499
|
+
Name: 'RefreshOperationsData',
|
|
500
|
+
Description: 'Refresh telephony and interaction operations data from the database.',
|
|
501
|
+
ParameterSchema: { type: 'object', properties: {} },
|
|
502
|
+
Handler: async () => {
|
|
503
|
+
await this.LoadData();
|
|
504
|
+
return { Success: true };
|
|
505
|
+
},
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
Name: 'SetTimeRange',
|
|
509
|
+
Description: 'Set the active time range window (24h, 7d, 30d, all).',
|
|
510
|
+
ParameterSchema: {
|
|
511
|
+
type: 'object',
|
|
512
|
+
properties: {
|
|
513
|
+
timeRange: {
|
|
514
|
+
type: 'string',
|
|
515
|
+
description: 'Time range to display: 24h, 7d, 30d, or all',
|
|
516
|
+
enum: ['24h', '7d', '30d', 'all'],
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
required: ['timeRange'],
|
|
520
|
+
},
|
|
521
|
+
Handler: async (params) => {
|
|
522
|
+
const validated = ValidateTimeRangeParam(params['timeRange']);
|
|
523
|
+
if (!validated.ok) {
|
|
524
|
+
return validated.result;
|
|
525
|
+
}
|
|
526
|
+
this.SetTimeRange(validated.value);
|
|
527
|
+
return { Success: true };
|
|
528
|
+
},
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
Name: 'FilterByChannel',
|
|
532
|
+
Description: 'Filter operations metrics by interaction channel (all, Phone, Web, Meeting).',
|
|
533
|
+
ParameterSchema: {
|
|
534
|
+
type: 'object',
|
|
535
|
+
properties: {
|
|
536
|
+
channel: {
|
|
537
|
+
type: 'string',
|
|
538
|
+
description: 'Channel to filter: all, Phone, Web, or Meeting',
|
|
539
|
+
enum: ['all', 'Phone', 'Web', 'Meeting'],
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
required: ['channel'],
|
|
543
|
+
},
|
|
544
|
+
Handler: async (params) => {
|
|
545
|
+
const validated = ValidateChannelParam(params['channel']);
|
|
546
|
+
if (!validated.ok) {
|
|
547
|
+
return validated.result;
|
|
548
|
+
}
|
|
549
|
+
this.SetChannelFilter(validated.value);
|
|
550
|
+
return { Success: true };
|
|
551
|
+
},
|
|
552
|
+
},
|
|
553
|
+
]);
|
|
554
|
+
}
|
|
555
|
+
static ɵfac = /*@__PURE__*/ (() => { let ɵTelephonyOperationsDashboardComponent_BaseFactory; return function TelephonyOperationsDashboardComponent_Factory(__ngFactoryType__) { return (ɵTelephonyOperationsDashboardComponent_BaseFactory || (ɵTelephonyOperationsDashboardComponent_BaseFactory = i0.ɵɵgetInheritedFactory(TelephonyOperationsDashboardComponent)))(__ngFactoryType__ || TelephonyOperationsDashboardComponent); }; })();
|
|
556
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: TelephonyOperationsDashboardComponent, selectors: [["mj-telephony-operations-dashboard"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 153, vars: 46, consts: [["Title", "Telephony & Interaction Operations", "Subtitle", "Real-time metrics, volume breakdown, answer rates, and cost analysis across channels", "Icon", "fa-solid fa-chart-line"], ["meta", ""], ["Label", "live now", 3, "Count", "Total", "Variant"], ["actions", ""], [3, "Clicked", "Loading"], ["toolbar", ""], [1, "toolbar-controls"], [3, "ValueChange", "Value"], ["role", "radiogroup", "aria-label", "Time Range", 1, "time-range-pills"], ["type", "button", 1, "range-pill", 3, "click"], ["role", "radiogroup", "aria-label", "Channel Filter", 1, "channel-filter-pills"], ["type", "button", 1, "channel-pill", 3, "click"], [1, "fa-solid", "fa-phone"], [1, "fa-solid", "fa-globe"], [1, "fa-solid", "fa-users"], ["role", "alert", 1, "error-banner"], [1, "kpi-grid"], [1, "kpi-card"], [1, "kpi-icon-wrap", "primary"], [1, "fa-solid", "fa-headset"], [1, "kpi-content"], [1, "kpi-label"], [1, "kpi-value"], [1, "kpi-subtext"], [1, "kpi-icon-wrap", "success"], [1, "live-dot-ping"], [1, "fa-solid", "fa-phone-volume"], [1, "fa-solid", "fa-circle-check"], [1, "kpi-icon-wrap", "warning"], [1, "fa-solid", "fa-phone-slash"], [1, "kpi-icon-wrap", "info"], [1, "fa-solid", "fa-clock"], [1, "kpi-icon-wrap", "accent"], [1, "fa-solid", "fa-arrows-split-up-and-left"], [1, "kpi-icon-wrap", "cost"], [1, "fa-solid", "fa-dollar-sign"], [1, "dashboard-section", "live-section"], [1, "section-header"], [1, "section-title"], [1, "live-pulse-badge"], [1, "count-pill"], [1, "empty-feed"], [1, "live-table-container"], [1, "two-col-grid"], [1, "dashboard-card"], [1, "card-header"], [1, "fa-solid", "fa-layer-group"], [1, "card-body"], [1, "distribution-group"], [1, "bars-container"], [1, "bar-row"], [1, "fa-solid", "fa-money-bill-wave"], [1, "cost-table-container"], [1, "dashboard-section"], [1, "fa-solid", "fa-list-check"], [1, "table-container"], [1, "fa-solid", "fa-triangle-exclamation"], [1, "fa-solid", "fa-phone-slash", "empty-icon"], [1, "ops-table"], [1, "live-row"], [1, "address-col"], [1, "fa-solid", "fa-user-circle", "address-icon"], [1, "address-text"], [1, "status-indicator", "active"], [1, "dot"], [1, "elapsed-col"], [1, "elapsed-badge"], [1, "bar-info"], [1, "bar-label"], [1, "bar-stats"], [1, "bar-track"], [1, "ops-table", "compact"], [1, "text-right"], [1, "font-medium"], [1, "text-right", "font-semibold"], [1, "datetime-cell"], [1, "date"], [1, "time"], [1, "end-reason-cell"]], template: function TelephonyOperationsDashboardComponent_Template(rf, ctx) { if (rf & 1) {
|
|
557
|
+
i0.ɵɵelementStart(0, "mj-page-layout")(1, "mj-page-header", 0)(2, "div", 1);
|
|
558
|
+
i0.ɵɵelement(3, "mj-stat-badge", 2);
|
|
559
|
+
i0.ɵɵelementEnd();
|
|
560
|
+
i0.ɵɵelementStart(4, "div", 3)(5, "mj-refresh-button", 4);
|
|
561
|
+
i0.ɵɵlistener("Clicked", function TelephonyOperationsDashboardComponent_Template_mj_refresh_button_Clicked_5_listener() { return ctx.LoadData(); });
|
|
562
|
+
i0.ɵɵelementEnd()();
|
|
563
|
+
i0.ɵɵelementStart(6, "div", 5)(7, "div", 6)(8, "mj-page-search", 7);
|
|
564
|
+
i0.ɵɵlistener("ValueChange", function TelephonyOperationsDashboardComponent_Template_mj_page_search_ValueChange_8_listener($event) { return ctx.OnSearchQueryChange($event); });
|
|
565
|
+
i0.ɵɵelementEnd();
|
|
566
|
+
i0.ɵɵelementStart(9, "div", 8)(10, "button", 9);
|
|
567
|
+
i0.ɵɵlistener("click", function TelephonyOperationsDashboardComponent_Template_button_click_10_listener() { return ctx.SetTimeRange("24h"); });
|
|
568
|
+
i0.ɵɵtext(11, " 24h ");
|
|
569
|
+
i0.ɵɵelementEnd();
|
|
570
|
+
i0.ɵɵelementStart(12, "button", 9);
|
|
571
|
+
i0.ɵɵlistener("click", function TelephonyOperationsDashboardComponent_Template_button_click_12_listener() { return ctx.SetTimeRange("7d"); });
|
|
572
|
+
i0.ɵɵtext(13, " 7d ");
|
|
573
|
+
i0.ɵɵelementEnd();
|
|
574
|
+
i0.ɵɵelementStart(14, "button", 9);
|
|
575
|
+
i0.ɵɵlistener("click", function TelephonyOperationsDashboardComponent_Template_button_click_14_listener() { return ctx.SetTimeRange("30d"); });
|
|
576
|
+
i0.ɵɵtext(15, " 30d ");
|
|
577
|
+
i0.ɵɵelementEnd();
|
|
578
|
+
i0.ɵɵelementStart(16, "button", 9);
|
|
579
|
+
i0.ɵɵlistener("click", function TelephonyOperationsDashboardComponent_Template_button_click_16_listener() { return ctx.SetTimeRange("all"); });
|
|
580
|
+
i0.ɵɵtext(17, " All Time ");
|
|
581
|
+
i0.ɵɵelementEnd()();
|
|
582
|
+
i0.ɵɵelementStart(18, "div", 10)(19, "button", 11);
|
|
583
|
+
i0.ɵɵlistener("click", function TelephonyOperationsDashboardComponent_Template_button_click_19_listener() { return ctx.SetChannelFilter("all"); });
|
|
584
|
+
i0.ɵɵtext(20, " All Channels ");
|
|
585
|
+
i0.ɵɵelementEnd();
|
|
586
|
+
i0.ɵɵelementStart(21, "button", 11);
|
|
587
|
+
i0.ɵɵlistener("click", function TelephonyOperationsDashboardComponent_Template_button_click_21_listener() { return ctx.SetChannelFilter("Phone"); });
|
|
588
|
+
i0.ɵɵelement(22, "i", 12);
|
|
589
|
+
i0.ɵɵtext(23, " Phone ");
|
|
590
|
+
i0.ɵɵelementEnd();
|
|
591
|
+
i0.ɵɵelementStart(24, "button", 11);
|
|
592
|
+
i0.ɵɵlistener("click", function TelephonyOperationsDashboardComponent_Template_button_click_24_listener() { return ctx.SetChannelFilter("Web"); });
|
|
593
|
+
i0.ɵɵelement(25, "i", 13);
|
|
594
|
+
i0.ɵɵtext(26, " Web ");
|
|
595
|
+
i0.ɵɵelementEnd();
|
|
596
|
+
i0.ɵɵelementStart(27, "button", 11);
|
|
597
|
+
i0.ɵɵlistener("click", function TelephonyOperationsDashboardComponent_Template_button_click_27_listener() { return ctx.SetChannelFilter("Meeting"); });
|
|
598
|
+
i0.ɵɵelement(28, "i", 14);
|
|
599
|
+
i0.ɵɵtext(29, " Meeting ");
|
|
600
|
+
i0.ɵɵelementEnd()()()()();
|
|
601
|
+
i0.ɵɵelementStart(30, "mj-page-body");
|
|
602
|
+
i0.ɵɵconditionalCreate(31, TelephonyOperationsDashboardComponent_Conditional_31_Template, 4, 1, "div", 15);
|
|
603
|
+
i0.ɵɵelementStart(32, "div", 16)(33, "div", 17)(34, "div", 18);
|
|
604
|
+
i0.ɵɵelement(35, "i", 19);
|
|
605
|
+
i0.ɵɵelementEnd();
|
|
606
|
+
i0.ɵɵelementStart(36, "div", 20)(37, "span", 21);
|
|
607
|
+
i0.ɵɵtext(38, "Total Volume");
|
|
608
|
+
i0.ɵɵelementEnd();
|
|
609
|
+
i0.ɵɵelementStart(39, "span", 22);
|
|
610
|
+
i0.ɵɵtext(40);
|
|
611
|
+
i0.ɵɵelementEnd();
|
|
612
|
+
i0.ɵɵelementStart(41, "span", 23);
|
|
613
|
+
i0.ɵɵtext(42, "in selected window");
|
|
614
|
+
i0.ɵɵelementEnd()()();
|
|
615
|
+
i0.ɵɵelementStart(43, "div", 17)(44, "div", 24);
|
|
616
|
+
i0.ɵɵelement(45, "span", 25)(46, "i", 26);
|
|
617
|
+
i0.ɵɵelementEnd();
|
|
618
|
+
i0.ɵɵelementStart(47, "div", 20)(48, "span", 21);
|
|
619
|
+
i0.ɵɵtext(49, "Live Now");
|
|
620
|
+
i0.ɵɵelementEnd();
|
|
621
|
+
i0.ɵɵelementStart(50, "span", 22);
|
|
622
|
+
i0.ɵɵtext(51);
|
|
623
|
+
i0.ɵɵelementEnd();
|
|
624
|
+
i0.ɵɵelementStart(52, "span", 23);
|
|
625
|
+
i0.ɵɵtext(53, "active sessions");
|
|
626
|
+
i0.ɵɵelementEnd()()();
|
|
627
|
+
i0.ɵɵelementStart(54, "div", 17)(55, "div", 24);
|
|
628
|
+
i0.ɵɵelement(56, "i", 27);
|
|
629
|
+
i0.ɵɵelementEnd();
|
|
630
|
+
i0.ɵɵelementStart(57, "div", 20)(58, "span", 21);
|
|
631
|
+
i0.ɵɵtext(59, "Answer Rate");
|
|
632
|
+
i0.ɵɵelementEnd();
|
|
633
|
+
i0.ɵɵelementStart(60, "span", 22);
|
|
634
|
+
i0.ɵɵtext(61);
|
|
635
|
+
i0.ɵɵpipe(62, "number");
|
|
636
|
+
i0.ɵɵelementEnd();
|
|
637
|
+
i0.ɵɵelementStart(63, "span", 23);
|
|
638
|
+
i0.ɵɵtext(64);
|
|
639
|
+
i0.ɵɵelementEnd()()();
|
|
640
|
+
i0.ɵɵelementStart(65, "div", 17)(66, "div", 28);
|
|
641
|
+
i0.ɵɵelement(67, "i", 29);
|
|
642
|
+
i0.ɵɵelementEnd();
|
|
643
|
+
i0.ɵɵelementStart(68, "div", 20)(69, "span", 21);
|
|
644
|
+
i0.ɵɵtext(70, "Abandonment Rate");
|
|
645
|
+
i0.ɵɵelementEnd();
|
|
646
|
+
i0.ɵɵelementStart(71, "span", 22);
|
|
647
|
+
i0.ɵɵtext(72);
|
|
648
|
+
i0.ɵɵpipe(73, "number");
|
|
649
|
+
i0.ɵɵelementEnd();
|
|
650
|
+
i0.ɵɵelementStart(74, "span", 23);
|
|
651
|
+
i0.ɵɵtext(75);
|
|
652
|
+
i0.ɵɵelementEnd()()();
|
|
653
|
+
i0.ɵɵelementStart(76, "div", 17)(77, "div", 30);
|
|
654
|
+
i0.ɵɵelement(78, "i", 31);
|
|
655
|
+
i0.ɵɵelementEnd();
|
|
656
|
+
i0.ɵɵelementStart(79, "div", 20)(80, "span", 21);
|
|
657
|
+
i0.ɵɵtext(81, "Avg Handle Time");
|
|
658
|
+
i0.ɵɵelementEnd();
|
|
659
|
+
i0.ɵɵelementStart(82, "span", 22);
|
|
660
|
+
i0.ɵɵtext(83);
|
|
661
|
+
i0.ɵɵelementEnd();
|
|
662
|
+
i0.ɵɵelementStart(84, "span", 23);
|
|
663
|
+
i0.ɵɵtext(85);
|
|
664
|
+
i0.ɵɵelementEnd()()();
|
|
665
|
+
i0.ɵɵelementStart(86, "div", 17)(87, "div", 32);
|
|
666
|
+
i0.ɵɵelement(88, "i", 33);
|
|
667
|
+
i0.ɵɵelementEnd();
|
|
668
|
+
i0.ɵɵelementStart(89, "div", 20)(90, "span", 21);
|
|
669
|
+
i0.ɵɵtext(91, "Transfers & Esc.");
|
|
670
|
+
i0.ɵɵelementEnd();
|
|
671
|
+
i0.ɵɵelementStart(92, "span", 22);
|
|
672
|
+
i0.ɵɵtext(93);
|
|
673
|
+
i0.ɵɵelementEnd();
|
|
674
|
+
i0.ɵɵelementStart(94, "span", 23);
|
|
675
|
+
i0.ɵɵtext(95, "transfers / escalations");
|
|
676
|
+
i0.ɵɵelementEnd()()();
|
|
677
|
+
i0.ɵɵelementStart(96, "div", 17)(97, "div", 34);
|
|
678
|
+
i0.ɵɵelement(98, "i", 35);
|
|
679
|
+
i0.ɵɵelementEnd();
|
|
680
|
+
i0.ɵɵelementStart(99, "div", 20)(100, "span", 21);
|
|
681
|
+
i0.ɵɵtext(101, "Total Cost Est.");
|
|
682
|
+
i0.ɵɵelementEnd();
|
|
683
|
+
i0.ɵɵelementStart(102, "span", 22);
|
|
684
|
+
i0.ɵɵtext(103);
|
|
685
|
+
i0.ɵɵelementEnd();
|
|
686
|
+
i0.ɵɵelementStart(104, "span", 23);
|
|
687
|
+
i0.ɵɵtext(105, "usage & carrier minutes");
|
|
688
|
+
i0.ɵɵelementEnd()()()();
|
|
689
|
+
i0.ɵɵelementStart(106, "div", 36)(107, "div", 37)(108, "div", 38);
|
|
690
|
+
i0.ɵɵelement(109, "span", 39);
|
|
691
|
+
i0.ɵɵelementStart(110, "h2");
|
|
692
|
+
i0.ɵɵtext(111, "Active Live Sessions");
|
|
693
|
+
i0.ɵɵelementEnd();
|
|
694
|
+
i0.ɵɵelementStart(112, "span", 40);
|
|
695
|
+
i0.ɵɵtext(113);
|
|
696
|
+
i0.ɵɵelementEnd()()();
|
|
697
|
+
i0.ɵɵconditionalCreate(114, TelephonyOperationsDashboardComponent_Conditional_114_Template, 4, 0, "div", 41)(115, TelephonyOperationsDashboardComponent_Conditional_115_Template, 19, 0, "div", 42);
|
|
698
|
+
i0.ɵɵelementEnd();
|
|
699
|
+
i0.ɵɵelementStart(116, "div", 43)(117, "div", 44)(118, "div", 45)(119, "h3");
|
|
700
|
+
i0.ɵɵelement(120, "i", 46);
|
|
701
|
+
i0.ɵɵtext(121, " Volume Distribution");
|
|
702
|
+
i0.ɵɵelementEnd()();
|
|
703
|
+
i0.ɵɵelementStart(122, "div", 47)(123, "div", 48)(124, "h4");
|
|
704
|
+
i0.ɵɵtext(125, "By Channel");
|
|
705
|
+
i0.ɵɵelementEnd();
|
|
706
|
+
i0.ɵɵelementStart(126, "div", 49);
|
|
707
|
+
i0.ɵɵrepeaterCreate(127, TelephonyOperationsDashboardComponent_For_128_Template, 8, 8, "div", 50, _forTrack0);
|
|
708
|
+
i0.ɵɵelementEnd()();
|
|
709
|
+
i0.ɵɵelementStart(129, "div", 48)(130, "h4");
|
|
710
|
+
i0.ɵɵtext(131, "By Direction");
|
|
711
|
+
i0.ɵɵelementEnd();
|
|
712
|
+
i0.ɵɵelementStart(132, "div", 49);
|
|
713
|
+
i0.ɵɵrepeaterCreate(133, TelephonyOperationsDashboardComponent_For_134_Template, 8, 8, "div", 50, _forTrack1);
|
|
714
|
+
i0.ɵɵelementEnd()()()();
|
|
715
|
+
i0.ɵɵelementStart(135, "div", 44)(136, "div", 45)(137, "h3");
|
|
716
|
+
i0.ɵɵelement(138, "i", 51);
|
|
717
|
+
i0.ɵɵtext(139, " Cost by Phone Number");
|
|
718
|
+
i0.ɵɵelementEnd()();
|
|
719
|
+
i0.ɵɵelementStart(140, "div", 47);
|
|
720
|
+
i0.ɵɵconditionalCreate(141, TelephonyOperationsDashboardComponent_Conditional_141_Template, 3, 0, "div", 41)(142, TelephonyOperationsDashboardComponent_Conditional_142_Template, 13, 0, "div", 52);
|
|
721
|
+
i0.ɵɵelementEnd()()();
|
|
722
|
+
i0.ɵɵelementStart(143, "div", 53)(144, "div", 37)(145, "div", 38);
|
|
723
|
+
i0.ɵɵelement(146, "i", 54);
|
|
724
|
+
i0.ɵɵelementStart(147, "h2");
|
|
725
|
+
i0.ɵɵtext(148, "Recent Interactions");
|
|
726
|
+
i0.ɵɵelementEnd();
|
|
727
|
+
i0.ɵɵelementStart(149, "span", 40);
|
|
728
|
+
i0.ɵɵtext(150);
|
|
729
|
+
i0.ɵɵelementEnd()()();
|
|
730
|
+
i0.ɵɵconditionalCreate(151, TelephonyOperationsDashboardComponent_Conditional_151_Template, 3, 0, "div", 41)(152, TelephonyOperationsDashboardComponent_Conditional_152_Template, 23, 0, "div", 55);
|
|
731
|
+
i0.ɵɵelementEnd()()();
|
|
732
|
+
} if (rf & 2) {
|
|
733
|
+
i0.ɵɵadvance(3);
|
|
734
|
+
i0.ɵɵproperty("Count", ctx.Metrics.LiveCallsCount)("Total", ctx.Metrics.TotalVolume)("Variant", ctx.Metrics.LiveCallsCount > 0 ? "success" : "default");
|
|
735
|
+
i0.ɵɵadvance(2);
|
|
736
|
+
i0.ɵɵproperty("Loading", ctx.IsLoading);
|
|
737
|
+
i0.ɵɵadvance(3);
|
|
738
|
+
i0.ɵɵproperty("Value", ctx.SearchQuery);
|
|
739
|
+
i0.ɵɵadvance(2);
|
|
740
|
+
i0.ɵɵclassProp("active", ctx.SelectedTimeRange === "24h");
|
|
741
|
+
i0.ɵɵadvance(2);
|
|
742
|
+
i0.ɵɵclassProp("active", ctx.SelectedTimeRange === "7d");
|
|
743
|
+
i0.ɵɵadvance(2);
|
|
744
|
+
i0.ɵɵclassProp("active", ctx.SelectedTimeRange === "30d");
|
|
745
|
+
i0.ɵɵadvance(2);
|
|
746
|
+
i0.ɵɵclassProp("active", ctx.SelectedTimeRange === "all");
|
|
747
|
+
i0.ɵɵadvance(3);
|
|
748
|
+
i0.ɵɵclassProp("active", ctx.SelectedChannelFilter === "all");
|
|
749
|
+
i0.ɵɵadvance(2);
|
|
750
|
+
i0.ɵɵclassProp("active", ctx.SelectedChannelFilter === "Phone");
|
|
751
|
+
i0.ɵɵadvance(3);
|
|
752
|
+
i0.ɵɵclassProp("active", ctx.SelectedChannelFilter === "Web");
|
|
753
|
+
i0.ɵɵadvance(3);
|
|
754
|
+
i0.ɵɵclassProp("active", ctx.SelectedChannelFilter === "Meeting");
|
|
755
|
+
i0.ɵɵadvance(4);
|
|
756
|
+
i0.ɵɵconditional(ctx.ErrorMessage ? 31 : -1);
|
|
757
|
+
i0.ɵɵadvance(9);
|
|
758
|
+
i0.ɵɵtextInterpolate(ctx.Metrics.TotalVolume);
|
|
759
|
+
i0.ɵɵadvance(3);
|
|
760
|
+
i0.ɵɵclassProp("live-active", ctx.Metrics.LiveCallsCount > 0);
|
|
761
|
+
i0.ɵɵadvance(8);
|
|
762
|
+
i0.ɵɵtextInterpolate(ctx.Metrics.LiveCallsCount);
|
|
763
|
+
i0.ɵɵadvance(10);
|
|
764
|
+
i0.ɵɵtextInterpolate1("", i0.ɵɵpipeBind2(62, 40, ctx.Metrics.AnswerRate, "1.0-1"), "%");
|
|
765
|
+
i0.ɵɵadvance(3);
|
|
766
|
+
i0.ɵɵtextInterpolate1("", ctx.Metrics.AnsweredCount, " answered");
|
|
767
|
+
i0.ɵɵadvance(8);
|
|
768
|
+
i0.ɵɵtextInterpolate1("", i0.ɵɵpipeBind2(73, 43, ctx.Metrics.AbandonmentRate, "1.0-1"), "%");
|
|
769
|
+
i0.ɵɵadvance(3);
|
|
770
|
+
i0.ɵɵtextInterpolate1("", ctx.Metrics.AbandonedCount, " caller abandons");
|
|
771
|
+
i0.ɵɵadvance(8);
|
|
772
|
+
i0.ɵɵtextInterpolate(ctx.Metrics.AverageHandleTimeFormatted);
|
|
773
|
+
i0.ɵɵadvance(2);
|
|
774
|
+
i0.ɵɵtextInterpolate1("ASA: ", ctx.Metrics.AverageSpeedToAnswerFormatted);
|
|
775
|
+
i0.ɵɵadvance(8);
|
|
776
|
+
i0.ɵɵtextInterpolate2("", ctx.Metrics.TransferCount, " / ", ctx.Metrics.EscalationCount);
|
|
777
|
+
i0.ɵɵadvance(10);
|
|
778
|
+
i0.ɵɵtextInterpolate(ctx.Metrics.TotalCostFormatted);
|
|
779
|
+
i0.ɵɵadvance(10);
|
|
780
|
+
i0.ɵɵtextInterpolate(ctx.FilteredLiveInteractions.length);
|
|
781
|
+
i0.ɵɵadvance();
|
|
782
|
+
i0.ɵɵconditional(ctx.FilteredLiveInteractions.length === 0 ? 114 : 115);
|
|
783
|
+
i0.ɵɵadvance(13);
|
|
784
|
+
i0.ɵɵrepeater(ctx.Metrics.VolumeByChannel);
|
|
785
|
+
i0.ɵɵadvance(6);
|
|
786
|
+
i0.ɵɵrepeater(ctx.Metrics.VolumeByDirection);
|
|
787
|
+
i0.ɵɵadvance(8);
|
|
788
|
+
i0.ɵɵconditional(ctx.Metrics.CostByNumber.length === 0 ? 141 : 142);
|
|
789
|
+
i0.ɵɵadvance(9);
|
|
790
|
+
i0.ɵɵtextInterpolate(ctx.FilteredRecentInteractions.length);
|
|
791
|
+
i0.ɵɵadvance();
|
|
792
|
+
i0.ɵɵconditional(ctx.FilteredRecentInteractions.length === 0 ? 151 : 152);
|
|
793
|
+
} }, dependencies: [i1.MJPageHeaderComponent, i1.MJPageLayoutComponent, i1.MJPageBodyComponent, i1.MJPageSearchComponent, i1.MJStatBadgeComponent, i1.MJRefreshButtonComponent, i2.DecimalPipe], styles: ["[_nghost-%COMP%] {\n display: block;\n height: 100%;\n}\n\n.toolbar-controls[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.time-range-pills[_ngcontent-%COMP%], \n.channel-filter-pills[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n background-color: var(--mj-bg-subtle);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 6px;\n padding: 2px;\n gap: 2px;\n}\n\n.range-pill[_ngcontent-%COMP%], \n.channel-pill[_ngcontent-%COMP%] {\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 12px;\n font-weight: 500;\n padding: 4px 10px;\n border-radius: 4px;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n transition: all 0.15s ease-in-out;\n}\n\n.range-pill[_ngcontent-%COMP%]:hover, \n.channel-pill[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n background-color: var(--mj-bg-surface);\n}\n\n.range-pill.active[_ngcontent-%COMP%], \n.channel-pill.active[_ngcontent-%COMP%] {\n background-color: var(--mj-bg-surface);\n color: var(--mj-color-primary);\n font-weight: 600;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n\n.error-banner[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n border-radius: 8px;\n margin-bottom: 20px;\n background-color: var(--mj-color-danger-subtle);\n border: 1px solid var(--mj-color-danger);\n color: var(--mj-color-danger);\n font-size: 14px;\n}\n\n\n.kpi-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n gap: 16px;\n margin-bottom: 24px;\n}\n\n.kpi-card[_ngcontent-%COMP%] {\n background-color: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n padding: 16px;\n display: flex;\n align-items: center;\n gap: 14px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03);\n}\n\n.kpi-card.live-active[_ngcontent-%COMP%] {\n border-color: var(--mj-color-success);\n}\n\n.kpi-icon-wrap[_ngcontent-%COMP%] {\n width: 44px;\n height: 44px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 18px;\n position: relative;\n flex-shrink: 0;\n}\n\n.kpi-icon-wrap.primary[_ngcontent-%COMP%] {\n background-color: var(--mj-color-primary-subtle);\n color: var(--mj-color-primary);\n}\n\n.kpi-icon-wrap.success[_ngcontent-%COMP%] {\n background-color: var(--mj-color-success-subtle);\n color: var(--mj-color-success);\n}\n\n.kpi-icon-wrap.warning[_ngcontent-%COMP%] {\n background-color: var(--mj-color-warning-subtle);\n color: var(--mj-color-warning);\n}\n\n.kpi-icon-wrap.info[_ngcontent-%COMP%] {\n background-color: var(--mj-bg-subtle);\n color: var(--mj-text-secondary);\n}\n\n.kpi-icon-wrap.accent[_ngcontent-%COMP%] {\n background-color: var(--mj-color-primary-subtle);\n color: var(--mj-color-primary);\n}\n\n.kpi-icon-wrap.cost[_ngcontent-%COMP%] {\n background-color: var(--mj-color-success-subtle);\n color: var(--mj-color-success);\n}\n\n.live-dot-ping[_ngcontent-%COMP%] {\n position: absolute;\n top: 6px;\n right: 6px;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background-color: var(--mj-color-success);\n animation: _ngcontent-%COMP%_pulse-ping 2s cubic-bezier(0, 0, 0.2, 1) infinite;\n}\n\n@keyframes _ngcontent-%COMP%_pulse-ping {\n 0% {\n transform: scale(0.95);\n opacity: 1;\n }\n 50% {\n transform: scale(1.4);\n opacity: 0.5;\n }\n 100% {\n transform: scale(0.95);\n opacity: 1;\n }\n}\n\n.kpi-content[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.kpi-label[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.kpi-value[_ngcontent-%COMP%] {\n font-size: 22px;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.2;\n margin: 2px 0;\n}\n\n.kpi-subtext[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n\n.dashboard-section[_ngcontent-%COMP%] {\n background-color: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n padding: 20px;\n margin-bottom: 24px;\n}\n\n.section-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 16px;\n}\n\n.section-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.section-title[_ngcontent-%COMP%] h2[_ngcontent-%COMP%] {\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0;\n}\n\n.count-pill[_ngcontent-%COMP%] {\n background-color: var(--mj-bg-subtle);\n color: var(--mj-text-secondary);\n font-size: 11px;\n font-weight: 600;\n padding: 2px 8px;\n border-radius: 12px;\n}\n\n.live-pulse-badge[_ngcontent-%COMP%] {\n display: inline-block;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background-color: var(--mj-color-success);\n}\n\n.empty-feed[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 32px 16px;\n color: var(--mj-text-muted);\n text-align: center;\n}\n\n.empty-icon[_ngcontent-%COMP%] {\n font-size: 28px;\n margin-bottom: 8px;\n}\n\n\n.two-col-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 24px;\n margin-bottom: 24px;\n}\n\n@media (max-width: 900px) {\n .two-col-grid[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n}\n\n.dashboard-card[_ngcontent-%COMP%] {\n background-color: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n padding: 20px;\n}\n\n.card-header[_ngcontent-%COMP%] {\n margin-bottom: 16px;\n}\n\n.card-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n\n.distribution-group[_ngcontent-%COMP%] {\n margin-bottom: 18px;\n}\n\n.distribution-group[_ngcontent-%COMP%]:last-child {\n margin-bottom: 0;\n}\n\n.distribution-group[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n margin: 0 0 10px 0;\n}\n\n.bars-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.bar-row[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.bar-info[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n font-size: 13px;\n}\n\n.bar-label[_ngcontent-%COMP%] {\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.bar-stats[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n font-size: 12px;\n}\n\n.bar-track[_ngcontent-%COMP%] {\n height: 8px;\n background-color: var(--mj-bg-subtle);\n border-radius: 4px;\n overflow: hidden;\n}\n\n.bar-fill[_ngcontent-%COMP%] {\n height: 100%;\n border-radius: 4px;\n transition: width 0.3s ease;\n}\n\n.bar-fill.channel-phone[_ngcontent-%COMP%] {\n background-color: var(--mj-color-primary);\n}\n\n.bar-fill.channel-web[_ngcontent-%COMP%] {\n background-color: var(--mj-color-success);\n}\n\n.bar-fill.channel-meeting[_ngcontent-%COMP%] {\n background-color: var(--mj-color-warning);\n}\n\n.bar-fill.direction-inbound[_ngcontent-%COMP%] {\n background-color: var(--mj-color-primary);\n}\n\n.bar-fill.direction-outbound[_ngcontent-%COMP%] {\n background-color: var(--mj-color-success);\n}\n\n.bar-fill.direction-internal[_ngcontent-%COMP%] {\n background-color: var(--mj-color-warning);\n}\n\n\n.table-container[_ngcontent-%COMP%], \n.live-table-container[_ngcontent-%COMP%], \n.cost-table-container[_ngcontent-%COMP%] {\n overflow-x: auto;\n}\n\n.ops-table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n text-align: left;\n}\n\n.ops-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n padding: 10px 12px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n border-bottom: 1px solid var(--mj-border-subtle);\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.ops-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 10px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n vertical-align: middle;\n}\n\n.ops-table.compact[_ngcontent-%COMP%] td[_ngcontent-%COMP%], \n.ops-table.compact[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n padding: 8px 10px;\n}\n\n.ops-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:hover {\n background-color: var(--mj-bg-subtle);\n}\n\n.address-col[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.address-icon[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n\n.badge[_ngcontent-%COMP%] {\n display: inline-block;\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 500;\n}\n\n.channel-badge.phone[_ngcontent-%COMP%] {\n background-color: var(--mj-color-primary-subtle);\n color: var(--mj-color-primary);\n}\n\n.channel-badge.web[_ngcontent-%COMP%] {\n background-color: var(--mj-color-success-subtle);\n color: var(--mj-color-success);\n}\n\n.channel-badge.meeting[_ngcontent-%COMP%] {\n background-color: var(--mj-color-warning-subtle);\n color: var(--mj-color-warning);\n}\n\n.direction-badge[_ngcontent-%COMP%] {\n background-color: var(--mj-bg-subtle);\n color: var(--mj-text-secondary);\n}\n\n.status-indicator[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n}\n\n.status-indicator[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%] {\n width: 7px;\n height: 7px;\n border-radius: 50%;\n}\n\n.status-indicator.active[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%] {\n background-color: var(--mj-color-success);\n}\n\n.status-indicator.queued[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%] {\n background-color: var(--mj-color-warning);\n}\n\n.status-indicator.ended[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%] {\n background-color: var(--mj-text-muted);\n}\n\n.status-indicator.abandoned[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%] {\n background-color: var(--mj-color-danger);\n}\n\n.status-indicator.failed[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%] {\n background-color: var(--mj-color-danger);\n}\n\n.elapsed-badge[_ngcontent-%COMP%] {\n display: inline-block;\n background-color: var(--mj-color-success-subtle);\n color: var(--mj-color-success);\n font-weight: 600;\n font-size: 12px;\n padding: 2px 6px;\n border-radius: 4px;\n}\n\n.datetime-cell[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n}\n\n.datetime-cell[_ngcontent-%COMP%] .date[_ngcontent-%COMP%] {\n font-weight: 500;\n}\n\n.datetime-cell[_ngcontent-%COMP%] .time[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.end-reason-cell[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-secondary);\n}\n\n.text-right[_ngcontent-%COMP%] {\n text-align: right;\n}\n\n.font-medium[_ngcontent-%COMP%] {\n font-weight: 500;\n}\n\n.font-semibold[_ngcontent-%COMP%] {\n font-weight: 600;\n}"] });
|
|
794
|
+
};
|
|
795
|
+
TelephonyOperationsDashboardComponent = __decorate([
|
|
796
|
+
RegisterClass(BaseResourceComponent, 'TelephonyOperationsDashboard'),
|
|
797
|
+
RegisterClass(BaseResourceComponent, 'InteractionOperationsDashboard')
|
|
798
|
+
], TelephonyOperationsDashboardComponent);
|
|
799
|
+
export { TelephonyOperationsDashboardComponent };
|
|
800
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TelephonyOperationsDashboardComponent, [{
|
|
801
|
+
type: Component,
|
|
802
|
+
args: [{ standalone: false, selector: 'mj-telephony-operations-dashboard', template: "<mj-page-layout>\n <mj-page-header\n Title=\"Telephony & Interaction Operations\"\n Subtitle=\"Real-time metrics, volume breakdown, answer rates, and cost analysis across channels\"\n Icon=\"fa-solid fa-chart-line\"\n >\n <div meta>\n <mj-stat-badge\n [Count]=\"Metrics.LiveCallsCount\"\n [Total]=\"Metrics.TotalVolume\"\n Label=\"live now\"\n [Variant]=\"Metrics.LiveCallsCount > 0 ? 'success' : 'default'\"\n />\n </div>\n\n <div actions>\n <mj-refresh-button [Loading]=\"IsLoading\" (Clicked)=\"LoadData()\" />\n </div>\n\n <div toolbar>\n <div class=\"toolbar-controls\">\n <mj-page-search [Value]=\"SearchQuery\" (ValueChange)=\"OnSearchQueryChange($event)\" />\n\n <div class=\"time-range-pills\" role=\"radiogroup\" aria-label=\"Time Range\">\n <button\n type=\"button\"\n class=\"range-pill\"\n [class.active]=\"SelectedTimeRange === '24h'\"\n (click)=\"SetTimeRange('24h')\"\n >\n 24h\n </button>\n <button\n type=\"button\"\n class=\"range-pill\"\n [class.active]=\"SelectedTimeRange === '7d'\"\n (click)=\"SetTimeRange('7d')\"\n >\n 7d\n </button>\n <button\n type=\"button\"\n class=\"range-pill\"\n [class.active]=\"SelectedTimeRange === '30d'\"\n (click)=\"SetTimeRange('30d')\"\n >\n 30d\n </button>\n <button\n type=\"button\"\n class=\"range-pill\"\n [class.active]=\"SelectedTimeRange === 'all'\"\n (click)=\"SetTimeRange('all')\"\n >\n All Time\n </button>\n </div>\n\n <div class=\"channel-filter-pills\" role=\"radiogroup\" aria-label=\"Channel Filter\">\n <button\n type=\"button\"\n class=\"channel-pill\"\n [class.active]=\"SelectedChannelFilter === 'all'\"\n (click)=\"SetChannelFilter('all')\"\n >\n All Channels\n </button>\n <button\n type=\"button\"\n class=\"channel-pill\"\n [class.active]=\"SelectedChannelFilter === 'Phone'\"\n (click)=\"SetChannelFilter('Phone')\"\n >\n <i class=\"fa-solid fa-phone\"></i> Phone\n </button>\n <button\n type=\"button\"\n class=\"channel-pill\"\n [class.active]=\"SelectedChannelFilter === 'Web'\"\n (click)=\"SetChannelFilter('Web')\"\n >\n <i class=\"fa-solid fa-globe\"></i> Web\n </button>\n <button\n type=\"button\"\n class=\"channel-pill\"\n [class.active]=\"SelectedChannelFilter === 'Meeting'\"\n (click)=\"SetChannelFilter('Meeting')\"\n >\n <i class=\"fa-solid fa-users\"></i> Meeting\n </button>\n </div>\n </div>\n </div>\n </mj-page-header>\n\n <mj-page-body>\n @if (ErrorMessage) {\n <div class=\"error-banner\" role=\"alert\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <span>{{ ErrorMessage }}</span>\n </div>\n }\n\n <!-- KPI Metric Cards Grid -->\n <div class=\"kpi-grid\">\n <div class=\"kpi-card\">\n <div class=\"kpi-icon-wrap primary\">\n <i class=\"fa-solid fa-headset\"></i>\n </div>\n <div class=\"kpi-content\">\n <span class=\"kpi-label\">Total Volume</span>\n <span class=\"kpi-value\">{{ Metrics.TotalVolume }}</span>\n <span class=\"kpi-subtext\">in selected window</span>\n </div>\n </div>\n\n <div class=\"kpi-card\" [class.live-active]=\"Metrics.LiveCallsCount > 0\">\n <div class=\"kpi-icon-wrap success\">\n <span class=\"live-dot-ping\"></span>\n <i class=\"fa-solid fa-phone-volume\"></i>\n </div>\n <div class=\"kpi-content\">\n <span class=\"kpi-label\">Live Now</span>\n <span class=\"kpi-value\">{{ Metrics.LiveCallsCount }}</span>\n <span class=\"kpi-subtext\">active sessions</span>\n </div>\n </div>\n\n <div class=\"kpi-card\">\n <div class=\"kpi-icon-wrap success\">\n <i class=\"fa-solid fa-circle-check\"></i>\n </div>\n <div class=\"kpi-content\">\n <span class=\"kpi-label\">Answer Rate</span>\n <span class=\"kpi-value\">{{ Metrics.AnswerRate | number:'1.0-1' }}%</span>\n <span class=\"kpi-subtext\">{{ Metrics.AnsweredCount }} answered</span>\n </div>\n </div>\n\n <div class=\"kpi-card\">\n <div class=\"kpi-icon-wrap warning\">\n <i class=\"fa-solid fa-phone-slash\"></i>\n </div>\n <div class=\"kpi-content\">\n <span class=\"kpi-label\">Abandonment Rate</span>\n <span class=\"kpi-value\">{{ Metrics.AbandonmentRate | number:'1.0-1' }}%</span>\n <span class=\"kpi-subtext\">{{ Metrics.AbandonedCount }} caller abandons</span>\n </div>\n </div>\n\n <div class=\"kpi-card\">\n <div class=\"kpi-icon-wrap info\">\n <i class=\"fa-solid fa-clock\"></i>\n </div>\n <div class=\"kpi-content\">\n <span class=\"kpi-label\">Avg Handle Time</span>\n <span class=\"kpi-value\">{{ Metrics.AverageHandleTimeFormatted }}</span>\n <span class=\"kpi-subtext\">ASA: {{ Metrics.AverageSpeedToAnswerFormatted }}</span>\n </div>\n </div>\n\n <div class=\"kpi-card\">\n <div class=\"kpi-icon-wrap accent\">\n <i class=\"fa-solid fa-arrows-split-up-and-left\"></i>\n </div>\n <div class=\"kpi-content\">\n <span class=\"kpi-label\">Transfers & Esc.</span>\n <span class=\"kpi-value\">{{ Metrics.TransferCount }} / {{ Metrics.EscalationCount }}</span>\n <span class=\"kpi-subtext\">transfers / escalations</span>\n </div>\n </div>\n\n <div class=\"kpi-card\">\n <div class=\"kpi-icon-wrap cost\">\n <i class=\"fa-solid fa-dollar-sign\"></i>\n </div>\n <div class=\"kpi-content\">\n <span class=\"kpi-label\">Total Cost Est.</span>\n <span class=\"kpi-value\">{{ Metrics.TotalCostFormatted }}</span>\n <span class=\"kpi-subtext\">usage & carrier minutes</span>\n </div>\n </div>\n </div>\n\n <!-- Active Live Calls Section -->\n <div class=\"dashboard-section live-section\">\n <div class=\"section-header\">\n <div class=\"section-title\">\n <span class=\"live-pulse-badge\"></span>\n <h2>Active Live Sessions</h2>\n <span class=\"count-pill\">{{ FilteredLiveInteractions.length }}</span>\n </div>\n </div>\n\n @if (FilteredLiveInteractions.length === 0) {\n <div class=\"empty-feed\">\n <i class=\"fa-solid fa-phone-slash empty-icon\"></i>\n <p>No active live interactions at this moment.</p>\n </div>\n } @else {\n <div class=\"live-table-container\">\n <table class=\"ops-table\">\n <thead>\n <tr>\n <th>Remote Address</th>\n <th>Channel</th>\n <th>Direction</th>\n <th>Status</th>\n <th>Started</th>\n <th>Elapsed</th>\n </tr>\n </thead>\n <tbody>\n @for (live of FilteredLiveInteractions; track live.ID) {\n <tr class=\"live-row\">\n <td class=\"address-col\">\n <i class=\"fa-solid fa-user-circle address-icon\"></i>\n <span class=\"address-text\">{{ live.RemoteAddress || 'Unknown' }}</span>\n </td>\n <td>\n <span class=\"badge channel-badge {{ live.Channel.toLowerCase() }}\">\n {{ live.Channel }}\n </span>\n </td>\n <td>\n <span class=\"badge direction-badge {{ live.Direction.toLowerCase() }}\">\n {{ live.Direction }}\n </span>\n </td>\n <td>\n <span class=\"status-indicator active\">\n <span class=\"dot\"></span>\n {{ live.Status }}\n </span>\n </td>\n <td>{{ FormatTime(live.StartedAt) }}</td>\n <td class=\"elapsed-col\">\n <span class=\"elapsed-badge\">{{ live.ElapsedLabel }}</span>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n\n <!-- Middle Row: Volume Breakdown Cards -->\n <div class=\"two-col-grid\">\n <!-- Volume by Channel & Direction -->\n <div class=\"dashboard-card\">\n <div class=\"card-header\">\n <h3><i class=\"fa-solid fa-layer-group\"></i> Volume Distribution</h3>\n </div>\n <div class=\"card-body\">\n <div class=\"distribution-group\">\n <h4>By Channel</h4>\n <div class=\"bars-container\">\n @for (item of Metrics.VolumeByChannel; track item.Channel) {\n <div class=\"bar-row\">\n <div class=\"bar-info\">\n <span class=\"bar-label\">{{ item.Channel }}</span>\n <span class=\"bar-stats\">{{ item.Count }} ({{ item.Percentage }}%)</span>\n </div>\n <div class=\"bar-track\">\n <div class=\"bar-fill channel-{{ item.Channel.toLowerCase() }}\" [style.width.%]=\"item.Percentage\"></div>\n </div>\n </div>\n }\n </div>\n </div>\n\n <div class=\"distribution-group\">\n <h4>By Direction</h4>\n <div class=\"bars-container\">\n @for (item of Metrics.VolumeByDirection; track item.Direction) {\n <div class=\"bar-row\">\n <div class=\"bar-info\">\n <span class=\"bar-label\">{{ item.Direction }}</span>\n <span class=\"bar-stats\">{{ item.Count }} ({{ item.Percentage }}%)</span>\n </div>\n <div class=\"bar-track\">\n <div class=\"bar-fill direction-{{ item.Direction.toLowerCase() }}\" [style.width.%]=\"item.Percentage\"></div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n\n <!-- Cost by Number -->\n <div class=\"dashboard-card\">\n <div class=\"card-header\">\n <h3><i class=\"fa-solid fa-money-bill-wave\"></i> Cost by Phone Number</h3>\n </div>\n <div class=\"card-body\">\n @if (Metrics.CostByNumber.length === 0) {\n <div class=\"empty-feed\">\n <p>No phone number cost records in this window.</p>\n </div>\n } @else {\n <div class=\"cost-table-container\">\n <table class=\"ops-table compact\">\n <thead>\n <tr>\n <th>Phone Number / Target</th>\n <th>Calls</th>\n <th class=\"text-right\">Est. Cost</th>\n </tr>\n </thead>\n <tbody>\n @for (num of Metrics.CostByNumber; track num.PhoneNumber) {\n <tr>\n <td class=\"font-medium\">{{ num.PhoneNumber }}</td>\n <td>{{ num.Count }}</td>\n <td class=\"text-right font-semibold\">{{ FormatCost(num.Cost) }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n </div>\n </div>\n\n <!-- Bottom Row: Recent Interactions Audit Table -->\n <div class=\"dashboard-section\">\n <div class=\"section-header\">\n <div class=\"section-title\">\n <i class=\"fa-solid fa-list-check\"></i>\n <h2>Recent Interactions</h2>\n <span class=\"count-pill\">{{ FilteredRecentInteractions.length }}</span>\n </div>\n </div>\n\n @if (FilteredRecentInteractions.length === 0) {\n <div class=\"empty-feed\">\n <p>No interactions found matching criteria.</p>\n </div>\n } @else {\n <div class=\"table-container\">\n <table class=\"ops-table\">\n <thead>\n <tr>\n <th>Date / Time</th>\n <th>Remote Party</th>\n <th>Channel</th>\n <th>Direction</th>\n <th>Status</th>\n <th>Duration</th>\n <th>End Reason</th>\n <th class=\"text-right\">Est. Cost</th>\n </tr>\n </thead>\n <tbody>\n @for (row of FilteredRecentInteractions; track row.ID) {\n <tr>\n <td>\n <div class=\"datetime-cell\">\n <span class=\"date\">{{ FormatDate(row.StartedAt) }}</span>\n <span class=\"time\">{{ FormatTime(row.StartedAt) }}</span>\n </div>\n </td>\n <td class=\"address-col\">\n <span>{{ row.RemoteAddress || 'Unknown' }}</span>\n </td>\n <td>\n <span class=\"badge channel-badge {{ row.Channel.toLowerCase() }}\">\n {{ row.Channel }}\n </span>\n </td>\n <td>\n <span class=\"badge direction-badge {{ row.Direction.toLowerCase() }}\">\n {{ row.Direction }}\n </span>\n </td>\n <td>\n <span class=\"status-indicator {{ row.Status.toLowerCase() }}\">\n <span class=\"dot\"></span>\n {{ row.Status }}\n </span>\n </td>\n <td>{{ ComputeDuration(row.StartedAt, row.EndedAt) }}</td>\n <td class=\"end-reason-cell\">{{ row.EndReason || '-' }}</td>\n <td class=\"text-right\">{{ FormatCost(row.CostEstimate) }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n </mj-page-body>\n</mj-page-layout>\n", styles: [":host {\n display: block;\n height: 100%;\n}\n\n.toolbar-controls {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.time-range-pills,\n.channel-filter-pills {\n display: inline-flex;\n align-items: center;\n background-color: var(--mj-bg-subtle);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 6px;\n padding: 2px;\n gap: 2px;\n}\n\n.range-pill,\n.channel-pill {\n border: none;\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 12px;\n font-weight: 500;\n padding: 4px 10px;\n border-radius: 4px;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n transition: all 0.15s ease-in-out;\n}\n\n.range-pill:hover,\n.channel-pill:hover {\n color: var(--mj-text-primary);\n background-color: var(--mj-bg-surface);\n}\n\n.range-pill.active,\n.channel-pill.active {\n background-color: var(--mj-bg-surface);\n color: var(--mj-color-primary);\n font-weight: 600;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n\n.error-banner {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 16px;\n border-radius: 8px;\n margin-bottom: 20px;\n background-color: var(--mj-color-danger-subtle);\n border: 1px solid var(--mj-color-danger);\n color: var(--mj-color-danger);\n font-size: 14px;\n}\n\n/* KPI Grid */\n.kpi-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n gap: 16px;\n margin-bottom: 24px;\n}\n\n.kpi-card {\n background-color: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n padding: 16px;\n display: flex;\n align-items: center;\n gap: 14px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03);\n}\n\n.kpi-card.live-active {\n border-color: var(--mj-color-success);\n}\n\n.kpi-icon-wrap {\n width: 44px;\n height: 44px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 18px;\n position: relative;\n flex-shrink: 0;\n}\n\n.kpi-icon-wrap.primary {\n background-color: var(--mj-color-primary-subtle);\n color: var(--mj-color-primary);\n}\n\n.kpi-icon-wrap.success {\n background-color: var(--mj-color-success-subtle);\n color: var(--mj-color-success);\n}\n\n.kpi-icon-wrap.warning {\n background-color: var(--mj-color-warning-subtle);\n color: var(--mj-color-warning);\n}\n\n.kpi-icon-wrap.info {\n background-color: var(--mj-bg-subtle);\n color: var(--mj-text-secondary);\n}\n\n.kpi-icon-wrap.accent {\n background-color: var(--mj-color-primary-subtle);\n color: var(--mj-color-primary);\n}\n\n.kpi-icon-wrap.cost {\n background-color: var(--mj-color-success-subtle);\n color: var(--mj-color-success);\n}\n\n.live-dot-ping {\n position: absolute;\n top: 6px;\n right: 6px;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background-color: var(--mj-color-success);\n animation: pulse-ping 2s cubic-bezier(0, 0, 0.2, 1) infinite;\n}\n\n@keyframes pulse-ping {\n 0% {\n transform: scale(0.95);\n opacity: 1;\n }\n 50% {\n transform: scale(1.4);\n opacity: 0.5;\n }\n 100% {\n transform: scale(0.95);\n opacity: 1;\n }\n}\n\n.kpi-content {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.kpi-label {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.kpi-value {\n font-size: 22px;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.2;\n margin: 2px 0;\n}\n\n.kpi-subtext {\n font-size: 11px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* Sections */\n.dashboard-section {\n background-color: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n padding: 20px;\n margin-bottom: 24px;\n}\n\n.section-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 16px;\n}\n\n.section-title {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.section-title h2 {\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0;\n}\n\n.count-pill {\n background-color: var(--mj-bg-subtle);\n color: var(--mj-text-secondary);\n font-size: 11px;\n font-weight: 600;\n padding: 2px 8px;\n border-radius: 12px;\n}\n\n.live-pulse-badge {\n display: inline-block;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background-color: var(--mj-color-success);\n}\n\n.empty-feed {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 32px 16px;\n color: var(--mj-text-muted);\n text-align: center;\n}\n\n.empty-icon {\n font-size: 28px;\n margin-bottom: 8px;\n}\n\n/* Two-column grid */\n.two-col-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 24px;\n margin-bottom: 24px;\n}\n\n@media (max-width: 900px) {\n .two-col-grid {\n grid-template-columns: 1fr;\n }\n}\n\n.dashboard-card {\n background-color: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n padding: 20px;\n}\n\n.card-header {\n margin-bottom: 16px;\n}\n\n.card-header h3 {\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n/* Distribution Bars */\n.distribution-group {\n margin-bottom: 18px;\n}\n\n.distribution-group:last-child {\n margin-bottom: 0;\n}\n\n.distribution-group h4 {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n margin: 0 0 10px 0;\n}\n\n.bars-container {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.bar-row {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.bar-info {\n display: flex;\n justify-content: space-between;\n font-size: 13px;\n}\n\n.bar-label {\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.bar-stats {\n color: var(--mj-text-secondary);\n font-size: 12px;\n}\n\n.bar-track {\n height: 8px;\n background-color: var(--mj-bg-subtle);\n border-radius: 4px;\n overflow: hidden;\n}\n\n.bar-fill {\n height: 100%;\n border-radius: 4px;\n transition: width 0.3s ease;\n}\n\n.bar-fill.channel-phone {\n background-color: var(--mj-color-primary);\n}\n\n.bar-fill.channel-web {\n background-color: var(--mj-color-success);\n}\n\n.bar-fill.channel-meeting {\n background-color: var(--mj-color-warning);\n}\n\n.bar-fill.direction-inbound {\n background-color: var(--mj-color-primary);\n}\n\n.bar-fill.direction-outbound {\n background-color: var(--mj-color-success);\n}\n\n.bar-fill.direction-internal {\n background-color: var(--mj-color-warning);\n}\n\n/* Tables */\n.table-container,\n.live-table-container,\n.cost-table-container {\n overflow-x: auto;\n}\n\n.ops-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n text-align: left;\n}\n\n.ops-table th {\n padding: 10px 12px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n border-bottom: 1px solid var(--mj-border-subtle);\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.ops-table td {\n padding: 10px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n vertical-align: middle;\n}\n\n.ops-table.compact td,\n.ops-table.compact th {\n padding: 8px 10px;\n}\n\n.ops-table tbody tr:hover {\n background-color: var(--mj-bg-subtle);\n}\n\n.address-col {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.address-icon {\n color: var(--mj-text-muted);\n}\n\n.badge {\n display: inline-block;\n padding: 2px 8px;\n border-radius: 4px;\n font-size: 11px;\n font-weight: 500;\n}\n\n.channel-badge.phone {\n background-color: var(--mj-color-primary-subtle);\n color: var(--mj-color-primary);\n}\n\n.channel-badge.web {\n background-color: var(--mj-color-success-subtle);\n color: var(--mj-color-success);\n}\n\n.channel-badge.meeting {\n background-color: var(--mj-color-warning-subtle);\n color: var(--mj-color-warning);\n}\n\n.direction-badge {\n background-color: var(--mj-bg-subtle);\n color: var(--mj-text-secondary);\n}\n\n.status-indicator {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n}\n\n.status-indicator .dot {\n width: 7px;\n height: 7px;\n border-radius: 50%;\n}\n\n.status-indicator.active .dot {\n background-color: var(--mj-color-success);\n}\n\n.status-indicator.queued .dot {\n background-color: var(--mj-color-warning);\n}\n\n.status-indicator.ended .dot {\n background-color: var(--mj-text-muted);\n}\n\n.status-indicator.abandoned .dot {\n background-color: var(--mj-color-danger);\n}\n\n.status-indicator.failed .dot {\n background-color: var(--mj-color-danger);\n}\n\n.elapsed-badge {\n display: inline-block;\n background-color: var(--mj-color-success-subtle);\n color: var(--mj-color-success);\n font-weight: 600;\n font-size: 12px;\n padding: 2px 6px;\n border-radius: 4px;\n}\n\n.datetime-cell {\n display: flex;\n flex-direction: column;\n}\n\n.datetime-cell .date {\n font-weight: 500;\n}\n\n.datetime-cell .time {\n font-size: 11px;\n color: var(--mj-text-muted);\n}\n\n.end-reason-cell {\n font-size: 12px;\n color: var(--mj-text-secondary);\n}\n\n.text-right {\n text-align: right;\n}\n\n.font-medium {\n font-weight: 500;\n}\n\n.font-semibold {\n font-weight: 600;\n}\n"] }]
|
|
803
|
+
}], null, null); })();
|
|
804
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TelephonyOperationsDashboardComponent, { className: "TelephonyOperationsDashboardComponent", filePath: "src/TelephonyOperations/telephony-operations-dashboard.component.ts", lineNumber: 45 }); })();
|
|
805
|
+
//# sourceMappingURL=telephony-operations-dashboard.component.js.map
|