@tuki-io/tuki-widgets 0.0.219-dev.54 → 0.0.219-dev.56
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/contact-center/cc-import/cc-import.module.d.ts +13 -0
- package/contact-center/cc-import/cc-import.service.d.ts +85 -0
- package/contact-center/cc-import/components/import-contact-center-panel/import-contact-center-panel.component.d.ts +56 -0
- package/contact-center/cc-import/components/import-contact-center-wizard/import-contact-center-wizard.component.d.ts +77 -0
- package/contact-center/cc-import/components/import-contact-center-wizard/import-contact-center-wizard.types.d.ts +3 -0
- package/contact-center/cc-import/constants/cc-import.constants.d.ts +37 -0
- package/contact-center/cc-import/mock/cc-import.mock.d.ts +4 -0
- package/contact-center/cc-import/types/cc-import.d.ts +73 -0
- package/contact-center/cc-import/types/uccx.d.ts +109 -0
- package/contact-center/cc-readiness/cc-readiness.module.d.ts +3 -3
- package/contact-center/public-api.d.ts +8 -0
- package/contact-center/shared/api.endpoints.d.ts +25 -0
- package/contact-center/shared/api.service.d.ts +3 -1
- package/contact-center/shared/components/card/card.component.d.ts +13 -0
- package/contact-center/shared/dcp-api.service.d.ts +15 -0
- package/contact-center/shared/shared.module.d.ts +11 -5
- package/esm2020/contact-center/cc-import/cc-import.module.mjs +45 -0
- package/esm2020/contact-center/cc-import/cc-import.service.mjs +228 -0
- package/esm2020/contact-center/cc-import/components/import-contact-center-panel/import-contact-center-panel.component.mjs +144 -0
- package/esm2020/contact-center/cc-import/components/import-contact-center-wizard/import-contact-center-wizard.component.mjs +243 -0
- package/esm2020/contact-center/cc-import/components/import-contact-center-wizard/import-contact-center-wizard.types.mjs +3 -0
- package/esm2020/contact-center/cc-import/constants/cc-import.constants.mjs +62 -0
- package/esm2020/contact-center/cc-import/mock/cc-import.mock.mjs +78 -0
- package/esm2020/contact-center/cc-import/types/cc-import.mjs +2 -0
- package/esm2020/contact-center/cc-import/types/uccx.mjs +24 -0
- package/esm2020/contact-center/cc-readiness/cc-readiness.module.mjs +7 -6
- package/esm2020/contact-center/cc-readiness/widgets/licences-requirement/licences-requirement.component.mjs +5 -5
- package/esm2020/contact-center/cc-readiness/widgets/summary-grid/summary-grid.component.mjs +2 -2
- package/esm2020/contact-center/public-api.mjs +9 -1
- package/esm2020/contact-center/shared/api.endpoints.mjs +26 -1
- package/esm2020/contact-center/shared/api.service.mjs +8 -3
- package/esm2020/contact-center/shared/components/card/card.component.mjs +24 -0
- package/esm2020/contact-center/shared/dcp-api.service.mjs +26 -0
- package/esm2020/contact-center/shared/shared.module.mjs +15 -5
- package/esm2020/user-device-manage/public-api.mjs +4 -1
- package/esm2020/user-device-manage/src/app.constants.mjs +9 -3
- package/esm2020/user-device-manage/src/associate-lines/associate-lines.component.mjs +311 -0
- package/esm2020/user-device-manage/src/classes/device.mjs +1 -1
- package/esm2020/user-device-manage/src/classes/deviceProfile.mjs +1 -1
- package/esm2020/user-device-manage/src/confirm-dialog/info-dialog.component.mjs +2 -2
- package/esm2020/user-device-manage/src/device-manage-widget.component.mjs +34 -17
- package/esm2020/user-device-manage/src/device-settings/device-settings.component.mjs +207 -0
- package/esm2020/user-device-manage/src/line-key-config/line-key-config.component.mjs +401 -0
- package/esm2020/user-device-manage/src/services/device.service.mjs +32 -2
- package/esm2020/user-device-manage/src/services/user.service.mjs +34 -6
- package/esm2020/user-device-manage/src/user-device-manage.module.mjs +19 -4
- package/esm2020/workspaces/src/components/workspace-detail/workspace-detail.component.mjs +17 -4
- package/esm2020/workspaces/src/components/workspace-device-detail/workspace-device-detail.component.mjs +66 -0
- package/esm2020/workspaces/src/components/workspaces-table/workspaces-table.component.mjs +6 -3
- package/esm2020/workspaces/src/services/workspace-detail.service.mjs +43 -1
- package/esm2020/workspaces/src/services/workspaces.service.mjs +2 -1
- package/esm2020/workspaces/src/types/workspace.mjs +1 -1
- package/esm2020/workspaces/src/workspaces.module.mjs +4 -1
- package/fesm2015/tuki-io-tuki-widgets-contact-center.mjs +998 -127
- package/fesm2015/tuki-io-tuki-widgets-contact-center.mjs.map +1 -1
- package/fesm2015/tuki-io-tuki-widgets-user-device-manage.mjs +1018 -40
- package/fesm2015/tuki-io-tuki-widgets-user-device-manage.mjs.map +1 -1
- package/fesm2015/tuki-io-tuki-widgets-workspaces.mjs +142 -16
- package/fesm2015/tuki-io-tuki-widgets-workspaces.mjs.map +1 -1
- package/fesm2020/tuki-io-tuki-widgets-contact-center.mjs +989 -127
- package/fesm2020/tuki-io-tuki-widgets-contact-center.mjs.map +1 -1
- package/fesm2020/tuki-io-tuki-widgets-user-device-manage.mjs +1001 -39
- package/fesm2020/tuki-io-tuki-widgets-user-device-manage.mjs.map +1 -1
- package/fesm2020/tuki-io-tuki-widgets-workspaces.mjs +133 -16
- package/fesm2020/tuki-io-tuki-widgets-workspaces.mjs.map +1 -1
- package/package.json +1 -1
- package/user-device-manage/public-api.d.ts +3 -0
- package/user-device-manage/src/app.constants.d.ts +6 -0
- package/user-device-manage/src/associate-lines/associate-lines.component.d.ts +95 -0
- package/user-device-manage/src/classes/device.d.ts +15 -0
- package/user-device-manage/src/classes/deviceProfile.d.ts +5 -0
- package/user-device-manage/src/device-associated-line/device-associated-line.component.d.ts +1 -0
- package/user-device-manage/src/device-manage-widget.component.d.ts +6 -1
- package/user-device-manage/src/device-settings/device-settings.component.d.ts +53 -0
- package/user-device-manage/src/line-key-config/line-key-config.component.d.ts +84 -0
- package/user-device-manage/src/services/device.service.d.ts +6 -1
- package/user-device-manage/src/services/user.service.d.ts +2 -1
- package/user-device-manage/src/user-device-manage.module.d.ts +10 -7
- package/workspaces/src/components/workspace-detail/workspace-detail.component.d.ts +6 -1
- package/workspaces/src/components/workspace-device-detail/workspace-device-detail.component.d.ts +25 -0
- package/workspaces/src/components/workspaces-table/workspaces-table.component.d.ts +1 -0
- package/workspaces/src/services/workspace-detail.service.d.ts +2 -0
- package/workspaces/src/types/workspace.d.ts +19 -0
- package/workspaces/src/workspaces.module.d.ts +8 -7
- package/contact-center/cc-readiness/components/card/card.component.d.ts +0 -6
- package/esm2020/contact-center/cc-readiness/components/card/card.component.mjs +0 -13
|
@@ -1,40 +1,42 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, inject, Component, Input, NgModule, ChangeDetectionStrategy, Inject, EventEmitter, Output, ViewChild } from '@angular/core';
|
|
2
|
+
import { Injectable, inject, Component, Input, NgModule, ChangeDetectionStrategy, Inject, EventEmitter, Output, ViewChild, ViewEncapsulation } from '@angular/core';
|
|
3
3
|
import * as i1$1 from '@angular/common';
|
|
4
4
|
import { CommonModule } from '@angular/common';
|
|
5
5
|
import * as i1 from '@angular/common/http';
|
|
6
6
|
import { HttpHeaders, HttpClientModule } from '@angular/common/http';
|
|
7
|
-
import { map, catchError, of, finalize, Subject, forkJoin, throwError } from 'rxjs';
|
|
8
|
-
import * as
|
|
9
|
-
import { NgApexchartsModule } from 'ng-apexcharts';
|
|
10
|
-
import * as i3 from '@angular/material/select';
|
|
7
|
+
import { map, catchError, of, finalize, Subject, forkJoin, throwError, delay, switchMap, Subscription } from 'rxjs';
|
|
8
|
+
import * as i6 from '@angular/material/select';
|
|
11
9
|
import { MatSelectModule } from '@angular/material/select';
|
|
12
|
-
import * as
|
|
10
|
+
import * as i3 from '@angular/material/core';
|
|
13
11
|
import { MatOptionModule } from '@angular/material/core';
|
|
12
|
+
import * as i5 from 'ng-apexcharts';
|
|
13
|
+
import { NgApexchartsModule } from 'ng-apexcharts';
|
|
14
|
+
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
|
15
|
+
import { MatTableModule } from '@angular/material/table';
|
|
16
|
+
import * as i3$1 from '@angular/material/tooltip';
|
|
17
|
+
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
18
|
+
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
14
19
|
import * as i1$4 from '@angular/material/button';
|
|
15
20
|
import { MatButtonModule } from '@angular/material/button';
|
|
16
|
-
import {
|
|
17
|
-
import
|
|
18
|
-
import
|
|
21
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
22
|
+
import * as i2$1 from '@angular/material/form-field';
|
|
23
|
+
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
24
|
+
import { MatInputModule } from '@angular/material/input';
|
|
25
|
+
import * as i4 from '@angular/material/progress-spinner';
|
|
19
26
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
20
|
-
import {
|
|
27
|
+
import { MatPaginatorModule } from '@angular/material/paginator';
|
|
21
28
|
import cytoscape from 'cytoscape';
|
|
22
29
|
import dagre from 'cytoscape-dagre';
|
|
23
30
|
import nodeHtmlLabel from 'cytoscape-node-html-label';
|
|
24
31
|
import * as i1$2 from '@angular/material/dialog';
|
|
25
32
|
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
|
26
33
|
import * as uuid from 'uuid';
|
|
27
|
-
import * as i3$1 from '@angular/material/tooltip';
|
|
28
|
-
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
29
34
|
import * as i1$3 from '@angular/platform-browser';
|
|
30
|
-
import * as i2
|
|
35
|
+
import * as i2 from '@angular/forms';
|
|
31
36
|
import { FormsModule } from '@angular/forms';
|
|
32
|
-
import * as i2$2 from '@angular/material/form-field';
|
|
33
|
-
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
34
37
|
import { mergeMap, takeUntil, finalize as finalize$1, tap } from 'rxjs/operators';
|
|
35
|
-
import
|
|
36
|
-
import {
|
|
37
|
-
import { MatInputModule } from '@angular/material/input';
|
|
38
|
+
import * as i2$2 from '@angular/material/stepper';
|
|
39
|
+
import { MatStepperModule } from '@angular/material/stepper';
|
|
38
40
|
|
|
39
41
|
class APIService {
|
|
40
42
|
constructor(httpClient) {
|
|
@@ -45,10 +47,15 @@ class APIService {
|
|
|
45
47
|
params = params || {};
|
|
46
48
|
return this.httpClient.get(this.apiUrl + url, { params: this.prepareEncodedParams(params), headers });
|
|
47
49
|
}
|
|
48
|
-
post(url, body, params = {}) {
|
|
50
|
+
post(url, body, params = {}, options = {}) {
|
|
49
51
|
body = body || null;
|
|
50
52
|
const headers = this.getHeaders();
|
|
51
|
-
return this.httpClient.post(this.apiUrl + url, body, {
|
|
53
|
+
return this.httpClient.post(this.apiUrl + url, body, {
|
|
54
|
+
params: this.prepareEncodedParams(params),
|
|
55
|
+
headers,
|
|
56
|
+
// Some migration endpoints answer with a file rather than JSON.
|
|
57
|
+
responseType: (options.responseType || 'json')
|
|
58
|
+
});
|
|
52
59
|
}
|
|
53
60
|
// use when response extended data is necessary:
|
|
54
61
|
postExtended(url, body = null, params = {}, headers = {}) {
|
|
@@ -117,6 +124,31 @@ const API = {
|
|
|
117
124
|
READINESS: {
|
|
118
125
|
UCCX_SERVER_ENTITY_STATS: (customerId) => `/api/uccx/migration/customer/${customerId}/entity-counts`,
|
|
119
126
|
LICENSE_USAGE_SUMMARY: (customerId) => `/api/uccx/migration/customer/${customerId}/cc-license-stats`,
|
|
127
|
+
},
|
|
128
|
+
/**
|
|
129
|
+
* The application-centric UCCX migration endpoints, as used by webex-ops-client
|
|
130
|
+
* (`ContactCenterService` / `constants.json`). There is no dedicated
|
|
131
|
+
* "contact center" API - a contact center *is* a UCCX application, and its
|
|
132
|
+
* entities are reached through the e2emigration batches that carry it.
|
|
133
|
+
*/
|
|
134
|
+
UCCX: {
|
|
135
|
+
/** Every UCCX application known for the customer. Served by the **dcp** host. */
|
|
136
|
+
APPLICATION_NAMES: (customerId) => `/api/cache/uccx/application/customer/${customerId}/getall`,
|
|
137
|
+
/** All UCCX batches for the customer, one per entity type. */
|
|
138
|
+
BATCHES: (customerId) => `/api/e2emigration/batch/${customerId}/UCCX`,
|
|
139
|
+
/**
|
|
140
|
+
* The entities of one batch.
|
|
141
|
+
*
|
|
142
|
+
* It also takes an `applicationName` param, but on dev2 that filter answers
|
|
143
|
+
* `[]` for every application - including ones whose items *do* carry a
|
|
144
|
+
* matching `applicationName` - so the widget reads the batch unfiltered and
|
|
145
|
+
* narrows on the item field itself.
|
|
146
|
+
*/
|
|
147
|
+
BATCH_ITEMS: (customerId, batchId) => `/api/e2emigration/batch/${batchId}/customer/${customerId}/uccx/filter`,
|
|
148
|
+
/** Per-AEF-script migration state (NEW / MIGRATED / MIGRATION_FAILED / ...). */
|
|
149
|
+
CALL_FLOW_MIGRATION_STATUSES: (customerId) => `/api/uccx/migration/customer/${customerId}/callflow/getAll`,
|
|
150
|
+
/** Runs the UCCX -> Webex Contact Center migration. Responds with an XLSX tracker. */
|
|
151
|
+
MIGRATE: (customerId) => `/api/migration/webex/customer/${customerId}/uccx/migrate`,
|
|
120
152
|
}
|
|
121
153
|
};
|
|
122
154
|
|
|
@@ -153,15 +185,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
|
|
|
153
185
|
}]
|
|
154
186
|
}], ctorParameters: function () { return []; } });
|
|
155
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Standard contact-center panel chrome. Content goes into the `card-content` slot;
|
|
190
|
+
* `card-title-adornment` takes a small element rendered next to the title (info icon,
|
|
191
|
+
* badge).
|
|
192
|
+
*/
|
|
156
193
|
class CardComponent {
|
|
194
|
+
constructor() {
|
|
195
|
+
/** Tightens the padding and inner spacing for compact panels. */
|
|
196
|
+
this.dense = false;
|
|
197
|
+
}
|
|
157
198
|
}
|
|
158
199
|
CardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
159
|
-
CardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: CardComponent, selector: "tk-card", inputs: { title: "title" }, ngImport: i0, template: "<article class=\"card\">\
|
|
200
|
+
CardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: CardComponent, selector: "tk-card", inputs: { title: "title", dense: "dense" }, ngImport: i0, template: "<article class=\"card\" [class.card--dense]=\"dense\">\n <header class=\"card__header\">\n <div class=\"card__title-group\">\n <h2>{{ title }}</h2>\n <ng-content select=\"card-title-adornment\"></ng-content>\n </div>\n <!--<div class=\"card__menu-button\">\n <img width=\"18\" src=\"assets/icons/menu_icon.png\" alt=\"Menu Icon\">\n </div>-->\n </header>\n <div class=\"card__content\">\n <ng-content select=\"card-content\"></ng-content>\n </div>\n</article>\n", styles: ["@import\"https://fonts.googleapis.com/css?family=Poppins:400,100,200,300,500,600,800,700,900\";@import\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400&display=swap\";:host{display:block;height:100%;box-sizing:border-box}.card{font-family:Inter,Inter,sans-serif;background-color:#fff;border-radius:1rem;padding:2rem;border:1px solid hsla(0,0%,0%,.2);min-width:300px;max-width:100%;height:100%;display:flex;flex-direction:column;box-sizing:border-box;gap:2.5rem}.card--dense{border-radius:8px;padding:1.5rem;gap:1.25rem}.card--dense .card__content{gap:1.25rem}.card__header{position:relative;display:flex;align-items:center;justify-content:space-between}.card__header h2{font-size:16px;margin:0}.card__header .card__menu-button{position:relative;display:flex;align-items:center}.card__title-group{display:flex;align-items:center;gap:.375rem}.card__content{display:flex;flex-direction:column;gap:2.5rem;flex-grow:1}\n"] });
|
|
160
201
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CardComponent, decorators: [{
|
|
161
202
|
type: Component,
|
|
162
|
-
args: [{ selector: 'tk-card', template: "<article class=\"card\">\
|
|
203
|
+
args: [{ selector: 'tk-card', template: "<article class=\"card\" [class.card--dense]=\"dense\">\n <header class=\"card__header\">\n <div class=\"card__title-group\">\n <h2>{{ title }}</h2>\n <ng-content select=\"card-title-adornment\"></ng-content>\n </div>\n <!--<div class=\"card__menu-button\">\n <img width=\"18\" src=\"assets/icons/menu_icon.png\" alt=\"Menu Icon\">\n </div>-->\n </header>\n <div class=\"card__content\">\n <ng-content select=\"card-content\"></ng-content>\n </div>\n</article>\n", styles: ["@import\"https://fonts.googleapis.com/css?family=Poppins:400,100,200,300,500,600,800,700,900\";@import\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400&display=swap\";:host{display:block;height:100%;box-sizing:border-box}.card{font-family:Inter,Inter,sans-serif;background-color:#fff;border-radius:1rem;padding:2rem;border:1px solid hsla(0,0%,0%,.2);min-width:300px;max-width:100%;height:100%;display:flex;flex-direction:column;box-sizing:border-box;gap:2.5rem}.card--dense{border-radius:8px;padding:1.5rem;gap:1.25rem}.card--dense .card__content{gap:1.25rem}.card__header{position:relative;display:flex;align-items:center;justify-content:space-between}.card__header h2{font-size:16px;margin:0}.card__header .card__menu-button{position:relative;display:flex;align-items:center}.card__title-group{display:flex;align-items:center;gap:.375rem}.card__content{display:flex;flex-direction:column;gap:2.5rem;flex-grow:1}\n"] }]
|
|
163
204
|
}], propDecorators: { title: [{
|
|
164
205
|
type: Input
|
|
206
|
+
}], dense: [{
|
|
207
|
+
type: Input
|
|
165
208
|
}] } });
|
|
166
209
|
|
|
167
210
|
const LEGEND_COLORS = {
|
|
@@ -337,7 +380,7 @@ class LicencesRequirementComponent {
|
|
|
337
380
|
}
|
|
338
381
|
}
|
|
339
382
|
LicencesRequirementComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: LicencesRequirementComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
340
|
-
LicencesRequirementComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: LicencesRequirementComponent, selector: "cc-licenses-requirement", inputs: { customerId: "customerId", host: "host", token: "token" }, usesOnChanges: true, ngImport: i0, template: "<tk-card title=\"Licenses requirement\">\n <ng-container ngProjectAs=\"card-content\">\n <div\n class=\"widget-body\"\n *ngIf=\"!loading && !error && viewModel; else widgetState\"\n >\n <div class=\"left-section\">\n <mat-select\n [(value)]=\"selectedSummaryIndex\"\n (selectionChange)=\"onFilterChange($event.value)\"\n class=\"filter-select\"\n >\n <mat-option *ngFor=\"let option of filters; index as i\" [value]=\"i\">\n {{ option }}\n </mat-option>\n </mat-select>\n\n <div class=\"chart-section\">\n <div class=\"chart-wrapper\">\n <apx-chart\n [series]=\"viewModel.chartOptions.series\"\n [chart]=\"viewModel.chartOptions.chart\"\n [labels]=\"viewModel.chartOptions.labels\"\n [colors]=\"viewModel.chartOptions.colors\"\n [legend]=\"viewModel.chartOptions.legend\"\n [plotOptions]=\"viewModel.chartOptions.plotOptions\"\n [dataLabels]=\"viewModel.chartOptions.dataLabels\"\n [tooltip]=\"viewModel.chartOptions.tooltip\"\n >\n </apx-chart>\n </div>\n <div class=\"legend\">\n <div class=\"legend-item\" *ngFor=\"let item of viewModel.legendItems\">\n <span\n class=\"legend-marker\"\n [style.backgroundColor]=\"item.color\"\n ></span>\n <span class=\"legend-label\">{{ item.label }}</span>\n <span class=\"legend-value\">{{ item.count | number }}</span>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"divider\" aria-hidden=\"true\"></div>\n\n <div class=\"table-section\">\n <div class=\"lic-table-header\">\n <span>License</span>\n <span>Required</span>\n </div>\n <div class=\"table-row\" *ngFor=\"let row of viewModel.rows\">\n <span>{{ row.label }}</span>\n <span>{{ row.value | number }}</span>\n </div>\n <div class=\"table-row total-row\">\n <span>Total</span>\n <span>{{ viewModel.totalRequired | number }}</span>\n </div>\n </div>\n </div>\n\n <ng-template #widgetState>\n <div class=\"state-message\" *ngIf=\"loading\">\n Loading license requirements\u2026\n </div>\n <div class=\"state-message error\" *ngIf=\"!loading && error\">\n Unable to load license requirements.\n </div>\n <div class=\"state-message\" *ngIf=\"!loading && !error && !viewModel\">\n No license data available.\n </div>\n </ng-template>\n </ng-container>\n</tk-card>\n", styles: ["@import\"https://fonts.googleapis.com/css?family=Poppins:400,100,200,300,500,600,800,700,900\";@import\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400&display=swap\";:host{display:block;width:100%;box-sizing:border-box}.filter-select{font-family:Inter,Inter,sans-serif;font-size:14px;min-width:140px;border:1px solid #e5e7eb;border-radius:.5rem;padding:.5rem .75rem;background-color:#fff}.filter-select:hover{border-color:#d1d5db}:host ::ng-deep .filter-select.mat-mdc-select{border:1px solid #e5e7eb;border-radius:.5rem;background-color:#fff}:host ::ng-deep .filter-select.mat-mdc-select:hover{border-color:#d1d5db}:host ::ng-deep .filter-select.mat-mdc-select.mat-focused{border-color:#16a693}:host ::ng-deep .filter-select .mat-mdc-select-trigger{font-size:14px;padding:.5rem .75rem}:host ::ng-deep .filter-select .mat-mdc-select-value{color:#111827}:host ::ng-deep .filter-select .mat-mdc-select-arrow-wrapper{padding-left:.5rem}.widget-body{display:flex;gap:2.5rem;align-items:flex-start}.left-section{flex:2;display:flex;flex-direction:column;gap:1rem}.chart-section{display:flex;gap:1.5rem;align-items:center}.chart-wrapper{width:220px;min-height:220px;flex-shrink:0}.chart-wrapper apx-chart{width:220px!important;height:220px!important}.legend{display:flex;flex-direction:column;gap:.75rem}.legend-item{display:flex;align-items:center;gap:.5rem;font-family:Inter,Inter,sans-serif;font-size:14px}.legend-marker{width:14px;height:14px;border-radius:50%;display:inline-flex}.legend-label{color:#4b5563}.legend-value{margin-left:auto;font-weight:600;color:#111827}.divider{width:1px;min-height:200px;align-self:stretch;background:#e5e7eb}.table-section{flex:3;display:flex;flex-direction:column;gap:.75rem;font-family:Inter,Inter,sans-serif}.lic-table-header,.table-row{display:flex;justify-content:space-between;font-size:14px}.lic-table-header{font-weight:600;color:#6b7280;padding-bottom:.5rem;border-bottom:1px solid #e5e7eb}.table-row{color:#111827}.total-row{font-weight:700;margin-top:.5rem;padding-top:.5rem;border-top:1px solid #e5e7eb}.state-message{font-family:Inter,Inter,sans-serif;font-size:14px;color:#6b7280}.state-message.error{color:#b91c1c}@media (max-width: 960px){.widget-body{flex-direction:column}.divider{display:none}.chart-section{justify-content:space-between}}:host ::ng-deep .apexcharts-legend{display:none!important}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type:
|
|
383
|
+
LicencesRequirementComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: LicencesRequirementComponent, selector: "cc-licenses-requirement", inputs: { customerId: "customerId", host: "host", token: "token" }, usesOnChanges: true, ngImport: i0, template: "<tk-card title=\"Licenses requirement\">\n <ng-container ngProjectAs=\"card-content\">\n <div\n class=\"widget-body\"\n *ngIf=\"!loading && !error && viewModel; else widgetState\"\n >\n <div class=\"left-section\">\n <mat-select\n [(value)]=\"selectedSummaryIndex\"\n (selectionChange)=\"onFilterChange($event.value)\"\n class=\"filter-select\"\n >\n <mat-option *ngFor=\"let option of filters; index as i\" [value]=\"i\">\n {{ option }}\n </mat-option>\n </mat-select>\n\n <div class=\"chart-section\">\n <div class=\"chart-wrapper\">\n <apx-chart\n [series]=\"viewModel.chartOptions.series\"\n [chart]=\"viewModel.chartOptions.chart\"\n [labels]=\"viewModel.chartOptions.labels\"\n [colors]=\"viewModel.chartOptions.colors\"\n [legend]=\"viewModel.chartOptions.legend\"\n [plotOptions]=\"viewModel.chartOptions.plotOptions\"\n [dataLabels]=\"viewModel.chartOptions.dataLabels\"\n [tooltip]=\"viewModel.chartOptions.tooltip\"\n >\n </apx-chart>\n </div>\n <div class=\"legend\">\n <div class=\"legend-item\" *ngFor=\"let item of viewModel.legendItems\">\n <span\n class=\"legend-marker\"\n [style.backgroundColor]=\"item.color\"\n ></span>\n <span class=\"legend-label\">{{ item.label }}</span>\n <span class=\"legend-value\">{{ item.count | number }}</span>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"divider\" aria-hidden=\"true\"></div>\n\n <div class=\"table-section\">\n <div class=\"lic-table-header\">\n <span>License</span>\n <span>Required</span>\n </div>\n <div class=\"table-row\" *ngFor=\"let row of viewModel.rows\">\n <span>{{ row.label }}</span>\n <span>{{ row.value | number }}</span>\n </div>\n <div class=\"table-row total-row\">\n <span>Total</span>\n <span>{{ viewModel.totalRequired | number }}</span>\n </div>\n </div>\n </div>\n\n <ng-template #widgetState>\n <div class=\"state-message\" *ngIf=\"loading\">\n Loading license requirements\u2026\n </div>\n <div class=\"state-message error\" *ngIf=\"!loading && error\">\n Unable to load license requirements.\n </div>\n <div class=\"state-message\" *ngIf=\"!loading && !error && !viewModel\">\n No license data available.\n </div>\n </ng-template>\n </ng-container>\n</tk-card>\n", styles: ["@import\"https://fonts.googleapis.com/css?family=Poppins:400,100,200,300,500,600,800,700,900\";@import\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400&display=swap\";:host{display:block;width:100%;box-sizing:border-box}.filter-select{font-family:Inter,Inter,sans-serif;font-size:14px;min-width:140px;border:1px solid #e5e7eb;border-radius:.5rem;padding:.5rem .75rem;background-color:#fff}.filter-select:hover{border-color:#d1d5db}:host ::ng-deep .filter-select.mat-mdc-select{border:1px solid #e5e7eb;border-radius:.5rem;background-color:#fff}:host ::ng-deep .filter-select.mat-mdc-select:hover{border-color:#d1d5db}:host ::ng-deep .filter-select.mat-mdc-select.mat-focused{border-color:#16a693}:host ::ng-deep .filter-select .mat-mdc-select-trigger{font-size:14px;padding:.5rem .75rem}:host ::ng-deep .filter-select .mat-mdc-select-value{color:#111827}:host ::ng-deep .filter-select .mat-mdc-select-arrow-wrapper{padding-left:.5rem}.widget-body{display:flex;gap:2.5rem;align-items:flex-start}.left-section{flex:2;display:flex;flex-direction:column;gap:1rem}.chart-section{display:flex;gap:1.5rem;align-items:center}.chart-wrapper{width:220px;min-height:220px;flex-shrink:0}.chart-wrapper apx-chart{width:220px!important;height:220px!important}.legend{display:flex;flex-direction:column;gap:.75rem}.legend-item{display:flex;align-items:center;gap:.5rem;font-family:Inter,Inter,sans-serif;font-size:14px}.legend-marker{width:14px;height:14px;border-radius:50%;display:inline-flex}.legend-label{color:#4b5563}.legend-value{margin-left:auto;font-weight:600;color:#111827}.divider{width:1px;min-height:200px;align-self:stretch;background:#e5e7eb}.table-section{flex:3;display:flex;flex-direction:column;gap:.75rem;font-family:Inter,Inter,sans-serif}.lic-table-header,.table-row{display:flex;justify-content:space-between;font-size:14px}.lic-table-header{font-weight:600;color:#6b7280;padding-bottom:.5rem;border-bottom:1px solid #e5e7eb}.table-row{color:#111827}.total-row{font-weight:700;margin-top:.5rem;padding-top:.5rem;border-top:1px solid #e5e7eb}.state-message{font-family:Inter,Inter,sans-serif;font-size:14px;color:#6b7280}.state-message.error{color:#b91c1c}@media (max-width: 960px){.widget-body{flex-direction:column}.divider{display:none}.chart-section{justify-content:space-between}}:host ::ng-deep .apexcharts-legend{display:none!important}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["disabled", "disableRipple", "tabIndex"], exportAs: ["matSelect"] }, { kind: "component", type: i3.MatOption, selector: "mat-option", exportAs: ["matOption"] }, { kind: "component", type: CardComponent, selector: "tk-card", inputs: ["title", "dense"] }, { kind: "component", type: i5.ChartComponent, selector: "apx-chart", inputs: ["chart", "annotations", "colors", "dataLabels", "series", "stroke", "labels", "legend", "markers", "noData", "fill", "tooltip", "plotOptions", "responsive", "xaxis", "yaxis", "forecastDataPoints", "grid", "states", "title", "subtitle", "theme", "autoUpdateSeries"] }, { kind: "pipe", type: i1$1.DecimalPipe, name: "number" }] });
|
|
341
384
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: LicencesRequirementComponent, decorators: [{
|
|
342
385
|
type: Component,
|
|
343
386
|
args: [{ selector: 'cc-licenses-requirement', template: "<tk-card title=\"Licenses requirement\">\n <ng-container ngProjectAs=\"card-content\">\n <div\n class=\"widget-body\"\n *ngIf=\"!loading && !error && viewModel; else widgetState\"\n >\n <div class=\"left-section\">\n <mat-select\n [(value)]=\"selectedSummaryIndex\"\n (selectionChange)=\"onFilterChange($event.value)\"\n class=\"filter-select\"\n >\n <mat-option *ngFor=\"let option of filters; index as i\" [value]=\"i\">\n {{ option }}\n </mat-option>\n </mat-select>\n\n <div class=\"chart-section\">\n <div class=\"chart-wrapper\">\n <apx-chart\n [series]=\"viewModel.chartOptions.series\"\n [chart]=\"viewModel.chartOptions.chart\"\n [labels]=\"viewModel.chartOptions.labels\"\n [colors]=\"viewModel.chartOptions.colors\"\n [legend]=\"viewModel.chartOptions.legend\"\n [plotOptions]=\"viewModel.chartOptions.plotOptions\"\n [dataLabels]=\"viewModel.chartOptions.dataLabels\"\n [tooltip]=\"viewModel.chartOptions.tooltip\"\n >\n </apx-chart>\n </div>\n <div class=\"legend\">\n <div class=\"legend-item\" *ngFor=\"let item of viewModel.legendItems\">\n <span\n class=\"legend-marker\"\n [style.backgroundColor]=\"item.color\"\n ></span>\n <span class=\"legend-label\">{{ item.label }}</span>\n <span class=\"legend-value\">{{ item.count | number }}</span>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"divider\" aria-hidden=\"true\"></div>\n\n <div class=\"table-section\">\n <div class=\"lic-table-header\">\n <span>License</span>\n <span>Required</span>\n </div>\n <div class=\"table-row\" *ngFor=\"let row of viewModel.rows\">\n <span>{{ row.label }}</span>\n <span>{{ row.value | number }}</span>\n </div>\n <div class=\"table-row total-row\">\n <span>Total</span>\n <span>{{ viewModel.totalRequired | number }}</span>\n </div>\n </div>\n </div>\n\n <ng-template #widgetState>\n <div class=\"state-message\" *ngIf=\"loading\">\n Loading license requirements\u2026\n </div>\n <div class=\"state-message error\" *ngIf=\"!loading && error\">\n Unable to load license requirements.\n </div>\n <div class=\"state-message\" *ngIf=\"!loading && !error && !viewModel\">\n No license data available.\n </div>\n </ng-template>\n </ng-container>\n</tk-card>\n", styles: ["@import\"https://fonts.googleapis.com/css?family=Poppins:400,100,200,300,500,600,800,700,900\";@import\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400&display=swap\";:host{display:block;width:100%;box-sizing:border-box}.filter-select{font-family:Inter,Inter,sans-serif;font-size:14px;min-width:140px;border:1px solid #e5e7eb;border-radius:.5rem;padding:.5rem .75rem;background-color:#fff}.filter-select:hover{border-color:#d1d5db}:host ::ng-deep .filter-select.mat-mdc-select{border:1px solid #e5e7eb;border-radius:.5rem;background-color:#fff}:host ::ng-deep .filter-select.mat-mdc-select:hover{border-color:#d1d5db}:host ::ng-deep .filter-select.mat-mdc-select.mat-focused{border-color:#16a693}:host ::ng-deep .filter-select .mat-mdc-select-trigger{font-size:14px;padding:.5rem .75rem}:host ::ng-deep .filter-select .mat-mdc-select-value{color:#111827}:host ::ng-deep .filter-select .mat-mdc-select-arrow-wrapper{padding-left:.5rem}.widget-body{display:flex;gap:2.5rem;align-items:flex-start}.left-section{flex:2;display:flex;flex-direction:column;gap:1rem}.chart-section{display:flex;gap:1.5rem;align-items:center}.chart-wrapper{width:220px;min-height:220px;flex-shrink:0}.chart-wrapper apx-chart{width:220px!important;height:220px!important}.legend{display:flex;flex-direction:column;gap:.75rem}.legend-item{display:flex;align-items:center;gap:.5rem;font-family:Inter,Inter,sans-serif;font-size:14px}.legend-marker{width:14px;height:14px;border-radius:50%;display:inline-flex}.legend-label{color:#4b5563}.legend-value{margin-left:auto;font-weight:600;color:#111827}.divider{width:1px;min-height:200px;align-self:stretch;background:#e5e7eb}.table-section{flex:3;display:flex;flex-direction:column;gap:.75rem;font-family:Inter,Inter,sans-serif}.lic-table-header,.table-row{display:flex;justify-content:space-between;font-size:14px}.lic-table-header{font-weight:600;color:#6b7280;padding-bottom:.5rem;border-bottom:1px solid #e5e7eb}.table-row{color:#111827}.total-row{font-weight:700;margin-top:.5rem;padding-top:.5rem;border-top:1px solid #e5e7eb}.state-message{font-family:Inter,Inter,sans-serif;font-size:14px;color:#6b7280}.state-message.error{color:#b91c1c}@media (max-width: 960px){.widget-body{flex-direction:column}.divider{display:none}.chart-section{justify-content:space-between}}:host ::ng-deep .apexcharts-legend{display:none!important}\n"] }]
|
|
@@ -381,7 +424,7 @@ class SummaryGridComponent {
|
|
|
381
424
|
}
|
|
382
425
|
}
|
|
383
426
|
SummaryGridComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SummaryGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
384
|
-
SummaryGridComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: SummaryGridComponent, selector: "cc-summary-grid", inputs: { customerId: "customerId", host: "host", token: "token" }, ngImport: i0, template: "<ng-container *ngIf=\"data$ | async as data\">\r\n <div *ngFor=\"let stats of data\">\r\n <tk-card [title]=\"stats.uccxIp\">\r\n <ng-container ngProjectAs=\"card-content\">\r\n <div class=\"grid-wrapper\">\r\n <div class=\"grid-item\" *ngFor=\"let item of stats.counts\">\r\n <div class=\"item-top\">\r\n <span class=\"item-icon\">\r\n <img width=\"24\" height=\"24\" src=\"assets/icons/{{ item.name }}.svg\" alt=\"{{ item.name }} Icon\">\r\n </span>\r\n <span class=\"item-count\">{{ item.value }}</span>\r\n </div>\r\n <span class=\"item-label\">{{ ENTITY_CONFIG[item.name].label }}</span>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </tk-card>\r\n </div>\r\n</ng-container>", styles: [".grid-wrapper{display:grid;grid-template-columns:repeat(6,1fr);gap:2rem;row-gap:2rem}.grid-item{display:flex;flex-direction:column;gap:.5rem}.item-top{display:flex;align-items:center;gap:inherit}.item-icon{display:flex;align-items:center;background-color:#f2f2f2;padding:.75rem;border-radius:.5rem}.item-count{font-size:30px}.item-label{font-size:14px;margin-left:.15rem}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: CardComponent, selector: "tk-card", inputs: ["title"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }] });
|
|
427
|
+
SummaryGridComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: SummaryGridComponent, selector: "cc-summary-grid", inputs: { customerId: "customerId", host: "host", token: "token" }, ngImport: i0, template: "<ng-container *ngIf=\"data$ | async as data\">\r\n <div *ngFor=\"let stats of data\">\r\n <tk-card [title]=\"stats.uccxIp\">\r\n <ng-container ngProjectAs=\"card-content\">\r\n <div class=\"grid-wrapper\">\r\n <div class=\"grid-item\" *ngFor=\"let item of stats.counts\">\r\n <div class=\"item-top\">\r\n <span class=\"item-icon\">\r\n <img width=\"24\" height=\"24\" src=\"assets/icons/{{ item.name }}.svg\" alt=\"{{ item.name }} Icon\">\r\n </span>\r\n <span class=\"item-count\">{{ item.value }}</span>\r\n </div>\r\n <span class=\"item-label\">{{ ENTITY_CONFIG[item.name].label }}</span>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </tk-card>\r\n </div>\r\n</ng-container>", styles: [".grid-wrapper{display:grid;grid-template-columns:repeat(6,1fr);gap:2rem;row-gap:2rem}.grid-item{display:flex;flex-direction:column;gap:.5rem}.item-top{display:flex;align-items:center;gap:inherit}.item-icon{display:flex;align-items:center;background-color:#f2f2f2;padding:.75rem;border-radius:.5rem}.item-count{font-size:30px}.item-label{font-size:14px;margin-left:.15rem}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: CardComponent, selector: "tk-card", inputs: ["title", "dense"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }] });
|
|
385
428
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SummaryGridComponent, decorators: [{
|
|
386
429
|
type: Component,
|
|
387
430
|
args: [{ selector: 'cc-summary-grid', template: "<ng-container *ngIf=\"data$ | async as data\">\r\n <div *ngFor=\"let stats of data\">\r\n <tk-card [title]=\"stats.uccxIp\">\r\n <ng-container ngProjectAs=\"card-content\">\r\n <div class=\"grid-wrapper\">\r\n <div class=\"grid-item\" *ngFor=\"let item of stats.counts\">\r\n <div class=\"item-top\">\r\n <span class=\"item-icon\">\r\n <img width=\"24\" height=\"24\" src=\"assets/icons/{{ item.name }}.svg\" alt=\"{{ item.name }} Icon\">\r\n </span>\r\n <span class=\"item-count\">{{ item.value }}</span>\r\n </div>\r\n <span class=\"item-label\">{{ ENTITY_CONFIG[item.name].label }}</span>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </tk-card>\r\n </div>\r\n</ng-container>", styles: [".grid-wrapper{display:grid;grid-template-columns:repeat(6,1fr);gap:2rem;row-gap:2rem}.grid-item{display:flex;flex-direction:column;gap:.5rem}.item-top{display:flex;align-items:center;gap:inherit}.item-icon{display:flex;align-items:center;background-color:#f2f2f2;padding:.75rem;border-radius:.5rem}.item-count{font-size:30px}.item-label{font-size:14px;margin-left:.15rem}\n"] }]
|
|
@@ -393,12 +436,116 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
|
|
|
393
436
|
type: Input
|
|
394
437
|
}] } });
|
|
395
438
|
|
|
439
|
+
class MaterialModule {
|
|
440
|
+
}
|
|
441
|
+
MaterialModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MaterialModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
442
|
+
MaterialModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: MaterialModule, imports: [MatProgressBarModule,
|
|
443
|
+
MatTooltipModule,
|
|
444
|
+
MatTableModule,
|
|
445
|
+
MatCheckboxModule,
|
|
446
|
+
MatButtonModule,
|
|
447
|
+
MatIconModule,
|
|
448
|
+
MatFormFieldModule,
|
|
449
|
+
MatInputModule,
|
|
450
|
+
MatSelectModule], exports: [MatProgressBarModule,
|
|
451
|
+
MatTooltipModule,
|
|
452
|
+
MatTableModule,
|
|
453
|
+
MatCheckboxModule,
|
|
454
|
+
MatButtonModule,
|
|
455
|
+
MatIconModule,
|
|
456
|
+
MatFormFieldModule,
|
|
457
|
+
MatInputModule,
|
|
458
|
+
MatSelectModule,
|
|
459
|
+
MatProgressSpinnerModule] });
|
|
460
|
+
MaterialModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MaterialModule, imports: [MatProgressBarModule,
|
|
461
|
+
MatTooltipModule,
|
|
462
|
+
MatTableModule,
|
|
463
|
+
MatCheckboxModule,
|
|
464
|
+
MatButtonModule,
|
|
465
|
+
MatIconModule,
|
|
466
|
+
MatFormFieldModule,
|
|
467
|
+
MatInputModule,
|
|
468
|
+
MatSelectModule, MatProgressBarModule,
|
|
469
|
+
MatTooltipModule,
|
|
470
|
+
MatTableModule,
|
|
471
|
+
MatCheckboxModule,
|
|
472
|
+
MatButtonModule,
|
|
473
|
+
MatIconModule,
|
|
474
|
+
MatFormFieldModule,
|
|
475
|
+
MatInputModule,
|
|
476
|
+
MatSelectModule,
|
|
477
|
+
MatProgressSpinnerModule] });
|
|
478
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MaterialModule, decorators: [{
|
|
479
|
+
type: NgModule,
|
|
480
|
+
args: [{
|
|
481
|
+
imports: [
|
|
482
|
+
MatProgressBarModule,
|
|
483
|
+
MatTooltipModule,
|
|
484
|
+
MatTableModule,
|
|
485
|
+
MatCheckboxModule,
|
|
486
|
+
MatButtonModule,
|
|
487
|
+
MatIconModule,
|
|
488
|
+
MatFormFieldModule,
|
|
489
|
+
MatInputModule,
|
|
490
|
+
MatSelectModule
|
|
491
|
+
],
|
|
492
|
+
exports: [
|
|
493
|
+
MatProgressBarModule,
|
|
494
|
+
MatTooltipModule,
|
|
495
|
+
MatTableModule,
|
|
496
|
+
MatCheckboxModule,
|
|
497
|
+
MatButtonModule,
|
|
498
|
+
MatIconModule,
|
|
499
|
+
MatFormFieldModule,
|
|
500
|
+
MatInputModule,
|
|
501
|
+
MatSelectModule,
|
|
502
|
+
MatProgressSpinnerModule
|
|
503
|
+
]
|
|
504
|
+
}]
|
|
505
|
+
}] });
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Presentational building blocks shared by the contact-center widgets.
|
|
509
|
+
* Widget modules import this to pick up the standard card chrome plus the
|
|
510
|
+
* Material/Apex modules they all need.
|
|
511
|
+
*/
|
|
512
|
+
class SharedModule {
|
|
513
|
+
}
|
|
514
|
+
SharedModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SharedModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
515
|
+
SharedModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: SharedModule, declarations: [CardComponent], imports: [CommonModule,
|
|
516
|
+
MaterialModule,
|
|
517
|
+
NgApexchartsModule,
|
|
518
|
+
HttpClientModule], exports: [MaterialModule,
|
|
519
|
+
CardComponent] });
|
|
520
|
+
SharedModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SharedModule, imports: [CommonModule,
|
|
521
|
+
MaterialModule,
|
|
522
|
+
NgApexchartsModule,
|
|
523
|
+
HttpClientModule, MaterialModule] });
|
|
524
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SharedModule, decorators: [{
|
|
525
|
+
type: NgModule,
|
|
526
|
+
args: [{
|
|
527
|
+
declarations: [
|
|
528
|
+
CardComponent
|
|
529
|
+
],
|
|
530
|
+
imports: [
|
|
531
|
+
CommonModule,
|
|
532
|
+
MaterialModule,
|
|
533
|
+
NgApexchartsModule,
|
|
534
|
+
HttpClientModule
|
|
535
|
+
],
|
|
536
|
+
exports: [
|
|
537
|
+
MaterialModule,
|
|
538
|
+
CardComponent
|
|
539
|
+
]
|
|
540
|
+
}]
|
|
541
|
+
}] });
|
|
542
|
+
|
|
396
543
|
class CcReadinessModule {
|
|
397
544
|
}
|
|
398
545
|
CcReadinessModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CcReadinessModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
399
546
|
CcReadinessModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: CcReadinessModule, declarations: [LicencesRequirementComponent,
|
|
400
|
-
SummaryGridComponent,
|
|
401
|
-
|
|
547
|
+
SummaryGridComponent], imports: [CommonModule,
|
|
548
|
+
SharedModule,
|
|
402
549
|
HttpClientModule,
|
|
403
550
|
NgApexchartsModule,
|
|
404
551
|
MatTableModule,
|
|
@@ -411,6 +558,7 @@ CcReadinessModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", versi
|
|
|
411
558
|
MatSelectModule], exports: [LicencesRequirementComponent,
|
|
412
559
|
SummaryGridComponent] });
|
|
413
560
|
CcReadinessModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CcReadinessModule, imports: [CommonModule,
|
|
561
|
+
SharedModule,
|
|
414
562
|
HttpClientModule,
|
|
415
563
|
NgApexchartsModule,
|
|
416
564
|
MatTableModule,
|
|
@@ -426,11 +574,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
|
|
|
426
574
|
args: [{
|
|
427
575
|
declarations: [
|
|
428
576
|
LicencesRequirementComponent,
|
|
429
|
-
SummaryGridComponent
|
|
430
|
-
CardComponent
|
|
577
|
+
SummaryGridComponent
|
|
431
578
|
],
|
|
432
579
|
imports: [
|
|
433
580
|
CommonModule,
|
|
581
|
+
SharedModule,
|
|
434
582
|
HttpClientModule,
|
|
435
583
|
NgApexchartsModule,
|
|
436
584
|
MatTableModule,
|
|
@@ -1454,7 +1602,7 @@ class NodeDetailsSidebarComponent {
|
|
|
1454
1602
|
}
|
|
1455
1603
|
}
|
|
1456
1604
|
NodeDetailsSidebarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: NodeDetailsSidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1457
|
-
NodeDetailsSidebarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: NodeDetailsSidebarComponent, selector: "node-details-sidebar", inputs: { isOpen: "isOpen", nodeData: "nodeData" }, outputs: { close: "close", activityTypeChange: "activityTypeChange", descriptionChange: "descriptionChange" }, usesOnChanges: true, ngImport: i0, template: "<!-- <div class=\"sidebar-overlay\" *ngIf=\"isOpen\" (click)=\"onClose()\"></div> -->\r\n\r\n<div class=\"node-details-sidebar\" [class.open]=\"isOpen\">\r\n <div class=\"sidebar-header\">\r\n <h3>Node Details</h3>\r\n <button class=\"close-btn\" (click)=\"onClose()\">\r\n <span>×</span>\r\n </button>\r\n </div>\r\n\r\n <div class=\"sidebar-content\" *ngIf=\"nodeData\">\r\n <!-- Node Preview -->\r\n <div class=\"node-preview\">\r\n <div class=\"node-preview-card\" [ngClass]=\"getNodeColorClass()\">\r\n <div class=\"node-icon\">\r\n <img [src]=\"getNodeIcon()\" alt=\"Node Icon\" />\r\n </div>\r\n <div class=\"node-info\">\r\n <div class=\"node-label\">{{ nodeData.name?.split(\"\\n\")?.[0] }}</div>\r\n <div class=\"node-type\">{{ transformedActivityName }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Activity Type Selector -->\r\n <div class=\"section\">\r\n <label class=\"section-label\">Activity Type</label>\r\n <div class=\"display-type-selector\">\r\n <select\r\n class=\"form-control\"\r\n title=\"Select activity type\"\r\n [value]=\"selectedActivityType?.value\"\r\n (change)=\"onActivityTypeSelectChange($event)\"\r\n >\r\n <option\r\n *ngFor=\"let type of activityDisplayTypes; trackBy: trackByValue\"\r\n [value]=\"type.value\"\r\n >\r\n {{ type.label }}\r\n </option>\r\n </select>\r\n </div>\r\n </div>\r\n\r\n <div class=\"section\">\r\n <label class=\"section-label\">Description</label>\r\n <div class=\"display-type-selector\">\r\n <textarea\r\n class=\"form-control\"\r\n aria-label=\"property-description\"\r\n (input)=\"onDescriptionChange($event)\"\r\n [value]=\"nodeData.suggestedProperties?.uccxDescription\"\r\n rows=\"3\"\r\n ></textarea>\r\n </div>\r\n </div>\r\n\r\n <!-- Node Information -->\r\n <div class=\"section\">\r\n <label class=\"section-label\">Information</label>\r\n <div class=\"info-grid\">\r\n <div class=\"info-item\">\r\n <span class=\"info-label\">UCCX Type:</span>\r\n <span class=\"info-value\" [title]=\"nodeData.uccxTypeName\">{{\r\n nodeData.uccxTypeName\r\n }}</span>\r\n </div>\r\n <div class=\"info-item\">\r\n <span class=\"info-label\">WxCC Activity Name:</span>\r\n <span class=\"info-value\" [title]=\"transformedActivityName\">{{\r\n transformedActivityName\r\n }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Review Status -->\r\n <div class=\"section\" *ngIf=\"nodeData.review\">\r\n <label class=\"section-label\">Review Status</label>\r\n <div class=\"review-status\">\r\n <div class=\"review-badge\" [style.background-color]=\"getReviewColor()\">\r\n {{ getReviewLabel() }}\r\n </div>\r\n <div\r\n class=\"review-reasons\"\r\n *ngIf=\"nodeData.review.reasons && nodeData.review.reasons.length > 0\"\r\n >\r\n <div class=\"reason\" *ngFor=\"let reason of nodeData.review.reasons\">\r\n <span class=\"reason-icon\">\u26A0</span>\r\n <span>{{ reason }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Suggested Properties -->\r\n <div class=\"section\" *ngIf=\"nodeData.suggestedProperties\">\r\n <label class=\"section-label\">Migration Information</label>\r\n <div class=\"migration-info\">\r\n <div\r\n class=\"info-card\"\r\n *ngIf=\"nodeData.suggestedProperties.closestWxccActivity\"\r\n >\r\n <div class=\"card-label\">Closest WXCC Activity</div>\r\n <div class=\"card-value\">\r\n {{ nodeData.suggestedProperties.closestWxccActivity }}\r\n </div>\r\n </div>\r\n <div\r\n class=\"info-card\"\r\n *ngIf=\"nodeData.suggestedProperties.mappingNotes\"\r\n >\r\n <div class=\"card-label\">Mapping Notes</div>\r\n <div class=\"card-value\">\r\n {{ nodeData.suggestedProperties.mappingNotes }}\r\n </div>\r\n </div>\r\n <div\r\n class=\"info-card\"\r\n *ngIf=\"nodeData.suggestedProperties.migrationInstructions\"\r\n >\r\n <div class=\"card-label\">Migration Instructions</div>\r\n <div class=\"card-value instructions\">\r\n {{ nodeData.suggestedProperties.migrationInstructions }}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: ["@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.node-details-sidebar{position:fixed;top:0;right:-420px;width:420px;height:100%;background:#ffffff;box-shadow:-2px 0 8px #00000026;z-index:1000;transition:right .4s cubic-bezier(.4,0,.2,1);display:flex;flex-direction:column;overflow:hidden}.node-details-sidebar.open{right:0}.node-details-sidebar .sidebar-header{display:flex;align-items:center;justify-content:space-between;padding:1.5rem;border-bottom:1px solid #e1e4e8;background:#f8f9fa}.node-details-sidebar .sidebar-header h3{margin:0;font-size:1.25rem;font-weight:600;color:#24292e}.node-details-sidebar .sidebar-header .close-btn{background:none;border:none;font-size:2rem;line-height:1;cursor:pointer;color:#586069;padding:0;width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all .2s}.node-details-sidebar .sidebar-header .close-btn:hover{background:rgba(0,0,0,.05);color:#24292e}.node-details-sidebar .sidebar-content{flex:1;overflow-y:auto;padding:1.5rem}.node-details-sidebar .sidebar-content .node-preview{margin-bottom:1.5rem}.node-details-sidebar .sidebar-content .node-preview .node-preview-card{display:flex;align-items:center;gap:1rem;padding:1rem;border-radius:8px}.node-details-sidebar .sidebar-content .node-preview .node-preview-card .node-icon{display:flex;align-items:center;justify-content:center;width:48px;height:48px;border-radius:8px;flex-shrink:0}.node-details-sidebar .sidebar-content .node-preview .node-preview-card .node-icon img{width:24px;height:24px}.node-details-sidebar .sidebar-content .node-preview .node-preview-card .node-info{flex:1;min-width:0}.node-details-sidebar .sidebar-content .node-preview .node-preview-card .node-info .node-label{font-size:1rem;font-weight:600;margin-bottom:.25rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.node-details-sidebar .sidebar-content .node-preview .node-preview-card .node-info .node-type{font-size:.875rem;color:#586069}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-start{background-color:#4caf500d}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-start .node-icon{background-color:#4caf501a}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-start .node-label{color:#4caf50}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-end{background-color:#f443360d}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-end .node-icon{background-color:#f443361a}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-end .node-label{color:#f44336}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-gateway{background-color:#ff98000d}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-gateway .node-icon{background-color:#ff98001a}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-gateway .node-label{color:#ff9800}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-action{background-color:#9c27b00d}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-action .node-icon{background-color:#9c27b01a}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-action .node-label{color:#9c27b0}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-default{background-color:#607d8b0d}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-default .node-icon{background-color:#607d8b1a}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-default .node-label{color:#607d8b}.node-details-sidebar .sidebar-content .section{margin-bottom:1.5rem}.node-details-sidebar .sidebar-content .section .section-label{display:block;font-size:.65rem;font-weight:600;color:#24292e;margin-bottom:.75rem;text-transform:uppercase;letter-spacing:.5px}.node-details-sidebar .sidebar-content .section .display-type-selector .form-control{width:100%;padding:.625rem .875rem;font-size:.875rem;border:1px solid #d1d5da;border-radius:6px;background:white;transition:border-color .2s;box-sizing:border-box;resize:vertical}.node-details-sidebar .sidebar-content .section .display-type-selector .form-control:focus{outline:none;border-color:#0366d6;box-shadow:0 0 0 3px #0366d61a}.node-details-sidebar .sidebar-content .section .info-grid{display:flex;flex-direction:column;gap:.75rem}.node-details-sidebar .sidebar-content .section .info-grid .info-item{display:flex;justify-content:space-between;align-items:flex-start;padding:.75rem;background:#f6f8fa;border-radius:6px}.node-details-sidebar .sidebar-content .section .info-grid .info-item .info-label{font-size:.8125rem;color:#586069;font-weight:500}.node-details-sidebar .sidebar-content .section .info-grid .info-item .info-value{font-size:.8125rem;color:#24292e;font-weight:600;text-align:right;max-width:60%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.node-details-sidebar .sidebar-content .section .review-status .review-badge{display:inline-block;padding:.5rem 1rem;border-radius:20px;color:#fff;font-size:.8125rem;font-weight:600;margin-bottom:.75rem}.node-details-sidebar .sidebar-content .section .review-status .review-reasons .reason{display:flex;align-items:flex-start;gap:.5rem;padding:.75rem;background:#fff3cd;border-radius:4px;margin-bottom:.5rem;font-size:.8125rem;color:#856404}.node-details-sidebar .sidebar-content .section .review-status .review-reasons .reason .reason-icon{flex-shrink:0}.node-details-sidebar .sidebar-content .section .migration-info .info-card{padding:1rem;background:#f6f8fa;border-radius:6px;margin-bottom:.75rem}.node-details-sidebar .sidebar-content .section .migration-info .info-card:last-child{margin-bottom:0}.node-details-sidebar .sidebar-content .section .migration-info .info-card .card-label{font-size:.75rem;font-weight:600;color:#586069;text-transform:uppercase;letter-spacing:.5px;margin-bottom:.5rem}.node-details-sidebar .sidebar-content .section .migration-info .info-card .card-value{font-size:.875rem;color:#24292e;line-height:1.5}.node-details-sidebar .sidebar-content .section .migration-info .info-card .card-value.instructions{white-space:pre-wrap}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1605
|
+
NodeDetailsSidebarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: NodeDetailsSidebarComponent, selector: "node-details-sidebar", inputs: { isOpen: "isOpen", nodeData: "nodeData" }, outputs: { close: "close", activityTypeChange: "activityTypeChange", descriptionChange: "descriptionChange" }, usesOnChanges: true, ngImport: i0, template: "<!-- <div class=\"sidebar-overlay\" *ngIf=\"isOpen\" (click)=\"onClose()\"></div> -->\r\n\r\n<div class=\"node-details-sidebar\" [class.open]=\"isOpen\">\r\n <div class=\"sidebar-header\">\r\n <h3>Node Details</h3>\r\n <button class=\"close-btn\" (click)=\"onClose()\">\r\n <span>×</span>\r\n </button>\r\n </div>\r\n\r\n <div class=\"sidebar-content\" *ngIf=\"nodeData\">\r\n <!-- Node Preview -->\r\n <div class=\"node-preview\">\r\n <div class=\"node-preview-card\" [ngClass]=\"getNodeColorClass()\">\r\n <div class=\"node-icon\">\r\n <img [src]=\"getNodeIcon()\" alt=\"Node Icon\" />\r\n </div>\r\n <div class=\"node-info\">\r\n <div class=\"node-label\">{{ nodeData.name?.split(\"\\n\")?.[0] }}</div>\r\n <div class=\"node-type\">{{ transformedActivityName }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Activity Type Selector -->\r\n <div class=\"section\">\r\n <label class=\"section-label\">Activity Type</label>\r\n <div class=\"display-type-selector\">\r\n <select\r\n class=\"form-control\"\r\n title=\"Select activity type\"\r\n [value]=\"selectedActivityType?.value\"\r\n (change)=\"onActivityTypeSelectChange($event)\"\r\n >\r\n <option\r\n *ngFor=\"let type of activityDisplayTypes; trackBy: trackByValue\"\r\n [value]=\"type.value\"\r\n >\r\n {{ type.label }}\r\n </option>\r\n </select>\r\n </div>\r\n </div>\r\n\r\n <div class=\"section\">\r\n <label class=\"section-label\">Description</label>\r\n <div class=\"display-type-selector\">\r\n <textarea\r\n class=\"form-control\"\r\n aria-label=\"property-description\"\r\n (input)=\"onDescriptionChange($event)\"\r\n [value]=\"nodeData.suggestedProperties?.uccxDescription\"\r\n rows=\"3\"\r\n ></textarea>\r\n </div>\r\n </div>\r\n\r\n <!-- Node Information -->\r\n <div class=\"section\">\r\n <label class=\"section-label\">Information</label>\r\n <div class=\"info-grid\">\r\n <div class=\"info-item\">\r\n <span class=\"info-label\">UCCX Type:</span>\r\n <span class=\"info-value\" [title]=\"nodeData.uccxTypeName\">{{\r\n nodeData.uccxTypeName\r\n }}</span>\r\n </div>\r\n <div class=\"info-item\">\r\n <span class=\"info-label\">WxCC Activity Name:</span>\r\n <span class=\"info-value\" [title]=\"transformedActivityName\">{{\r\n transformedActivityName\r\n }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Review Status -->\r\n <div class=\"section\" *ngIf=\"nodeData.review\">\r\n <label class=\"section-label\">Review Status</label>\r\n <div class=\"review-status\">\r\n <div class=\"review-badge\" [style.background-color]=\"getReviewColor()\">\r\n {{ getReviewLabel() }}\r\n </div>\r\n <div\r\n class=\"review-reasons\"\r\n *ngIf=\"nodeData.review.reasons && nodeData.review.reasons.length > 0\"\r\n >\r\n <div class=\"reason\" *ngFor=\"let reason of nodeData.review.reasons\">\r\n <span class=\"reason-icon\">\u26A0</span>\r\n <span>{{ reason }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Suggested Properties -->\r\n <div class=\"section\" *ngIf=\"nodeData.suggestedProperties\">\r\n <label class=\"section-label\">Migration Information</label>\r\n <div class=\"migration-info\">\r\n <div\r\n class=\"info-card\"\r\n *ngIf=\"nodeData.suggestedProperties.closestWxccActivity\"\r\n >\r\n <div class=\"card-label\">Closest WXCC Activity</div>\r\n <div class=\"card-value\">\r\n {{ nodeData.suggestedProperties.closestWxccActivity }}\r\n </div>\r\n </div>\r\n <div\r\n class=\"info-card\"\r\n *ngIf=\"nodeData.suggestedProperties.mappingNotes\"\r\n >\r\n <div class=\"card-label\">Mapping Notes</div>\r\n <div class=\"card-value\">\r\n {{ nodeData.suggestedProperties.mappingNotes }}\r\n </div>\r\n </div>\r\n <div\r\n class=\"info-card\"\r\n *ngIf=\"nodeData.suggestedProperties.migrationInstructions\"\r\n >\r\n <div class=\"card-label\">Migration Instructions</div>\r\n <div class=\"card-value instructions\">\r\n {{ nodeData.suggestedProperties.migrationInstructions }}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: ["@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.node-details-sidebar{position:fixed;top:0;right:-420px;width:420px;height:100%;background:#ffffff;box-shadow:-2px 0 8px #00000026;z-index:1000;transition:right .4s cubic-bezier(.4,0,.2,1);display:flex;flex-direction:column;overflow:hidden}.node-details-sidebar.open{right:0}.node-details-sidebar .sidebar-header{display:flex;align-items:center;justify-content:space-between;padding:1.5rem;border-bottom:1px solid #e1e4e8;background:#f8f9fa}.node-details-sidebar .sidebar-header h3{margin:0;font-size:1.25rem;font-weight:600;color:#24292e}.node-details-sidebar .sidebar-header .close-btn{background:none;border:none;font-size:2rem;line-height:1;cursor:pointer;color:#586069;padding:0;width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all .2s}.node-details-sidebar .sidebar-header .close-btn:hover{background:rgba(0,0,0,.05);color:#24292e}.node-details-sidebar .sidebar-content{flex:1;overflow-y:auto;padding:1.5rem}.node-details-sidebar .sidebar-content .node-preview{margin-bottom:1.5rem}.node-details-sidebar .sidebar-content .node-preview .node-preview-card{display:flex;align-items:center;gap:1rem;padding:1rem;border-radius:8px}.node-details-sidebar .sidebar-content .node-preview .node-preview-card .node-icon{display:flex;align-items:center;justify-content:center;width:48px;height:48px;border-radius:8px;flex-shrink:0}.node-details-sidebar .sidebar-content .node-preview .node-preview-card .node-icon img{width:24px;height:24px}.node-details-sidebar .sidebar-content .node-preview .node-preview-card .node-info{flex:1;min-width:0}.node-details-sidebar .sidebar-content .node-preview .node-preview-card .node-info .node-label{font-size:1rem;font-weight:600;margin-bottom:.25rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.node-details-sidebar .sidebar-content .node-preview .node-preview-card .node-info .node-type{font-size:.875rem;color:#586069}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-start{background-color:#4caf500d}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-start .node-icon{background-color:#4caf501a}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-start .node-label{color:#4caf50}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-end{background-color:#f443360d}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-end .node-icon{background-color:#f443361a}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-end .node-label{color:#f44336}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-gateway{background-color:#ff98000d}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-gateway .node-icon{background-color:#ff98001a}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-gateway .node-label{color:#ff9800}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-action{background-color:#9c27b00d}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-action .node-icon{background-color:#9c27b01a}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-action .node-label{color:#9c27b0}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-default{background-color:#607d8b0d}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-default .node-icon{background-color:#607d8b1a}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-default .node-label{color:#607d8b}.node-details-sidebar .sidebar-content .section{margin-bottom:1.5rem}.node-details-sidebar .sidebar-content .section .section-label{display:block;font-size:.65rem;font-weight:600;color:#24292e;margin-bottom:.75rem;text-transform:uppercase;letter-spacing:.5px}.node-details-sidebar .sidebar-content .section .display-type-selector .form-control{width:100%;padding:.625rem .875rem;font-size:.875rem;border:1px solid #d1d5da;border-radius:6px;background:white;transition:border-color .2s;box-sizing:border-box;resize:vertical}.node-details-sidebar .sidebar-content .section .display-type-selector .form-control:focus{outline:none;border-color:#0366d6;box-shadow:0 0 0 3px #0366d61a}.node-details-sidebar .sidebar-content .section .info-grid{display:flex;flex-direction:column;gap:.75rem}.node-details-sidebar .sidebar-content .section .info-grid .info-item{display:flex;justify-content:space-between;align-items:flex-start;padding:.75rem;background:#f6f8fa;border-radius:6px}.node-details-sidebar .sidebar-content .section .info-grid .info-item .info-label{font-size:.8125rem;color:#586069;font-weight:500}.node-details-sidebar .sidebar-content .section .info-grid .info-item .info-value{font-size:.8125rem;color:#24292e;font-weight:600;text-align:right;max-width:60%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.node-details-sidebar .sidebar-content .section .review-status .review-badge{display:inline-block;padding:.5rem 1rem;border-radius:20px;color:#fff;font-size:.8125rem;font-weight:600;margin-bottom:.75rem}.node-details-sidebar .sidebar-content .section .review-status .review-reasons .reason{display:flex;align-items:flex-start;gap:.5rem;padding:.75rem;background:#fff3cd;border-radius:4px;margin-bottom:.5rem;font-size:.8125rem;color:#856404}.node-details-sidebar .sidebar-content .section .review-status .review-reasons .reason .reason-icon{flex-shrink:0}.node-details-sidebar .sidebar-content .section .migration-info .info-card{padding:1rem;background:#f6f8fa;border-radius:6px;margin-bottom:.75rem}.node-details-sidebar .sidebar-content .section .migration-info .info-card:last-child{margin-bottom:0}.node-details-sidebar .sidebar-content .section .migration-info .info-card .card-label{font-size:.75rem;font-weight:600;color:#586069;text-transform:uppercase;letter-spacing:.5px;margin-bottom:.5rem}.node-details-sidebar .sidebar-content .section .migration-info .info-card .card-value{font-size:.875rem;color:#24292e;line-height:1.5}.node-details-sidebar .sidebar-content .section .migration-info .info-card .card-value.instructions{white-space:pre-wrap}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1458
1606
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: NodeDetailsSidebarComponent, decorators: [{
|
|
1459
1607
|
type: Component,
|
|
1460
1608
|
args: [{ selector: 'node-details-sidebar', changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- <div class=\"sidebar-overlay\" *ngIf=\"isOpen\" (click)=\"onClose()\"></div> -->\r\n\r\n<div class=\"node-details-sidebar\" [class.open]=\"isOpen\">\r\n <div class=\"sidebar-header\">\r\n <h3>Node Details</h3>\r\n <button class=\"close-btn\" (click)=\"onClose()\">\r\n <span>×</span>\r\n </button>\r\n </div>\r\n\r\n <div class=\"sidebar-content\" *ngIf=\"nodeData\">\r\n <!-- Node Preview -->\r\n <div class=\"node-preview\">\r\n <div class=\"node-preview-card\" [ngClass]=\"getNodeColorClass()\">\r\n <div class=\"node-icon\">\r\n <img [src]=\"getNodeIcon()\" alt=\"Node Icon\" />\r\n </div>\r\n <div class=\"node-info\">\r\n <div class=\"node-label\">{{ nodeData.name?.split(\"\\n\")?.[0] }}</div>\r\n <div class=\"node-type\">{{ transformedActivityName }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Activity Type Selector -->\r\n <div class=\"section\">\r\n <label class=\"section-label\">Activity Type</label>\r\n <div class=\"display-type-selector\">\r\n <select\r\n class=\"form-control\"\r\n title=\"Select activity type\"\r\n [value]=\"selectedActivityType?.value\"\r\n (change)=\"onActivityTypeSelectChange($event)\"\r\n >\r\n <option\r\n *ngFor=\"let type of activityDisplayTypes; trackBy: trackByValue\"\r\n [value]=\"type.value\"\r\n >\r\n {{ type.label }}\r\n </option>\r\n </select>\r\n </div>\r\n </div>\r\n\r\n <div class=\"section\">\r\n <label class=\"section-label\">Description</label>\r\n <div class=\"display-type-selector\">\r\n <textarea\r\n class=\"form-control\"\r\n aria-label=\"property-description\"\r\n (input)=\"onDescriptionChange($event)\"\r\n [value]=\"nodeData.suggestedProperties?.uccxDescription\"\r\n rows=\"3\"\r\n ></textarea>\r\n </div>\r\n </div>\r\n\r\n <!-- Node Information -->\r\n <div class=\"section\">\r\n <label class=\"section-label\">Information</label>\r\n <div class=\"info-grid\">\r\n <div class=\"info-item\">\r\n <span class=\"info-label\">UCCX Type:</span>\r\n <span class=\"info-value\" [title]=\"nodeData.uccxTypeName\">{{\r\n nodeData.uccxTypeName\r\n }}</span>\r\n </div>\r\n <div class=\"info-item\">\r\n <span class=\"info-label\">WxCC Activity Name:</span>\r\n <span class=\"info-value\" [title]=\"transformedActivityName\">{{\r\n transformedActivityName\r\n }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Review Status -->\r\n <div class=\"section\" *ngIf=\"nodeData.review\">\r\n <label class=\"section-label\">Review Status</label>\r\n <div class=\"review-status\">\r\n <div class=\"review-badge\" [style.background-color]=\"getReviewColor()\">\r\n {{ getReviewLabel() }}\r\n </div>\r\n <div\r\n class=\"review-reasons\"\r\n *ngIf=\"nodeData.review.reasons && nodeData.review.reasons.length > 0\"\r\n >\r\n <div class=\"reason\" *ngFor=\"let reason of nodeData.review.reasons\">\r\n <span class=\"reason-icon\">\u26A0</span>\r\n <span>{{ reason }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Suggested Properties -->\r\n <div class=\"section\" *ngIf=\"nodeData.suggestedProperties\">\r\n <label class=\"section-label\">Migration Information</label>\r\n <div class=\"migration-info\">\r\n <div\r\n class=\"info-card\"\r\n *ngIf=\"nodeData.suggestedProperties.closestWxccActivity\"\r\n >\r\n <div class=\"card-label\">Closest WXCC Activity</div>\r\n <div class=\"card-value\">\r\n {{ nodeData.suggestedProperties.closestWxccActivity }}\r\n </div>\r\n </div>\r\n <div\r\n class=\"info-card\"\r\n *ngIf=\"nodeData.suggestedProperties.mappingNotes\"\r\n >\r\n <div class=\"card-label\">Mapping Notes</div>\r\n <div class=\"card-value\">\r\n {{ nodeData.suggestedProperties.mappingNotes }}\r\n </div>\r\n </div>\r\n <div\r\n class=\"info-card\"\r\n *ngIf=\"nodeData.suggestedProperties.migrationInstructions\"\r\n >\r\n <div class=\"card-label\">Migration Instructions</div>\r\n <div class=\"card-value instructions\">\r\n {{ nodeData.suggestedProperties.migrationInstructions }}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n", styles: ["@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.node-details-sidebar{position:fixed;top:0;right:-420px;width:420px;height:100%;background:#ffffff;box-shadow:-2px 0 8px #00000026;z-index:1000;transition:right .4s cubic-bezier(.4,0,.2,1);display:flex;flex-direction:column;overflow:hidden}.node-details-sidebar.open{right:0}.node-details-sidebar .sidebar-header{display:flex;align-items:center;justify-content:space-between;padding:1.5rem;border-bottom:1px solid #e1e4e8;background:#f8f9fa}.node-details-sidebar .sidebar-header h3{margin:0;font-size:1.25rem;font-weight:600;color:#24292e}.node-details-sidebar .sidebar-header .close-btn{background:none;border:none;font-size:2rem;line-height:1;cursor:pointer;color:#586069;padding:0;width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all .2s}.node-details-sidebar .sidebar-header .close-btn:hover{background:rgba(0,0,0,.05);color:#24292e}.node-details-sidebar .sidebar-content{flex:1;overflow-y:auto;padding:1.5rem}.node-details-sidebar .sidebar-content .node-preview{margin-bottom:1.5rem}.node-details-sidebar .sidebar-content .node-preview .node-preview-card{display:flex;align-items:center;gap:1rem;padding:1rem;border-radius:8px}.node-details-sidebar .sidebar-content .node-preview .node-preview-card .node-icon{display:flex;align-items:center;justify-content:center;width:48px;height:48px;border-radius:8px;flex-shrink:0}.node-details-sidebar .sidebar-content .node-preview .node-preview-card .node-icon img{width:24px;height:24px}.node-details-sidebar .sidebar-content .node-preview .node-preview-card .node-info{flex:1;min-width:0}.node-details-sidebar .sidebar-content .node-preview .node-preview-card .node-info .node-label{font-size:1rem;font-weight:600;margin-bottom:.25rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.node-details-sidebar .sidebar-content .node-preview .node-preview-card .node-info .node-type{font-size:.875rem;color:#586069}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-start{background-color:#4caf500d}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-start .node-icon{background-color:#4caf501a}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-start .node-label{color:#4caf50}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-end{background-color:#f443360d}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-end .node-icon{background-color:#f443361a}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-end .node-label{color:#f44336}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-gateway{background-color:#ff98000d}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-gateway .node-icon{background-color:#ff98001a}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-gateway .node-label{color:#ff9800}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-action{background-color:#9c27b00d}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-action .node-icon{background-color:#9c27b01a}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-action .node-label{color:#9c27b0}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-default{background-color:#607d8b0d}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-default .node-icon{background-color:#607d8b1a}.node-details-sidebar .sidebar-content .node-preview .node-preview-card.node-default .node-label{color:#607d8b}.node-details-sidebar .sidebar-content .section{margin-bottom:1.5rem}.node-details-sidebar .sidebar-content .section .section-label{display:block;font-size:.65rem;font-weight:600;color:#24292e;margin-bottom:.75rem;text-transform:uppercase;letter-spacing:.5px}.node-details-sidebar .sidebar-content .section .display-type-selector .form-control{width:100%;padding:.625rem .875rem;font-size:.875rem;border:1px solid #d1d5da;border-radius:6px;background:white;transition:border-color .2s;box-sizing:border-box;resize:vertical}.node-details-sidebar .sidebar-content .section .display-type-selector .form-control:focus{outline:none;border-color:#0366d6;box-shadow:0 0 0 3px #0366d61a}.node-details-sidebar .sidebar-content .section .info-grid{display:flex;flex-direction:column;gap:.75rem}.node-details-sidebar .sidebar-content .section .info-grid .info-item{display:flex;justify-content:space-between;align-items:flex-start;padding:.75rem;background:#f6f8fa;border-radius:6px}.node-details-sidebar .sidebar-content .section .info-grid .info-item .info-label{font-size:.8125rem;color:#586069;font-weight:500}.node-details-sidebar .sidebar-content .section .info-grid .info-item .info-value{font-size:.8125rem;color:#24292e;font-weight:600;text-align:right;max-width:60%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.node-details-sidebar .sidebar-content .section .review-status .review-badge{display:inline-block;padding:.5rem 1rem;border-radius:20px;color:#fff;font-size:.8125rem;font-weight:600;margin-bottom:.75rem}.node-details-sidebar .sidebar-content .section .review-status .review-reasons .reason{display:flex;align-items:flex-start;gap:.5rem;padding:.75rem;background:#fff3cd;border-radius:4px;margin-bottom:.5rem;font-size:.8125rem;color:#856404}.node-details-sidebar .sidebar-content .section .review-status .review-reasons .reason .reason-icon{flex-shrink:0}.node-details-sidebar .sidebar-content .section .migration-info .info-card{padding:1rem;background:#f6f8fa;border-radius:6px;margin-bottom:.75rem}.node-details-sidebar .sidebar-content .section .migration-info .info-card:last-child{margin-bottom:0}.node-details-sidebar .sidebar-content .section .migration-info .info-card .card-label{font-size:.75rem;font-weight:600;color:#586069;text-transform:uppercase;letter-spacing:.5px;margin-bottom:.5rem}.node-details-sidebar .sidebar-content .section .migration-info .info-card .card-value{font-size:.875rem;color:#24292e;line-height:1.5}.node-details-sidebar .sidebar-content .section .migration-info .info-card .card-value.instructions{white-space:pre-wrap}\n"] }]
|
|
@@ -2115,7 +2263,7 @@ class WxCCFileSelectionBarComponent {
|
|
|
2115
2263
|
}
|
|
2116
2264
|
}
|
|
2117
2265
|
WxCCFileSelectionBarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: WxCCFileSelectionBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2118
|
-
WxCCFileSelectionBarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: WxCCFileSelectionBarComponent, selector: "wxcc-file-selection-bar", inputs: { fileNames: "fileNames", selectedFileName: "selectedFileName", selectedHost: "selectedHost" }, outputs: { fileNameSelectionChange: "fileNameSelectionChange" }, ngImport: i0, template: "<div class=\"file-selection-bar\">\r\n <mat-form-field appearance=\"outline\" class=\"host-selection\" *ngIf=\"hosts?.length\">\r\n <!-- <mat-label>Host</mat-label> -->\r\n <mat-select\r\n [value]=\"selectedHost\"\r\n (selectionChange)=\"onHostSelectionChange($event.value)\">\r\n <mat-option\r\n *ngFor=\"let host of hosts; trackBy: trackByHost\"\r\n title=\"{{ host }}\"\r\n [value]=\"host\">\r\n {{ host }}\r\n </mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n\r\n <mat-form-field appearance=\"outline\" class=\"file-selection\">\r\n <!-- <mat-label>Script file</mat-label> -->\r\n <mat-select\r\n [value]=\"selectedFileName\"\r\n (selectionChange)=\"onFileSelectionChange($event.value)\">\r\n <mat-option\r\n *ngFor=\"let name of fileNamesByHost; trackBy: trackByFileName\"\r\n title=\"{{ name }}\"\r\n [value]=\"name\">\r\n {{ name }}\r\n </mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css?family=Poppins:400,100,200,300,500,600,800,700,900\";@import\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400&display=swap\";:host ::ng-deep .mat-form-field-appearance-outline .mat-form-field-wrapper{margin:0}:host ::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix{padding:.25rem 0 .5rem}:host ::ng-deep .mat-select-trigger{display:block}:host ::ng-deep .mat-select-trigger .mat-select-arrow-wrapper{transform:none}.file-selection-bar{display:flex;gap:1rem}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2$
|
|
2266
|
+
WxCCFileSelectionBarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: WxCCFileSelectionBarComponent, selector: "wxcc-file-selection-bar", inputs: { fileNames: "fileNames", selectedFileName: "selectedFileName", selectedHost: "selectedHost" }, outputs: { fileNameSelectionChange: "fileNameSelectionChange" }, ngImport: i0, template: "<div class=\"file-selection-bar\">\r\n <mat-form-field appearance=\"outline\" class=\"host-selection\" *ngIf=\"hosts?.length\">\r\n <!-- <mat-label>Host</mat-label> -->\r\n <mat-select\r\n [value]=\"selectedHost\"\r\n (selectionChange)=\"onHostSelectionChange($event.value)\">\r\n <mat-option\r\n *ngFor=\"let host of hosts; trackBy: trackByHost\"\r\n title=\"{{ host }}\"\r\n [value]=\"host\">\r\n {{ host }}\r\n </mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n\r\n <mat-form-field appearance=\"outline\" class=\"file-selection\">\r\n <!-- <mat-label>Script file</mat-label> -->\r\n <mat-select\r\n [value]=\"selectedFileName\"\r\n (selectionChange)=\"onFileSelectionChange($event.value)\">\r\n <mat-option\r\n *ngFor=\"let name of fileNamesByHost; trackBy: trackByFileName\"\r\n title=\"{{ name }}\"\r\n [value]=\"name\">\r\n {{ name }}\r\n </mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css?family=Poppins:400,100,200,300,500,600,800,700,900\";@import\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400&display=swap\";:host ::ng-deep .mat-form-field-appearance-outline .mat-form-field-wrapper{margin:0}:host ::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix{padding:.25rem 0 .5rem}:host ::ng-deep .mat-select-trigger{display:block}:host ::ng-deep .mat-select-trigger .mat-select-arrow-wrapper{transform:none}.file-selection-bar{display:flex;gap:1rem}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["color", "appearance", "hideRequiredMarker", "hintLabel", "floatLabel"], exportAs: ["matFormField"] }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["disabled", "disableRipple", "tabIndex"], exportAs: ["matSelect"] }, { kind: "component", type: i3.MatOption, selector: "mat-option", exportAs: ["matOption"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2119
2267
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: WxCCFileSelectionBarComponent, decorators: [{
|
|
2120
2268
|
type: Component,
|
|
2121
2269
|
args: [{ selector: 'wxcc-file-selection-bar', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"file-selection-bar\">\r\n <mat-form-field appearance=\"outline\" class=\"host-selection\" *ngIf=\"hosts?.length\">\r\n <!-- <mat-label>Host</mat-label> -->\r\n <mat-select\r\n [value]=\"selectedHost\"\r\n (selectionChange)=\"onHostSelectionChange($event.value)\">\r\n <mat-option\r\n *ngFor=\"let host of hosts; trackBy: trackByHost\"\r\n title=\"{{ host }}\"\r\n [value]=\"host\">\r\n {{ host }}\r\n </mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n\r\n <mat-form-field appearance=\"outline\" class=\"file-selection\">\r\n <!-- <mat-label>Script file</mat-label> -->\r\n <mat-select\r\n [value]=\"selectedFileName\"\r\n (selectionChange)=\"onFileSelectionChange($event.value)\">\r\n <mat-option\r\n *ngFor=\"let name of fileNamesByHost; trackBy: trackByFileName\"\r\n title=\"{{ name }}\"\r\n [value]=\"name\">\r\n {{ name }}\r\n </mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css?family=Poppins:400,100,200,300,500,600,800,700,900\";@import\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400&display=swap\";:host ::ng-deep .mat-form-field-appearance-outline .mat-form-field-wrapper{margin:0}:host ::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix{padding:.25rem 0 .5rem}:host ::ng-deep .mat-select-trigger{display:block}:host ::ng-deep .mat-select-trigger .mat-select-arrow-wrapper{transform:none}.file-selection-bar{display:flex;gap:1rem}\n"] }]
|
|
@@ -3270,7 +3418,7 @@ class WxCCFlowchartPageComponent {
|
|
|
3270
3418
|
}
|
|
3271
3419
|
}
|
|
3272
3420
|
WxCCFlowchartPageComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: WxCCFlowchartPageComponent, deps: [{ token: WxCCAPIService }, { token: WxCC2GraphService }], target: i0.ɵɵFactoryTarget.Component });
|
|
3273
|
-
WxCCFlowchartPageComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: WxCCFlowchartPageComponent, selector: "cc-script-editor", inputs: { customerId: "customerId", host: "host", token: "token" }, ngImport: i0, template: "<div class=\"wxcc-flowchart-wrapper\">\r\n <header class=\"wxcc-flowchart-header\">\r\n <wxcc-file-selection-bar\r\n *ngIf=\"state.fileNames && state.fileNames.length\"\r\n [fileNames]=\"state.fileNames\"\r\n [selectedHost]=\"state.uccxIp\"\r\n [selectedFileName]=\"state.selectedFileName\"\r\n (fileNameSelectionChange)=\"onFileNameSelectionChange($event)\">\r\n </wxcc-file-selection-bar>\r\n <wxcc-actions-bar\r\n *ngIf=\"state.flowchart\"\r\n (save)=\"onSave()\"\r\n (reset)=\"onReset()\"\r\n (exportNonIr)=\"onExportNonIr()\"\r\n (complete)=\"onComplete()\">\r\n </wxcc-actions-bar>\r\n </header>\r\n <ng-container *ngIf=\"!state.loading; else loadingTemplate\">\r\n <wxcc-flowchart\r\n *ngIf=\"state.flowchart; else errorTemplate\"\r\n [flowchart]=\"state.flowchart\">\r\n </wxcc-flowchart>\r\n </ng-container>\r\n</div>\r\n\r\n<ng-template #loadingTemplate>\r\n <div class=\"placeholder-wrapper\">\r\n <mat-spinner diameter=\"40\"></mat-spinner>\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #errorTemplate>\r\n <div class=\"placeholder-wrapper\">\r\n Error loading script\r\n </div>\r\n</ng-template>\r\n", styles: [":host{display:block;width:100%;height:100%}.wxcc-flowchart-wrapper{width:100%;height:100%;display:flex;gap:.5rem;flex-direction:column}.wxcc-flowchart-wrapper .wxcc-flowchart-header{display:flex;align-items:center;justify-content:space-between;flex-shrink:0}.wxcc-flowchart-wrapper .placeholder-wrapper{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%}\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i4
|
|
3421
|
+
WxCCFlowchartPageComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: WxCCFlowchartPageComponent, selector: "cc-script-editor", inputs: { customerId: "customerId", host: "host", token: "token" }, ngImport: i0, template: "<div class=\"wxcc-flowchart-wrapper\">\r\n <header class=\"wxcc-flowchart-header\">\r\n <wxcc-file-selection-bar\r\n *ngIf=\"state.fileNames && state.fileNames.length\"\r\n [fileNames]=\"state.fileNames\"\r\n [selectedHost]=\"state.uccxIp\"\r\n [selectedFileName]=\"state.selectedFileName\"\r\n (fileNameSelectionChange)=\"onFileNameSelectionChange($event)\">\r\n </wxcc-file-selection-bar>\r\n <wxcc-actions-bar\r\n *ngIf=\"state.flowchart\"\r\n (save)=\"onSave()\"\r\n (reset)=\"onReset()\"\r\n (exportNonIr)=\"onExportNonIr()\"\r\n (complete)=\"onComplete()\">\r\n </wxcc-actions-bar>\r\n </header>\r\n <ng-container *ngIf=\"!state.loading; else loadingTemplate\">\r\n <wxcc-flowchart\r\n *ngIf=\"state.flowchart; else errorTemplate\"\r\n [flowchart]=\"state.flowchart\">\r\n </wxcc-flowchart>\r\n </ng-container>\r\n</div>\r\n\r\n<ng-template #loadingTemplate>\r\n <div class=\"placeholder-wrapper\">\r\n <mat-spinner diameter=\"40\"></mat-spinner>\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #errorTemplate>\r\n <div class=\"placeholder-wrapper\">\r\n Error loading script\r\n </div>\r\n</ng-template>\r\n", styles: [":host{display:block;width:100%;height:100%}.wxcc-flowchart-wrapper{width:100%;height:100%;display:flex;gap:.5rem;flex-direction:column}.wxcc-flowchart-wrapper .wxcc-flowchart-header{display:flex;align-items:center;justify-content:space-between;flex-shrink:0}.wxcc-flowchart-wrapper .placeholder-wrapper{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%}\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i4.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "diameter", "strokeWidth", "mode", "value"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: WxCCFlowchartComponent, selector: "wxcc-flowchart", inputs: ["flowchart"] }, { kind: "component", type: WxCCFileSelectionBarComponent, selector: "wxcc-file-selection-bar", inputs: ["fileNames", "selectedFileName", "selectedHost"], outputs: ["fileNameSelectionChange"] }, { kind: "component", type: WxCCActionsBarComponent, selector: "wxcc-actions-bar", outputs: ["save", "reset", "complete", "exportNonIr"] }] });
|
|
3274
3422
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: WxCCFlowchartPageComponent, decorators: [{
|
|
3275
3423
|
type: Component,
|
|
3276
3424
|
args: [{ selector: 'cc-script-editor', template: "<div class=\"wxcc-flowchart-wrapper\">\r\n <header class=\"wxcc-flowchart-header\">\r\n <wxcc-file-selection-bar\r\n *ngIf=\"state.fileNames && state.fileNames.length\"\r\n [fileNames]=\"state.fileNames\"\r\n [selectedHost]=\"state.uccxIp\"\r\n [selectedFileName]=\"state.selectedFileName\"\r\n (fileNameSelectionChange)=\"onFileNameSelectionChange($event)\">\r\n </wxcc-file-selection-bar>\r\n <wxcc-actions-bar\r\n *ngIf=\"state.flowchart\"\r\n (save)=\"onSave()\"\r\n (reset)=\"onReset()\"\r\n (exportNonIr)=\"onExportNonIr()\"\r\n (complete)=\"onComplete()\">\r\n </wxcc-actions-bar>\r\n </header>\r\n <ng-container *ngIf=\"!state.loading; else loadingTemplate\">\r\n <wxcc-flowchart\r\n *ngIf=\"state.flowchart; else errorTemplate\"\r\n [flowchart]=\"state.flowchart\">\r\n </wxcc-flowchart>\r\n </ng-container>\r\n</div>\r\n\r\n<ng-template #loadingTemplate>\r\n <div class=\"placeholder-wrapper\">\r\n <mat-spinner diameter=\"40\"></mat-spinner>\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #errorTemplate>\r\n <div class=\"placeholder-wrapper\">\r\n Error loading script\r\n </div>\r\n</ng-template>\r\n", styles: [":host{display:block;width:100%;height:100%}.wxcc-flowchart-wrapper{width:100%;height:100%;display:flex;gap:.5rem;flex-direction:column}.wxcc-flowchart-wrapper .wxcc-flowchart-header{display:flex;align-items:center;justify-content:space-between;flex-shrink:0}.wxcc-flowchart-wrapper .placeholder-wrapper{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%}\n"] }]
|
|
@@ -3282,101 +3430,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
|
|
|
3282
3430
|
type: Input
|
|
3283
3431
|
}] } });
|
|
3284
3432
|
|
|
3285
|
-
class MaterialModule {
|
|
3286
|
-
}
|
|
3287
|
-
MaterialModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MaterialModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
3288
|
-
MaterialModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: MaterialModule, imports: [MatProgressBarModule,
|
|
3289
|
-
MatTooltipModule,
|
|
3290
|
-
MatTableModule,
|
|
3291
|
-
MatCheckboxModule,
|
|
3292
|
-
MatButtonModule,
|
|
3293
|
-
MatIconModule,
|
|
3294
|
-
MatFormFieldModule,
|
|
3295
|
-
MatInputModule,
|
|
3296
|
-
MatSelectModule], exports: [MatProgressBarModule,
|
|
3297
|
-
MatTooltipModule,
|
|
3298
|
-
MatTableModule,
|
|
3299
|
-
MatCheckboxModule,
|
|
3300
|
-
MatButtonModule,
|
|
3301
|
-
MatIconModule,
|
|
3302
|
-
MatFormFieldModule,
|
|
3303
|
-
MatInputModule,
|
|
3304
|
-
MatSelectModule,
|
|
3305
|
-
MatProgressSpinnerModule] });
|
|
3306
|
-
MaterialModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MaterialModule, imports: [MatProgressBarModule,
|
|
3307
|
-
MatTooltipModule,
|
|
3308
|
-
MatTableModule,
|
|
3309
|
-
MatCheckboxModule,
|
|
3310
|
-
MatButtonModule,
|
|
3311
|
-
MatIconModule,
|
|
3312
|
-
MatFormFieldModule,
|
|
3313
|
-
MatInputModule,
|
|
3314
|
-
MatSelectModule, MatProgressBarModule,
|
|
3315
|
-
MatTooltipModule,
|
|
3316
|
-
MatTableModule,
|
|
3317
|
-
MatCheckboxModule,
|
|
3318
|
-
MatButtonModule,
|
|
3319
|
-
MatIconModule,
|
|
3320
|
-
MatFormFieldModule,
|
|
3321
|
-
MatInputModule,
|
|
3322
|
-
MatSelectModule,
|
|
3323
|
-
MatProgressSpinnerModule] });
|
|
3324
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MaterialModule, decorators: [{
|
|
3325
|
-
type: NgModule,
|
|
3326
|
-
args: [{
|
|
3327
|
-
imports: [
|
|
3328
|
-
MatProgressBarModule,
|
|
3329
|
-
MatTooltipModule,
|
|
3330
|
-
MatTableModule,
|
|
3331
|
-
MatCheckboxModule,
|
|
3332
|
-
MatButtonModule,
|
|
3333
|
-
MatIconModule,
|
|
3334
|
-
MatFormFieldModule,
|
|
3335
|
-
MatInputModule,
|
|
3336
|
-
MatSelectModule
|
|
3337
|
-
],
|
|
3338
|
-
exports: [
|
|
3339
|
-
MatProgressBarModule,
|
|
3340
|
-
MatTooltipModule,
|
|
3341
|
-
MatTableModule,
|
|
3342
|
-
MatCheckboxModule,
|
|
3343
|
-
MatButtonModule,
|
|
3344
|
-
MatIconModule,
|
|
3345
|
-
MatFormFieldModule,
|
|
3346
|
-
MatInputModule,
|
|
3347
|
-
MatSelectModule,
|
|
3348
|
-
MatProgressSpinnerModule
|
|
3349
|
-
]
|
|
3350
|
-
}]
|
|
3351
|
-
}] });
|
|
3352
|
-
|
|
3353
|
-
class SharedModule {
|
|
3354
|
-
}
|
|
3355
|
-
SharedModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SharedModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
3356
|
-
SharedModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: SharedModule, imports: [CommonModule,
|
|
3357
|
-
MaterialModule,
|
|
3358
|
-
NgApexchartsModule,
|
|
3359
|
-
HttpClientModule], exports: [MaterialModule] });
|
|
3360
|
-
SharedModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SharedModule, imports: [CommonModule,
|
|
3361
|
-
MaterialModule,
|
|
3362
|
-
NgApexchartsModule,
|
|
3363
|
-
HttpClientModule, MaterialModule] });
|
|
3364
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SharedModule, decorators: [{
|
|
3365
|
-
type: NgModule,
|
|
3366
|
-
args: [{
|
|
3367
|
-
declarations: [],
|
|
3368
|
-
imports: [
|
|
3369
|
-
CommonModule,
|
|
3370
|
-
MaterialModule,
|
|
3371
|
-
NgApexchartsModule,
|
|
3372
|
-
HttpClientModule
|
|
3373
|
-
],
|
|
3374
|
-
exports: [
|
|
3375
|
-
MaterialModule
|
|
3376
|
-
]
|
|
3377
|
-
}]
|
|
3378
|
-
}] });
|
|
3379
|
-
|
|
3380
3433
|
class FloatingToolbarModule {
|
|
3381
3434
|
}
|
|
3382
3435
|
FloatingToolbarModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: FloatingToolbarModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
@@ -3463,9 +3516,818 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
|
|
|
3463
3516
|
}]
|
|
3464
3517
|
}] });
|
|
3465
3518
|
|
|
3519
|
+
/**
|
|
3520
|
+
* Same contract as {@link APIService}, pointed at the **dcp** host instead of the
|
|
3521
|
+
* main API host. A handful of UCCX endpoints (application names, file downloads,
|
|
3522
|
+
* dependency analysis) are only served there - webex-ops-client does the same thing
|
|
3523
|
+
* with its `ContactCenterService.customUrl`.
|
|
3524
|
+
*
|
|
3525
|
+
* Registered separately from `APIService` so each keeps its own `apiUrl`/`token`.
|
|
3526
|
+
*/
|
|
3527
|
+
class DcpApiService extends APIService {
|
|
3528
|
+
constructor() {
|
|
3529
|
+
super(...arguments);
|
|
3530
|
+
this.apiUrl = window.location.protocol + '//' + window.location.hostname + '/dcp';
|
|
3531
|
+
}
|
|
3532
|
+
}
|
|
3533
|
+
DcpApiService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: DcpApiService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
3534
|
+
DcpApiService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: DcpApiService, providedIn: 'root' });
|
|
3535
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: DcpApiService, decorators: [{
|
|
3536
|
+
type: Injectable,
|
|
3537
|
+
args: [{
|
|
3538
|
+
providedIn: 'root'
|
|
3539
|
+
}]
|
|
3540
|
+
}] });
|
|
3541
|
+
|
|
3542
|
+
/**
|
|
3543
|
+
* The UCCX migration payloads as the backend returns them, mirroring
|
|
3544
|
+
* webex-ops-client's `pages/contact-center/interfaces/uccx-batch.interface.ts`.
|
|
3545
|
+
* Only the fields this widget reads are declared.
|
|
3546
|
+
*/
|
|
3547
|
+
/** The `type` values a {@link UccxItem} can carry. */
|
|
3548
|
+
const UCCX_ITEM_TYPE = {
|
|
3549
|
+
RESOURCE_GROUP: 'Resource Group',
|
|
3550
|
+
TRIGGER: 'Trigger',
|
|
3551
|
+
SCRIPT: 'Script',
|
|
3552
|
+
APPLICATION: 'Application',
|
|
3553
|
+
CSQ: 'CSQ',
|
|
3554
|
+
DOCUMENT: 'Document',
|
|
3555
|
+
PHONE_BOOK: 'Phone Book',
|
|
3556
|
+
PROMPT: 'Prompt',
|
|
3557
|
+
REASON_CODE: 'Reason Code',
|
|
3558
|
+
RESOURCE: 'Resource',
|
|
3559
|
+
SKILL: 'Skill',
|
|
3560
|
+
TEAM: 'Team',
|
|
3561
|
+
WRAP_UP_CODE: 'Wrap Up Code'
|
|
3562
|
+
};
|
|
3563
|
+
/** The only call-flow state that counts as imported. */
|
|
3564
|
+
const CALL_FLOW_MIGRATED = 'MIGRATED';
|
|
3565
|
+
|
|
3566
|
+
const CHART_COLORS = {
|
|
3567
|
+
customerAssist: '#177A5C',
|
|
3568
|
+
webexContactCenter: '#D0D0D0',
|
|
3569
|
+
requiresReview: '#1268E3'
|
|
3570
|
+
};
|
|
3571
|
+
/** Compatibility score thresholds, per the ticket's scoring table. */
|
|
3572
|
+
const COMPATIBILITY_THRESHOLDS = {
|
|
3573
|
+
/** 80-100 - migrates to Customer Assist with no functional loss. */
|
|
3574
|
+
migratable: 80,
|
|
3575
|
+
/** 60-79 - migrates to Customer Assist, minor unsupported steps dropped. */
|
|
3576
|
+
requiresReview: 60
|
|
3577
|
+
};
|
|
3578
|
+
/**
|
|
3579
|
+
* Slices of the panel's pie chart, in order: the recommended destination for every
|
|
3580
|
+
* contact center detected in the connected UCCX environment.
|
|
3581
|
+
*
|
|
3582
|
+
* The panel is slice-driven, so re-cutting the chart when the design changes means
|
|
3583
|
+
* editing this array - no component change.
|
|
3584
|
+
*/
|
|
3585
|
+
const IMPORT_PANEL_SLICES = [
|
|
3586
|
+
{
|
|
3587
|
+
label: 'To Customer Assist',
|
|
3588
|
+
color: CHART_COLORS.customerAssist,
|
|
3589
|
+
tooltip: 'Fully supported by Customer Assist.',
|
|
3590
|
+
match: contactCenter => contactCenter.compatibilityStatus === 'migratable'
|
|
3591
|
+
},
|
|
3592
|
+
{
|
|
3593
|
+
label: 'Requires Review',
|
|
3594
|
+
color: CHART_COLORS.requiresReview,
|
|
3595
|
+
tooltip: 'Supported by Customer Assist, but a few unsupported steps would change. '
|
|
3596
|
+
+ 'Can also go to Webex Contact Center for full feature coverage.',
|
|
3597
|
+
match: contactCenter => contactCenter.compatibilityStatus === 'requires-review'
|
|
3598
|
+
},
|
|
3599
|
+
{
|
|
3600
|
+
label: 'To Webex Contact Center',
|
|
3601
|
+
color: CHART_COLORS.webexContactCenter,
|
|
3602
|
+
tooltip: 'Not supported by Customer Assist - migrate to Webex Contact Center.',
|
|
3603
|
+
match: contactCenter => contactCenter.compatibilityStatus === 'unsupported'
|
|
3604
|
+
}
|
|
3605
|
+
];
|
|
3606
|
+
/**
|
|
3607
|
+
* How UCCX entity types roll up into the groups the Overview step shows, in display
|
|
3608
|
+
* order. Types not listed here get a group of their own under their raw type name,
|
|
3609
|
+
* so nothing the backend returns is silently dropped.
|
|
3610
|
+
*/
|
|
3611
|
+
const OVERVIEW_ENTITY_GROUPS = [
|
|
3612
|
+
{ label: 'Channel', types: [UCCX_ITEM_TYPE.TRIGGER] },
|
|
3613
|
+
{ label: 'Queues', types: [UCCX_ITEM_TYPE.CSQ] },
|
|
3614
|
+
{ label: 'Audio files', types: [UCCX_ITEM_TYPE.PROMPT, UCCX_ITEM_TYPE.DOCUMENT] },
|
|
3615
|
+
{ label: 'Flows', types: [UCCX_ITEM_TYPE.SCRIPT] },
|
|
3616
|
+
{ label: 'Skill', types: [UCCX_ITEM_TYPE.SKILL] },
|
|
3617
|
+
{ label: 'Contact center users', types: [UCCX_ITEM_TYPE.RESOURCE] }
|
|
3618
|
+
];
|
|
3619
|
+
const IMPORT_PANEL_TITLE = 'Import Contact center';
|
|
3620
|
+
const IMPORT_PANEL_TOOLTIP = 'Contact centers detected in the connected UCCX environment, and where each one can migrate to.';
|
|
3621
|
+
const IMPORT_WIZARD_TITLE = 'Contact Center';
|
|
3622
|
+
/** Shown once a "requires review" contact center is picked. Dismissible. */
|
|
3623
|
+
const CUSTOMER_ASSIST_NOTICE = 'This contact center may also be supported by Customer Assist. Check compatibility before continuing.';
|
|
3624
|
+
/** Tooltip on the marker the picker puts next to those same contact centers. */
|
|
3625
|
+
const CUSTOMER_ASSIST_MARKER_TOOLTIP = 'May also be supported by Customer Assist.';
|
|
3626
|
+
|
|
3627
|
+
/**
|
|
3628
|
+
* Stand-in data used when `CcImportService.useMockData` is on.
|
|
3629
|
+
*
|
|
3630
|
+
* Shapes here mirror exactly what `CcImportService` produces from the real backend,
|
|
3631
|
+
* so switching over is a matter of flipping `useMockData` to false.
|
|
3632
|
+
*/
|
|
3633
|
+
const APPLICATION_NAMES = [
|
|
3634
|
+
'Operations', 'Sales', 'Support', 'Billing', 'Reception', 'Helpdesk', 'Retention',
|
|
3635
|
+
'Onboarding', 'Escalations', 'Emergency', 'Pharmacy', 'Appointments', 'Claims',
|
|
3636
|
+
'Collections', 'Dispatch', 'Front Desk', 'IT Service Desk', 'Loyalty', 'Orders', 'Returns'
|
|
3637
|
+
];
|
|
3638
|
+
/** One tab per group on the wizard's Overview step, in the order they are shown. */
|
|
3639
|
+
const ENTITY_GROUP_TEMPLATE = [
|
|
3640
|
+
{ label: 'Channel', suffixes: ['_CC'] },
|
|
3641
|
+
{ label: 'Queues', suffixes: ['_Queue'] },
|
|
3642
|
+
{ label: 'Audio files', suffixes: [' Welcome', ' Music On Hold', ' Closed Message'] },
|
|
3643
|
+
{ label: 'Flows', suffixes: ['_Flow'] },
|
|
3644
|
+
{ label: 'Skill', suffixes: ['_Skill'] },
|
|
3645
|
+
{ label: 'Contact center users', suffixes: [' Supervisor', ' Agent 1', ' Agent 2'] }
|
|
3646
|
+
];
|
|
3647
|
+
/**
|
|
3648
|
+
* The ten contact centers that still need a decision - the only ones the wizard
|
|
3649
|
+
* offers, so these are exactly the rows the picker shows, in this order. The flagged
|
|
3650
|
+
* three are "requires review" and carry the marker plus the advisory notice.
|
|
3651
|
+
*/
|
|
3652
|
+
const REVIEW_CONTACT_CENTERS = [
|
|
3653
|
+
{ name: 'Operations', requiresReview: true },
|
|
3654
|
+
{ name: 'Sales_inquiry' },
|
|
3655
|
+
{ name: 'Tech Support' },
|
|
3656
|
+
{ name: 'Tech_Info' },
|
|
3657
|
+
{ name: 'Finance', requiresReview: true },
|
|
3658
|
+
{ name: 'Innovation', requiresReview: true },
|
|
3659
|
+
{ name: 'Marketing' },
|
|
3660
|
+
{ name: 'Gen_Support' },
|
|
3661
|
+
{ name: 'Legal' },
|
|
3662
|
+
{ name: 'Sales Support' }
|
|
3663
|
+
];
|
|
3664
|
+
/** 100 contact centers: 70 migratable, 20 unsupported, 10 still to be imported. */
|
|
3665
|
+
function mockContactCenters() {
|
|
3666
|
+
const contactCenters = [];
|
|
3667
|
+
// 90 already-imported ones, split across the two destinations.
|
|
3668
|
+
for (let i = 0; i < 90; i++) {
|
|
3669
|
+
const name = `${APPLICATION_NAMES[i % APPLICATION_NAMES.length]}_${Math.floor(i / APPLICATION_NAMES.length) + 1}`;
|
|
3670
|
+
const compatibilityStatus = i < 70 ? 'migratable' : 'unsupported';
|
|
3671
|
+
contactCenters.push({
|
|
3672
|
+
applicationId: `app-${i + 1}`,
|
|
3673
|
+
applicationName: name,
|
|
3674
|
+
scriptName: `${name}_AA_Queue.aef`,
|
|
3675
|
+
compatibilityStatus,
|
|
3676
|
+
compatibilityScore: compatibilityStatus === 'migratable' ? 92 : 41,
|
|
3677
|
+
migrationState: 'migrated'
|
|
3678
|
+
});
|
|
3679
|
+
}
|
|
3680
|
+
REVIEW_CONTACT_CENTERS.forEach((contactCenter, i) => {
|
|
3681
|
+
const compatibilityStatus = contactCenter.requiresReview ? 'requires-review' : 'unsupported';
|
|
3682
|
+
contactCenters.push({
|
|
3683
|
+
applicationId: `app-${91 + i}`,
|
|
3684
|
+
applicationName: contactCenter.name,
|
|
3685
|
+
scriptName: `${contactCenter.name}_AA_Queue.aef`,
|
|
3686
|
+
compatibilityStatus,
|
|
3687
|
+
compatibilityScore: contactCenter.requiresReview ? 68 : 44,
|
|
3688
|
+
migrationState: 'not-migrated',
|
|
3689
|
+
customerAssistCapable: compatibilityStatus === 'requires-review'
|
|
3690
|
+
});
|
|
3691
|
+
});
|
|
3692
|
+
return contactCenters;
|
|
3693
|
+
}
|
|
3694
|
+
function mockContactCenterEntities(contactCenter) {
|
|
3695
|
+
const base = contactCenter.applicationName;
|
|
3696
|
+
return {
|
|
3697
|
+
applicationId: contactCenter.applicationId,
|
|
3698
|
+
groups: ENTITY_GROUP_TEMPLATE.map(group => ({
|
|
3699
|
+
label: group.label,
|
|
3700
|
+
items: group.suffixes.map(suffix => `${base}${suffix}`)
|
|
3701
|
+
}))
|
|
3702
|
+
};
|
|
3703
|
+
}
|
|
3704
|
+
|
|
3705
|
+
/** Simulated network latency for the mock responses, so loading states are exercised. */
|
|
3706
|
+
const MOCK_LATENCY_MS = 300;
|
|
3707
|
+
class CcImportService {
|
|
3708
|
+
constructor() {
|
|
3709
|
+
this.api = inject(APIService);
|
|
3710
|
+
this.dcpApi = inject(DcpApiService);
|
|
3711
|
+
/**
|
|
3712
|
+
* Answer everything from `mock/cc-import.mock.ts` instead of calling the backend.
|
|
3713
|
+
*
|
|
3714
|
+
* Note this is all-or-nothing: with it off, the contact-center list, the entity
|
|
3715
|
+
* overview and the import all hit real endpoints. Compatibility scoring is the
|
|
3716
|
+
* exception - see {@link resolveCompatibility}.
|
|
3717
|
+
*/
|
|
3718
|
+
this.useMockData = true;
|
|
3719
|
+
}
|
|
3720
|
+
setToken(token) {
|
|
3721
|
+
this.api.token = token;
|
|
3722
|
+
this.dcpApi.token = token;
|
|
3723
|
+
}
|
|
3724
|
+
setHost(host) {
|
|
3725
|
+
this.api.apiUrl = host;
|
|
3726
|
+
}
|
|
3727
|
+
/** Base URL of the dcp host, which serves the UCCX application list. */
|
|
3728
|
+
setDcpHost(dcpHost) {
|
|
3729
|
+
this.dcpApi.apiUrl = dcpHost;
|
|
3730
|
+
}
|
|
3731
|
+
/**
|
|
3732
|
+
* Every UCCX contact center Tuki can import.
|
|
3733
|
+
*
|
|
3734
|
+
* A "contact center" is a UCCX application. The list comes from the dcp
|
|
3735
|
+
* application cache; whether each one has already been imported is joined on from
|
|
3736
|
+
* the per-AEF-script call-flow migration states.
|
|
3737
|
+
*/
|
|
3738
|
+
fetchContactCenters(customerId) {
|
|
3739
|
+
if (this.useMockData) {
|
|
3740
|
+
return of(mockContactCenters()).pipe(delay(MOCK_LATENCY_MS));
|
|
3741
|
+
}
|
|
3742
|
+
return forkJoin({
|
|
3743
|
+
applications: this.dcpApi.fetch(API.UCCX.APPLICATION_NAMES(customerId)),
|
|
3744
|
+
callFlows: this.api.fetch(API.UCCX.CALL_FLOW_MIGRATION_STATUSES(customerId))
|
|
3745
|
+
}).pipe(map(({ applications, callFlows }) => this.toContactCenters(this.asArray(applications), this.asArray(callFlows))));
|
|
3746
|
+
}
|
|
3747
|
+
/**
|
|
3748
|
+
* Application-centric entity breakdown: everything Tuki will create on import.
|
|
3749
|
+
*
|
|
3750
|
+
* Note this only ever returns entities the backend has actually linked to the
|
|
3751
|
+
* application. Where that linkage is missing the groups come back empty rather
|
|
3752
|
+
* than padded with unrelated entities of the right type.
|
|
3753
|
+
*/
|
|
3754
|
+
fetchContactCenterEntities(customerId, contactCenter) {
|
|
3755
|
+
if (this.useMockData) {
|
|
3756
|
+
return of(mockContactCenterEntities(contactCenter)).pipe(delay(MOCK_LATENCY_MS));
|
|
3757
|
+
}
|
|
3758
|
+
return this.fetchApplicationItems(customerId, contactCenter.applicationName).pipe(map(items => ({
|
|
3759
|
+
applicationId: contactCenter.applicationId,
|
|
3760
|
+
groups: this.groupEntities(items)
|
|
3761
|
+
})));
|
|
3762
|
+
}
|
|
3763
|
+
/**
|
|
3764
|
+
* Kicks off the UCCX -> Webex Contact Center import of one contact center, as a
|
|
3765
|
+
* single migration unit: every entity of the application goes in one request.
|
|
3766
|
+
*
|
|
3767
|
+
* The endpoint is synchronous and answers with an XLSX tracker rather than a job
|
|
3768
|
+
* id - execution and progress tracking are defined in a separate ticket.
|
|
3769
|
+
*
|
|
3770
|
+
* @param siteId required by the backend when a Resource Group is in scope.
|
|
3771
|
+
* @param locationId required by the backend when a Trigger is in scope.
|
|
3772
|
+
*/
|
|
3773
|
+
importContactCenter(customerId, applicationId, siteId, locationId) {
|
|
3774
|
+
if (this.useMockData) {
|
|
3775
|
+
return of({ applicationId, accepted: true }).pipe(delay(MOCK_LATENCY_MS));
|
|
3776
|
+
}
|
|
3777
|
+
return this.fetchApplicationItems(customerId, applicationId).pipe(switchMap(items => {
|
|
3778
|
+
const payload = {
|
|
3779
|
+
uccxItemsList: items.length ? [items.map(item => ({ ...item, migrate: true }))] : []
|
|
3780
|
+
};
|
|
3781
|
+
return this.api.post(API.UCCX.MIGRATE(customerId), payload, { siteId, locationId }, { responseType: 'blob' });
|
|
3782
|
+
}), map((report) => ({
|
|
3783
|
+
applicationId,
|
|
3784
|
+
accepted: true,
|
|
3785
|
+
report: report instanceof Blob ? report : undefined
|
|
3786
|
+
})));
|
|
3787
|
+
}
|
|
3788
|
+
/**
|
|
3789
|
+
* Every UCCX entity belonging to one application, gathered across the batches.
|
|
3790
|
+
*
|
|
3791
|
+
* UCCX batches hold one entity type each, so this reads them all and keeps the
|
|
3792
|
+
* items whose `applicationName` matches. The narrowing is done here rather than
|
|
3793
|
+
* through the endpoint's own `applicationName` param because that param answers
|
|
3794
|
+
* `[]` on dev2 even for items that plainly carry the name.
|
|
3795
|
+
*/
|
|
3796
|
+
fetchApplicationItems(customerId, applicationName) {
|
|
3797
|
+
return this.api.fetch(API.UCCX.BATCHES(customerId)).pipe(switchMap(response => {
|
|
3798
|
+
const batches = this.asArray(response);
|
|
3799
|
+
if (!batches.length) {
|
|
3800
|
+
return of([]);
|
|
3801
|
+
}
|
|
3802
|
+
return forkJoin(batches.map(batch => this.api.fetch(API.UCCX.BATCH_ITEMS(customerId, batch.id))
|
|
3803
|
+
.pipe(map((wrapper) => this.flattenItems(wrapper))))).pipe(map(perBatch => this.dedupeItems(perBatch.flat().filter(item => item.applicationName === applicationName))));
|
|
3804
|
+
}));
|
|
3805
|
+
}
|
|
3806
|
+
/** The items endpoint nests items one level; older responses come back flat. */
|
|
3807
|
+
flattenItems(wrapper) {
|
|
3808
|
+
const items = wrapper?.uccxItemsList ?? [];
|
|
3809
|
+
const flattened = Array.isArray(items[0])
|
|
3810
|
+
? items.flat()
|
|
3811
|
+
: items;
|
|
3812
|
+
return flattened.filter(Boolean);
|
|
3813
|
+
}
|
|
3814
|
+
/** The same entity can be carried by more than one batch. */
|
|
3815
|
+
dedupeItems(items) {
|
|
3816
|
+
const seen = new Set();
|
|
3817
|
+
return items.filter(item => {
|
|
3818
|
+
const key = `${item.type}::${item.name}::${item.sourceUCCXIp}`;
|
|
3819
|
+
if (seen.has(key)) {
|
|
3820
|
+
return false;
|
|
3821
|
+
}
|
|
3822
|
+
seen.add(key);
|
|
3823
|
+
return true;
|
|
3824
|
+
});
|
|
3825
|
+
}
|
|
3826
|
+
groupEntities(items) {
|
|
3827
|
+
const remaining = new Map();
|
|
3828
|
+
items.forEach(item => {
|
|
3829
|
+
const names = remaining.get(item.type) ?? [];
|
|
3830
|
+
names.push(item.name);
|
|
3831
|
+
remaining.set(item.type, names);
|
|
3832
|
+
});
|
|
3833
|
+
const groups = OVERVIEW_ENTITY_GROUPS.map(group => {
|
|
3834
|
+
const collected = [];
|
|
3835
|
+
group.types.forEach(type => {
|
|
3836
|
+
collected.push(...(remaining.get(type) ?? []));
|
|
3837
|
+
remaining.delete(type);
|
|
3838
|
+
});
|
|
3839
|
+
return { label: group.label, items: collected };
|
|
3840
|
+
});
|
|
3841
|
+
// Anything the backend returned that has no group of its own keeps its raw
|
|
3842
|
+
// type as a label, rather than being dropped from the overview.
|
|
3843
|
+
remaining.forEach((names, type) => groups.push({ label: type, items: names }));
|
|
3844
|
+
return groups;
|
|
3845
|
+
}
|
|
3846
|
+
toContactCenters(applications, callFlows) {
|
|
3847
|
+
return applications
|
|
3848
|
+
.filter(application => !!application?.name)
|
|
3849
|
+
.map(application => {
|
|
3850
|
+
const uccxIp = application.uccx?.host;
|
|
3851
|
+
const scriptName = this.toScriptName(application);
|
|
3852
|
+
const compatibilityStatus = this.resolveCompatibility(application);
|
|
3853
|
+
const contactCenter = {
|
|
3854
|
+
// UCCX identifies an application by name - it is what the entity
|
|
3855
|
+
// records link back to - so it doubles as the widget's key.
|
|
3856
|
+
applicationId: application.name,
|
|
3857
|
+
applicationName: application.name,
|
|
3858
|
+
scriptName,
|
|
3859
|
+
uccxIp,
|
|
3860
|
+
compatibilityStatus,
|
|
3861
|
+
migrationState: this.resolveMigrationState(application, scriptName, uccxIp, callFlows),
|
|
3862
|
+
// Per the ticket, "requires review" is exactly the set that can go
|
|
3863
|
+
// to either destination.
|
|
3864
|
+
customerAssistCapable: compatibilityStatus === 'requires-review'
|
|
3865
|
+
};
|
|
3866
|
+
return contactCenter;
|
|
3867
|
+
});
|
|
3868
|
+
}
|
|
3869
|
+
/** UCCX wraps the backing script as `SCRIPT[Foo.aef]`; the widget shows `Foo.aef`. */
|
|
3870
|
+
toScriptName(application) {
|
|
3871
|
+
const raw = application.scriptName ?? application.aefFiles?.[0] ?? '';
|
|
3872
|
+
return raw.replace(/^SCRIPT\[(.*)\]$/, '$1');
|
|
3873
|
+
}
|
|
3874
|
+
/**
|
|
3875
|
+
* Whether Tuki has already imported this contact center.
|
|
3876
|
+
*
|
|
3877
|
+
* The application record carries its own `migrationStatus`, but it is null
|
|
3878
|
+
* throughout dev2, so this falls back to the per-AEF-script call-flow state.
|
|
3879
|
+
*/
|
|
3880
|
+
resolveMigrationState(application, scriptName, uccxIp, callFlows) {
|
|
3881
|
+
if (application.migrationStatus) {
|
|
3882
|
+
return application.migrationStatus === CALL_FLOW_MIGRATED ? 'migrated' : 'not-migrated';
|
|
3883
|
+
}
|
|
3884
|
+
const callFlow = callFlows.find(flow => flow.scriptName === scriptName && (!uccxIp || flow.uccxIp === uccxIp));
|
|
3885
|
+
return callFlow?.status === CALL_FLOW_MIGRATED ? 'migrated' : 'not-migrated';
|
|
3886
|
+
}
|
|
3887
|
+
/**
|
|
3888
|
+
* Turns an AEF compatibility score into a status.
|
|
3889
|
+
*
|
|
3890
|
+
* The scoring API does not exist yet - the AEF step mapping is still a pending
|
|
3891
|
+
* input on the ticket - so when the backend sends no score every contact center
|
|
3892
|
+
* falls back to "requires review": the status that offers both destinations and
|
|
3893
|
+
* claims nothing about compatibility we have not actually measured.
|
|
3894
|
+
*
|
|
3895
|
+
* Once the backend returns `compatibilityScore`, the thresholds below take over
|
|
3896
|
+
* with no further change here.
|
|
3897
|
+
*/
|
|
3898
|
+
resolveCompatibility(application) {
|
|
3899
|
+
const score = application.compatibilityScore;
|
|
3900
|
+
if (typeof score !== 'number') {
|
|
3901
|
+
return 'requires-review';
|
|
3902
|
+
}
|
|
3903
|
+
if (score >= COMPATIBILITY_THRESHOLDS.migratable) {
|
|
3904
|
+
return 'migratable';
|
|
3905
|
+
}
|
|
3906
|
+
if (score >= COMPATIBILITY_THRESHOLDS.requiresReview) {
|
|
3907
|
+
return 'requires-review';
|
|
3908
|
+
}
|
|
3909
|
+
return 'unsupported';
|
|
3910
|
+
}
|
|
3911
|
+
asArray(value) {
|
|
3912
|
+
return Array.isArray(value) ? value : [];
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3915
|
+
CcImportService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CcImportService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
3916
|
+
CcImportService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CcImportService, providedIn: 'root' });
|
|
3917
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CcImportService, decorators: [{
|
|
3918
|
+
type: Injectable,
|
|
3919
|
+
args: [{
|
|
3920
|
+
providedIn: 'root'
|
|
3921
|
+
}]
|
|
3922
|
+
}] });
|
|
3923
|
+
|
|
3924
|
+
/**
|
|
3925
|
+
* "Import Contact center" panel: how many UCCX contact centers were detected, how many
|
|
3926
|
+
* have been imported, and an Import button that asks the host to open the import wizard.
|
|
3927
|
+
*/
|
|
3928
|
+
class ImportContactCenterPanelComponent {
|
|
3929
|
+
constructor() {
|
|
3930
|
+
/** Serve mock data instead of calling the UCCX migration API. */
|
|
3931
|
+
this.useMockData = true;
|
|
3932
|
+
/**
|
|
3933
|
+
* Asked for the import wizard, carrying the contact centers already loaded here so
|
|
3934
|
+
* the wizard opens without refetching them. The host renders
|
|
3935
|
+
* `cc-import-contact-center-wizard` as a page in response.
|
|
3936
|
+
*/
|
|
3937
|
+
this.importRequested = new EventEmitter();
|
|
3938
|
+
this.ccImportService = inject(CcImportService);
|
|
3939
|
+
this.title = IMPORT_PANEL_TITLE;
|
|
3940
|
+
this.tooltip = IMPORT_PANEL_TOOLTIP;
|
|
3941
|
+
this.segments = [];
|
|
3942
|
+
this.total = 0;
|
|
3943
|
+
this.loading = false;
|
|
3944
|
+
this.error = false;
|
|
3945
|
+
this.contactCenters = [];
|
|
3946
|
+
}
|
|
3947
|
+
ngOnInit() {
|
|
3948
|
+
// Render an empty donut up front so the template never reads unset chart options,
|
|
3949
|
+
// including when load() bails out because the widget has no credentials yet.
|
|
3950
|
+
this.buildViewModel();
|
|
3951
|
+
this.load();
|
|
3952
|
+
}
|
|
3953
|
+
ngOnChanges(changes) {
|
|
3954
|
+
const reloadTriggers = ['customerId', 'host', 'dcpHost', 'token', 'useMockData'];
|
|
3955
|
+
if (reloadTriggers.some(input => changes[input] && !changes[input].firstChange)) {
|
|
3956
|
+
this.load();
|
|
3957
|
+
}
|
|
3958
|
+
}
|
|
3959
|
+
ngOnDestroy() {
|
|
3960
|
+
this.subscription?.unsubscribe();
|
|
3961
|
+
}
|
|
3962
|
+
onImport() {
|
|
3963
|
+
this.importRequested.emit(this.contactCenters);
|
|
3964
|
+
}
|
|
3965
|
+
/** Call after an import has run so the donut picks up the new counts. */
|
|
3966
|
+
refresh() {
|
|
3967
|
+
this.load();
|
|
3968
|
+
}
|
|
3969
|
+
load() {
|
|
3970
|
+
const customerId = Number(this.customerId);
|
|
3971
|
+
this.ccImportService.useMockData = this.useMockData;
|
|
3972
|
+
if (!this.useMockData && (!customerId || !this.token)) {
|
|
3973
|
+
return;
|
|
3974
|
+
}
|
|
3975
|
+
if (this.host) {
|
|
3976
|
+
this.ccImportService.setHost(this.host);
|
|
3977
|
+
}
|
|
3978
|
+
if (this.dcpHost) {
|
|
3979
|
+
this.ccImportService.setDcpHost(this.dcpHost);
|
|
3980
|
+
}
|
|
3981
|
+
if (this.token) {
|
|
3982
|
+
this.ccImportService.setToken(this.token);
|
|
3983
|
+
}
|
|
3984
|
+
this.subscription?.unsubscribe();
|
|
3985
|
+
this.loading = true;
|
|
3986
|
+
this.error = false;
|
|
3987
|
+
this.subscription = this.ccImportService.fetchContactCenters(customerId)
|
|
3988
|
+
.pipe(catchError(error => {
|
|
3989
|
+
console.error('Unable to load the contact centers', error);
|
|
3990
|
+
this.error = true;
|
|
3991
|
+
return of([]);
|
|
3992
|
+
}), finalize(() => this.loading = false))
|
|
3993
|
+
.subscribe(contactCenters => {
|
|
3994
|
+
this.contactCenters = contactCenters;
|
|
3995
|
+
this.buildViewModel();
|
|
3996
|
+
});
|
|
3997
|
+
}
|
|
3998
|
+
buildViewModel() {
|
|
3999
|
+
this.total = this.contactCenters.length;
|
|
4000
|
+
this.segments = IMPORT_PANEL_SLICES.map(slice => {
|
|
4001
|
+
const count = this.contactCenters.filter(slice.match).length;
|
|
4002
|
+
return {
|
|
4003
|
+
label: slice.label,
|
|
4004
|
+
color: slice.color,
|
|
4005
|
+
tooltip: slice.tooltip,
|
|
4006
|
+
count,
|
|
4007
|
+
percentage: this.total > 0 ? Math.round((count / this.total) * 100) : 0
|
|
4008
|
+
};
|
|
4009
|
+
});
|
|
4010
|
+
this.chartOptions = this.buildChartOptions();
|
|
4011
|
+
}
|
|
4012
|
+
buildChartOptions() {
|
|
4013
|
+
const charted = this.segments.filter(segment => segment.count > 0);
|
|
4014
|
+
const isEmpty = charted.length === 0;
|
|
4015
|
+
return {
|
|
4016
|
+
series: isEmpty ? [1] : charted.map(segment => segment.count),
|
|
4017
|
+
labels: isEmpty ? ['No contact centers'] : charted.map(segment => segment.label),
|
|
4018
|
+
colors: isEmpty ? [CHART_COLORS.webexContactCenter] : charted.map(segment => segment.color),
|
|
4019
|
+
chart: {
|
|
4020
|
+
type: 'donut',
|
|
4021
|
+
height: 150,
|
|
4022
|
+
width: 150,
|
|
4023
|
+
sparkline: { enabled: true }
|
|
4024
|
+
},
|
|
4025
|
+
legend: { show: false },
|
|
4026
|
+
plotOptions: {
|
|
4027
|
+
pie: {
|
|
4028
|
+
expandOnClick: false,
|
|
4029
|
+
donut: { size: '72%' }
|
|
4030
|
+
}
|
|
4031
|
+
},
|
|
4032
|
+
dataLabels: { enabled: false },
|
|
4033
|
+
tooltip: { enabled: !isEmpty },
|
|
4034
|
+
states: {
|
|
4035
|
+
active: { filter: { type: 'none', value: 0 } }
|
|
4036
|
+
}
|
|
4037
|
+
};
|
|
4038
|
+
}
|
|
4039
|
+
}
|
|
4040
|
+
ImportContactCenterPanelComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ImportContactCenterPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4041
|
+
ImportContactCenterPanelComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: ImportContactCenterPanelComponent, selector: "cc-import-contact-center-panel", inputs: { customerId: "customerId", host: "host", dcpHost: "dcpHost", token: "token", useMockData: "useMockData" }, outputs: { importRequested: "importRequested" }, usesOnChanges: true, ngImport: i0, template: "<tk-card [title]=\"title\" [dense]=\"true\">\n <ng-container ngProjectAs=\"card-title-adornment\">\n <span class=\"cc-import-panel__info\" [matTooltip]=\"tooltip\">\n <ng-container *ngTemplateOutlet=\"infoIcon\"></ng-container>\n </span>\n </ng-container>\n\n <ng-container ngProjectAs=\"card-content\">\n <div class=\"cc-import-panel__body\" *ngIf=\"!loading && !error; else panelState\">\n <div class=\"cc-import-panel__chart\">\n <apx-chart\n [series]=\"chartOptions.series\"\n [chart]=\"chartOptions.chart\"\n [labels]=\"chartOptions.labels\"\n [colors]=\"chartOptions.colors\"\n [legend]=\"chartOptions.legend\"\n [plotOptions]=\"chartOptions.plotOptions\"\n [dataLabels]=\"chartOptions.dataLabels\"\n [tooltip]=\"chartOptions.tooltip\"\n [states]=\"chartOptions.states\"\n ></apx-chart>\n <div class=\"cc-import-panel__chart-center\">\n <span class=\"cc-import-panel__chart-total\">{{ total | number }}</span>\n <span class=\"cc-import-panel__chart-caption\">Contact Center</span>\n </div>\n </div>\n\n <ul class=\"cc-import-panel__legend\">\n <li class=\"cc-import-panel__legend-item\" *ngFor=\"let segment of segments\">\n <span class=\"cc-import-panel__legend-marker\" [style.backgroundColor]=\"segment.color\"></span>\n <span class=\"cc-import-panel__legend-label\">{{ segment.label }}</span>\n <span class=\"cc-import-panel__info\" [matTooltip]=\"segment.tooltip\">\n <ng-container *ngTemplateOutlet=\"infoIcon\"></ng-container>\n </span>\n <span class=\"cc-import-panel__legend-value\">{{ segment.count | number }} ({{ segment.percentage }}%)</span>\n </li>\n </ul>\n </div>\n\n <ng-template #panelState>\n <div class=\"cc-import-panel__state\" *ngIf=\"loading\">Loading contact centers\u2026</div>\n <div class=\"cc-import-panel__state cc-import-panel__state--error\" *ngIf=\"!loading && error\">\n Unable to load contact centers.\n </div>\n </ng-template>\n\n <div class=\"cc-import-panel__actions\">\n <button\n type=\"button\"\n class=\"cc-import-panel__button\"\n [disabled]=\"loading || error\"\n (click)=\"onImport()\"\n >\n Import\n </button>\n </div>\n </ng-container>\n</tk-card>\n\n<ng-template #infoIcon>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"8\" cy=\"8\" r=\"6.5\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <path d=\"M8 7.2v4\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n <circle cx=\"8\" cy=\"5\" r=\"0.85\" fill=\"currentColor\" />\n </svg>\n</ng-template>\n", styles: ["@import\"https://fonts.googleapis.com/css?family=Poppins:400,100,200,300,500,600,800,700,900\";@import\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400&display=swap\";:host{display:block;box-sizing:border-box;font-family:Inter,Inter,sans-serif}.cc-import-panel__info{display:inline-flex;align-items:center;color:#636363;cursor:help}.cc-import-panel__body{display:flex;align-items:center;gap:1.5rem;flex-wrap:wrap;width:100%}.cc-import-panel__chart{position:relative;width:150px;height:150px;flex-shrink:0}.cc-import-panel__chart-center{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;pointer-events:none}.cc-import-panel__chart-total{font-size:1.625rem;line-height:1.1;color:#252525}.cc-import-panel__chart-caption{font-size:.6875rem;color:#636363}.cc-import-panel__legend{display:flex;flex-direction:column;gap:.625rem;margin:0;padding:0;list-style:none;min-width:17.5rem;flex:1}.cc-import-panel__legend-item{display:flex;align-items:center;gap:.5rem;font-size:.75rem;color:#636363}.cc-import-panel__legend-marker{width:10px;height:10px;border-radius:2px;flex-shrink:0}.cc-import-panel__legend-label{color:#252525}.cc-import-panel__legend-value{margin-left:auto;white-space:nowrap}.cc-import-panel__state{font-size:.875rem;color:#636363}.cc-import-panel__state--error{color:#bc1e1b}.cc-import-panel__actions{display:flex;margin-top:auto}.cc-import-panel__button{display:inline-flex;align-items:center;gap:.25rem;padding:.5rem 1rem;background:#fff;border:1px solid rgba(0,0,0,.5019607843);border-radius:6.25rem;font-family:inherit;font-size:.875rem;font-weight:500;color:#000000f2;cursor:pointer;white-space:nowrap}.cc-import-panel__button:hover:not(:disabled){background:#ededed}.cc-import-panel__button:disabled{color:#babcc5;border-color:#dfe3e7;cursor:not-allowed}@media (max-width: 560px){.cc-import-panel__legend{min-width:0}}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i5.ChartComponent, selector: "apx-chart", inputs: ["chart", "annotations", "colors", "dataLabels", "series", "stroke", "labels", "legend", "markers", "noData", "fill", "tooltip", "plotOptions", "responsive", "xaxis", "yaxis", "forecastDataPoints", "grid", "states", "title", "subtitle", "theme", "autoUpdateSeries"] }, { kind: "directive", type: i3$1.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }, { kind: "component", type: CardComponent, selector: "tk-card", inputs: ["title", "dense"] }, { kind: "pipe", type: i1$1.DecimalPipe, name: "number" }] });
|
|
4042
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ImportContactCenterPanelComponent, decorators: [{
|
|
4043
|
+
type: Component,
|
|
4044
|
+
args: [{ selector: 'cc-import-contact-center-panel', template: "<tk-card [title]=\"title\" [dense]=\"true\">\n <ng-container ngProjectAs=\"card-title-adornment\">\n <span class=\"cc-import-panel__info\" [matTooltip]=\"tooltip\">\n <ng-container *ngTemplateOutlet=\"infoIcon\"></ng-container>\n </span>\n </ng-container>\n\n <ng-container ngProjectAs=\"card-content\">\n <div class=\"cc-import-panel__body\" *ngIf=\"!loading && !error; else panelState\">\n <div class=\"cc-import-panel__chart\">\n <apx-chart\n [series]=\"chartOptions.series\"\n [chart]=\"chartOptions.chart\"\n [labels]=\"chartOptions.labels\"\n [colors]=\"chartOptions.colors\"\n [legend]=\"chartOptions.legend\"\n [plotOptions]=\"chartOptions.plotOptions\"\n [dataLabels]=\"chartOptions.dataLabels\"\n [tooltip]=\"chartOptions.tooltip\"\n [states]=\"chartOptions.states\"\n ></apx-chart>\n <div class=\"cc-import-panel__chart-center\">\n <span class=\"cc-import-panel__chart-total\">{{ total | number }}</span>\n <span class=\"cc-import-panel__chart-caption\">Contact Center</span>\n </div>\n </div>\n\n <ul class=\"cc-import-panel__legend\">\n <li class=\"cc-import-panel__legend-item\" *ngFor=\"let segment of segments\">\n <span class=\"cc-import-panel__legend-marker\" [style.backgroundColor]=\"segment.color\"></span>\n <span class=\"cc-import-panel__legend-label\">{{ segment.label }}</span>\n <span class=\"cc-import-panel__info\" [matTooltip]=\"segment.tooltip\">\n <ng-container *ngTemplateOutlet=\"infoIcon\"></ng-container>\n </span>\n <span class=\"cc-import-panel__legend-value\">{{ segment.count | number }} ({{ segment.percentage }}%)</span>\n </li>\n </ul>\n </div>\n\n <ng-template #panelState>\n <div class=\"cc-import-panel__state\" *ngIf=\"loading\">Loading contact centers\u2026</div>\n <div class=\"cc-import-panel__state cc-import-panel__state--error\" *ngIf=\"!loading && error\">\n Unable to load contact centers.\n </div>\n </ng-template>\n\n <div class=\"cc-import-panel__actions\">\n <button\n type=\"button\"\n class=\"cc-import-panel__button\"\n [disabled]=\"loading || error\"\n (click)=\"onImport()\"\n >\n Import\n </button>\n </div>\n </ng-container>\n</tk-card>\n\n<ng-template #infoIcon>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\n <circle cx=\"8\" cy=\"8\" r=\"6.5\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <path d=\"M8 7.2v4\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n <circle cx=\"8\" cy=\"5\" r=\"0.85\" fill=\"currentColor\" />\n </svg>\n</ng-template>\n", styles: ["@import\"https://fonts.googleapis.com/css?family=Poppins:400,100,200,300,500,600,800,700,900\";@import\"https://fonts.googleapis.com/css2?family=Inter:wght@300;400&display=swap\";:host{display:block;box-sizing:border-box;font-family:Inter,Inter,sans-serif}.cc-import-panel__info{display:inline-flex;align-items:center;color:#636363;cursor:help}.cc-import-panel__body{display:flex;align-items:center;gap:1.5rem;flex-wrap:wrap;width:100%}.cc-import-panel__chart{position:relative;width:150px;height:150px;flex-shrink:0}.cc-import-panel__chart-center{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;pointer-events:none}.cc-import-panel__chart-total{font-size:1.625rem;line-height:1.1;color:#252525}.cc-import-panel__chart-caption{font-size:.6875rem;color:#636363}.cc-import-panel__legend{display:flex;flex-direction:column;gap:.625rem;margin:0;padding:0;list-style:none;min-width:17.5rem;flex:1}.cc-import-panel__legend-item{display:flex;align-items:center;gap:.5rem;font-size:.75rem;color:#636363}.cc-import-panel__legend-marker{width:10px;height:10px;border-radius:2px;flex-shrink:0}.cc-import-panel__legend-label{color:#252525}.cc-import-panel__legend-value{margin-left:auto;white-space:nowrap}.cc-import-panel__state{font-size:.875rem;color:#636363}.cc-import-panel__state--error{color:#bc1e1b}.cc-import-panel__actions{display:flex;margin-top:auto}.cc-import-panel__button{display:inline-flex;align-items:center;gap:.25rem;padding:.5rem 1rem;background:#fff;border:1px solid rgba(0,0,0,.5019607843);border-radius:6.25rem;font-family:inherit;font-size:.875rem;font-weight:500;color:#000000f2;cursor:pointer;white-space:nowrap}.cc-import-panel__button:hover:not(:disabled){background:#ededed}.cc-import-panel__button:disabled{color:#babcc5;border-color:#dfe3e7;cursor:not-allowed}@media (max-width: 560px){.cc-import-panel__legend{min-width:0}}\n"] }]
|
|
4045
|
+
}], propDecorators: { customerId: [{
|
|
4046
|
+
type: Input
|
|
4047
|
+
}], host: [{
|
|
4048
|
+
type: Input
|
|
4049
|
+
}], dcpHost: [{
|
|
4050
|
+
type: Input
|
|
4051
|
+
}], token: [{
|
|
4052
|
+
type: Input
|
|
4053
|
+
}], useMockData: [{
|
|
4054
|
+
type: Input
|
|
4055
|
+
}], importRequested: [{
|
|
4056
|
+
type: Output
|
|
4057
|
+
}] } });
|
|
4058
|
+
|
|
4059
|
+
/** The wizard's steps, in order. Index-aligned with the `mat-stepper` steps. */
|
|
4060
|
+
const IMPORT_WIZARD_STEPS = ['select', 'overview'];
|
|
4061
|
+
|
|
4062
|
+
/**
|
|
4063
|
+
* Full-page "Import contact center" wizard: pick a UCCX contact center, review the
|
|
4064
|
+
* entities Tuki will create for it, then start the import.
|
|
4065
|
+
*
|
|
4066
|
+
* Rendered as a page by the host app (like `tk-user-creation-wizard`), not as a dialog -
|
|
4067
|
+
* the host swaps it in when the panel's Import button is pressed and swaps it back out
|
|
4068
|
+
* on `cancel` / `imported`.
|
|
4069
|
+
*/
|
|
4070
|
+
class ImportContactCenterWizardComponent {
|
|
4071
|
+
constructor() {
|
|
4072
|
+
this.title = IMPORT_WIZARD_TITLE;
|
|
4073
|
+
/** Serve mock data instead of calling the UCCX migration API. */
|
|
4074
|
+
this.useMockData = true;
|
|
4075
|
+
/**
|
|
4076
|
+
* Contact centers the caller has already loaded (the panel has them), so the wizard
|
|
4077
|
+
* opens without a second round trip. Left unset, the wizard fetches them itself.
|
|
4078
|
+
*/
|
|
4079
|
+
this.contactCenters = null;
|
|
4080
|
+
this.cancel = new EventEmitter();
|
|
4081
|
+
this.imported = new EventEmitter();
|
|
4082
|
+
this.ccImportService = inject(CcImportService);
|
|
4083
|
+
this.subscriptions = new Subscription();
|
|
4084
|
+
this.customerAssistNotice = CUSTOMER_ASSIST_NOTICE;
|
|
4085
|
+
this.customerAssistMarkerTooltip = CUSTOMER_ASSIST_MARKER_TOOLTIP;
|
|
4086
|
+
this.selectedIndex = 0;
|
|
4087
|
+
/** Contact centers awaiting review - the ones there is something to import. */
|
|
4088
|
+
this.options = [];
|
|
4089
|
+
/** `options` narrowed by the picker's search box. */
|
|
4090
|
+
this.filteredOptions = [];
|
|
4091
|
+
this.searchTerm = '';
|
|
4092
|
+
this.selected = null;
|
|
4093
|
+
this.noticeDismissed = false;
|
|
4094
|
+
this.loading = false;
|
|
4095
|
+
this.error = false;
|
|
4096
|
+
this.entityGroups = [];
|
|
4097
|
+
/** Which entity group the Overview step's tab bar is showing. */
|
|
4098
|
+
this.activeGroupIndex = 0;
|
|
4099
|
+
this.entitiesLoading = false;
|
|
4100
|
+
this.entitiesError = false;
|
|
4101
|
+
this.importing = false;
|
|
4102
|
+
this.importError = false;
|
|
4103
|
+
}
|
|
4104
|
+
ngOnInit() {
|
|
4105
|
+
this.load();
|
|
4106
|
+
}
|
|
4107
|
+
ngOnChanges(changes) {
|
|
4108
|
+
const reloadTriggers = ['customerId', 'host', 'dcpHost', 'token', 'useMockData', 'contactCenters'];
|
|
4109
|
+
if (reloadTriggers.some(input => changes[input] && !changes[input].firstChange)) {
|
|
4110
|
+
this.load();
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
ngOnDestroy() {
|
|
4114
|
+
this.subscriptions.unsubscribe();
|
|
4115
|
+
}
|
|
4116
|
+
get canGoNext() {
|
|
4117
|
+
return !!this.selected;
|
|
4118
|
+
}
|
|
4119
|
+
/** The Customer Assist advisory, until the user dismisses it or picks something else. */
|
|
4120
|
+
get showCustomerAssistNotice() {
|
|
4121
|
+
return !!this.selected?.customerAssistCapable && !this.noticeDismissed;
|
|
4122
|
+
}
|
|
4123
|
+
get canImport() {
|
|
4124
|
+
return !!this.selected && !this.importing && !this.entitiesLoading && !this.entitiesError;
|
|
4125
|
+
}
|
|
4126
|
+
onCancel() {
|
|
4127
|
+
this.cancel.emit();
|
|
4128
|
+
}
|
|
4129
|
+
onSelect(applicationId) {
|
|
4130
|
+
const selected = this.options.find(option => option.applicationId === applicationId) ?? null;
|
|
4131
|
+
if (selected === this.selected) {
|
|
4132
|
+
return;
|
|
4133
|
+
}
|
|
4134
|
+
this.selected = selected;
|
|
4135
|
+
this.noticeDismissed = false;
|
|
4136
|
+
// Entities belong to the previously selected contact center - drop them so the
|
|
4137
|
+
// overview step refetches for the new one.
|
|
4138
|
+
this.entityGroups = [];
|
|
4139
|
+
this.activeGroupIndex = 0;
|
|
4140
|
+
this.entitiesError = false;
|
|
4141
|
+
}
|
|
4142
|
+
get activeGroup() {
|
|
4143
|
+
return this.entityGroups[this.activeGroupIndex];
|
|
4144
|
+
}
|
|
4145
|
+
selectGroup(index) {
|
|
4146
|
+
this.activeGroupIndex = index;
|
|
4147
|
+
}
|
|
4148
|
+
dismissNotice() {
|
|
4149
|
+
this.noticeDismissed = true;
|
|
4150
|
+
}
|
|
4151
|
+
onSearch(event) {
|
|
4152
|
+
this.searchTerm = event.target.value;
|
|
4153
|
+
this.filter();
|
|
4154
|
+
}
|
|
4155
|
+
/** Resets the search box each time the picker's panel is closed. */
|
|
4156
|
+
onPanelToggle(opened) {
|
|
4157
|
+
if (!opened && this.searchTerm) {
|
|
4158
|
+
this.searchTerm = '';
|
|
4159
|
+
this.filter();
|
|
4160
|
+
}
|
|
4161
|
+
}
|
|
4162
|
+
onStepSelectionChange(event) {
|
|
4163
|
+
this.selectedIndex = event.selectedIndex;
|
|
4164
|
+
if (this.stepIdAt(event.selectedIndex) === 'overview') {
|
|
4165
|
+
this.loadEntities();
|
|
4166
|
+
}
|
|
4167
|
+
}
|
|
4168
|
+
onNext(stepper) {
|
|
4169
|
+
if (this.canGoNext) {
|
|
4170
|
+
stepper.next();
|
|
4171
|
+
}
|
|
4172
|
+
}
|
|
4173
|
+
onPrevious(stepper) {
|
|
4174
|
+
stepper.previous();
|
|
4175
|
+
}
|
|
4176
|
+
runImport() {
|
|
4177
|
+
if (!this.selected || this.importing) {
|
|
4178
|
+
return;
|
|
4179
|
+
}
|
|
4180
|
+
this.importing = true;
|
|
4181
|
+
this.importError = false;
|
|
4182
|
+
this.subscriptions.add(this.ccImportService.importContactCenter(Number(this.customerId), this.selected.applicationId)
|
|
4183
|
+
.pipe(catchError(error => {
|
|
4184
|
+
console.error('Unable to start the contact-center import', error);
|
|
4185
|
+
this.importError = true;
|
|
4186
|
+
return of(null);
|
|
4187
|
+
}), finalize(() => this.importing = false))
|
|
4188
|
+
.subscribe(result => {
|
|
4189
|
+
if (result) {
|
|
4190
|
+
this.imported.emit(result);
|
|
4191
|
+
}
|
|
4192
|
+
}));
|
|
4193
|
+
}
|
|
4194
|
+
stepIdAt(index) {
|
|
4195
|
+
return IMPORT_WIZARD_STEPS[index];
|
|
4196
|
+
}
|
|
4197
|
+
load() {
|
|
4198
|
+
this.ccImportService.useMockData = this.useMockData;
|
|
4199
|
+
if (this.host) {
|
|
4200
|
+
this.ccImportService.setHost(this.host);
|
|
4201
|
+
}
|
|
4202
|
+
if (this.dcpHost) {
|
|
4203
|
+
this.ccImportService.setDcpHost(this.dcpHost);
|
|
4204
|
+
}
|
|
4205
|
+
if (this.token) {
|
|
4206
|
+
this.ccImportService.setToken(this.token);
|
|
4207
|
+
}
|
|
4208
|
+
if (this.contactCenters) {
|
|
4209
|
+
this.setOptions(this.contactCenters);
|
|
4210
|
+
return;
|
|
4211
|
+
}
|
|
4212
|
+
const customerId = Number(this.customerId);
|
|
4213
|
+
if (!this.useMockData && (!customerId || !this.token)) {
|
|
4214
|
+
return;
|
|
4215
|
+
}
|
|
4216
|
+
this.loading = true;
|
|
4217
|
+
this.error = false;
|
|
4218
|
+
this.subscriptions.add(this.ccImportService.fetchContactCenters(customerId)
|
|
4219
|
+
.pipe(catchError(error => {
|
|
4220
|
+
console.error('Unable to load the contact centers', error);
|
|
4221
|
+
this.error = true;
|
|
4222
|
+
return of([]);
|
|
4223
|
+
}), finalize(() => this.loading = false))
|
|
4224
|
+
.subscribe(contactCenters => this.setOptions(contactCenters)));
|
|
4225
|
+
}
|
|
4226
|
+
setOptions(contactCenters) {
|
|
4227
|
+
// Left in source order on purpose - the picker shows them the way the backend
|
|
4228
|
+
// (and the mock) hands them over, not alphabetically.
|
|
4229
|
+
//
|
|
4230
|
+
// Eligible for a Webex Contact Center import: not imported yet, and either
|
|
4231
|
+
// unsupported on Customer Assist or reviewable for full feature coverage.
|
|
4232
|
+
this.options = contactCenters.filter(contactCenter => contactCenter.migrationState !== 'migrated'
|
|
4233
|
+
&& (contactCenter.compatibilityStatus === 'unsupported'
|
|
4234
|
+
|| contactCenter.compatibilityStatus === 'requires-review'));
|
|
4235
|
+
// A contact center selected before a reload may no longer be importable.
|
|
4236
|
+
if (this.selected && !this.options.some(option => option.applicationId === this.selected?.applicationId)) {
|
|
4237
|
+
this.selected = null;
|
|
4238
|
+
this.entityGroups = [];
|
|
4239
|
+
this.activeGroupIndex = 0;
|
|
4240
|
+
}
|
|
4241
|
+
this.filter();
|
|
4242
|
+
}
|
|
4243
|
+
filter() {
|
|
4244
|
+
const needle = this.searchTerm.trim().toLowerCase();
|
|
4245
|
+
this.filteredOptions = needle
|
|
4246
|
+
? this.options.filter(option => option.applicationName.toLowerCase().includes(needle))
|
|
4247
|
+
: this.options;
|
|
4248
|
+
}
|
|
4249
|
+
loadEntities() {
|
|
4250
|
+
if (!this.selected || this.entityGroups.length || this.entitiesLoading) {
|
|
4251
|
+
return;
|
|
4252
|
+
}
|
|
4253
|
+
this.entitiesLoading = true;
|
|
4254
|
+
this.entitiesError = false;
|
|
4255
|
+
this.subscriptions.add(this.ccImportService.fetchContactCenterEntities(Number(this.customerId), this.selected)
|
|
4256
|
+
.pipe(catchError(error => {
|
|
4257
|
+
console.error('Unable to load the contact-center entities', error);
|
|
4258
|
+
this.entitiesError = true;
|
|
4259
|
+
return of(null);
|
|
4260
|
+
}), finalize(() => this.entitiesLoading = false))
|
|
4261
|
+
.subscribe(entities => {
|
|
4262
|
+
this.entityGroups = entities ? entities.groups : [];
|
|
4263
|
+
this.activeGroupIndex = 0;
|
|
4264
|
+
}));
|
|
4265
|
+
}
|
|
4266
|
+
}
|
|
4267
|
+
ImportContactCenterWizardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ImportContactCenterWizardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4268
|
+
ImportContactCenterWizardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: ImportContactCenterWizardComponent, selector: "cc-import-contact-center-wizard", inputs: { title: "title", customerId: "customerId", host: "host", dcpHost: "dcpHost", token: "token", useMockData: "useMockData", contactCenters: "contactCenters" }, outputs: { cancel: "cancel", imported: "imported" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"move-user-stepper-container\" [attr.aria-busy]=\"importing\">\n <header class=\"move-user-header\">\n <div class=\"move-user-header__title\">{{ title }}</div>\n <div class=\"move-user-header__close\">\n <span class=\"app-icon cc-import-close-icon\" (click)=\"onCancel()\" role=\"button\" aria-label=\"Close\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M5 5L19 19M19 5L5 19\" stroke=\"currentColor\" stroke-width=\"1.7\" stroke-linecap=\"round\" />\n </svg>\n </span>\n </div>\n </header>\n\n <div id=\"cc-import-stepper\">\n <mat-stepper\n #stepper\n [linear]=\"true\"\n labelPosition=\"bottom\"\n [selectedIndex]=\"selectedIndex\"\n (selectionChange)=\"onStepSelectionChange($event)\"\n >\n\n <!-- Step 1: pick the UCCX contact center -->\n <mat-step [completed]=\"canGoNext\">\n <ng-template matStepLabel>Select</ng-template>\n\n <div class=\"cc-import-notice\" *ngIf=\"showCustomerAssistNotice\" role=\"status\">\n <span class=\"cc-import-notice__icon\" aria-hidden=\"true\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\">\n <circle cx=\"8\" cy=\"8\" r=\"7\" fill=\"currentColor\" />\n <path d=\"M8 7.1v4.2\" stroke=\"#fff\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n <circle cx=\"8\" cy=\"4.8\" r=\"0.9\" fill=\"#fff\" />\n </svg>\n </span>\n <span class=\"cc-import-notice__text\">{{ customerAssistNotice }}</span>\n <button type=\"button\" class=\"cc-import-notice__close\" aria-label=\"Dismiss\" (click)=\"dismissNotice()\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M5 5L19 19M19 5L5 19\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" />\n </svg>\n </button>\n </div>\n\n <div class=\"step-title\">Step 1: Select contact center</div>\n\n <div class=\"move-user-tab-content-box\">\n <div class=\"cc-import-state\" *ngIf=\"loading\">Loading contact centers…</div>\n <div class=\"cc-import-state cc-import-state--error\" *ngIf=\"!loading && error\">\n Unable to load contact centers.\n </div>\n\n <section class=\"cc-import-step-content\" *ngIf=\"!loading && !error\">\n <div class=\"cc-import-field\">\n <label class=\"cc-import-label\" for=\"cc-import-contact-center\">Contact center</label>\n\n <mat-form-field class=\"cc-import-select\" appearance=\"outline\">\n <mat-select\n id=\"cc-import-contact-center\"\n panelClass=\"cc-import-select-panel\"\n placeholder=\"Select contact center\"\n [disableOptionCentering]=\"true\"\n [value]=\"selected?.applicationId ?? null\"\n (selectionChange)=\"onSelect($event.value)\"\n (openedChange)=\"onPanelToggle($event)\"\n >\n <mat-select-trigger>{{ selected?.applicationName }}</mat-select-trigger>\n\n <!-- Search box pinned above the options. Keystrokes are stopped here so\n mat-select's own typeahead does not steal them. -->\n <div class=\"cc-import-select-search\" (click)=\"$event.stopPropagation()\">\n <span class=\"cc-import-select-search__icon\" aria-hidden=\"true\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\">\n <circle cx=\"7\" cy=\"7\" r=\"4.6\" stroke=\"currentColor\" stroke-width=\"1.4\" />\n <path d=\"M10.6 10.6L14 14\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n </svg>\n </span>\n <input\n class=\"cc-import-select-search__input\"\n type=\"text\"\n autocomplete=\"off\"\n placeholder=\"Search\"\n aria-label=\"Search contact centers\"\n [value]=\"searchTerm\"\n (input)=\"onSearch($event)\"\n (keydown)=\"$event.stopPropagation()\"\n />\n </div>\n\n <mat-option *ngFor=\"let option of filteredOptions\" [value]=\"option.applicationId\">\n <div class=\"option-row\">\n <span class=\"name\">{{ option.applicationName }}</span>\n <span\n class=\"cc-import-option-marker\"\n *ngIf=\"option.customerAssistCapable\"\n [matTooltip]=\"customerAssistMarkerTooltip\"\n aria-hidden=\"true\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M8 2.2 14.6 13.4H1.4L8 2.2Z\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linejoin=\"round\" />\n <path d=\"M8 6.5v3\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\" />\n <circle cx=\"8\" cy=\"11.3\" r=\"0.7\" fill=\"currentColor\" />\n </svg>\n </span>\n </div>\n </mat-option>\n </mat-select>\n </mat-form-field>\n\n <p class=\"cc-import-empty\" *ngIf=\"!options.length\">\n Every detected contact center has already been imported.\n </p>\n </div>\n </section>\n </div>\n </mat-step>\n\n <!-- Step 2: the entities Tuki will create for it -->\n <mat-step>\n <ng-template matStepLabel>Overview</ng-template>\n\n <div class=\"step-title\">Overview</div>\n\n <div class=\"move-user-tab-content-box\">\n <h3 class=\"cc-import-card-title\">Review Contact Center Settings</h3>\n <p class=\"cc-import-card-text\">\n Review the settings for your new Contact center to make sure everything is correct.<br />\n You can make changes at any time by clicking on the name of the contact center on the dedicated page.\n </p>\n\n <div class=\"cc-import-state\" *ngIf=\"entitiesLoading\">Loading entities…</div>\n <div class=\"cc-import-state cc-import-state--error\" *ngIf=\"entitiesError\">\n Unable to load the related entities.\n </div>\n\n <ng-container *ngIf=\"!entitiesLoading && !entitiesError && entityGroups.length\">\n <div class=\"cc-import-tabs\" role=\"tablist\">\n <button\n type=\"button\"\n class=\"cc-import-tab\"\n *ngFor=\"let group of entityGroups; index as i\"\n role=\"tab\"\n [attr.aria-selected]=\"i === activeGroupIndex\"\n [class.cc-import-tab--active]=\"i === activeGroupIndex\"\n (click)=\"selectGroup(i)\"\n >\n {{ group.label }}\n </button>\n </div>\n\n <ul class=\"cc-import-items\" role=\"tabpanel\">\n <li class=\"cc-import-item\" *ngFor=\"let item of activeGroup?.items\">{{ item }}</li>\n <li class=\"cc-import-item cc-import-item--empty\" *ngIf=\"!activeGroup?.items?.length\">\n Nothing to create for {{ activeGroup?.label }}.\n </li>\n </ul>\n </ng-container>\n\n <div class=\"cc-import-state cc-import-state--error\" *ngIf=\"importError\">\n Unable to start the import. Please try again.\n </div>\n </div>\n </mat-step>\n </mat-stepper>\n\n <div class=\"wizard-actions\">\n <ng-container [ngSwitch]=\"stepper.selectedIndex\">\n <ng-container *ngSwitchCase=\"0\">\n <button\n mat-button\n class=\"uc-btn uc-btn-primary\"\n type=\"button\"\n [disabled]=\"!canGoNext\"\n (click)=\"onNext(stepper)\"\n >\n Next\n </button>\n <button mat-button class=\"uc-btn uc-btn-secondary\" type=\"button\" (click)=\"onCancel()\">\n Cancel\n </button>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"1\">\n <button\n mat-button\n class=\"uc-btn uc-btn-primary\"\n type=\"button\"\n [disabled]=\"!canImport\"\n (click)=\"runImport()\"\n >\n <span class=\"uc-btn__inline-content\">\n <span class=\"uc-btn__spinner\" *ngIf=\"importing\" aria-hidden=\"true\"></span>\n <span>{{ importing ? 'Importing...' : 'Import' }}</span>\n </span>\n </button>\n <button\n mat-button\n class=\"uc-btn uc-btn-secondary\"\n type=\"button\"\n [disabled]=\"importing\"\n (click)=\"onPrevious(stepper)\"\n >\n Previous\n </button>\n </ng-container>\n </ng-container>\n </div>\n </div>\n\n <div class=\"wizard-loading-overlay\" [class.wizard-loading-overlay--visible]=\"importing\" aria-hidden=\"true\"></div>\n</div>\n", styles: ["cc-import-contact-center-wizard{display:block;width:100%;min-height:100vh}.move-user-stepper-container{background-color:#f3f3f3;padding:0 0 16px;position:relative;overflow:hidden;box-sizing:border-box;min-height:100vh;display:flex;flex-direction:column}.move-user-header{background-color:#1170cf;height:45px;padding:1rem 1.5rem;display:flex;align-items:center;justify-content:space-between;color:#fff;font-size:18px;font-weight:500}.move-user-header__close{cursor:pointer;display:flex;align-items:center;justify-content:center;line-height:0}.app-icon{height:20px;width:20px;display:block;cursor:pointer}.cc-import-close-icon{color:#fffffff2}#cc-import-stepper{display:flex;flex-direction:column;width:100%;flex:1 1 auto;min-height:0}#cc-import-stepper .mat-stepper-horizontal{margin:0 auto;width:100%;background-color:transparent;display:flex;flex-direction:column;flex:1 1 auto;min-height:0}#cc-import-stepper .mat-horizontal-stepper-header-container{margin:0 auto;width:800px}#cc-import-stepper .mat-step-icon{width:28px;height:28px;border:3px solid hsl(0,0%,50.2%)}#cc-import-stepper .mat-step-icon-selected{background-color:#c6c6c6!important;border-color:#19487d}#cc-import-stepper .mat-step-icon-state-number{background-color:#fff}#cc-import-stepper .mat-step-icon-state-edit,#cc-import-stepper .mat-step-header .mat-step-icon-state-done{background-color:#1170cf;border-color:#1170cf}#cc-import-stepper .mat-step-icon-content{display:none}#cc-import-stepper .mat-stepper-label-position-bottom .mat-horizontal-stepper-header:not(:first-child):before,#cc-import-stepper [dir=rtl] .mat-stepper-label-position-bottom .mat-horizontal-stepper-header:not(:last-child):before,#cc-import-stepper .mat-stepper-label-position-bottom .mat-horizontal-stepper-header:not(:last-child):after,#cc-import-stepper [dir=rtl] .mat-stepper-label-position-bottom .mat-horizontal-stepper-header:not(:first-child):after{border-top-width:3px!important}#cc-import-stepper .mat-horizontal-stepper-header:before,#cc-import-stepper .mat-horizontal-stepper-header:after,#cc-import-stepper .mat-stepper-horizontal-line{border-top-color:#1170cf!important;border-top-width:3px!important;border-top-style:solid!important}#cc-import-stepper .mat-horizontal-stepper-header[aria-selected=true]~.mat-stepper-horizontal-line,#cc-import-stepper .mat-horizontal-stepper-header[aria-selected=true]~.mat-horizontal-stepper-header:before,#cc-import-stepper .mat-horizontal-stepper-header[aria-selected=true]~.mat-horizontal-stepper-header:after{border-top-width:3px!important;background-color:#7b7b7b!important;border-top-color:#7b7b7b!important}#cc-import-stepper .mat-horizontal-content-container{padding:0!important;margin-top:3rem}#cc-import-stepper .mat-horizontal-stepper-content[aria-expanded=true]{padding-bottom:0}.step-title{font-size:26px;font-weight:500;width:70%;margin:0 auto 1rem;min-height:32px;display:flex;align-items:center}.wizard-actions{display:flex;flex-flow:row-reverse;justify-content:flex-start;align-items:center;margin:16px 16px 0 0;align-self:flex-end;gap:1rem;position:relative;z-index:30}.uc-btn{border-radius:50px!important;padding:8px 16px!important;min-width:80px!important;line-height:normal!important;cursor:pointer}.uc-btn__inline-content{display:inline-flex;align-items:center;gap:8px}.uc-btn__spinner{width:14px;height:14px;border:2px solid rgba(255,255,255,.5);border-top-color:#fff;border-radius:50%;animation:uc-btn-spin .7s linear infinite}@keyframes uc-btn-spin{to{transform:rotate(360deg)}}.uc-btn-primary{background-color:#000!important;color:#fff!important;border:1px solid black!important}.uc-btn-secondary{background-color:#fff!important;color:#000!important;border:1px solid black!important}.wizard-actions .uc-btn-primary.mat-button-disabled{background-color:#bbb;color:#fff;border:1px solid grey;cursor:default!important;opacity:.3}.move-user-tab-content-box{width:70%;margin:0 auto;background-color:#fff;min-height:350px;border:1px solid #c3c1c1;border-radius:8px;box-sizing:border-box;padding:25px}.wizard-loading-overlay{position:absolute;inset:0;background:rgba(243,243,243,.72);backdrop-filter:blur(1px);opacity:0;pointer-events:none;transition:opacity .18s ease;z-index:20}.wizard-loading-overlay--visible{opacity:1;pointer-events:auto}.cc-import-notice{width:70%;margin:0 auto 1rem;box-sizing:border-box;display:flex;align-items:center;gap:10px;padding:10px 14px;border-radius:4px;background-color:#e8f2fc;color:#2a2f3b;font-size:13px;line-height:1.4}.cc-import-notice__icon{display:flex;color:#1170cf;line-height:0}.cc-import-notice__text{flex:1}.cc-import-notice__close{background:none;border:none;padding:0;cursor:pointer;display:flex;color:#5a6172;line-height:0}.cc-import-step-content{display:flex;flex-direction:column;gap:20px;max-width:420px}.cc-import-field{display:flex;flex-direction:column;gap:10px}.cc-import-label{font-size:14px;font-weight:400;line-height:100%;letter-spacing:.1px;color:#2a2f3b}.cc-import-select{width:100%;max-width:333px}.cc-import-empty{margin:0;font-size:13px;color:#5a6172}.cc-import-state{font-size:14px;color:#5a6172}.cc-import-state--error{color:#c0392b}.cc-import-card-title{margin:0 0 8px;font-size:15px;font-weight:700;color:#2a2f3b}.cc-import-card-text{margin:0 0 22px;font-size:14px;line-height:1.6;color:#2a2f3b}.cc-import-tabs{display:flex;flex-wrap:wrap;gap:20px}.cc-import-tab{background:none;border:none;border-bottom:2px solid transparent;padding:0 0 8px;font:inherit;font-size:14px;color:#5a6172;cursor:pointer}.cc-import-tab--active{color:#000;font-weight:600;border-bottom-color:#000}.cc-import-items{list-style:none;margin:18px 0 0;padding:0;display:flex;flex-direction:column;gap:10px}.cc-import-item{font-size:14px;color:#2a2f3b}.cc-import-item--empty{color:#5a6172;font-style:italic}.cc-import-select .mat-form-field-wrapper{padding-bottom:0}.cc-import-select .mat-form-field-flex{align-items:center;height:32px;border:1px solid #767676;border-radius:6px;padding:0 12px}.cc-import-select .mat-form-field-outline,.cc-import-select .mat-form-field-outline-start,.cc-import-select .mat-form-field-outline-end,.cc-import-select .mat-form-field-outline-gap{display:none}.cc-import-select .mat-form-field-infix{width:100%;border-top:0;padding:0}.cc-import-select .mat-select-trigger{display:flex;align-items:center;justify-content:space-between;height:100%}.cc-import-select-panel .option-row{display:flex;justify-content:space-between;align-items:center;width:100%;gap:12px}.cc-import-select-panel .cc-import-option-marker{display:flex;color:#b26a00;line-height:0}.cc-import-select-search{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:8px;margin:4px 8px 8px;padding:0 10px;height:30px;border:1px solid #c3c1c1;border-radius:50px;background-color:#fff}.cc-import-select-search__icon{display:flex;color:#5a6172;line-height:0}.cc-import-select-search__input{flex:1;border:none;outline:none;font:inherit;font-size:13px;color:#2a2f3b;background:transparent}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "component", type: i2$2.MatStep, selector: "mat-step", inputs: ["color"], exportAs: ["matStep"] }, { kind: "directive", type: i2$2.MatStepLabel, selector: "[matStepLabel]" }, { kind: "component", type: i2$2.MatStepper, selector: "mat-stepper, mat-vertical-stepper, mat-horizontal-stepper, [matStepper]", inputs: ["selectedIndex", "disableRipple", "color", "labelPosition", "headerPosition", "animationDuration"], outputs: ["animationDone"], exportAs: ["matStepper", "matVerticalStepper", "matHorizontalStepper"] }, { kind: "directive", type: i3$1.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }, { kind: "component", type: i1$4.MatButton, selector: "button[mat-button], button[mat-raised-button], button[mat-icon-button], button[mat-fab], button[mat-mini-fab], button[mat-stroked-button], button[mat-flat-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "component", type: i2$1.MatFormField, selector: "mat-form-field", inputs: ["color", "appearance", "hideRequiredMarker", "hintLabel", "floatLabel"], exportAs: ["matFormField"] }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["disabled", "disableRipple", "tabIndex"], exportAs: ["matSelect"] }, { kind: "directive", type: i6.MatSelectTrigger, selector: "mat-select-trigger" }, { kind: "component", type: i3.MatOption, selector: "mat-option", exportAs: ["matOption"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
4269
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ImportContactCenterWizardComponent, decorators: [{
|
|
4270
|
+
type: Component,
|
|
4271
|
+
args: [{ selector: 'cc-import-contact-center-wizard', encapsulation: ViewEncapsulation.None, template: "<div class=\"move-user-stepper-container\" [attr.aria-busy]=\"importing\">\n <header class=\"move-user-header\">\n <div class=\"move-user-header__title\">{{ title }}</div>\n <div class=\"move-user-header__close\">\n <span class=\"app-icon cc-import-close-icon\" (click)=\"onCancel()\" role=\"button\" aria-label=\"Close\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M5 5L19 19M19 5L5 19\" stroke=\"currentColor\" stroke-width=\"1.7\" stroke-linecap=\"round\" />\n </svg>\n </span>\n </div>\n </header>\n\n <div id=\"cc-import-stepper\">\n <mat-stepper\n #stepper\n [linear]=\"true\"\n labelPosition=\"bottom\"\n [selectedIndex]=\"selectedIndex\"\n (selectionChange)=\"onStepSelectionChange($event)\"\n >\n\n <!-- Step 1: pick the UCCX contact center -->\n <mat-step [completed]=\"canGoNext\">\n <ng-template matStepLabel>Select</ng-template>\n\n <div class=\"cc-import-notice\" *ngIf=\"showCustomerAssistNotice\" role=\"status\">\n <span class=\"cc-import-notice__icon\" aria-hidden=\"true\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\">\n <circle cx=\"8\" cy=\"8\" r=\"7\" fill=\"currentColor\" />\n <path d=\"M8 7.1v4.2\" stroke=\"#fff\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n <circle cx=\"8\" cy=\"4.8\" r=\"0.9\" fill=\"#fff\" />\n </svg>\n </span>\n <span class=\"cc-import-notice__text\">{{ customerAssistNotice }}</span>\n <button type=\"button\" class=\"cc-import-notice__close\" aria-label=\"Dismiss\" (click)=\"dismissNotice()\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M5 5L19 19M19 5L5 19\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" />\n </svg>\n </button>\n </div>\n\n <div class=\"step-title\">Step 1: Select contact center</div>\n\n <div class=\"move-user-tab-content-box\">\n <div class=\"cc-import-state\" *ngIf=\"loading\">Loading contact centers…</div>\n <div class=\"cc-import-state cc-import-state--error\" *ngIf=\"!loading && error\">\n Unable to load contact centers.\n </div>\n\n <section class=\"cc-import-step-content\" *ngIf=\"!loading && !error\">\n <div class=\"cc-import-field\">\n <label class=\"cc-import-label\" for=\"cc-import-contact-center\">Contact center</label>\n\n <mat-form-field class=\"cc-import-select\" appearance=\"outline\">\n <mat-select\n id=\"cc-import-contact-center\"\n panelClass=\"cc-import-select-panel\"\n placeholder=\"Select contact center\"\n [disableOptionCentering]=\"true\"\n [value]=\"selected?.applicationId ?? null\"\n (selectionChange)=\"onSelect($event.value)\"\n (openedChange)=\"onPanelToggle($event)\"\n >\n <mat-select-trigger>{{ selected?.applicationName }}</mat-select-trigger>\n\n <!-- Search box pinned above the options. Keystrokes are stopped here so\n mat-select's own typeahead does not steal them. -->\n <div class=\"cc-import-select-search\" (click)=\"$event.stopPropagation()\">\n <span class=\"cc-import-select-search__icon\" aria-hidden=\"true\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\">\n <circle cx=\"7\" cy=\"7\" r=\"4.6\" stroke=\"currentColor\" stroke-width=\"1.4\" />\n <path d=\"M10.6 10.6L14 14\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n </svg>\n </span>\n <input\n class=\"cc-import-select-search__input\"\n type=\"text\"\n autocomplete=\"off\"\n placeholder=\"Search\"\n aria-label=\"Search contact centers\"\n [value]=\"searchTerm\"\n (input)=\"onSearch($event)\"\n (keydown)=\"$event.stopPropagation()\"\n />\n </div>\n\n <mat-option *ngFor=\"let option of filteredOptions\" [value]=\"option.applicationId\">\n <div class=\"option-row\">\n <span class=\"name\">{{ option.applicationName }}</span>\n <span\n class=\"cc-import-option-marker\"\n *ngIf=\"option.customerAssistCapable\"\n [matTooltip]=\"customerAssistMarkerTooltip\"\n aria-hidden=\"true\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M8 2.2 14.6 13.4H1.4L8 2.2Z\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linejoin=\"round\" />\n <path d=\"M8 6.5v3\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\" />\n <circle cx=\"8\" cy=\"11.3\" r=\"0.7\" fill=\"currentColor\" />\n </svg>\n </span>\n </div>\n </mat-option>\n </mat-select>\n </mat-form-field>\n\n <p class=\"cc-import-empty\" *ngIf=\"!options.length\">\n Every detected contact center has already been imported.\n </p>\n </div>\n </section>\n </div>\n </mat-step>\n\n <!-- Step 2: the entities Tuki will create for it -->\n <mat-step>\n <ng-template matStepLabel>Overview</ng-template>\n\n <div class=\"step-title\">Overview</div>\n\n <div class=\"move-user-tab-content-box\">\n <h3 class=\"cc-import-card-title\">Review Contact Center Settings</h3>\n <p class=\"cc-import-card-text\">\n Review the settings for your new Contact center to make sure everything is correct.<br />\n You can make changes at any time by clicking on the name of the contact center on the dedicated page.\n </p>\n\n <div class=\"cc-import-state\" *ngIf=\"entitiesLoading\">Loading entities…</div>\n <div class=\"cc-import-state cc-import-state--error\" *ngIf=\"entitiesError\">\n Unable to load the related entities.\n </div>\n\n <ng-container *ngIf=\"!entitiesLoading && !entitiesError && entityGroups.length\">\n <div class=\"cc-import-tabs\" role=\"tablist\">\n <button\n type=\"button\"\n class=\"cc-import-tab\"\n *ngFor=\"let group of entityGroups; index as i\"\n role=\"tab\"\n [attr.aria-selected]=\"i === activeGroupIndex\"\n [class.cc-import-tab--active]=\"i === activeGroupIndex\"\n (click)=\"selectGroup(i)\"\n >\n {{ group.label }}\n </button>\n </div>\n\n <ul class=\"cc-import-items\" role=\"tabpanel\">\n <li class=\"cc-import-item\" *ngFor=\"let item of activeGroup?.items\">{{ item }}</li>\n <li class=\"cc-import-item cc-import-item--empty\" *ngIf=\"!activeGroup?.items?.length\">\n Nothing to create for {{ activeGroup?.label }}.\n </li>\n </ul>\n </ng-container>\n\n <div class=\"cc-import-state cc-import-state--error\" *ngIf=\"importError\">\n Unable to start the import. Please try again.\n </div>\n </div>\n </mat-step>\n </mat-stepper>\n\n <div class=\"wizard-actions\">\n <ng-container [ngSwitch]=\"stepper.selectedIndex\">\n <ng-container *ngSwitchCase=\"0\">\n <button\n mat-button\n class=\"uc-btn uc-btn-primary\"\n type=\"button\"\n [disabled]=\"!canGoNext\"\n (click)=\"onNext(stepper)\"\n >\n Next\n </button>\n <button mat-button class=\"uc-btn uc-btn-secondary\" type=\"button\" (click)=\"onCancel()\">\n Cancel\n </button>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"1\">\n <button\n mat-button\n class=\"uc-btn uc-btn-primary\"\n type=\"button\"\n [disabled]=\"!canImport\"\n (click)=\"runImport()\"\n >\n <span class=\"uc-btn__inline-content\">\n <span class=\"uc-btn__spinner\" *ngIf=\"importing\" aria-hidden=\"true\"></span>\n <span>{{ importing ? 'Importing...' : 'Import' }}</span>\n </span>\n </button>\n <button\n mat-button\n class=\"uc-btn uc-btn-secondary\"\n type=\"button\"\n [disabled]=\"importing\"\n (click)=\"onPrevious(stepper)\"\n >\n Previous\n </button>\n </ng-container>\n </ng-container>\n </div>\n </div>\n\n <div class=\"wizard-loading-overlay\" [class.wizard-loading-overlay--visible]=\"importing\" aria-hidden=\"true\"></div>\n</div>\n", styles: ["cc-import-contact-center-wizard{display:block;width:100%;min-height:100vh}.move-user-stepper-container{background-color:#f3f3f3;padding:0 0 16px;position:relative;overflow:hidden;box-sizing:border-box;min-height:100vh;display:flex;flex-direction:column}.move-user-header{background-color:#1170cf;height:45px;padding:1rem 1.5rem;display:flex;align-items:center;justify-content:space-between;color:#fff;font-size:18px;font-weight:500}.move-user-header__close{cursor:pointer;display:flex;align-items:center;justify-content:center;line-height:0}.app-icon{height:20px;width:20px;display:block;cursor:pointer}.cc-import-close-icon{color:#fffffff2}#cc-import-stepper{display:flex;flex-direction:column;width:100%;flex:1 1 auto;min-height:0}#cc-import-stepper .mat-stepper-horizontal{margin:0 auto;width:100%;background-color:transparent;display:flex;flex-direction:column;flex:1 1 auto;min-height:0}#cc-import-stepper .mat-horizontal-stepper-header-container{margin:0 auto;width:800px}#cc-import-stepper .mat-step-icon{width:28px;height:28px;border:3px solid hsl(0,0%,50.2%)}#cc-import-stepper .mat-step-icon-selected{background-color:#c6c6c6!important;border-color:#19487d}#cc-import-stepper .mat-step-icon-state-number{background-color:#fff}#cc-import-stepper .mat-step-icon-state-edit,#cc-import-stepper .mat-step-header .mat-step-icon-state-done{background-color:#1170cf;border-color:#1170cf}#cc-import-stepper .mat-step-icon-content{display:none}#cc-import-stepper .mat-stepper-label-position-bottom .mat-horizontal-stepper-header:not(:first-child):before,#cc-import-stepper [dir=rtl] .mat-stepper-label-position-bottom .mat-horizontal-stepper-header:not(:last-child):before,#cc-import-stepper .mat-stepper-label-position-bottom .mat-horizontal-stepper-header:not(:last-child):after,#cc-import-stepper [dir=rtl] .mat-stepper-label-position-bottom .mat-horizontal-stepper-header:not(:first-child):after{border-top-width:3px!important}#cc-import-stepper .mat-horizontal-stepper-header:before,#cc-import-stepper .mat-horizontal-stepper-header:after,#cc-import-stepper .mat-stepper-horizontal-line{border-top-color:#1170cf!important;border-top-width:3px!important;border-top-style:solid!important}#cc-import-stepper .mat-horizontal-stepper-header[aria-selected=true]~.mat-stepper-horizontal-line,#cc-import-stepper .mat-horizontal-stepper-header[aria-selected=true]~.mat-horizontal-stepper-header:before,#cc-import-stepper .mat-horizontal-stepper-header[aria-selected=true]~.mat-horizontal-stepper-header:after{border-top-width:3px!important;background-color:#7b7b7b!important;border-top-color:#7b7b7b!important}#cc-import-stepper .mat-horizontal-content-container{padding:0!important;margin-top:3rem}#cc-import-stepper .mat-horizontal-stepper-content[aria-expanded=true]{padding-bottom:0}.step-title{font-size:26px;font-weight:500;width:70%;margin:0 auto 1rem;min-height:32px;display:flex;align-items:center}.wizard-actions{display:flex;flex-flow:row-reverse;justify-content:flex-start;align-items:center;margin:16px 16px 0 0;align-self:flex-end;gap:1rem;position:relative;z-index:30}.uc-btn{border-radius:50px!important;padding:8px 16px!important;min-width:80px!important;line-height:normal!important;cursor:pointer}.uc-btn__inline-content{display:inline-flex;align-items:center;gap:8px}.uc-btn__spinner{width:14px;height:14px;border:2px solid rgba(255,255,255,.5);border-top-color:#fff;border-radius:50%;animation:uc-btn-spin .7s linear infinite}@keyframes uc-btn-spin{to{transform:rotate(360deg)}}.uc-btn-primary{background-color:#000!important;color:#fff!important;border:1px solid black!important}.uc-btn-secondary{background-color:#fff!important;color:#000!important;border:1px solid black!important}.wizard-actions .uc-btn-primary.mat-button-disabled{background-color:#bbb;color:#fff;border:1px solid grey;cursor:default!important;opacity:.3}.move-user-tab-content-box{width:70%;margin:0 auto;background-color:#fff;min-height:350px;border:1px solid #c3c1c1;border-radius:8px;box-sizing:border-box;padding:25px}.wizard-loading-overlay{position:absolute;inset:0;background:rgba(243,243,243,.72);backdrop-filter:blur(1px);opacity:0;pointer-events:none;transition:opacity .18s ease;z-index:20}.wizard-loading-overlay--visible{opacity:1;pointer-events:auto}.cc-import-notice{width:70%;margin:0 auto 1rem;box-sizing:border-box;display:flex;align-items:center;gap:10px;padding:10px 14px;border-radius:4px;background-color:#e8f2fc;color:#2a2f3b;font-size:13px;line-height:1.4}.cc-import-notice__icon{display:flex;color:#1170cf;line-height:0}.cc-import-notice__text{flex:1}.cc-import-notice__close{background:none;border:none;padding:0;cursor:pointer;display:flex;color:#5a6172;line-height:0}.cc-import-step-content{display:flex;flex-direction:column;gap:20px;max-width:420px}.cc-import-field{display:flex;flex-direction:column;gap:10px}.cc-import-label{font-size:14px;font-weight:400;line-height:100%;letter-spacing:.1px;color:#2a2f3b}.cc-import-select{width:100%;max-width:333px}.cc-import-empty{margin:0;font-size:13px;color:#5a6172}.cc-import-state{font-size:14px;color:#5a6172}.cc-import-state--error{color:#c0392b}.cc-import-card-title{margin:0 0 8px;font-size:15px;font-weight:700;color:#2a2f3b}.cc-import-card-text{margin:0 0 22px;font-size:14px;line-height:1.6;color:#2a2f3b}.cc-import-tabs{display:flex;flex-wrap:wrap;gap:20px}.cc-import-tab{background:none;border:none;border-bottom:2px solid transparent;padding:0 0 8px;font:inherit;font-size:14px;color:#5a6172;cursor:pointer}.cc-import-tab--active{color:#000;font-weight:600;border-bottom-color:#000}.cc-import-items{list-style:none;margin:18px 0 0;padding:0;display:flex;flex-direction:column;gap:10px}.cc-import-item{font-size:14px;color:#2a2f3b}.cc-import-item--empty{color:#5a6172;font-style:italic}.cc-import-select .mat-form-field-wrapper{padding-bottom:0}.cc-import-select .mat-form-field-flex{align-items:center;height:32px;border:1px solid #767676;border-radius:6px;padding:0 12px}.cc-import-select .mat-form-field-outline,.cc-import-select .mat-form-field-outline-start,.cc-import-select .mat-form-field-outline-end,.cc-import-select .mat-form-field-outline-gap{display:none}.cc-import-select .mat-form-field-infix{width:100%;border-top:0;padding:0}.cc-import-select .mat-select-trigger{display:flex;align-items:center;justify-content:space-between;height:100%}.cc-import-select-panel .option-row{display:flex;justify-content:space-between;align-items:center;width:100%;gap:12px}.cc-import-select-panel .cc-import-option-marker{display:flex;color:#b26a00;line-height:0}.cc-import-select-search{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:8px;margin:4px 8px 8px;padding:0 10px;height:30px;border:1px solid #c3c1c1;border-radius:50px;background-color:#fff}.cc-import-select-search__icon{display:flex;color:#5a6172;line-height:0}.cc-import-select-search__input{flex:1;border:none;outline:none;font:inherit;font-size:13px;color:#2a2f3b;background:transparent}\n"] }]
|
|
4272
|
+
}], propDecorators: { title: [{
|
|
4273
|
+
type: Input
|
|
4274
|
+
}], customerId: [{
|
|
4275
|
+
type: Input
|
|
4276
|
+
}], host: [{
|
|
4277
|
+
type: Input
|
|
4278
|
+
}], dcpHost: [{
|
|
4279
|
+
type: Input
|
|
4280
|
+
}], token: [{
|
|
4281
|
+
type: Input
|
|
4282
|
+
}], useMockData: [{
|
|
4283
|
+
type: Input
|
|
4284
|
+
}], contactCenters: [{
|
|
4285
|
+
type: Input
|
|
4286
|
+
}], cancel: [{
|
|
4287
|
+
type: Output
|
|
4288
|
+
}], imported: [{
|
|
4289
|
+
type: Output
|
|
4290
|
+
}] } });
|
|
4291
|
+
|
|
4292
|
+
class CcImportModule {
|
|
4293
|
+
}
|
|
4294
|
+
CcImportModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CcImportModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
4295
|
+
CcImportModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: CcImportModule, declarations: [ImportContactCenterPanelComponent,
|
|
4296
|
+
ImportContactCenterWizardComponent], imports: [CommonModule,
|
|
4297
|
+
HttpClientModule,
|
|
4298
|
+
NgApexchartsModule,
|
|
4299
|
+
MatStepperModule,
|
|
4300
|
+
SharedModule], exports: [ImportContactCenterPanelComponent,
|
|
4301
|
+
ImportContactCenterWizardComponent] });
|
|
4302
|
+
CcImportModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CcImportModule, imports: [CommonModule,
|
|
4303
|
+
HttpClientModule,
|
|
4304
|
+
NgApexchartsModule,
|
|
4305
|
+
MatStepperModule,
|
|
4306
|
+
SharedModule] });
|
|
4307
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CcImportModule, decorators: [{
|
|
4308
|
+
type: NgModule,
|
|
4309
|
+
args: [{
|
|
4310
|
+
declarations: [
|
|
4311
|
+
ImportContactCenterPanelComponent,
|
|
4312
|
+
ImportContactCenterWizardComponent
|
|
4313
|
+
],
|
|
4314
|
+
imports: [
|
|
4315
|
+
CommonModule,
|
|
4316
|
+
HttpClientModule,
|
|
4317
|
+
NgApexchartsModule,
|
|
4318
|
+
MatStepperModule,
|
|
4319
|
+
SharedModule
|
|
4320
|
+
],
|
|
4321
|
+
exports: [
|
|
4322
|
+
ImportContactCenterPanelComponent,
|
|
4323
|
+
ImportContactCenterWizardComponent
|
|
4324
|
+
]
|
|
4325
|
+
}]
|
|
4326
|
+
}] });
|
|
4327
|
+
|
|
3466
4328
|
/**
|
|
3467
4329
|
* Generated bundle index. Do not edit.
|
|
3468
4330
|
*/
|
|
3469
4331
|
|
|
3470
|
-
export { CcReadinessModule, LicencesRequirementComponent, SummaryGridComponent, WxCCFlowchartModule, WxCCFlowchartPageComponent };
|
|
4332
|
+
export { CALL_FLOW_MIGRATED, CHART_COLORS, COMPATIBILITY_THRESHOLDS, CUSTOMER_ASSIST_MARKER_TOOLTIP, CUSTOMER_ASSIST_NOTICE, CcImportModule, CcImportService, CcReadinessModule, IMPORT_PANEL_SLICES, IMPORT_PANEL_TITLE, IMPORT_PANEL_TOOLTIP, IMPORT_WIZARD_STEPS, IMPORT_WIZARD_TITLE, ImportContactCenterPanelComponent, ImportContactCenterWizardComponent, LicencesRequirementComponent, OVERVIEW_ENTITY_GROUPS, SummaryGridComponent, UCCX_ITEM_TYPE, WxCCFlowchartModule, WxCCFlowchartPageComponent };
|
|
3471
4333
|
//# sourceMappingURL=tuki-io-tuki-widgets-contact-center.mjs.map
|