@memberjunction/ng-dashboards 6.2.0-edge.1 → 6.2.0-edge.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.js +3 -1
- package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.js.map +1 -1
- package/dist/AI/components/autotagging/tabs/sources-tab.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/tabs/sources-tab.component.js +1 -2
- package/dist/AI/components/autotagging/tabs/sources-tab.component.js.map +1 -1
- package/dist/AI/components/rubric-drift-series.d.ts +32 -0
- package/dist/AI/components/rubric-drift-series.d.ts.map +1 -0
- package/dist/AI/components/rubric-drift-series.js +49 -0
- package/dist/AI/components/rubric-drift-series.js.map +1 -0
- package/dist/AI/components/rubric-drift.component.d.ts +42 -0
- package/dist/AI/components/rubric-drift.component.d.ts.map +1 -0
- package/dist/AI/components/rubric-drift.component.js +197 -0
- package/dist/AI/components/rubric-drift.component.js.map +1 -0
- package/dist/AI/components/tags/tags-resource.component.d.ts.map +1 -1
- package/dist/AI/components/tags/tags-resource.component.js +4 -3
- package/dist/AI/components/tags/tags-resource.component.js.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.js +5 -2
- package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
- package/dist/ComponentStudio/components/form-override-dialog.component.d.ts +12 -1
- package/dist/ComponentStudio/components/form-override-dialog.component.d.ts.map +1 -1
- package/dist/ComponentStudio/components/form-override-dialog.component.js +128 -81
- package/dist/ComponentStudio/components/form-override-dialog.component.js.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +4 -0
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.js +47 -13
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
- package/dist/Integration/components/connections/connections.component.d.ts +5 -0
- package/dist/Integration/components/connections/connections.component.d.ts.map +1 -1
- package/dist/Integration/components/connections/connections.component.js +151 -136
- package/dist/Integration/components/connections/connections.component.js.map +1 -1
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.d.ts.map +1 -1
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js +3 -2
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js.map +1 -1
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.d.ts +11 -0
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.d.ts.map +1 -1
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.js +17 -1
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.js.map +1 -1
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js +52 -45
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js.map +1 -1
- package/dist/QueryBrowser/query-browser-resource.component.d.ts +11 -0
- package/dist/QueryBrowser/query-browser-resource.component.d.ts.map +1 -1
- package/dist/QueryBrowser/query-browser-resource.component.js +47 -10
- package/dist/QueryBrowser/query-browser-resource.component.js.map +1 -1
- package/dist/Rubrics/catalog-grid.d.ts +4 -0
- package/dist/Rubrics/catalog-grid.d.ts.map +1 -0
- package/dist/Rubrics/catalog-grid.js +22 -0
- package/dist/Rubrics/catalog-grid.js.map +1 -0
- package/dist/Rubrics/rubrics-dashboard.component.d.ts +31 -0
- package/dist/Rubrics/rubrics-dashboard.component.d.ts.map +1 -0
- package/dist/Rubrics/rubrics-dashboard.component.js +285 -0
- package/dist/Rubrics/rubrics-dashboard.component.js.map +1 -0
- package/dist/Rubrics/rubrics-resource.component.d.ts +13 -0
- package/dist/Rubrics/rubrics-resource.component.d.ts.map +1 -0
- package/dist/Rubrics/rubrics-resource.component.js +42 -0
- package/dist/Rubrics/rubrics-resource.component.js.map +1 -0
- package/dist/Rubrics/scales-dashboard.component.d.ts +34 -0
- package/dist/Rubrics/scales-dashboard.component.d.ts.map +1 -0
- package/dist/Rubrics/scales-dashboard.component.js +204 -0
- package/dist/Rubrics/scales-dashboard.component.js.map +1 -0
- package/dist/Rubrics/scales-resource.component.d.ts +13 -0
- package/dist/Rubrics/scales-resource.component.d.ts.map +1 -0
- package/dist/Rubrics/scales-resource.component.js +42 -0
- package/dist/Rubrics/scales-resource.component.js.map +1 -0
- package/dist/TelephonyOperations/telephony-operations-agent-context.d.ts +107 -0
- package/dist/TelephonyOperations/telephony-operations-agent-context.d.ts.map +1 -0
- package/dist/TelephonyOperations/telephony-operations-agent-context.js +216 -0
- package/dist/TelephonyOperations/telephony-operations-agent-context.js.map +1 -0
- package/dist/TelephonyOperations/telephony-operations-dashboard.component.d.ts +57 -0
- package/dist/TelephonyOperations/telephony-operations-dashboard.component.d.ts.map +1 -0
- package/dist/TelephonyOperations/telephony-operations-dashboard.component.js +805 -0
- package/dist/TelephonyOperations/telephony-operations-dashboard.component.js.map +1 -0
- package/dist/Testing/components/testing-analytics.component.d.ts +15 -0
- package/dist/Testing/components/testing-analytics.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-analytics.component.js +465 -296
- package/dist/Testing/components/testing-analytics.component.js.map +1 -1
- package/dist/Testing/components/testing-review.component.d.ts +6 -0
- package/dist/Testing/components/testing-review.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-review.component.js +289 -173
- package/dist/Testing/components/testing-review.component.js.map +1 -1
- package/dist/Testing/components/widgets/test-run-detail-panel.component.d.ts +8 -1
- package/dist/Testing/components/widgets/test-run-detail-panel.component.d.ts.map +1 -1
- package/dist/Testing/components/widgets/test-run-detail-panel.component.js +68 -43
- package/dist/Testing/components/widgets/test-run-detail-panel.component.js.map +1 -1
- package/dist/WorkQueue/components/work-queue-bindings.component.d.ts +37 -0
- package/dist/WorkQueue/components/work-queue-bindings.component.d.ts.map +1 -0
- package/dist/WorkQueue/components/work-queue-bindings.component.js +210 -0
- package/dist/WorkQueue/components/work-queue-bindings.component.js.map +1 -0
- package/dist/WorkQueue/components/work-queue-dead-letters.component.d.ts +70 -0
- package/dist/WorkQueue/components/work-queue-dead-letters.component.d.ts.map +1 -0
- package/dist/WorkQueue/components/work-queue-dead-letters.component.js +496 -0
- package/dist/WorkQueue/components/work-queue-dead-letters.component.js.map +1 -0
- package/dist/WorkQueue/components/work-queue-discard-dialog.component.d.ts +22 -0
- package/dist/WorkQueue/components/work-queue-discard-dialog.component.d.ts.map +1 -0
- package/dist/WorkQueue/components/work-queue-discard-dialog.component.js +99 -0
- package/dist/WorkQueue/components/work-queue-discard-dialog.component.js.map +1 -0
- package/dist/WorkQueue/components/work-queue-overview.component.d.ts +29 -0
- package/dist/WorkQueue/components/work-queue-overview.component.d.ts.map +1 -0
- package/dist/WorkQueue/components/work-queue-overview.component.js +278 -0
- package/dist/WorkQueue/components/work-queue-overview.component.js.map +1 -0
- package/dist/WorkQueue/components/work-queue-partitions.component.d.ts +59 -0
- package/dist/WorkQueue/components/work-queue-partitions.component.d.ts.map +1 -0
- package/dist/WorkQueue/components/work-queue-partitions.component.js +359 -0
- package/dist/WorkQueue/components/work-queue-partitions.component.js.map +1 -0
- package/dist/WorkQueue/services/work-queue-operator.service.d.ts +22 -0
- package/dist/WorkQueue/services/work-queue-operator.service.d.ts.map +1 -0
- package/dist/WorkQueue/services/work-queue-operator.service.js +44 -0
- package/dist/WorkQueue/services/work-queue-operator.service.js.map +1 -0
- package/dist/WorkQueue/work-queue-agent-context.d.ts +67 -0
- package/dist/WorkQueue/work-queue-agent-context.d.ts.map +1 -0
- package/dist/WorkQueue/work-queue-agent-context.js +119 -0
- package/dist/WorkQueue/work-queue-agent-context.js.map +1 -0
- package/dist/WorkQueue/work-queue-dashboard.component.d.ts +94 -0
- package/dist/WorkQueue/work-queue-dashboard.component.d.ts.map +1 -0
- package/dist/WorkQueue/work-queue-dashboard.component.js +545 -0
- package/dist/WorkQueue/work-queue-dashboard.component.js.map +1 -0
- package/dist/WorkQueue/work-queue-types.d.ts +40 -0
- package/dist/WorkQueue/work-queue-types.d.ts.map +1 -0
- package/dist/WorkQueue/work-queue-types.js +2 -0
- package/dist/WorkQueue/work-queue-types.js.map +1 -0
- package/dist/core-dashboards.module.d.ts +29 -28
- package/dist/core-dashboards.module.d.ts.map +1 -1
- package/dist/core-dashboards.module.js +7 -0
- package/dist/core-dashboards.module.js.map +1 -1
- package/dist/module.d.ts +24 -22
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +14 -0
- package/dist/module.js.map +1 -1
- package/dist/public-api.d.ts +5 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +7 -0
- package/dist/public-api.js.map +1 -1
- package/dist/rubrics-dashboards.module.d.ts +17 -0
- package/dist/rubrics-dashboards.module.d.ts.map +1 -0
- package/dist/rubrics-dashboards.module.js +76 -0
- package/dist/rubrics-dashboards.module.js.map +1 -0
- package/dist/work-queue-dashboards.module.d.ts +23 -0
- package/dist/work-queue-dashboards.module.d.ts.map +1 -0
- package/dist/work-queue-dashboards.module.js +92 -0
- package/dist/work-queue-dashboards.module.js.map +1 -0
- package/package.json +76 -64
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject } from '@angular/core';
|
|
8
|
+
import { CompositeKey, RunView } from '@memberjunction/core';
|
|
9
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
10
|
+
import { BaseDashboard } from '@memberjunction/ng-shared';
|
|
11
|
+
import { CatalogGridTheme } from './catalog-grid';
|
|
12
|
+
import * as i0 from "@angular/core";
|
|
13
|
+
import * as i1 from "ag-grid-angular";
|
|
14
|
+
import * as i2 from "@memberjunction/ng-shared-generic";
|
|
15
|
+
import * as i3 from "@memberjunction/ng-ui-components";
|
|
16
|
+
const _forTrack0 = ($index, $item) => $item.Id;
|
|
17
|
+
function RubricScalesDashboardComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
18
|
+
i0.ɵɵelement(0, "mj-loading", 6);
|
|
19
|
+
} }
|
|
20
|
+
function RubricScalesDashboardComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
|
|
21
|
+
i0.ɵɵelementStart(0, "p", 7);
|
|
22
|
+
i0.ɵɵtext(1);
|
|
23
|
+
i0.ɵɵelementEnd();
|
|
24
|
+
} if (rf & 2) {
|
|
25
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
26
|
+
i0.ɵɵadvance();
|
|
27
|
+
i0.ɵɵtextInterpolate(ctx_r0.LoadError);
|
|
28
|
+
} }
|
|
29
|
+
function RubricScalesDashboardComponent_Conditional_11_Template(rf, ctx) { if (rf & 1) {
|
|
30
|
+
i0.ɵɵelement(0, "mj-empty-state", 8);
|
|
31
|
+
} }
|
|
32
|
+
function RubricScalesDashboardComponent_Conditional_12_For_3_Template(rf, ctx) { if (rf & 1) {
|
|
33
|
+
const _r3 = i0.ɵɵgetCurrentView();
|
|
34
|
+
i0.ɵɵelementStart(0, "article", 11)(1, "strong");
|
|
35
|
+
i0.ɵɵtext(2);
|
|
36
|
+
i0.ɵɵelementEnd();
|
|
37
|
+
i0.ɵɵelementStart(3, "span");
|
|
38
|
+
i0.ɵɵtext(4);
|
|
39
|
+
i0.ɵɵelementEnd();
|
|
40
|
+
i0.ɵɵelementStart(5, "span");
|
|
41
|
+
i0.ɵɵtext(6);
|
|
42
|
+
i0.ɵɵelementEnd();
|
|
43
|
+
i0.ɵɵelementStart(7, "button", 12);
|
|
44
|
+
i0.ɵɵlistener("click", function RubricScalesDashboardComponent_Conditional_12_For_3_Template_button_click_7_listener() { const row_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.Open(row_r4.Id)); });
|
|
45
|
+
i0.ɵɵtext(8, "Open");
|
|
46
|
+
i0.ɵɵelementEnd()();
|
|
47
|
+
} if (rf & 2) {
|
|
48
|
+
const row_r4 = ctx.$implicit;
|
|
49
|
+
i0.ɵɵadvance(2);
|
|
50
|
+
i0.ɵɵtextInterpolate(row_r4.Name);
|
|
51
|
+
i0.ɵɵadvance(2);
|
|
52
|
+
i0.ɵɵtextInterpolate(row_r4.Type);
|
|
53
|
+
i0.ɵɵadvance(2);
|
|
54
|
+
i0.ɵɵtextInterpolate(row_r4.Frozen ? "Locked by a published rubric" : "Editable");
|
|
55
|
+
} }
|
|
56
|
+
function RubricScalesDashboardComponent_Conditional_12_Template(rf, ctx) { if (rf & 1) {
|
|
57
|
+
const _r2 = i0.ɵɵgetCurrentView();
|
|
58
|
+
i0.ɵɵelementStart(0, "ag-grid-angular", 9);
|
|
59
|
+
i0.ɵɵlistener("rowClicked", function RubricScalesDashboardComponent_Conditional_12_Template_ag_grid_angular_rowClicked_0_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnRow($event)); });
|
|
60
|
+
i0.ɵɵelementEnd();
|
|
61
|
+
i0.ɵɵelementStart(1, "div", 10);
|
|
62
|
+
i0.ɵɵrepeaterCreate(2, RubricScalesDashboardComponent_Conditional_12_For_3_Template, 9, 3, "article", 11, _forTrack0);
|
|
63
|
+
i0.ɵɵelementEnd();
|
|
64
|
+
} if (rf & 2) {
|
|
65
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
66
|
+
i0.ɵɵproperty("theme", ctx_r0.Theme)("columnDefs", ctx_r0.ColumnDefs)("gridOptions", ctx_r0.GridOptions)("rowData", ctx_r0.Visible);
|
|
67
|
+
i0.ɵɵadvance(2);
|
|
68
|
+
i0.ɵɵrepeater(ctx_r0.Visible);
|
|
69
|
+
} }
|
|
70
|
+
/** Scales list. A scale is frozen when a published version uses it. */
|
|
71
|
+
let RubricScalesDashboardComponent = class RubricScalesDashboardComponent extends BaseDashboard {
|
|
72
|
+
Loading = true;
|
|
73
|
+
LoadError = '';
|
|
74
|
+
Search = '';
|
|
75
|
+
Rows = [];
|
|
76
|
+
Theme = CatalogGridTheme();
|
|
77
|
+
GridOptions = { domLayout: 'autoHeight', suppressCellFocus: true, animateRows: true };
|
|
78
|
+
ColumnDefs = [
|
|
79
|
+
{ field: 'Name', headerName: 'Name', flex: 2 },
|
|
80
|
+
{ field: 'Type', headerName: 'Type', flex: 1 },
|
|
81
|
+
{ field: 'Frozen', headerName: 'Published use', flex: 1, valueFormatter: params => params.value ? 'Locked by a published rubric' : 'Editable' },
|
|
82
|
+
];
|
|
83
|
+
changeDetector = inject(ChangeDetectorRef);
|
|
84
|
+
initDashboard() { }
|
|
85
|
+
async GetResourceDisplayName() {
|
|
86
|
+
return 'Scales';
|
|
87
|
+
}
|
|
88
|
+
async loadData() {
|
|
89
|
+
this.Loading = true;
|
|
90
|
+
this.changeDetector.markForCheck();
|
|
91
|
+
try {
|
|
92
|
+
const view = RunView.FromMetadataProvider(this.ProviderToUse);
|
|
93
|
+
const user = this.ProviderToUse.CurrentUser;
|
|
94
|
+
const [scales, criteria, versions] = await view.RunViews([
|
|
95
|
+
{ EntityName: 'MJ: Rubric Scales', ResultType: 'simple', MaxRows: 300, OrderBy: 'Name' },
|
|
96
|
+
{ EntityName: 'MJ: Rubric Criteria', ResultType: 'simple', MaxRows: 2000 },
|
|
97
|
+
{ EntityName: 'MJ: Rubric Versions', ExtraFilter: "Status='Published'", ResultType: 'simple', MaxRows: 500 },
|
|
98
|
+
], user);
|
|
99
|
+
const failed = [scales, criteria, versions].find(result => !result.Success);
|
|
100
|
+
if (failed) {
|
|
101
|
+
this.LoadError = failed.ErrorMessage || 'Could not load scales.';
|
|
102
|
+
this.Rows = [];
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
this.LoadError = '';
|
|
106
|
+
const published = new Set(((versions.Results ?? [])).map(row => String(row.ID).toLowerCase()));
|
|
107
|
+
const frozenScales = new Set(((criteria.Results ?? []))
|
|
108
|
+
.filter(row => published.has(String(row.RubricVersionID ?? '').toLowerCase()) && row.ScaleID)
|
|
109
|
+
.map(row => String(row.ScaleID).toLowerCase()));
|
|
110
|
+
this.Rows = ((scales.Results ?? [])).map(row => ({
|
|
111
|
+
Id: String(row.ID),
|
|
112
|
+
Name: String(row.Name ?? ''),
|
|
113
|
+
Type: String(row.ScaleType ?? ''),
|
|
114
|
+
Frozen: frozenScales.has(String(row.ID).toLowerCase()),
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
this.Loading = false;
|
|
119
|
+
this.changeDetector.markForCheck();
|
|
120
|
+
this.publishAgent();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
ngAfterViewInit() {
|
|
124
|
+
this.publishAgent();
|
|
125
|
+
}
|
|
126
|
+
get Visible() {
|
|
127
|
+
const query = this.Search.trim().toLowerCase();
|
|
128
|
+
if (!query)
|
|
129
|
+
return this.Rows;
|
|
130
|
+
return this.Rows.filter(row => `${row.Name} ${row.Type}`.toLowerCase().includes(query));
|
|
131
|
+
}
|
|
132
|
+
OnSearch(value) {
|
|
133
|
+
this.Search = value;
|
|
134
|
+
this.publishAgent();
|
|
135
|
+
this.changeDetector.markForCheck();
|
|
136
|
+
}
|
|
137
|
+
Open(id) {
|
|
138
|
+
this.navigationService.OpenEntityRecord('MJ: Rubric Scales', CompositeKey.FromID(id));
|
|
139
|
+
}
|
|
140
|
+
OnRow(event) {
|
|
141
|
+
if (event.data)
|
|
142
|
+
this.Open(event.data.Id);
|
|
143
|
+
}
|
|
144
|
+
NewScale() {
|
|
145
|
+
this.navigationService.OpenNewEntityRecord('MJ: Rubric Scales');
|
|
146
|
+
}
|
|
147
|
+
publishAgent() {
|
|
148
|
+
this.navigationService.SetAgentContext(this, { Search: this.Search, RowCount: this.Visible.length });
|
|
149
|
+
this.navigationService.SetAgentClientTools(this, [
|
|
150
|
+
{
|
|
151
|
+
Name: 'OpenScale',
|
|
152
|
+
Description: 'Open a rubric scale record by its id.',
|
|
153
|
+
ParameterSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
|
|
154
|
+
Handler: async (params) => {
|
|
155
|
+
const id = String(params['id'] ?? '');
|
|
156
|
+
if (!this.Rows.some(row => row.Id.toLowerCase() === id.toLowerCase()))
|
|
157
|
+
return { Success: false, ErrorMessage: 'That scale is not in the list.' };
|
|
158
|
+
this.Open(id);
|
|
159
|
+
return { Success: true };
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
Name: 'NewScale',
|
|
164
|
+
Description: 'Start a new rubric scale.',
|
|
165
|
+
ParameterSchema: { type: 'object', properties: {} },
|
|
166
|
+
Handler: async () => {
|
|
167
|
+
this.NewScale();
|
|
168
|
+
return { Success: true };
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
]);
|
|
172
|
+
}
|
|
173
|
+
static ɵfac = /*@__PURE__*/ (() => { let ɵRubricScalesDashboardComponent_BaseFactory; return function RubricScalesDashboardComponent_Factory(__ngFactoryType__) { return (ɵRubricScalesDashboardComponent_BaseFactory || (ɵRubricScalesDashboardComponent_BaseFactory = i0.ɵɵgetInheritedFactory(RubricScalesDashboardComponent)))(__ngFactoryType__ || RubricScalesDashboardComponent); }; })();
|
|
174
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RubricScalesDashboardComponent, selectors: [["mj-rubric-scales-dashboard"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 13, vars: 2, consts: [["Title", "Scales", "Icon", "fa-solid fa-sliders", "Subtitle", "Level and numeric scales used by criteria."], ["actions", ""], [3, "Clicked"], ["mjButton", "", "variant", "primary", "type", "button", 3, "click"], ["toolbar", ""], ["Placeholder", "Search scales", 3, "ValueChange", "Value"], ["text", "Loading scales..."], ["role", "alert"], ["Icon", "fa-solid fa-sliders", "Title", "No scales", "Message", "Create a scale before a criterion can use it."], [1, "catalog-grid", 3, "rowClicked", "theme", "columnDefs", "gridOptions", "rowData"], [1, "catalog-cards"], [1, "catalog-card"], ["mjButton", "", "variant", "outline", "size", "sm", "type", "button", 3, "click"]], template: function RubricScalesDashboardComponent_Template(rf, ctx) { if (rf & 1) {
|
|
175
|
+
i0.ɵɵelementStart(0, "mj-page-layout")(1, "mj-page-header", 0)(2, "div", 1)(3, "mj-refresh-button", 2);
|
|
176
|
+
i0.ɵɵlistener("Clicked", function RubricScalesDashboardComponent_Template_mj_refresh_button_Clicked_3_listener() { return ctx.Refresh(); });
|
|
177
|
+
i0.ɵɵelementEnd();
|
|
178
|
+
i0.ɵɵelementStart(4, "button", 3);
|
|
179
|
+
i0.ɵɵlistener("click", function RubricScalesDashboardComponent_Template_button_click_4_listener() { return ctx.NewScale(); });
|
|
180
|
+
i0.ɵɵtext(5, "New scale");
|
|
181
|
+
i0.ɵɵelementEnd()();
|
|
182
|
+
i0.ɵɵelementStart(6, "div", 4)(7, "mj-page-search", 5);
|
|
183
|
+
i0.ɵɵlistener("ValueChange", function RubricScalesDashboardComponent_Template_mj_page_search_ValueChange_7_listener($event) { return ctx.OnSearch($event); });
|
|
184
|
+
i0.ɵɵelementEnd()()();
|
|
185
|
+
i0.ɵɵelementStart(8, "mj-page-body");
|
|
186
|
+
i0.ɵɵconditionalCreate(9, RubricScalesDashboardComponent_Conditional_9_Template, 1, 0, "mj-loading", 6)(10, RubricScalesDashboardComponent_Conditional_10_Template, 2, 1, "p", 7)(11, RubricScalesDashboardComponent_Conditional_11_Template, 1, 0, "mj-empty-state", 8)(12, RubricScalesDashboardComponent_Conditional_12_Template, 4, 4);
|
|
187
|
+
i0.ɵɵelementEnd()();
|
|
188
|
+
} if (rf & 2) {
|
|
189
|
+
i0.ɵɵadvance(7);
|
|
190
|
+
i0.ɵɵproperty("Value", ctx.Search);
|
|
191
|
+
i0.ɵɵadvance(2);
|
|
192
|
+
i0.ɵɵconditional(ctx.Loading ? 9 : ctx.LoadError ? 10 : ctx.Visible.length === 0 ? 11 : 12);
|
|
193
|
+
} }, dependencies: [i1.AgGridAngular, i2.LoadingComponent, i3.MJButtonDirective, i3.MJEmptyStateComponent, i3.MJPageBodyComponent, i3.MJPageHeaderComponent, i3.MJPageLayoutComponent, i3.MJPageSearchComponent, i3.MJRefreshButtonComponent], styles: [".catalog-grid[_ngcontent-%COMP%] { width: 100%; }\n.catalog-note[_ngcontent-%COMP%] { display: block; color: var(--mj-text-secondary); font-size: 0.85rem; }\n.catalog-cards[_ngcontent-%COMP%] { display: none; }\n.catalog-card[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: var(--mj-space-2, 0.5rem);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 6px);\n padding: var(--mj-space-3, 0.75rem);\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-primary);\n}\n\n@media (max-width: 520px) {\n .catalog-grid[_ngcontent-%COMP%] { display: none; }\n .catalog-cards[_ngcontent-%COMP%] { display: flex; flex-direction: column; gap: var(--mj-space-3, 0.75rem); }\n}"], changeDetection: 0 });
|
|
194
|
+
};
|
|
195
|
+
RubricScalesDashboardComponent = __decorate([
|
|
196
|
+
RegisterClass(BaseDashboard, 'RubricScalesDashboard')
|
|
197
|
+
], RubricScalesDashboardComponent);
|
|
198
|
+
export { RubricScalesDashboardComponent };
|
|
199
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RubricScalesDashboardComponent, [{
|
|
200
|
+
type: Component,
|
|
201
|
+
args: [{ standalone: false, selector: 'mj-rubric-scales-dashboard', changeDetection: ChangeDetectionStrategy.OnPush, template: "<mj-page-layout>\n <mj-page-header Title=\"Scales\" Icon=\"fa-solid fa-sliders\" Subtitle=\"Level and numeric scales used by criteria.\">\n <div actions>\n <mj-refresh-button (Clicked)=\"Refresh()\"></mj-refresh-button>\n <button mjButton variant=\"primary\" type=\"button\" (click)=\"NewScale()\">New scale</button>\n </div>\n <div toolbar>\n <mj-page-search Placeholder=\"Search scales\" [Value]=\"Search\" (ValueChange)=\"OnSearch($event)\"></mj-page-search>\n </div>\n </mj-page-header>\n <mj-page-body>\n @if (Loading) {\n <mj-loading text=\"Loading scales...\"></mj-loading>\n } @else if (LoadError) {\n <p role=\"alert\">{{ LoadError }}</p>\n } @else if (Visible.length === 0) {\n <mj-empty-state Icon=\"fa-solid fa-sliders\" Title=\"No scales\" Message=\"Create a scale before a criterion can use it.\"></mj-empty-state>\n } @else {\n <ag-grid-angular\n class=\"catalog-grid\"\n [theme]=\"Theme\"\n [columnDefs]=\"ColumnDefs\"\n [gridOptions]=\"GridOptions\"\n [rowData]=\"Visible\"\n (rowClicked)=\"OnRow($event)\">\n </ag-grid-angular>\n <div class=\"catalog-cards\">\n @for (row of Visible; track row.Id) {\n <article class=\"catalog-card\">\n <strong>{{ row.Name }}</strong>\n <span>{{ row.Type }}</span>\n <span>{{ row.Frozen ? 'Locked by a published rubric' : 'Editable' }}</span>\n <button mjButton variant=\"outline\" size=\"sm\" type=\"button\" (click)=\"Open(row.Id)\">Open</button>\n </article>\n }\n </div>\n }\n </mj-page-body>\n</mj-page-layout>\n", styles: [".catalog-grid { width: 100%; }\n.catalog-note { display: block; color: var(--mj-text-secondary); font-size: 0.85rem; }\n.catalog-cards { display: none; }\n.catalog-card {\n display: flex;\n flex-direction: column;\n gap: var(--mj-space-2, 0.5rem);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 6px);\n padding: var(--mj-space-3, 0.75rem);\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-primary);\n}\n\n@media (max-width: 520px) {\n .catalog-grid { display: none; }\n .catalog-cards { display: flex; flex-direction: column; gap: var(--mj-space-3, 0.75rem); }\n}\n"] }]
|
|
202
|
+
}], null, null); })();
|
|
203
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RubricScalesDashboardComponent, { className: "RubricScalesDashboardComponent", filePath: "src/Rubrics/scales-dashboard.component.ts", lineNumber: 26 }); })();
|
|
204
|
+
//# sourceMappingURL=scales-dashboard.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scales-dashboard.component.js","sourceRoot":"","sources":["../../src/Rubrics/scales-dashboard.component.ts","../../src/Rubrics/scales-dashboard.component.html"],"names":[],"mappings":"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","sourcesContent":["import { ChangeDetectionStrategy, ChangeDetectorRef, Component, AfterViewInit, inject } from '@angular/core';\nimport { ColDef, GridOptions, RowClickedEvent } from 'ag-grid-community';\nimport { CompositeKey, RunView } from '@memberjunction/core';\nimport { RegisterClass } from '@memberjunction/global';\nimport { BaseDashboard } from '@memberjunction/ng-shared';\nimport { CatalogGridTheme } from './catalog-grid';\n\ntype Row = Record<string, unknown>;\n\nexport interface ScaleCatalogRow {\n Id: string;\n Name: string;\n Type: string;\n Frozen: boolean;\n}\n\n/** Scales list. A scale is frozen when a published version uses it. */\n@RegisterClass(BaseDashboard, 'RubricScalesDashboard')\n@Component({\n standalone: false,\n selector: 'mj-rubric-scales-dashboard',\n templateUrl: './scales-dashboard.component.html',\n styleUrls: ['./rubrics-catalog.css'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class RubricScalesDashboardComponent extends BaseDashboard implements AfterViewInit {\n public Loading = true;\n public LoadError = '';\n public Search = '';\n public Rows: ScaleCatalogRow[] = [];\n public readonly Theme = CatalogGridTheme();\n public readonly GridOptions: GridOptions<ScaleCatalogRow> = { domLayout: 'autoHeight', suppressCellFocus: true, animateRows: true };\n public readonly ColumnDefs: ColDef<ScaleCatalogRow>[] = [\n { field: 'Name', headerName: 'Name', flex: 2 },\n { field: 'Type', headerName: 'Type', flex: 1 },\n { field: 'Frozen', headerName: 'Published use', flex: 1, valueFormatter: params => params.value ? 'Locked by a published rubric' : 'Editable' },\n ];\n private readonly changeDetector = inject(ChangeDetectorRef);\n\n protected initDashboard(): void {}\n\n public override async GetResourceDisplayName(): Promise<string> {\n return 'Scales';\n }\n\n protected async loadData(): Promise<void> {\n this.Loading = true;\n this.changeDetector.markForCheck();\n try {\n const view = RunView.FromMetadataProvider(this.ProviderToUse);\n const user = this.ProviderToUse.CurrentUser;\n const [scales, criteria, versions] = await view.RunViews([\n { EntityName: 'MJ: Rubric Scales', ResultType: 'simple', MaxRows: 300, OrderBy: 'Name' },\n { EntityName: 'MJ: Rubric Criteria', ResultType: 'simple', MaxRows: 2000 },\n { EntityName: 'MJ: Rubric Versions', ExtraFilter: \"Status='Published'\", ResultType: 'simple', MaxRows: 500 },\n ], user);\n const failed = [scales, criteria, versions].find(result => !result.Success);\n if (failed) {\n this.LoadError = failed.ErrorMessage || 'Could not load scales.';\n this.Rows = [];\n return;\n }\n this.LoadError = '';\n const published = new Set(((versions.Results as Row[] ?? [])).map(row => String(row.ID).toLowerCase()));\n const frozenScales = new Set(((criteria.Results as Row[] ?? []))\n .filter(row => published.has(String(row.RubricVersionID ?? '').toLowerCase()) && row.ScaleID)\n .map(row => String(row.ScaleID).toLowerCase()));\n this.Rows = ((scales.Results as Row[] ?? [])).map(row => ({\n Id: String(row.ID),\n Name: String(row.Name ?? ''),\n Type: String(row.ScaleType ?? ''),\n Frozen: frozenScales.has(String(row.ID).toLowerCase()),\n }));\n } finally {\n this.Loading = false;\n this.changeDetector.markForCheck();\n this.publishAgent();\n }\n }\n\n public ngAfterViewInit(): void {\n this.publishAgent();\n }\n\n public get Visible(): ScaleCatalogRow[] {\n const query = this.Search.trim().toLowerCase();\n if (!query) return this.Rows;\n return this.Rows.filter(row => `${row.Name} ${row.Type}`.toLowerCase().includes(query));\n }\n\n public OnSearch(value: string): void {\n this.Search = value;\n this.publishAgent();\n this.changeDetector.markForCheck();\n }\n\n public Open(id: string): void {\n this.navigationService.OpenEntityRecord('MJ: Rubric Scales', CompositeKey.FromID(id));\n }\n\n public OnRow(event: RowClickedEvent<ScaleCatalogRow>): void {\n if (event.data) this.Open(event.data.Id);\n }\n\n public NewScale(): void {\n this.navigationService.OpenNewEntityRecord('MJ: Rubric Scales');\n }\n\n private publishAgent(): void {\n this.navigationService.SetAgentContext(this, { Search: this.Search, RowCount: this.Visible.length });\n this.navigationService.SetAgentClientTools(this, [\n {\n Name: 'OpenScale',\n Description: 'Open a rubric scale record by its id.',\n ParameterSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },\n Handler: async (params: Record<string, unknown>) => {\n const id = String(params['id'] ?? '');\n if (!this.Rows.some(row => row.Id.toLowerCase() === id.toLowerCase())) return { Success: false, ErrorMessage: 'That scale is not in the list.' };\n this.Open(id);\n return { Success: true };\n },\n },\n {\n Name: 'NewScale',\n Description: 'Start a new rubric scale.',\n ParameterSchema: { type: 'object', properties: {} },\n Handler: async () => {\n this.NewScale();\n return { Success: true };\n },\n },\n ]);\n }\n}\n","<mj-page-layout>\n <mj-page-header Title=\"Scales\" Icon=\"fa-solid fa-sliders\" Subtitle=\"Level and numeric scales used by criteria.\">\n <div actions>\n <mj-refresh-button (Clicked)=\"Refresh()\"></mj-refresh-button>\n <button mjButton variant=\"primary\" type=\"button\" (click)=\"NewScale()\">New scale</button>\n </div>\n <div toolbar>\n <mj-page-search Placeholder=\"Search scales\" [Value]=\"Search\" (ValueChange)=\"OnSearch($event)\"></mj-page-search>\n </div>\n </mj-page-header>\n <mj-page-body>\n @if (Loading) {\n <mj-loading text=\"Loading scales...\"></mj-loading>\n } @else if (LoadError) {\n <p role=\"alert\">{{ LoadError }}</p>\n } @else if (Visible.length === 0) {\n <mj-empty-state Icon=\"fa-solid fa-sliders\" Title=\"No scales\" Message=\"Create a scale before a criterion can use it.\"></mj-empty-state>\n } @else {\n <ag-grid-angular\n class=\"catalog-grid\"\n [theme]=\"Theme\"\n [columnDefs]=\"ColumnDefs\"\n [gridOptions]=\"GridOptions\"\n [rowData]=\"Visible\"\n (rowClicked)=\"OnRow($event)\">\n </ag-grid-angular>\n <div class=\"catalog-cards\">\n @for (row of Visible; track row.Id) {\n <article class=\"catalog-card\">\n <strong>{{ row.Name }}</strong>\n <span>{{ row.Type }}</span>\n <span>{{ row.Frozen ? 'Locked by a published rubric' : 'Editable' }}</span>\n <button mjButton variant=\"outline\" size=\"sm\" type=\"button\" (click)=\"Open(row.Id)\">Open</button>\n </article>\n }\n </div>\n }\n </mj-page-body>\n</mj-page-layout>\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { OnInit } from '@angular/core';
|
|
2
|
+
import { ResourceData } from '@memberjunction/core-entities';
|
|
3
|
+
import { BaseResourceComponent } from '@memberjunction/ng-shared';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
/** Thin nav shim for the scales list. */
|
|
6
|
+
export declare class RubricScalesResourceComponent extends BaseResourceComponent implements OnInit {
|
|
7
|
+
ngOnInit(): void;
|
|
8
|
+
GetResourceDisplayName(_data: ResourceData): Promise<string>;
|
|
9
|
+
GetResourceIconClass(_data: ResourceData): Promise<string>;
|
|
10
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<RubricScalesResourceComponent, never>;
|
|
11
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<RubricScalesResourceComponent, "mj-rubric-scales-resource", never, {}, {}, never, never, false, never>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=scales-resource.component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scales-resource.component.d.ts","sourceRoot":"","sources":["../../src/Rubrics/scales-resource.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,MAAM,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;;AAElE,yCAAyC;AACzC,qBAMa,6BAA8B,SAAQ,qBAAsB,YAAW,MAAM;IACtE,QAAQ,IAAI,IAAI;IAKV,sBAAsB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAI5D,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;yCAVvE,6BAA6B;2CAA7B,6BAA6B;CAazC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { Component } from '@angular/core';
|
|
8
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
9
|
+
import { BaseResourceComponent } from '@memberjunction/ng-shared';
|
|
10
|
+
import * as i0 from "@angular/core";
|
|
11
|
+
import * as i1 from "./scales-dashboard.component";
|
|
12
|
+
/** Thin nav shim for the scales list. */
|
|
13
|
+
let RubricScalesResourceComponent = class RubricScalesResourceComponent extends BaseResourceComponent {
|
|
14
|
+
ngOnInit() {
|
|
15
|
+
super.ngOnInit();
|
|
16
|
+
this.NotifyLoadComplete();
|
|
17
|
+
}
|
|
18
|
+
async GetResourceDisplayName(_data) {
|
|
19
|
+
return 'Scales';
|
|
20
|
+
}
|
|
21
|
+
async GetResourceIconClass(_data) {
|
|
22
|
+
return 'fa-solid fa-sliders';
|
|
23
|
+
}
|
|
24
|
+
static ɵfac = /*@__PURE__*/ (() => { let ɵRubricScalesResourceComponent_BaseFactory; return function RubricScalesResourceComponent_Factory(__ngFactoryType__) { return (ɵRubricScalesResourceComponent_BaseFactory || (ɵRubricScalesResourceComponent_BaseFactory = i0.ɵɵgetInheritedFactory(RubricScalesResourceComponent)))(__ngFactoryType__ || RubricScalesResourceComponent); }; })();
|
|
25
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RubricScalesResourceComponent, selectors: [["mj-rubric-scales-resource"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 1, vars: 0, template: function RubricScalesResourceComponent_Template(rf, ctx) { if (rf & 1) {
|
|
26
|
+
i0.ɵɵelement(0, "mj-rubric-scales-dashboard");
|
|
27
|
+
} }, dependencies: [i1.RubricScalesDashboardComponent], encapsulation: 2 });
|
|
28
|
+
};
|
|
29
|
+
RubricScalesResourceComponent = __decorate([
|
|
30
|
+
RegisterClass(BaseResourceComponent, 'RubricScalesResource')
|
|
31
|
+
], RubricScalesResourceComponent);
|
|
32
|
+
export { RubricScalesResourceComponent };
|
|
33
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RubricScalesResourceComponent, [{
|
|
34
|
+
type: Component,
|
|
35
|
+
args: [{
|
|
36
|
+
standalone: false,
|
|
37
|
+
selector: 'mj-rubric-scales-resource',
|
|
38
|
+
template: `<mj-rubric-scales-dashboard></mj-rubric-scales-dashboard>`,
|
|
39
|
+
}]
|
|
40
|
+
}], null, null); })();
|
|
41
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RubricScalesResourceComponent, { className: "RubricScalesResourceComponent", filePath: "src/Rubrics/scales-resource.component.ts", lineNumber: 13 }); })();
|
|
42
|
+
//# sourceMappingURL=scales-resource.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scales-resource.component.js","sourceRoot":"","sources":["../../src/Rubrics/scales-resource.component.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,SAAS,EAAU,MAAM,eAAe,CAAC;AAElD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;;;AAElE,yCAAyC;AAOlC,IAAM,6BAA6B,GAAnC,MAAM,6BAA8B,SAAQ,qBAAqB;IACpD,QAAQ;QACpB,KAAK,CAAC,QAAQ,EAAE,CAAC;QACjB,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAC9B,CAAC;IAEe,KAAK,CAAC,sBAAsB,CAAC,KAAmB;QAC5D,OAAO,QAAQ,CAAC;IACpB,CAAC;IAEe,KAAK,CAAC,oBAAoB,CAAC,KAAmB;QAC1D,OAAO,qBAAqB,CAAC;IACjC,CAAC;iSAZQ,6BAA6B,yBAA7B,6BAA6B;6DAA7B,6BAA6B;YAF3B,6CAAyD;;;AAE3D,6BAA6B;IANzC,aAAa,CAAC,qBAAqB,EAAE,sBAAsB,CAAC;GAMhD,6BAA6B,CAazC;;iFAbY,6BAA6B;cALzC,SAAS;eAAC;gBACP,UAAU,EAAE,KAAK;gBACjB,QAAQ,EAAE,2BAA2B;gBACrC,QAAQ,EAAE,2DAA2D;aACxE;;kFACY,6BAA6B","sourcesContent":["import { Component, OnInit } from '@angular/core';\nimport { ResourceData } from '@memberjunction/core-entities';\nimport { RegisterClass } from '@memberjunction/global';\nimport { BaseResourceComponent } from '@memberjunction/ng-shared';\n\n/** Thin nav shim for the scales list. */\n@RegisterClass(BaseResourceComponent, 'RubricScalesResource')\n@Component({\n standalone: false,\n selector: 'mj-rubric-scales-resource',\n template: `<mj-rubric-scales-dashboard></mj-rubric-scales-dashboard>`,\n})\nexport class RubricScalesResourceComponent extends BaseResourceComponent implements OnInit {\n public override ngOnInit(): void {\n super.ngOnInit();\n this.NotifyLoadComplete();\n }\n\n public override async GetResourceDisplayName(_data: ResourceData): Promise<string> {\n return 'Scales';\n }\n\n public override async GetResourceIconClass(_data: ResourceData): Promise<string> {\n return 'fa-solid fa-sliders';\n }\n}\n"]}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { type AgentToolResult } from '../shared/agent-tool-validation';
|
|
2
|
+
export type TimeRangeFilter = '24h' | '7d' | '30d' | 'all';
|
|
3
|
+
export type ChannelFilter = 'all' | 'Phone' | 'Web' | 'Meeting';
|
|
4
|
+
export declare const VALID_TIME_RANGES: readonly TimeRangeFilter[];
|
|
5
|
+
export declare const VALID_CHANNELS: readonly ChannelFilter[];
|
|
6
|
+
export interface ChannelMetrics {
|
|
7
|
+
Channel: 'Phone' | 'Web' | 'Meeting';
|
|
8
|
+
Count: number;
|
|
9
|
+
Percentage: number;
|
|
10
|
+
}
|
|
11
|
+
export interface DirectionMetrics {
|
|
12
|
+
Direction: 'Inbound' | 'Outbound' | 'Internal';
|
|
13
|
+
Count: number;
|
|
14
|
+
Percentage: number;
|
|
15
|
+
}
|
|
16
|
+
export interface CostByDay {
|
|
17
|
+
Date: string;
|
|
18
|
+
Cost: number;
|
|
19
|
+
Count: number;
|
|
20
|
+
}
|
|
21
|
+
export interface CostByNumber {
|
|
22
|
+
PhoneNumberID: string | null;
|
|
23
|
+
PhoneNumber: string;
|
|
24
|
+
Cost: number;
|
|
25
|
+
Count: number;
|
|
26
|
+
}
|
|
27
|
+
export interface LiveInteractionSummary {
|
|
28
|
+
ID: string;
|
|
29
|
+
Channel: 'Phone' | 'Web' | 'Meeting';
|
|
30
|
+
Direction: 'Inbound' | 'Outbound' | 'Internal';
|
|
31
|
+
Status: string;
|
|
32
|
+
RemoteAddress: string | null;
|
|
33
|
+
StartedAt: Date;
|
|
34
|
+
ElapsedSeconds: number;
|
|
35
|
+
ElapsedLabel: string;
|
|
36
|
+
}
|
|
37
|
+
export interface OperationsMetrics {
|
|
38
|
+
TotalVolume: number;
|
|
39
|
+
LiveCallsCount: number;
|
|
40
|
+
AnsweredCount: number;
|
|
41
|
+
AnswerRate: number;
|
|
42
|
+
AbandonedCount: number;
|
|
43
|
+
AbandonmentRate: number;
|
|
44
|
+
AverageHandleTimeSeconds: number;
|
|
45
|
+
AverageHandleTimeFormatted: string;
|
|
46
|
+
AverageSpeedToAnswerSeconds: number;
|
|
47
|
+
AverageSpeedToAnswerFormatted: string;
|
|
48
|
+
TransferCount: number;
|
|
49
|
+
EscalationCount: number;
|
|
50
|
+
TotalCost: number;
|
|
51
|
+
TotalCostFormatted: string;
|
|
52
|
+
VolumeByChannel: ChannelMetrics[];
|
|
53
|
+
VolumeByDirection: DirectionMetrics[];
|
|
54
|
+
CostByDay: CostByDay[];
|
|
55
|
+
CostByNumber: CostByNumber[];
|
|
56
|
+
LiveInteractions: LiveInteractionSummary[];
|
|
57
|
+
}
|
|
58
|
+
export interface InteractionRecordLike {
|
|
59
|
+
ID: string;
|
|
60
|
+
Channel: 'Meeting' | 'Phone' | 'Web' | string;
|
|
61
|
+
Direction: 'Inbound' | 'Internal' | 'Outbound' | string;
|
|
62
|
+
Status: 'Abandoned' | 'Active' | 'Ended' | 'Failed' | 'Queued' | string;
|
|
63
|
+
AgentSessionID: string | null;
|
|
64
|
+
RoomName: string | null;
|
|
65
|
+
PhoneNumberID: string | null;
|
|
66
|
+
RemoteAddress: string | null;
|
|
67
|
+
StartedAt: Date | string;
|
|
68
|
+
AnsweredAt: Date | string | null;
|
|
69
|
+
EndedAt: Date | string | null;
|
|
70
|
+
EndReason: string | null;
|
|
71
|
+
RecordingEnabled: boolean;
|
|
72
|
+
CostEstimate: number | null;
|
|
73
|
+
}
|
|
74
|
+
export interface InteractionEventRecordLike {
|
|
75
|
+
ID: string;
|
|
76
|
+
InteractionID: string;
|
|
77
|
+
EventType: 'Abandoned' | 'Accepted' | 'Answered' | 'Created' | 'Declined' | 'Ended' | 'Escalated' | 'Held' | 'Offered' | 'Queued' | 'RecordingStarted' | 'RecordingStopped' | 'Resumed' | 'Transferred' | string;
|
|
78
|
+
OccurredAt: Date | string;
|
|
79
|
+
ActorUserID: string | null;
|
|
80
|
+
ActorAgentID: string | null;
|
|
81
|
+
Details: string | null;
|
|
82
|
+
}
|
|
83
|
+
export interface PhoneNumberRecordLike {
|
|
84
|
+
ID: string;
|
|
85
|
+
PhoneNumber: string;
|
|
86
|
+
FriendlyName?: string | null;
|
|
87
|
+
}
|
|
88
|
+
export declare function FormatDuration(seconds: number): string;
|
|
89
|
+
export declare function FormatCurrency(amount: number): string;
|
|
90
|
+
export declare function ParseDate(val: Date | string | null | undefined): Date | null;
|
|
91
|
+
export declare function CalculateOperationsMetrics(interactions: readonly InteractionRecordLike[], events: readonly InteractionEventRecordLike[], phoneNumbers: readonly PhoneNumberRecordLike[], now?: Date): OperationsMetrics;
|
|
92
|
+
export declare function BuildTelephonyOperationsAgentContext(metrics: OperationsMetrics, timeRange: TimeRangeFilter, channelFilter: ChannelFilter): Record<string, unknown>;
|
|
93
|
+
export declare function ValidateTimeRangeParam(raw: unknown): {
|
|
94
|
+
ok: true;
|
|
95
|
+
value: TimeRangeFilter;
|
|
96
|
+
} | {
|
|
97
|
+
ok: false;
|
|
98
|
+
result: AgentToolResult;
|
|
99
|
+
};
|
|
100
|
+
export declare function ValidateChannelParam(raw: unknown): {
|
|
101
|
+
ok: true;
|
|
102
|
+
value: ChannelFilter;
|
|
103
|
+
} | {
|
|
104
|
+
ok: false;
|
|
105
|
+
result: AgentToolResult;
|
|
106
|
+
};
|
|
107
|
+
//# sourceMappingURL=telephony-operations-agent-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telephony-operations-agent-context.d.ts","sourceRoot":"","sources":["../../src/TelephonyOperations/telephony-operations-agent-context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { BoundNameList, ValidateEnumParam } from '../shared/agent-tool-validation';
|
|
2
|
+
export const VALID_TIME_RANGES = ['24h', '7d', '30d', 'all'];
|
|
3
|
+
export const VALID_CHANNELS = ['all', 'Phone', 'Web', 'Meeting'];
|
|
4
|
+
export function FormatDuration(seconds) {
|
|
5
|
+
if (!Number.isFinite(seconds) || seconds <= 0) {
|
|
6
|
+
return '0s';
|
|
7
|
+
}
|
|
8
|
+
const rounded = Math.floor(seconds);
|
|
9
|
+
const mins = Math.floor(rounded / 60);
|
|
10
|
+
const secs = rounded % 60;
|
|
11
|
+
if (mins === 0) {
|
|
12
|
+
return `${secs}s`;
|
|
13
|
+
}
|
|
14
|
+
return `${mins}m ${secs.toString().padStart(2, '0')}s`;
|
|
15
|
+
}
|
|
16
|
+
export function FormatCurrency(amount) {
|
|
17
|
+
if (!Number.isFinite(amount) || amount <= 0) {
|
|
18
|
+
return '$0.00';
|
|
19
|
+
}
|
|
20
|
+
return new Intl.NumberFormat('en-US', {
|
|
21
|
+
style: 'currency',
|
|
22
|
+
currency: 'USD',
|
|
23
|
+
minimumFractionDigits: 2,
|
|
24
|
+
maximumFractionDigits: 2,
|
|
25
|
+
}).format(amount);
|
|
26
|
+
}
|
|
27
|
+
export function ParseDate(val) {
|
|
28
|
+
if (!val)
|
|
29
|
+
return null;
|
|
30
|
+
if (val instanceof Date)
|
|
31
|
+
return val;
|
|
32
|
+
const parsed = new Date(val);
|
|
33
|
+
return isNaN(parsed.getTime()) ? null : parsed;
|
|
34
|
+
}
|
|
35
|
+
export function CalculateOperationsMetrics(interactions, events, phoneNumbers, now = new Date()) {
|
|
36
|
+
const totalVolume = interactions.length;
|
|
37
|
+
const phoneNumberMap = new Map();
|
|
38
|
+
for (const pn of phoneNumbers) {
|
|
39
|
+
phoneNumberMap.set(pn.ID.toLowerCase(), pn.FriendlyName?.trim() || pn.PhoneNumber);
|
|
40
|
+
}
|
|
41
|
+
let answeredCount = 0;
|
|
42
|
+
let abandonedCount = 0;
|
|
43
|
+
let totalHandleTime = 0;
|
|
44
|
+
let completedHandledCount = 0;
|
|
45
|
+
let totalWaitTime = 0;
|
|
46
|
+
let speedToAnswerCount = 0;
|
|
47
|
+
let totalCost = 0;
|
|
48
|
+
const channelCounts = { Phone: 0, Web: 0, Meeting: 0 };
|
|
49
|
+
const directionCounts = { Inbound: 0, Outbound: 0, Internal: 0 };
|
|
50
|
+
const costByDayMap = new Map();
|
|
51
|
+
const costByNumberMap = new Map();
|
|
52
|
+
const liveInteractions = [];
|
|
53
|
+
// Group events by interaction id for quick checks
|
|
54
|
+
const eventsByInteraction = new Map();
|
|
55
|
+
let transferCount = 0;
|
|
56
|
+
let escalationCount = 0;
|
|
57
|
+
for (const evt of events) {
|
|
58
|
+
if (evt.EventType === 'Transferred')
|
|
59
|
+
transferCount++;
|
|
60
|
+
if (evt.EventType === 'Escalated')
|
|
61
|
+
escalationCount++;
|
|
62
|
+
const key = evt.InteractionID.toLowerCase();
|
|
63
|
+
const existing = eventsByInteraction.get(key);
|
|
64
|
+
if (existing) {
|
|
65
|
+
existing.push(evt);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
eventsByInteraction.set(key, [evt]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
for (const row of interactions) {
|
|
72
|
+
const started = ParseDate(row.StartedAt) ?? new Date();
|
|
73
|
+
const answered = ParseDate(row.AnsweredAt);
|
|
74
|
+
const ended = ParseDate(row.EndedAt);
|
|
75
|
+
const status = row.Status;
|
|
76
|
+
// Channel count
|
|
77
|
+
const ch = row.Channel || 'Phone';
|
|
78
|
+
if (ch in channelCounts) {
|
|
79
|
+
channelCounts[ch]++;
|
|
80
|
+
}
|
|
81
|
+
// Direction count
|
|
82
|
+
const dir = row.Direction || 'Inbound';
|
|
83
|
+
if (dir in directionCounts) {
|
|
84
|
+
directionCounts[dir]++;
|
|
85
|
+
}
|
|
86
|
+
// Cost accumulation
|
|
87
|
+
const cost = typeof row.CostEstimate === 'number' && Number.isFinite(row.CostEstimate) ? row.CostEstimate : 0;
|
|
88
|
+
totalCost += cost;
|
|
89
|
+
// Cost by day
|
|
90
|
+
const dayKey = started.toISOString().split('T')[0];
|
|
91
|
+
const dayEntry = costByDayMap.get(dayKey) ?? { cost: 0, count: 0 };
|
|
92
|
+
dayEntry.cost += cost;
|
|
93
|
+
dayEntry.count++;
|
|
94
|
+
costByDayMap.set(dayKey, dayEntry);
|
|
95
|
+
// Cost by number
|
|
96
|
+
const pnId = row.PhoneNumberID ? row.PhoneNumberID.toLowerCase() : null;
|
|
97
|
+
const pnLabel = pnId ? (phoneNumberMap.get(pnId) ?? row.RemoteAddress ?? 'Assigned Pool') : (row.RemoteAddress ?? 'Non-Phone');
|
|
98
|
+
const pnKey = pnId ?? pnLabel;
|
|
99
|
+
const numberEntry = costByNumberMap.get(pnKey) ?? { phoneNumber: pnLabel, cost: 0, count: 0 };
|
|
100
|
+
numberEntry.cost += cost;
|
|
101
|
+
numberEntry.count++;
|
|
102
|
+
costByNumberMap.set(pnKey, numberEntry);
|
|
103
|
+
// Live interactions
|
|
104
|
+
const isLive = (status === 'Active' || status === 'Queued') && ended === null;
|
|
105
|
+
if (isLive) {
|
|
106
|
+
const elapsed = Math.max(0, Math.floor((now.getTime() - started.getTime()) / 1000));
|
|
107
|
+
liveInteractions.push({
|
|
108
|
+
ID: row.ID,
|
|
109
|
+
Channel: ch,
|
|
110
|
+
Direction: dir,
|
|
111
|
+
Status: status,
|
|
112
|
+
RemoteAddress: row.RemoteAddress,
|
|
113
|
+
StartedAt: started,
|
|
114
|
+
ElapsedSeconds: elapsed,
|
|
115
|
+
ElapsedLabel: FormatDuration(elapsed),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
// Answered check
|
|
119
|
+
const hasAnswerEvent = (eventsByInteraction.get(row.ID.toLowerCase()) ?? []).some((e) => e.EventType === 'Answered' || e.EventType === 'Accepted');
|
|
120
|
+
const isAnswered = answered !== null || hasAnswerEvent || status === 'Active' || status === 'Ended';
|
|
121
|
+
if (isAnswered) {
|
|
122
|
+
answeredCount++;
|
|
123
|
+
}
|
|
124
|
+
// Abandoned check
|
|
125
|
+
const isAbandoned = status === 'Abandoned' || (eventsByInteraction.get(row.ID.toLowerCase()) ?? []).some((e) => e.EventType === 'Abandoned');
|
|
126
|
+
if (isAbandoned) {
|
|
127
|
+
abandonedCount++;
|
|
128
|
+
}
|
|
129
|
+
// Handle time: for ended interactions
|
|
130
|
+
if (ended !== null) {
|
|
131
|
+
const effectiveStart = answered ?? started;
|
|
132
|
+
const durationSec = Math.max(0, Math.floor((ended.getTime() - effectiveStart.getTime()) / 1000));
|
|
133
|
+
totalHandleTime += durationSec;
|
|
134
|
+
completedHandledCount++;
|
|
135
|
+
}
|
|
136
|
+
// Speed to answer
|
|
137
|
+
if (answered !== null) {
|
|
138
|
+
const waitSec = Math.max(0, Math.floor((answered.getTime() - started.getTime()) / 1000));
|
|
139
|
+
totalWaitTime += waitSec;
|
|
140
|
+
speedToAnswerCount++;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const answerRate = totalVolume > 0 ? (answeredCount / totalVolume) * 100 : 0;
|
|
144
|
+
const abandonmentRate = totalVolume > 0 ? (abandonedCount / totalVolume) * 100 : 0;
|
|
145
|
+
const avgHandleTime = completedHandledCount > 0 ? totalHandleTime / completedHandledCount : 0;
|
|
146
|
+
const avgSpeedToAnswer = speedToAnswerCount > 0 ? totalWaitTime / speedToAnswerCount : 0;
|
|
147
|
+
const volumeByChannel = ['Phone', 'Web', 'Meeting'].map((channel) => ({
|
|
148
|
+
Channel: channel,
|
|
149
|
+
Count: channelCounts[channel],
|
|
150
|
+
Percentage: totalVolume > 0 ? Math.round((channelCounts[channel] / totalVolume) * 100) : 0,
|
|
151
|
+
}));
|
|
152
|
+
const volumeByDirection = ['Inbound', 'Outbound', 'Internal'].map((direction) => ({
|
|
153
|
+
Direction: direction,
|
|
154
|
+
Count: directionCounts[direction],
|
|
155
|
+
Percentage: totalVolume > 0 ? Math.round((directionCounts[direction] / totalVolume) * 100) : 0,
|
|
156
|
+
}));
|
|
157
|
+
const costByDay = Array.from(costByDayMap.entries())
|
|
158
|
+
.map(([date, data]) => ({ Date: date, Cost: data.cost, Count: data.count }))
|
|
159
|
+
.sort((a, b) => a.Date.localeCompare(b.Date));
|
|
160
|
+
const costByNumber = Array.from(costByNumberMap.entries())
|
|
161
|
+
.map(([id, data]) => ({
|
|
162
|
+
PhoneNumberID: id.includes('-') ? id : null,
|
|
163
|
+
PhoneNumber: data.phoneNumber,
|
|
164
|
+
Cost: data.cost,
|
|
165
|
+
Count: data.count,
|
|
166
|
+
}))
|
|
167
|
+
.sort((a, b) => b.Cost - a.Cost);
|
|
168
|
+
return {
|
|
169
|
+
TotalVolume: totalVolume,
|
|
170
|
+
LiveCallsCount: liveInteractions.length,
|
|
171
|
+
AnsweredCount: answeredCount,
|
|
172
|
+
AnswerRate: answerRate,
|
|
173
|
+
AbandonedCount: abandonedCount,
|
|
174
|
+
AbandonmentRate: abandonmentRate,
|
|
175
|
+
AverageHandleTimeSeconds: avgHandleTime,
|
|
176
|
+
AverageHandleTimeFormatted: FormatDuration(avgHandleTime),
|
|
177
|
+
AverageSpeedToAnswerSeconds: avgSpeedToAnswer,
|
|
178
|
+
AverageSpeedToAnswerFormatted: FormatDuration(avgSpeedToAnswer),
|
|
179
|
+
TransferCount: transferCount,
|
|
180
|
+
EscalationCount: escalationCount,
|
|
181
|
+
TotalCost: totalCost,
|
|
182
|
+
TotalCostFormatted: FormatCurrency(totalCost),
|
|
183
|
+
VolumeByChannel: volumeByChannel,
|
|
184
|
+
VolumeByDirection: volumeByDirection,
|
|
185
|
+
CostByDay: costByDay,
|
|
186
|
+
CostByNumber: costByNumber,
|
|
187
|
+
LiveInteractions: liveInteractions.sort((a, b) => b.ElapsedSeconds - a.ElapsedSeconds),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
export function BuildTelephonyOperationsAgentContext(metrics, timeRange, channelFilter) {
|
|
191
|
+
return {
|
|
192
|
+
Surface: 'TelephonyOperationsDashboard',
|
|
193
|
+
TimeRange: timeRange,
|
|
194
|
+
ChannelFilter: channelFilter,
|
|
195
|
+
TotalVolume: metrics.TotalVolume,
|
|
196
|
+
LiveCallsCount: metrics.LiveCallsCount,
|
|
197
|
+
AnswerRatePercent: Math.round(metrics.AnswerRate * 10) / 10,
|
|
198
|
+
AbandonmentRatePercent: Math.round(metrics.AbandonmentRate * 10) / 10,
|
|
199
|
+
AverageHandleTime: metrics.AverageHandleTimeFormatted,
|
|
200
|
+
AverageSpeedToAnswer: metrics.AverageSpeedToAnswerFormatted,
|
|
201
|
+
TransferCount: metrics.TransferCount,
|
|
202
|
+
EscalationCount: metrics.EscalationCount,
|
|
203
|
+
TotalCostEstimate: metrics.TotalCostFormatted,
|
|
204
|
+
VolumeByChannel: metrics.VolumeByChannel.map((c) => `${c.Channel}: ${c.Count} (${c.Percentage}%)`),
|
|
205
|
+
VolumeByDirection: metrics.VolumeByDirection.map((d) => `${d.Direction}: ${d.Count} (${d.Percentage}%)`),
|
|
206
|
+
VisibleLiveCallAddresses: BoundNameList(metrics.LiveInteractions.map((i) => `${i.Channel} (${i.Direction}): ${i.RemoteAddress || i.ID} [${i.ElapsedLabel}]`)),
|
|
207
|
+
TopCostNumbers: BoundNameList(metrics.CostByNumber.map((n) => `${n.PhoneNumber}: $${n.Cost.toFixed(2)} (${n.Count} calls)`)),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
export function ValidateTimeRangeParam(raw) {
|
|
211
|
+
return ValidateEnumParam(raw, VALID_TIME_RANGES, 'timeRange');
|
|
212
|
+
}
|
|
213
|
+
export function ValidateChannelParam(raw) {
|
|
214
|
+
return ValidateEnumParam(raw, VALID_CHANNELS, 'channel');
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=telephony-operations-agent-context.js.map
|