@memberjunction/ng-dashboards 5.7.0 → 5.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/Integration/components/connection-studio/connection-studio.component.d.ts +81 -0
  2. package/dist/Integration/components/connection-studio/connection-studio.component.d.ts.map +1 -0
  3. package/dist/Integration/components/connection-studio/connection-studio.component.js +960 -0
  4. package/dist/Integration/components/connection-studio/connection-studio.component.js.map +1 -0
  5. package/dist/Integration/components/control-tower/control-tower.component.d.ts +43 -0
  6. package/dist/Integration/components/control-tower/control-tower.component.d.ts.map +1 -0
  7. package/dist/Integration/components/control-tower/control-tower.component.js +446 -0
  8. package/dist/Integration/components/control-tower/control-tower.component.js.map +1 -0
  9. package/dist/Integration/components/mapping-workspace/mapping-workspace.component.d.ts +43 -0
  10. package/dist/Integration/components/mapping-workspace/mapping-workspace.component.d.ts.map +1 -0
  11. package/dist/Integration/components/mapping-workspace/mapping-workspace.component.js +467 -0
  12. package/dist/Integration/components/mapping-workspace/mapping-workspace.component.js.map +1 -0
  13. package/dist/Integration/components/sync-activity/sync-activity.component.d.ts +65 -0
  14. package/dist/Integration/components/sync-activity/sync-activity.component.d.ts.map +1 -0
  15. package/dist/Integration/components/sync-activity/sync-activity.component.js +671 -0
  16. package/dist/Integration/components/sync-activity/sync-activity.component.js.map +1 -0
  17. package/dist/Integration/components/widgets/integration-card.component.d.ts +22 -0
  18. package/dist/Integration/components/widgets/integration-card.component.d.ts.map +1 -0
  19. package/dist/Integration/components/widgets/integration-card.component.js +262 -0
  20. package/dist/Integration/components/widgets/integration-card.component.js.map +1 -0
  21. package/dist/Integration/components/widgets/run-history-panel.component.d.ts +29 -0
  22. package/dist/Integration/components/widgets/run-history-panel.component.d.ts.map +1 -0
  23. package/dist/Integration/components/widgets/run-history-panel.component.js +398 -0
  24. package/dist/Integration/components/widgets/run-history-panel.component.js.map +1 -0
  25. package/dist/Integration/index.d.ts +9 -0
  26. package/dist/Integration/index.d.ts.map +1 -0
  27. package/dist/Integration/index.js +16 -0
  28. package/dist/Integration/index.js.map +1 -0
  29. package/dist/Integration/integration.module.d.ts +22 -0
  30. package/dist/Integration/integration.module.d.ts.map +1 -0
  31. package/dist/Integration/integration.module.js +88 -0
  32. package/dist/Integration/integration.module.js.map +1 -0
  33. package/dist/Integration/services/integration-data.service.d.ts +154 -0
  34. package/dist/Integration/services/integration-data.service.d.ts.map +1 -0
  35. package/dist/Integration/services/integration-data.service.js +292 -0
  36. package/dist/Integration/services/integration-data.service.js.map +1 -0
  37. package/dist/__tests__/connection-studio.test.d.ts +2 -0
  38. package/dist/__tests__/connection-studio.test.d.ts.map +1 -0
  39. package/dist/__tests__/connection-studio.test.js +186 -0
  40. package/dist/__tests__/connection-studio.test.js.map +1 -0
  41. package/dist/__tests__/integration-data-service.test.d.ts +2 -0
  42. package/dist/__tests__/integration-data-service.test.d.ts.map +1 -0
  43. package/dist/__tests__/integration-data-service.test.js +131 -0
  44. package/dist/__tests__/integration-data-service.test.js.map +1 -0
  45. package/dist/module.d.ts +2 -1
  46. package/dist/module.d.ts.map +1 -1
  47. package/dist/module.js +17 -6
  48. package/dist/module.js.map +1 -1
  49. package/dist/public-api.d.ts +2 -1
  50. package/dist/public-api.d.ts.map +1 -1
  51. package/dist/public-api.js +3 -1
  52. package/dist/public-api.js.map +1 -1
  53. package/package.json +40 -39
@@ -0,0 +1,446 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { ChangeDetectorRef, Component, inject } from '@angular/core';
8
+ import { RegisterClass, UUIDsEqual } from '@memberjunction/global';
9
+ import { BaseResourceComponent } from '@memberjunction/ng-shared';
10
+ import { IntegrationDataService } from '../../services/integration-data.service';
11
+ import * as i0 from "@angular/core";
12
+ import * as i1 from "@progress/kendo-angular-buttons";
13
+ import * as i2 from "@progress/kendo-angular-grid";
14
+ import * as i3 from "@memberjunction/ng-shared-generic";
15
+ import * as i4 from "@angular/common";
16
+ const _forTrack0 = ($index, $item) => $item.RunID;
17
+ const _forTrack1 = ($index, $item) => $item.Date;
18
+ function ControlTowerComponent_Conditional_8_Template(rf, ctx) { if (rf & 1) {
19
+ i0.ɵɵelement(0, "mj-loading", 5);
20
+ } }
21
+ function ControlTowerComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
22
+ i0.ɵɵelementStart(0, "div", 6);
23
+ i0.ɵɵelement(1, "i", 7);
24
+ i0.ɵɵelementStart(2, "h3");
25
+ i0.ɵɵtext(3, "No Integrations Configured");
26
+ i0.ɵɵelementEnd();
27
+ i0.ɵɵelementStart(4, "p");
28
+ i0.ɵɵtext(5, "Set up your first integration using the Connection Studio to start syncing data.");
29
+ i0.ɵɵelementEnd();
30
+ i0.ɵɵelementStart(6, "button", 8);
31
+ i0.ɵɵelement(7, "i", 9);
32
+ i0.ɵɵtext(8, " Create Integration ");
33
+ i0.ɵɵelementEnd()();
34
+ } if (rf & 2) {
35
+ i0.ɵɵadvance(6);
36
+ i0.ɵɵproperty("themeColor", "primary");
37
+ } }
38
+ function ControlTowerComponent_Conditional_10_ng_template_48_Template(rf, ctx) { if (rf & 1) {
39
+ i0.ɵɵelementStart(0, "div", 46);
40
+ i0.ɵɵelement(1, "i", 47);
41
+ i0.ɵɵelementStart(2, "span");
42
+ i0.ɵɵtext(3);
43
+ i0.ɵɵelementEnd()();
44
+ } if (rf & 2) {
45
+ const dataItem_r1 = ctx.$implicit;
46
+ const ctx_r1 = i0.ɵɵnextContext(2);
47
+ i0.ɵɵadvance();
48
+ i0.ɵɵclassMap(ctx_r1.SourceIconClass(dataItem_r1));
49
+ i0.ɵɵadvance(2);
50
+ i0.ɵɵtextInterpolate(dataItem_r1.Integration.Name);
51
+ } }
52
+ function ControlTowerComponent_Conditional_10_ng_template_50_Template(rf, ctx) { if (rf & 1) {
53
+ i0.ɵɵtext(0);
54
+ } if (rf & 2) {
55
+ const dataItem_r3 = ctx.$implicit;
56
+ i0.ɵɵtextInterpolate1(" ", (dataItem_r3.SourceType == null ? null : dataItem_r3.SourceType.Name) ?? "Unknown", " ");
57
+ } }
58
+ function ControlTowerComponent_Conditional_10_ng_template_52_Template(rf, ctx) { if (rf & 1) {
59
+ i0.ɵɵelementStart(0, "span");
60
+ i0.ɵɵtext(1);
61
+ i0.ɵɵelementEnd();
62
+ } if (rf & 2) {
63
+ const dataItem_r4 = ctx.$implicit;
64
+ const ctx_r1 = i0.ɵɵnextContext(2);
65
+ i0.ɵɵclassMap(ctx_r1.StatusChipClass(dataItem_r4.StatusColor));
66
+ i0.ɵɵadvance();
67
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.StatusLabel(dataItem_r4.StatusColor), " ");
68
+ } }
69
+ function ControlTowerComponent_Conditional_10_ng_template_54_Template(rf, ctx) { if (rf & 1) {
70
+ i0.ɵɵtext(0);
71
+ } if (rf & 2) {
72
+ const dataItem_r5 = ctx.$implicit;
73
+ i0.ɵɵtextInterpolate1(" ", dataItem_r5.RelativeTime, " ");
74
+ } }
75
+ function ControlTowerComponent_Conditional_10_ng_template_56_Template(rf, ctx) { if (rf & 1) {
76
+ i0.ɵɵtext(0);
77
+ } if (rf & 2) {
78
+ const dataItem_r6 = ctx.$implicit;
79
+ const ctx_r1 = i0.ɵɵnextContext(2);
80
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.FormatDuration(dataItem_r6.DurationMs), " ");
81
+ } }
82
+ function ControlTowerComponent_Conditional_10_ng_template_58_Template(rf, ctx) { if (rf & 1) {
83
+ i0.ɵɵtext(0);
84
+ i0.ɵɵpipe(1, "number");
85
+ } if (rf & 2) {
86
+ const dataItem_r7 = ctx.$implicit;
87
+ i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind1(1, 1, (dataItem_r7.LatestRun == null ? null : dataItem_r7.LatestRun.TotalRecords) ?? 0), " ");
88
+ } }
89
+ function ControlTowerComponent_Conditional_10_ng_template_60_Template(rf, ctx) { if (rf & 1) {
90
+ i0.ɵɵelementStart(0, "span");
91
+ i0.ɵɵtext(1);
92
+ i0.ɵɵelementEnd();
93
+ } if (rf & 2) {
94
+ const dataItem_r8 = ctx.$implicit;
95
+ i0.ɵɵclassProp("error-count", dataItem_r8.TotalErrors > 0);
96
+ i0.ɵɵadvance();
97
+ i0.ɵɵtextInterpolate1(" ", dataItem_r8.TotalErrors, " ");
98
+ } }
99
+ function ControlTowerComponent_Conditional_10_ng_template_62_Template(rf, ctx) { if (rf & 1) {
100
+ const _r9 = i0.ɵɵgetCurrentView();
101
+ i0.ɵɵelementStart(0, "button", 48);
102
+ i0.ɵɵlistener("click", function ControlTowerComponent_Conditional_10_ng_template_62_Template_button_click_0_listener() { const dataItem_r10 = i0.ɵɵrestoreView(_r9).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnRunNow(dataItem_r10.Integration.ID)); });
103
+ i0.ɵɵelement(1, "i", 49);
104
+ i0.ɵɵtext(2, " Run ");
105
+ i0.ɵɵelementEnd();
106
+ } if (rf & 2) {
107
+ const dataItem_r10 = ctx.$implicit;
108
+ i0.ɵɵproperty("look", "flat")("themeColor", "primary")("size", "small")("disabled", !dataItem_r10.Integration.IsActive);
109
+ } }
110
+ function ControlTowerComponent_Conditional_10_Conditional_68_Template(rf, ctx) { if (rf & 1) {
111
+ i0.ɵɵelementStart(0, "p", 41);
112
+ i0.ɵɵtext(1, "No recent activity.");
113
+ i0.ɵɵelementEnd();
114
+ } }
115
+ function ControlTowerComponent_Conditional_10_Conditional_69_For_2_Template(rf, ctx) { if (rf & 1) {
116
+ i0.ɵɵelementStart(0, "div", 50);
117
+ i0.ɵɵelement(1, "i");
118
+ i0.ɵɵelementStart(2, "div", 51)(3, "span", 52);
119
+ i0.ɵɵtext(4);
120
+ i0.ɵɵelementEnd();
121
+ i0.ɵɵelementStart(5, "span", 53);
122
+ i0.ɵɵtext(6);
123
+ i0.ɵɵpipe(7, "number");
124
+ i0.ɵɵelementEnd()();
125
+ i0.ɵɵelementStart(8, "span", 54);
126
+ i0.ɵɵtext(9);
127
+ i0.ɵɵelementEnd()();
128
+ } if (rf & 2) {
129
+ const item_r11 = ctx.$implicit;
130
+ const ctx_r1 = i0.ɵɵnextContext(3);
131
+ i0.ɵɵadvance();
132
+ i0.ɵɵclassMap(ctx_r1.ActivityStatusIcon(item_r11.Status));
133
+ i0.ɵɵclassProp("status-icon-green", item_r11.StatusColor === "green")("status-icon-red", item_r11.StatusColor === "red")("status-icon-amber", item_r11.StatusColor === "amber");
134
+ i0.ɵɵadvance(3);
135
+ i0.ɵɵtextInterpolate(item_r11.IntegrationName);
136
+ i0.ɵɵadvance(2);
137
+ i0.ɵɵtextInterpolate3(" ", i0.ɵɵpipeBind1(7, 15, item_r11.TotalRecords), " records \u00B7 ", item_r11.RelativeTime, " \u00B7 ", item_r11.RunByUser, " ");
138
+ i0.ɵɵadvance(2);
139
+ i0.ɵɵclassMap("activity-" + item_r11.StatusColor);
140
+ i0.ɵɵadvance();
141
+ i0.ɵɵtextInterpolate1(" ", item_r11.Status, " ");
142
+ } }
143
+ function ControlTowerComponent_Conditional_10_Conditional_69_Template(rf, ctx) { if (rf & 1) {
144
+ i0.ɵɵelementStart(0, "div", 42);
145
+ i0.ɵɵrepeaterCreate(1, ControlTowerComponent_Conditional_10_Conditional_69_For_2_Template, 10, 17, "div", 50, _forTrack0);
146
+ i0.ɵɵelementEnd();
147
+ } if (rf & 2) {
148
+ const ctx_r1 = i0.ɵɵnextContext(2);
149
+ i0.ɵɵadvance();
150
+ i0.ɵɵrepeater(ctx_r1.ActivityFeed);
151
+ } }
152
+ function ControlTowerComponent_Conditional_10_Conditional_74_Template(rf, ctx) { if (rf & 1) {
153
+ i0.ɵɵelementStart(0, "p", 41);
154
+ i0.ɵɵtext(1, "No data available.");
155
+ i0.ɵɵelementEnd();
156
+ } }
157
+ function ControlTowerComponent_Conditional_10_Conditional_75_For_2_Template(rf, ctx) { if (rf & 1) {
158
+ i0.ɵɵelementStart(0, "div", 55)(1, "div", 56);
159
+ i0.ɵɵtext(2);
160
+ i0.ɵɵpipe(3, "number");
161
+ i0.ɵɵelementEnd();
162
+ i0.ɵɵelement(4, "div", 57);
163
+ i0.ɵɵelementStart(5, "div", 58);
164
+ i0.ɵɵtext(6);
165
+ i0.ɵɵelementEnd()();
166
+ } if (rf & 2) {
167
+ const day_r12 = ctx.$implicit;
168
+ const ctx_r1 = i0.ɵɵnextContext(3);
169
+ i0.ɵɵadvance(2);
170
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(3, 4, day_r12.Records));
171
+ i0.ɵɵadvance(2);
172
+ i0.ɵɵstyleProp("height", ctx_r1.BarHeight(day_r12.Records), "%");
173
+ i0.ɵɵadvance(2);
174
+ i0.ɵɵtextInterpolate(day_r12.Label);
175
+ } }
176
+ function ControlTowerComponent_Conditional_10_Conditional_75_Template(rf, ctx) { if (rf & 1) {
177
+ i0.ɵɵelementStart(0, "div", 45);
178
+ i0.ɵɵrepeaterCreate(1, ControlTowerComponent_Conditional_10_Conditional_75_For_2_Template, 7, 6, "div", 55, _forTrack1);
179
+ i0.ɵɵelementEnd();
180
+ } if (rf & 2) {
181
+ const ctx_r1 = i0.ɵɵnextContext(2);
182
+ i0.ɵɵadvance();
183
+ i0.ɵɵrepeater(ctx_r1.DailyCounts);
184
+ } }
185
+ function ControlTowerComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
186
+ i0.ɵɵelementStart(0, "div", 10)(1, "div", 11)(2, "div", 12);
187
+ i0.ɵɵelement(3, "i", 13);
188
+ i0.ɵɵelementEnd();
189
+ i0.ɵɵelementStart(4, "div", 14)(5, "span", 15);
190
+ i0.ɵɵtext(6);
191
+ i0.ɵɵelementEnd();
192
+ i0.ɵɵelementStart(7, "span", 16);
193
+ i0.ɵɵtext(8, "Total Integrations");
194
+ i0.ɵɵelementEnd()()();
195
+ i0.ɵɵelementStart(9, "div", 17)(10, "div", 12);
196
+ i0.ɵɵelement(11, "i", 18);
197
+ i0.ɵɵelementEnd();
198
+ i0.ɵɵelementStart(12, "div", 14)(13, "span", 15);
199
+ i0.ɵɵtext(14);
200
+ i0.ɵɵelementEnd();
201
+ i0.ɵɵelementStart(15, "span", 16);
202
+ i0.ɵɵtext(16, "Active Syncs");
203
+ i0.ɵɵelementEnd()()();
204
+ i0.ɵɵelementStart(17, "div", 19)(18, "div", 12);
205
+ i0.ɵɵelement(19, "i", 20);
206
+ i0.ɵɵelementEnd();
207
+ i0.ɵɵelementStart(20, "div", 14)(21, "span", 15);
208
+ i0.ɵɵtext(22);
209
+ i0.ɵɵpipe(23, "number");
210
+ i0.ɵɵelementEnd();
211
+ i0.ɵɵelementStart(24, "span", 16);
212
+ i0.ɵɵtext(25, "Records Synced Today");
213
+ i0.ɵɵelementEnd()()();
214
+ i0.ɵɵelementStart(26, "div", 21)(27, "div", 12);
215
+ i0.ɵɵelement(28, "i", 22);
216
+ i0.ɵɵelementEnd();
217
+ i0.ɵɵelementStart(29, "div", 14)(30, "span", 15);
218
+ i0.ɵɵtext(31);
219
+ i0.ɵɵelementEnd();
220
+ i0.ɵɵelementStart(32, "span", 16);
221
+ i0.ɵɵtext(33, "Error Rate");
222
+ i0.ɵɵelementEnd()()();
223
+ i0.ɵɵelementStart(34, "div", 23)(35, "div", 12);
224
+ i0.ɵɵelement(36, "i", 24);
225
+ i0.ɵɵelementEnd();
226
+ i0.ɵɵelementStart(37, "div", 14)(38, "span", 15);
227
+ i0.ɵɵtext(39);
228
+ i0.ɵɵelementEnd();
229
+ i0.ɵɵelementStart(40, "span", 16);
230
+ i0.ɵɵtext(41, "Avg Sync Duration");
231
+ i0.ɵɵelementEnd()()()();
232
+ i0.ɵɵelementStart(42, "div", 25)(43, "h3");
233
+ i0.ɵɵelement(44, "i", 26);
234
+ i0.ɵɵtext(45, " Integration Health");
235
+ i0.ɵɵelementEnd()();
236
+ i0.ɵɵelementStart(46, "kendo-grid", 27)(47, "kendo-grid-column", 28);
237
+ i0.ɵɵtemplate(48, ControlTowerComponent_Conditional_10_ng_template_48_Template, 4, 3, "ng-template", 29);
238
+ i0.ɵɵelementEnd();
239
+ i0.ɵɵelementStart(49, "kendo-grid-column", 30);
240
+ i0.ɵɵtemplate(50, ControlTowerComponent_Conditional_10_ng_template_50_Template, 1, 1, "ng-template", 29);
241
+ i0.ɵɵelementEnd();
242
+ i0.ɵɵelementStart(51, "kendo-grid-column", 31);
243
+ i0.ɵɵtemplate(52, ControlTowerComponent_Conditional_10_ng_template_52_Template, 2, 3, "ng-template", 29);
244
+ i0.ɵɵelementEnd();
245
+ i0.ɵɵelementStart(53, "kendo-grid-column", 32);
246
+ i0.ɵɵtemplate(54, ControlTowerComponent_Conditional_10_ng_template_54_Template, 1, 1, "ng-template", 29);
247
+ i0.ɵɵelementEnd();
248
+ i0.ɵɵelementStart(55, "kendo-grid-column", 33);
249
+ i0.ɵɵtemplate(56, ControlTowerComponent_Conditional_10_ng_template_56_Template, 1, 1, "ng-template", 29);
250
+ i0.ɵɵelementEnd();
251
+ i0.ɵɵelementStart(57, "kendo-grid-column", 34);
252
+ i0.ɵɵtemplate(58, ControlTowerComponent_Conditional_10_ng_template_58_Template, 2, 3, "ng-template", 29);
253
+ i0.ɵɵelementEnd();
254
+ i0.ɵɵelementStart(59, "kendo-grid-column", 35);
255
+ i0.ɵɵtemplate(60, ControlTowerComponent_Conditional_10_ng_template_60_Template, 2, 3, "ng-template", 29);
256
+ i0.ɵɵelementEnd();
257
+ i0.ɵɵelementStart(61, "kendo-grid-column", 36);
258
+ i0.ɵɵtemplate(62, ControlTowerComponent_Conditional_10_ng_template_62_Template, 3, 4, "ng-template", 29);
259
+ i0.ɵɵelementEnd()();
260
+ i0.ɵɵelementStart(63, "div", 37)(64, "div", 38)(65, "h3", 39);
261
+ i0.ɵɵelement(66, "i", 40);
262
+ i0.ɵɵtext(67, " Recent Activity");
263
+ i0.ɵɵelementEnd();
264
+ i0.ɵɵconditionalCreate(68, ControlTowerComponent_Conditional_10_Conditional_68_Template, 2, 0, "p", 41)(69, ControlTowerComponent_Conditional_10_Conditional_69_Template, 3, 0, "div", 42);
265
+ i0.ɵɵelementEnd();
266
+ i0.ɵɵelementStart(70, "div", 43)(71, "h3", 39);
267
+ i0.ɵɵelement(72, "i", 44);
268
+ i0.ɵɵtext(73, " Records Synced (7 days)");
269
+ i0.ɵɵelementEnd();
270
+ i0.ɵɵconditionalCreate(74, ControlTowerComponent_Conditional_10_Conditional_74_Template, 2, 0, "p", 41)(75, ControlTowerComponent_Conditional_10_Conditional_75_Template, 3, 0, "div", 45);
271
+ i0.ɵɵelementEnd()();
272
+ } if (rf & 2) {
273
+ const ctx_r1 = i0.ɵɵnextContext();
274
+ i0.ɵɵadvance(6);
275
+ i0.ɵɵtextInterpolate(ctx_r1.KPIs.TotalIntegrations);
276
+ i0.ɵɵadvance(8);
277
+ i0.ɵɵtextInterpolate(ctx_r1.KPIs.ActiveSyncs);
278
+ i0.ɵɵadvance(8);
279
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(23, 17, ctx_r1.KPIs.RecordsSyncedToday));
280
+ i0.ɵɵadvance(9);
281
+ i0.ɵɵtextInterpolate(ctx_r1.FormatErrorRate(ctx_r1.KPIs.ErrorRate));
282
+ i0.ɵɵadvance(8);
283
+ i0.ɵɵtextInterpolate(ctx_r1.FormatDuration(ctx_r1.KPIs.AverageSyncDurationMs));
284
+ i0.ɵɵadvance(7);
285
+ i0.ɵɵproperty("data", ctx_r1.Summaries)("resizable", true);
286
+ i0.ɵɵadvance();
287
+ i0.ɵɵproperty("width", 200);
288
+ i0.ɵɵadvance(2);
289
+ i0.ɵɵproperty("width", 140);
290
+ i0.ɵɵadvance(2);
291
+ i0.ɵɵproperty("width", 110);
292
+ i0.ɵɵadvance(2);
293
+ i0.ɵɵproperty("width", 120);
294
+ i0.ɵɵadvance(2);
295
+ i0.ɵɵproperty("width", 110);
296
+ i0.ɵɵadvance(2);
297
+ i0.ɵɵproperty("width", 100);
298
+ i0.ɵɵadvance(2);
299
+ i0.ɵɵproperty("width", 90);
300
+ i0.ɵɵadvance(2);
301
+ i0.ɵɵproperty("width", 100);
302
+ i0.ɵɵadvance(7);
303
+ i0.ɵɵconditional(ctx_r1.ActivityFeed.length === 0 ? 68 : 69);
304
+ i0.ɵɵadvance(6);
305
+ i0.ɵɵconditional(ctx_r1.DailyCounts.length === 0 ? 74 : 75);
306
+ } }
307
+ let ControlTowerComponent = class ControlTowerComponent extends BaseResourceComponent {
308
+ /**
309
+ * The Provider @Input is inherited from BaseAngularComponent.
310
+ * Use this.RunViewToUse to get the appropriate IRunViewProvider
311
+ * for multi-MJ-instance support.
312
+ */
313
+ Summaries = [];
314
+ KPIs = {
315
+ TotalIntegrations: 0,
316
+ ActiveSyncs: 0,
317
+ RecordsSyncedToday: 0,
318
+ ErrorRate: 0,
319
+ AverageSyncDurationMs: null
320
+ };
321
+ ActivityFeed = [];
322
+ DailyCounts = [];
323
+ IsLoading = false;
324
+ ExpandedID = null;
325
+ dataService = inject(IntegrationDataService);
326
+ cdr = inject(ChangeDetectorRef);
327
+ async ngOnInit() {
328
+ await this.LoadData();
329
+ }
330
+ async LoadData() {
331
+ this.IsLoading = true;
332
+ this.cdr.detectChanges();
333
+ try {
334
+ const provider = this.RunViewToUse;
335
+ const [summaries, activityFeed, dailyCounts] = await Promise.all([
336
+ this.dataService.LoadIntegrationSummaries(provider),
337
+ this.dataService.LoadRecentRuns(20, provider),
338
+ this.dataService.LoadDailyRecordCounts(7, provider)
339
+ ]);
340
+ this.Summaries = summaries;
341
+ this.KPIs = this.dataService.ComputeKPIs(summaries);
342
+ this.ActivityFeed = activityFeed;
343
+ this.DailyCounts = dailyCounts;
344
+ }
345
+ catch (err) {
346
+ console.error('[IntegrationControlTower] Failed to load data:', err);
347
+ }
348
+ finally {
349
+ this.IsLoading = false;
350
+ this.cdr.detectChanges();
351
+ }
352
+ }
353
+ async Refresh() {
354
+ await this.LoadData();
355
+ }
356
+ OnRunNow(integrationID) {
357
+ console.log('[IntegrationControlTower] Run Now clicked for integration:', integrationID);
358
+ }
359
+ OnExpandToggle(integrationID) {
360
+ this.ExpandedID = UUIDsEqual(this.ExpandedID, integrationID) ? null : integrationID;
361
+ }
362
+ IsExpanded(integrationID) {
363
+ return UUIDsEqual(this.ExpandedID, integrationID);
364
+ }
365
+ get MaxDailyRecords() {
366
+ if (this.DailyCounts.length === 0)
367
+ return 1;
368
+ const maxVal = Math.max(...this.DailyCounts.map(d => d.Records));
369
+ return maxVal > 0 ? maxVal : 1;
370
+ }
371
+ BarHeight(records) {
372
+ return Math.max((records / this.MaxDailyRecords) * 100, 2);
373
+ }
374
+ FormatDuration(ms) {
375
+ return this.dataService.FormatDuration(ms);
376
+ }
377
+ FormatErrorRate(rate) {
378
+ return rate > 0 ? `${rate}%` : '0%';
379
+ }
380
+ StatusChipClass(color) {
381
+ return `status-chip status-${color}`;
382
+ }
383
+ SourceIconClass(summary) {
384
+ return summary.SourceType?.IconClass ?? 'fa-solid fa-plug';
385
+ }
386
+ StatusLabel(color) {
387
+ if (color === 'green')
388
+ return 'Healthy';
389
+ if (color === 'amber')
390
+ return 'Warning';
391
+ if (color === 'red')
392
+ return 'Failed';
393
+ return 'Inactive';
394
+ }
395
+ FormatRelativeTime(dateStr) {
396
+ return this.dataService.ComputeRelativeTime(dateStr);
397
+ }
398
+ ActivityStatusIcon(status) {
399
+ if (status === 'Success')
400
+ return 'fa-solid fa-circle-check';
401
+ if (status === 'Failed')
402
+ return 'fa-solid fa-circle-xmark';
403
+ if (status === 'In Progress')
404
+ return 'fa-solid fa-spinner fa-spin';
405
+ return 'fa-solid fa-clock';
406
+ }
407
+ async GetResourceDisplayName(_data) {
408
+ return 'Integration Control Tower';
409
+ }
410
+ async GetResourceIconClass(_data) {
411
+ return 'fa-solid fa-tower-broadcast';
412
+ }
413
+ static ɵfac = /*@__PURE__*/ (() => { let ɵControlTowerComponent_BaseFactory; return function ControlTowerComponent_Factory(__ngFactoryType__) { return (ɵControlTowerComponent_BaseFactory || (ɵControlTowerComponent_BaseFactory = i0.ɵɵgetInheritedFactory(ControlTowerComponent)))(__ngFactoryType__ || ControlTowerComponent); }; })();
414
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ControlTowerComponent, selectors: [["app-control-tower"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 11, vars: 3, consts: [[1, "control-tower"], [1, "tower-header"], [1, "fa-solid", "fa-tower-broadcast"], ["kendoButton", "", 3, "click", "look", "themeColor"], [1, "fa-solid", "fa-arrows-rotate"], ["text", "Loading integrations...", "size", "medium"], [1, "empty-state"], [1, "fa-solid", "fa-plug-circle-xmark"], ["kendoButton", "", 1, "empty-cta", 3, "themeColor"], [1, "fa-solid", "fa-plus"], [1, "kpi-strip"], [1, "kpi-card", "kpi-blue"], [1, "kpi-icon"], [1, "fa-solid", "fa-plug"], [1, "kpi-content"], [1, "kpi-value"], [1, "kpi-label"], [1, "kpi-card", "kpi-green"], [1, "fa-solid", "fa-sync", "fa-spin"], [1, "kpi-card", "kpi-purple"], [1, "fa-solid", "fa-database"], [1, "kpi-card", "kpi-red"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "kpi-card", "kpi-orange"], [1, "fa-solid", "fa-clock"], [1, "section-header"], [1, "fa-solid", "fa-heartbeat"], [1, "health-grid", 3, "data", "resizable"], ["field", "Integration.Name", "title", "Integration Name", 3, "width"], ["kendoGridCellTemplate", ""], ["title", "Source Type", 3, "width"], ["title", "Status", 3, "width"], ["title", "Last Run", 3, "width"], ["title", "Duration", 3, "width"], ["title", "Records", 3, "width"], ["title", "Errors", 3, "width"], ["title", "Actions", 3, "width"], [1, "bottom-section"], [1, "activity-feed-panel"], [1, "section-title"], [1, "fa-solid", "fa-stream"], [1, "empty-hint"], [1, "activity-list"], [1, "performance-panel"], [1, "fa-solid", "fa-chart-bar"], [1, "bar-chart"], [1, "grid-integration-name"], [1, "grid-source-icon"], ["kendoButton", "", 3, "click", "look", "themeColor", "size", "disabled"], [1, "fa-solid", "fa-play"], [1, "activity-item"], [1, "activity-details"], [1, "activity-name"], [1, "activity-meta"], [1, "activity-status-chip"], [1, "bar-column"], [1, "bar-value"], [1, "bar-fill"], [1, "bar-label"]], template: function ControlTowerComponent_Template(rf, ctx) { if (rf & 1) {
415
+ i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "h2");
416
+ i0.ɵɵelement(3, "i", 2);
417
+ i0.ɵɵtext(4, " Integration Control Tower");
418
+ i0.ɵɵelementEnd();
419
+ i0.ɵɵelementStart(5, "button", 3);
420
+ i0.ɵɵlistener("click", function ControlTowerComponent_Template_button_click_5_listener() { return ctx.Refresh(); });
421
+ i0.ɵɵelement(6, "i", 4);
422
+ i0.ɵɵtext(7, " Refresh ");
423
+ i0.ɵɵelementEnd()();
424
+ i0.ɵɵconditionalCreate(8, ControlTowerComponent_Conditional_8_Template, 1, 0, "mj-loading", 5)(9, ControlTowerComponent_Conditional_9_Template, 9, 1, "div", 6)(10, ControlTowerComponent_Conditional_10_Template, 76, 19);
425
+ i0.ɵɵelementEnd();
426
+ } if (rf & 2) {
427
+ i0.ɵɵadvance(5);
428
+ i0.ɵɵproperty("look", "flat")("themeColor", "primary");
429
+ i0.ɵɵadvance(3);
430
+ i0.ɵɵconditional(ctx.IsLoading ? 8 : ctx.Summaries.length === 0 ? 9 : 10);
431
+ } }, dependencies: [i1.ButtonComponent, i2.GridComponent, i2.ColumnComponent, i2.CellTemplateDirective, i3.LoadingComponent, i4.DecimalPipe], styles: [".control-tower[_ngcontent-%COMP%] {\n padding: 24px;\n max-width: 1400px;\n margin: 0 auto;\n}\n\n.tower-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 24px;\n}\n\n.tower-header[_ngcontent-%COMP%] h2[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n}\n\n.tower-header[_ngcontent-%COMP%] h2[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { margin-right: 8px; }\n\n\n\n.empty-state[_ngcontent-%COMP%] {\n text-align: center;\n padding: 80px 20px;\n color: #888;\n}\n\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 64px;\n margin-bottom: 20px;\n display: block;\n color: #bbb;\n}\n\n.empty-state[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n font-size: 20px;\n color: #555;\n}\n\n.empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0 0 24px 0;\n font-size: 14px;\n}\n\n.empty-cta[_ngcontent-%COMP%] {\n margin-top: 8px;\n}\n\n\n\n.kpi-strip[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(5, 1fr);\n gap: 16px;\n margin-bottom: 28px;\n}\n\n.kpi-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 18px 16px;\n border-radius: 10px;\n background: #fff;\n border: 1px solid #e8e8e8;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);\n}\n\n.kpi-icon[_ngcontent-%COMP%] {\n width: 44px;\n height: 44px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 18px;\n flex-shrink: 0;\n}\n\n.kpi-content[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.kpi-value[_ngcontent-%COMP%] {\n font-size: 22px;\n font-weight: 700;\n line-height: 1.2;\n}\n\n.kpi-label[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #888;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n\n\n.kpi-blue[_ngcontent-%COMP%] .kpi-icon[_ngcontent-%COMP%] { background: #e8f0fe; color: #1a73e8; }\n.kpi-blue[_ngcontent-%COMP%] .kpi-value[_ngcontent-%COMP%] { color: #1a73e8; }\n.kpi-green[_ngcontent-%COMP%] .kpi-icon[_ngcontent-%COMP%] { background: #e6f9ed; color: #1b7a3d; }\n.kpi-green[_ngcontent-%COMP%] .kpi-value[_ngcontent-%COMP%] { color: #1b7a3d; }\n.kpi-purple[_ngcontent-%COMP%] .kpi-icon[_ngcontent-%COMP%] { background: #f0e8ff; color: #7c3aed; }\n.kpi-purple[_ngcontent-%COMP%] .kpi-value[_ngcontent-%COMP%] { color: #7c3aed; }\n.kpi-red[_ngcontent-%COMP%] .kpi-icon[_ngcontent-%COMP%] { background: #fde8e8; color: #c62828; }\n.kpi-red[_ngcontent-%COMP%] .kpi-value[_ngcontent-%COMP%] { color: #c62828; }\n.kpi-orange[_ngcontent-%COMP%] .kpi-icon[_ngcontent-%COMP%] { background: #fff3e0; color: #e65100; }\n.kpi-orange[_ngcontent-%COMP%] .kpi-value[_ngcontent-%COMP%] { color: #e65100; }\n\n\n\n.section-header[_ngcontent-%COMP%] {\n margin-bottom: 12px;\n}\n\n.section-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n}\n\n.section-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { margin-right: 8px; color: #666; }\n\n\n\n.health-grid[_ngcontent-%COMP%] {\n margin-bottom: 28px;\n}\n\n.grid-integration-name[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 500;\n}\n\n.grid-source-icon[_ngcontent-%COMP%] {\n color: #4a6cf7;\n font-size: 14px;\n}\n\n\n\n.status-chip[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 600;\n padding: 3px 10px;\n border-radius: 12px;\n text-transform: uppercase;\n white-space: nowrap;\n display: inline-block;\n}\n.status-green[_ngcontent-%COMP%] { background: #e6f9ed; color: #1b7a3d; }\n.status-amber[_ngcontent-%COMP%] { background: #fff7e0; color: #b5850a; }\n.status-red[_ngcontent-%COMP%] { background: #fde8e8; color: #c62828; }\n.status-gray[_ngcontent-%COMP%] { background: #f0f0f0; color: #757575; }\n\n.error-count[_ngcontent-%COMP%] {\n color: #c62828;\n font-weight: 600;\n}\n\n\n\n.bottom-section[_ngcontent-%COMP%] {\n display: flex;\n gap: 24px;\n margin-top: 4px;\n}\n\n.section-title[_ngcontent-%COMP%] {\n margin: 0 0 16px 0;\n font-size: 15px;\n font-weight: 600;\n}\n\n.section-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n margin-right: 8px;\n color: #666;\n}\n\n\n\n.activity-feed-panel[_ngcontent-%COMP%] {\n flex: 6;\n background: #fff;\n border: 1px solid #e8e8e8;\n border-radius: 10px;\n padding: 20px;\n max-height: 400px;\n overflow-y: auto;\n}\n\n.activity-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.activity-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n border-radius: 6px;\n transition: background 0.15s;\n}\n\n.activity-item[_ngcontent-%COMP%]:hover {\n background: #f8f9fa;\n}\n\n.status-icon-green[_ngcontent-%COMP%] { color: #1b7a3d; }\n.status-icon-red[_ngcontent-%COMP%] { color: #c62828; }\n.status-icon-amber[_ngcontent-%COMP%] { color: #b5850a; }\n\n.activity-details[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.activity-name[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 600;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.activity-meta[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #888;\n}\n\n.activity-status-chip[_ngcontent-%COMP%] {\n font-size: 10px;\n font-weight: 600;\n padding: 2px 8px;\n border-radius: 10px;\n text-transform: uppercase;\n white-space: nowrap;\n flex-shrink: 0;\n}\n.activity-green[_ngcontent-%COMP%] { background: #e6f9ed; color: #1b7a3d; }\n.activity-amber[_ngcontent-%COMP%] { background: #fff7e0; color: #b5850a; }\n.activity-red[_ngcontent-%COMP%] { background: #fde8e8; color: #c62828; }\n\n\n\n.performance-panel[_ngcontent-%COMP%] {\n flex: 4;\n background: #fff;\n border: 1px solid #e8e8e8;\n border-radius: 10px;\n padding: 20px;\n}\n\n.bar-chart[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-end;\n justify-content: space-between;\n gap: 12px;\n height: 200px;\n padding-top: 24px;\n}\n\n.bar-column[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: flex-end;\n height: 100%;\n position: relative;\n}\n\n.bar-value[_ngcontent-%COMP%] {\n font-size: 10px;\n font-weight: 600;\n color: #666;\n margin-bottom: 4px;\n}\n\n.bar-fill[_ngcontent-%COMP%] {\n width: 100%;\n max-width: 40px;\n background: linear-gradient(180deg, #4a6cf7 0%, #7c95f8 100%);\n border-radius: 4px 4px 0 0;\n min-height: 2px;\n transition: height 0.3s ease;\n}\n\n.bar-label[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #888;\n margin-top: 6px;\n text-align: center;\n}\n\n.empty-hint[_ngcontent-%COMP%] {\n color: #999;\n font-size: 13px;\n font-style: italic;\n}\n\n\n\n@media (max-width: 1200px) {\n .kpi-strip[_ngcontent-%COMP%] {\n grid-template-columns: repeat(3, 1fr);\n }\n}\n\n@media (max-width: 768px) {\n .kpi-strip[_ngcontent-%COMP%] {\n grid-template-columns: repeat(2, 1fr);\n }\n .bottom-section[_ngcontent-%COMP%] {\n flex-direction: column;\n }\n}"] });
432
+ };
433
+ ControlTowerComponent = __decorate([
434
+ RegisterClass(BaseResourceComponent, 'IntegrationControlTower')
435
+ ], ControlTowerComponent);
436
+ export { ControlTowerComponent };
437
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ControlTowerComponent, [{
438
+ type: Component,
439
+ args: [{ standalone: false, selector: 'app-control-tower', template: "<div class=\"control-tower\">\n <div class=\"tower-header\">\n <h2><i class=\"fa-solid fa-tower-broadcast\"></i> Integration Control Tower</h2>\n <button kendoButton [look]=\"'flat'\" [themeColor]=\"'primary'\" (click)=\"Refresh()\">\n <i class=\"fa-solid fa-arrows-rotate\"></i> Refresh\n </button>\n </div>\n\n @if (IsLoading) {\n <mj-loading text=\"Loading integrations...\" size=\"medium\"></mj-loading>\n } @else if (Summaries.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-plug-circle-xmark\"></i>\n <h3>No Integrations Configured</h3>\n <p>Set up your first integration using the Connection Studio to start syncing data.</p>\n <button kendoButton [themeColor]=\"'primary'\" class=\"empty-cta\">\n <i class=\"fa-solid fa-plus\"></i> Create Integration\n </button>\n </div>\n } @else {\n <!-- KPI Strip -->\n <div class=\"kpi-strip\">\n <div class=\"kpi-card kpi-blue\">\n <div class=\"kpi-icon\"><i class=\"fa-solid fa-plug\"></i></div>\n <div class=\"kpi-content\">\n <span class=\"kpi-value\">{{ KPIs.TotalIntegrations }}</span>\n <span class=\"kpi-label\">Total Integrations</span>\n </div>\n </div>\n <div class=\"kpi-card kpi-green\">\n <div class=\"kpi-icon\"><i class=\"fa-solid fa-sync fa-spin\"></i></div>\n <div class=\"kpi-content\">\n <span class=\"kpi-value\">{{ KPIs.ActiveSyncs }}</span>\n <span class=\"kpi-label\">Active Syncs</span>\n </div>\n </div>\n <div class=\"kpi-card kpi-purple\">\n <div class=\"kpi-icon\"><i class=\"fa-solid fa-database\"></i></div>\n <div class=\"kpi-content\">\n <span class=\"kpi-value\">{{ KPIs.RecordsSyncedToday | number }}</span>\n <span class=\"kpi-label\">Records Synced Today</span>\n </div>\n </div>\n <div class=\"kpi-card kpi-red\">\n <div class=\"kpi-icon\"><i class=\"fa-solid fa-exclamation-triangle\"></i></div>\n <div class=\"kpi-content\">\n <span class=\"kpi-value\">{{ FormatErrorRate(KPIs.ErrorRate) }}</span>\n <span class=\"kpi-label\">Error Rate</span>\n </div>\n </div>\n <div class=\"kpi-card kpi-orange\">\n <div class=\"kpi-icon\"><i class=\"fa-solid fa-clock\"></i></div>\n <div class=\"kpi-content\">\n <span class=\"kpi-value\">{{ FormatDuration(KPIs.AverageSyncDurationMs) }}</span>\n <span class=\"kpi-label\">Avg Sync Duration</span>\n </div>\n </div>\n </div>\n\n <!-- Integration Health Grid -->\n <div class=\"section-header\">\n <h3><i class=\"fa-solid fa-heartbeat\"></i> Integration Health</h3>\n </div>\n <kendo-grid [data]=\"Summaries\" [resizable]=\"true\" class=\"health-grid\">\n <kendo-grid-column field=\"Integration.Name\" title=\"Integration Name\" [width]=\"200\">\n <ng-template kendoGridCellTemplate let-dataItem>\n <div class=\"grid-integration-name\">\n <i [class]=\"SourceIconClass(dataItem)\" class=\"grid-source-icon\"></i>\n <span>{{ dataItem.Integration.Name }}</span>\n </div>\n </ng-template>\n </kendo-grid-column>\n <kendo-grid-column title=\"Source Type\" [width]=\"140\">\n <ng-template kendoGridCellTemplate let-dataItem>\n {{ dataItem.SourceType?.Name ?? 'Unknown' }}\n </ng-template>\n </kendo-grid-column>\n <kendo-grid-column title=\"Status\" [width]=\"110\">\n <ng-template kendoGridCellTemplate let-dataItem>\n <span [class]=\"StatusChipClass(dataItem.StatusColor)\">\n {{ StatusLabel(dataItem.StatusColor) }}\n </span>\n </ng-template>\n </kendo-grid-column>\n <kendo-grid-column title=\"Last Run\" [width]=\"120\">\n <ng-template kendoGridCellTemplate let-dataItem>\n {{ dataItem.RelativeTime }}\n </ng-template>\n </kendo-grid-column>\n <kendo-grid-column title=\"Duration\" [width]=\"110\">\n <ng-template kendoGridCellTemplate let-dataItem>\n {{ FormatDuration(dataItem.DurationMs) }}\n </ng-template>\n </kendo-grid-column>\n <kendo-grid-column title=\"Records\" [width]=\"100\">\n <ng-template kendoGridCellTemplate let-dataItem>\n {{ (dataItem.LatestRun?.TotalRecords ?? 0) | number }}\n </ng-template>\n </kendo-grid-column>\n <kendo-grid-column title=\"Errors\" [width]=\"90\">\n <ng-template kendoGridCellTemplate let-dataItem>\n <span [class.error-count]=\"dataItem.TotalErrors > 0\">\n {{ dataItem.TotalErrors }}\n </span>\n </ng-template>\n </kendo-grid-column>\n <kendo-grid-column title=\"Actions\" [width]=\"100\">\n <ng-template kendoGridCellTemplate let-dataItem>\n <button kendoButton [look]=\"'flat'\" [themeColor]=\"'primary'\" [size]=\"'small'\"\n (click)=\"OnRunNow(dataItem.Integration.ID)\"\n [disabled]=\"!dataItem.Integration.IsActive\">\n <i class=\"fa-solid fa-play\"></i> Run\n </button>\n </ng-template>\n </kendo-grid-column>\n </kendo-grid>\n\n <!-- Bottom Section: Activity Feed & Performance -->\n <div class=\"bottom-section\">\n <!-- Activity Feed -->\n <div class=\"activity-feed-panel\">\n <h3 class=\"section-title\"><i class=\"fa-solid fa-stream\"></i> Recent Activity</h3>\n @if (ActivityFeed.length === 0) {\n <p class=\"empty-hint\">No recent activity.</p>\n } @else {\n <div class=\"activity-list\">\n @for (item of ActivityFeed; track item.RunID) {\n <div class=\"activity-item\">\n <i [class]=\"ActivityStatusIcon(item.Status)\"\n [class.status-icon-green]=\"item.StatusColor === 'green'\"\n [class.status-icon-red]=\"item.StatusColor === 'red'\"\n [class.status-icon-amber]=\"item.StatusColor === 'amber'\">\n </i>\n <div class=\"activity-details\">\n <span class=\"activity-name\">{{ item.IntegrationName }}</span>\n <span class=\"activity-meta\">\n {{ item.TotalRecords | number }} records &middot; {{ item.RelativeTime }}\n &middot; {{ item.RunByUser }}\n </span>\n </div>\n <span class=\"activity-status-chip\" [class]=\"'activity-' + item.StatusColor\">\n {{ item.Status }}\n </span>\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Performance Bar Chart -->\n <div class=\"performance-panel\">\n <h3 class=\"section-title\"><i class=\"fa-solid fa-chart-bar\"></i> Records Synced (7 days)</h3>\n @if (DailyCounts.length === 0) {\n <p class=\"empty-hint\">No data available.</p>\n } @else {\n <div class=\"bar-chart\">\n @for (day of DailyCounts; track day.Date) {\n <div class=\"bar-column\">\n <div class=\"bar-value\">{{ day.Records | number }}</div>\n <div class=\"bar-fill\" [style.height.%]=\"BarHeight(day.Records)\"></div>\n <div class=\"bar-label\">{{ day.Label }}</div>\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n</div>\n", styles: [".control-tower {\n padding: 24px;\n max-width: 1400px;\n margin: 0 auto;\n}\n\n.tower-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 24px;\n}\n\n.tower-header h2 {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n}\n\n.tower-header h2 i { margin-right: 8px; }\n\n/* Empty State */\n.empty-state {\n text-align: center;\n padding: 80px 20px;\n color: #888;\n}\n\n.empty-state i {\n font-size: 64px;\n margin-bottom: 20px;\n display: block;\n color: #bbb;\n}\n\n.empty-state h3 {\n margin: 0 0 8px 0;\n font-size: 20px;\n color: #555;\n}\n\n.empty-state p {\n margin: 0 0 24px 0;\n font-size: 14px;\n}\n\n.empty-cta {\n margin-top: 8px;\n}\n\n/* KPI Strip */\n.kpi-strip {\n display: grid;\n grid-template-columns: repeat(5, 1fr);\n gap: 16px;\n margin-bottom: 28px;\n}\n\n.kpi-card {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 18px 16px;\n border-radius: 10px;\n background: #fff;\n border: 1px solid #e8e8e8;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);\n}\n\n.kpi-icon {\n width: 44px;\n height: 44px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 18px;\n flex-shrink: 0;\n}\n\n.kpi-content {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.kpi-value {\n font-size: 22px;\n font-weight: 700;\n line-height: 1.2;\n}\n\n.kpi-label {\n font-size: 11px;\n color: #888;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* KPI Color Variants */\n.kpi-blue .kpi-icon { background: #e8f0fe; color: #1a73e8; }\n.kpi-blue .kpi-value { color: #1a73e8; }\n.kpi-green .kpi-icon { background: #e6f9ed; color: #1b7a3d; }\n.kpi-green .kpi-value { color: #1b7a3d; }\n.kpi-purple .kpi-icon { background: #f0e8ff; color: #7c3aed; }\n.kpi-purple .kpi-value { color: #7c3aed; }\n.kpi-red .kpi-icon { background: #fde8e8; color: #c62828; }\n.kpi-red .kpi-value { color: #c62828; }\n.kpi-orange .kpi-icon { background: #fff3e0; color: #e65100; }\n.kpi-orange .kpi-value { color: #e65100; }\n\n/* Section Headers */\n.section-header {\n margin-bottom: 12px;\n}\n\n.section-header h3 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n}\n\n.section-header h3 i { margin-right: 8px; color: #666; }\n\n/* Health Grid */\n.health-grid {\n margin-bottom: 28px;\n}\n\n.grid-integration-name {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 500;\n}\n\n.grid-source-icon {\n color: #4a6cf7;\n font-size: 14px;\n}\n\n/* Status Chips */\n.status-chip {\n font-size: 11px;\n font-weight: 600;\n padding: 3px 10px;\n border-radius: 12px;\n text-transform: uppercase;\n white-space: nowrap;\n display: inline-block;\n}\n.status-green { background: #e6f9ed; color: #1b7a3d; }\n.status-amber { background: #fff7e0; color: #b5850a; }\n.status-red { background: #fde8e8; color: #c62828; }\n.status-gray { background: #f0f0f0; color: #757575; }\n\n.error-count {\n color: #c62828;\n font-weight: 600;\n}\n\n/* Bottom Section */\n.bottom-section {\n display: flex;\n gap: 24px;\n margin-top: 4px;\n}\n\n.section-title {\n margin: 0 0 16px 0;\n font-size: 15px;\n font-weight: 600;\n}\n\n.section-title i {\n margin-right: 8px;\n color: #666;\n}\n\n/* Activity Feed */\n.activity-feed-panel {\n flex: 6;\n background: #fff;\n border: 1px solid #e8e8e8;\n border-radius: 10px;\n padding: 20px;\n max-height: 400px;\n overflow-y: auto;\n}\n\n.activity-list {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.activity-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n border-radius: 6px;\n transition: background 0.15s;\n}\n\n.activity-item:hover {\n background: #f8f9fa;\n}\n\n.status-icon-green { color: #1b7a3d; }\n.status-icon-red { color: #c62828; }\n.status-icon-amber { color: #b5850a; }\n\n.activity-details {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.activity-name {\n font-size: 13px;\n font-weight: 600;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.activity-meta {\n font-size: 11px;\n color: #888;\n}\n\n.activity-status-chip {\n font-size: 10px;\n font-weight: 600;\n padding: 2px 8px;\n border-radius: 10px;\n text-transform: uppercase;\n white-space: nowrap;\n flex-shrink: 0;\n}\n.activity-green { background: #e6f9ed; color: #1b7a3d; }\n.activity-amber { background: #fff7e0; color: #b5850a; }\n.activity-red { background: #fde8e8; color: #c62828; }\n\n/* Performance Panel */\n.performance-panel {\n flex: 4;\n background: #fff;\n border: 1px solid #e8e8e8;\n border-radius: 10px;\n padding: 20px;\n}\n\n.bar-chart {\n display: flex;\n align-items: flex-end;\n justify-content: space-between;\n gap: 12px;\n height: 200px;\n padding-top: 24px;\n}\n\n.bar-column {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: flex-end;\n height: 100%;\n position: relative;\n}\n\n.bar-value {\n font-size: 10px;\n font-weight: 600;\n color: #666;\n margin-bottom: 4px;\n}\n\n.bar-fill {\n width: 100%;\n max-width: 40px;\n background: linear-gradient(180deg, #4a6cf7 0%, #7c95f8 100%);\n border-radius: 4px 4px 0 0;\n min-height: 2px;\n transition: height 0.3s ease;\n}\n\n.bar-label {\n font-size: 11px;\n color: #888;\n margin-top: 6px;\n text-align: center;\n}\n\n.empty-hint {\n color: #999;\n font-size: 13px;\n font-style: italic;\n}\n\n/* Responsive */\n@media (max-width: 1200px) {\n .kpi-strip {\n grid-template-columns: repeat(3, 1fr);\n }\n}\n\n@media (max-width: 768px) {\n .kpi-strip {\n grid-template-columns: repeat(2, 1fr);\n }\n .bottom-section {\n flex-direction: column;\n }\n}\n"] }]
440
+ }], null, null); })();
441
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ControlTowerComponent, { className: "ControlTowerComponent", filePath: "src/Integration/components/control-tower/control-tower.component.ts", lineNumber: 22 }); })();
442
+ export function LoadIntegrationDashboard() {
443
+ // Tree-shaking prevention: importing this module causes
444
+ // @RegisterClass decorators to fire, registering components.
445
+ }
446
+ //# sourceMappingURL=control-tower.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control-tower.component.js","sourceRoot":"","sources":["../../../../src/Integration/components/control-tower/control-tower.component.ts","../../../../src/Integration/components/control-tower/control-tower.component.html"],"names":[],"mappings":"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","sourcesContent":["import { ChangeDetectorRef, Component, OnInit, inject } from '@angular/core';\nimport { RegisterClass, UUIDsEqual } from '@memberjunction/global';\nimport { BaseResourceComponent } from '@memberjunction/ng-shared';\nimport { ResourceData } from '@memberjunction/core-entities';\nimport {\n IntegrationDataService,\n IntegrationSummary,\n IntegrationKPIs,\n ActivityFeedItem,\n DailyRecordCount\n} from '../../services/integration-data.service';\n\ntype StatusColorType = 'green' | 'amber' | 'red' | 'gray';\n\n@RegisterClass(BaseResourceComponent, 'IntegrationControlTower')\n@Component({\n standalone: false,\n selector: 'app-control-tower',\n templateUrl: './control-tower.component.html',\n styleUrls: ['./control-tower.component.css']\n})\nexport class ControlTowerComponent extends BaseResourceComponent implements OnInit {\n /**\n * The Provider @Input is inherited from BaseAngularComponent.\n * Use this.RunViewToUse to get the appropriate IRunViewProvider\n * for multi-MJ-instance support.\n */\n\n Summaries: IntegrationSummary[] = [];\n KPIs: IntegrationKPIs = {\n TotalIntegrations: 0,\n ActiveSyncs: 0,\n RecordsSyncedToday: 0,\n ErrorRate: 0,\n AverageSyncDurationMs: null\n };\n ActivityFeed: ActivityFeedItem[] = [];\n DailyCounts: DailyRecordCount[] = [];\n\n IsLoading = false;\n ExpandedID: string | null = null;\n\n private dataService = inject(IntegrationDataService);\n private cdr = inject(ChangeDetectorRef);\n\n async ngOnInit(): Promise<void> {\n await this.LoadData();\n }\n\n async LoadData(): Promise<void> {\n this.IsLoading = true;\n this.cdr.detectChanges();\n try {\n const provider = this.RunViewToUse;\n const [summaries, activityFeed, dailyCounts] = await Promise.all([\n this.dataService.LoadIntegrationSummaries(provider),\n this.dataService.LoadRecentRuns(20, provider),\n this.dataService.LoadDailyRecordCounts(7, provider)\n ]);\n this.Summaries = summaries;\n this.KPIs = this.dataService.ComputeKPIs(summaries);\n this.ActivityFeed = activityFeed;\n this.DailyCounts = dailyCounts;\n } catch (err) {\n console.error('[IntegrationControlTower] Failed to load data:', err);\n } finally {\n this.IsLoading = false;\n this.cdr.detectChanges();\n }\n }\n\n async Refresh(): Promise<void> {\n await this.LoadData();\n }\n\n OnRunNow(integrationID: string): void {\n console.log('[IntegrationControlTower] Run Now clicked for integration:', integrationID);\n }\n\n OnExpandToggle(integrationID: string): void {\n this.ExpandedID = UUIDsEqual(this.ExpandedID, integrationID) ? null : integrationID;\n }\n\n IsExpanded(integrationID: string): boolean {\n return UUIDsEqual(this.ExpandedID, integrationID);\n }\n\n get MaxDailyRecords(): number {\n if (this.DailyCounts.length === 0) return 1;\n const maxVal = Math.max(...this.DailyCounts.map(d => d.Records));\n return maxVal > 0 ? maxVal : 1;\n }\n\n BarHeight(records: number): number {\n return Math.max((records / this.MaxDailyRecords) * 100, 2);\n }\n\n FormatDuration(ms: number | null): string {\n return this.dataService.FormatDuration(ms);\n }\n\n FormatErrorRate(rate: number): string {\n return rate > 0 ? `${rate}%` : '0%';\n }\n\n StatusChipClass(color: StatusColorType): string {\n return `status-chip status-${color}`;\n }\n\n SourceIconClass(summary: IntegrationSummary): string {\n return summary.SourceType?.IconClass ?? 'fa-solid fa-plug';\n }\n\n StatusLabel(color: StatusColorType): string {\n if (color === 'green') return 'Healthy';\n if (color === 'amber') return 'Warning';\n if (color === 'red') return 'Failed';\n return 'Inactive';\n }\n\n FormatRelativeTime(dateStr: string | null): string {\n return this.dataService.ComputeRelativeTime(dateStr);\n }\n\n ActivityStatusIcon(status: string): string {\n if (status === 'Success') return 'fa-solid fa-circle-check';\n if (status === 'Failed') return 'fa-solid fa-circle-xmark';\n if (status === 'In Progress') return 'fa-solid fa-spinner fa-spin';\n return 'fa-solid fa-clock';\n }\n\n async GetResourceDisplayName(_data: ResourceData): Promise<string> {\n return 'Integration Control Tower';\n }\n\n async GetResourceIconClass(_data: ResourceData): Promise<string> {\n return 'fa-solid fa-tower-broadcast';\n }\n}\n\nexport function LoadIntegrationDashboard(): void {\n // Tree-shaking prevention: importing this module causes\n // @RegisterClass decorators to fire, registering components.\n}\n","<div class=\"control-tower\">\n <div class=\"tower-header\">\n <h2><i class=\"fa-solid fa-tower-broadcast\"></i> Integration Control Tower</h2>\n <button kendoButton [look]=\"'flat'\" [themeColor]=\"'primary'\" (click)=\"Refresh()\">\n <i class=\"fa-solid fa-arrows-rotate\"></i> Refresh\n </button>\n </div>\n\n @if (IsLoading) {\n <mj-loading text=\"Loading integrations...\" size=\"medium\"></mj-loading>\n } @else if (Summaries.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-plug-circle-xmark\"></i>\n <h3>No Integrations Configured</h3>\n <p>Set up your first integration using the Connection Studio to start syncing data.</p>\n <button kendoButton [themeColor]=\"'primary'\" class=\"empty-cta\">\n <i class=\"fa-solid fa-plus\"></i> Create Integration\n </button>\n </div>\n } @else {\n <!-- KPI Strip -->\n <div class=\"kpi-strip\">\n <div class=\"kpi-card kpi-blue\">\n <div class=\"kpi-icon\"><i class=\"fa-solid fa-plug\"></i></div>\n <div class=\"kpi-content\">\n <span class=\"kpi-value\">{{ KPIs.TotalIntegrations }}</span>\n <span class=\"kpi-label\">Total Integrations</span>\n </div>\n </div>\n <div class=\"kpi-card kpi-green\">\n <div class=\"kpi-icon\"><i class=\"fa-solid fa-sync fa-spin\"></i></div>\n <div class=\"kpi-content\">\n <span class=\"kpi-value\">{{ KPIs.ActiveSyncs }}</span>\n <span class=\"kpi-label\">Active Syncs</span>\n </div>\n </div>\n <div class=\"kpi-card kpi-purple\">\n <div class=\"kpi-icon\"><i class=\"fa-solid fa-database\"></i></div>\n <div class=\"kpi-content\">\n <span class=\"kpi-value\">{{ KPIs.RecordsSyncedToday | number }}</span>\n <span class=\"kpi-label\">Records Synced Today</span>\n </div>\n </div>\n <div class=\"kpi-card kpi-red\">\n <div class=\"kpi-icon\"><i class=\"fa-solid fa-exclamation-triangle\"></i></div>\n <div class=\"kpi-content\">\n <span class=\"kpi-value\">{{ FormatErrorRate(KPIs.ErrorRate) }}</span>\n <span class=\"kpi-label\">Error Rate</span>\n </div>\n </div>\n <div class=\"kpi-card kpi-orange\">\n <div class=\"kpi-icon\"><i class=\"fa-solid fa-clock\"></i></div>\n <div class=\"kpi-content\">\n <span class=\"kpi-value\">{{ FormatDuration(KPIs.AverageSyncDurationMs) }}</span>\n <span class=\"kpi-label\">Avg Sync Duration</span>\n </div>\n </div>\n </div>\n\n <!-- Integration Health Grid -->\n <div class=\"section-header\">\n <h3><i class=\"fa-solid fa-heartbeat\"></i> Integration Health</h3>\n </div>\n <kendo-grid [data]=\"Summaries\" [resizable]=\"true\" class=\"health-grid\">\n <kendo-grid-column field=\"Integration.Name\" title=\"Integration Name\" [width]=\"200\">\n <ng-template kendoGridCellTemplate let-dataItem>\n <div class=\"grid-integration-name\">\n <i [class]=\"SourceIconClass(dataItem)\" class=\"grid-source-icon\"></i>\n <span>{{ dataItem.Integration.Name }}</span>\n </div>\n </ng-template>\n </kendo-grid-column>\n <kendo-grid-column title=\"Source Type\" [width]=\"140\">\n <ng-template kendoGridCellTemplate let-dataItem>\n {{ dataItem.SourceType?.Name ?? 'Unknown' }}\n </ng-template>\n </kendo-grid-column>\n <kendo-grid-column title=\"Status\" [width]=\"110\">\n <ng-template kendoGridCellTemplate let-dataItem>\n <span [class]=\"StatusChipClass(dataItem.StatusColor)\">\n {{ StatusLabel(dataItem.StatusColor) }}\n </span>\n </ng-template>\n </kendo-grid-column>\n <kendo-grid-column title=\"Last Run\" [width]=\"120\">\n <ng-template kendoGridCellTemplate let-dataItem>\n {{ dataItem.RelativeTime }}\n </ng-template>\n </kendo-grid-column>\n <kendo-grid-column title=\"Duration\" [width]=\"110\">\n <ng-template kendoGridCellTemplate let-dataItem>\n {{ FormatDuration(dataItem.DurationMs) }}\n </ng-template>\n </kendo-grid-column>\n <kendo-grid-column title=\"Records\" [width]=\"100\">\n <ng-template kendoGridCellTemplate let-dataItem>\n {{ (dataItem.LatestRun?.TotalRecords ?? 0) | number }}\n </ng-template>\n </kendo-grid-column>\n <kendo-grid-column title=\"Errors\" [width]=\"90\">\n <ng-template kendoGridCellTemplate let-dataItem>\n <span [class.error-count]=\"dataItem.TotalErrors > 0\">\n {{ dataItem.TotalErrors }}\n </span>\n </ng-template>\n </kendo-grid-column>\n <kendo-grid-column title=\"Actions\" [width]=\"100\">\n <ng-template kendoGridCellTemplate let-dataItem>\n <button kendoButton [look]=\"'flat'\" [themeColor]=\"'primary'\" [size]=\"'small'\"\n (click)=\"OnRunNow(dataItem.Integration.ID)\"\n [disabled]=\"!dataItem.Integration.IsActive\">\n <i class=\"fa-solid fa-play\"></i> Run\n </button>\n </ng-template>\n </kendo-grid-column>\n </kendo-grid>\n\n <!-- Bottom Section: Activity Feed & Performance -->\n <div class=\"bottom-section\">\n <!-- Activity Feed -->\n <div class=\"activity-feed-panel\">\n <h3 class=\"section-title\"><i class=\"fa-solid fa-stream\"></i> Recent Activity</h3>\n @if (ActivityFeed.length === 0) {\n <p class=\"empty-hint\">No recent activity.</p>\n } @else {\n <div class=\"activity-list\">\n @for (item of ActivityFeed; track item.RunID) {\n <div class=\"activity-item\">\n <i [class]=\"ActivityStatusIcon(item.Status)\"\n [class.status-icon-green]=\"item.StatusColor === 'green'\"\n [class.status-icon-red]=\"item.StatusColor === 'red'\"\n [class.status-icon-amber]=\"item.StatusColor === 'amber'\">\n </i>\n <div class=\"activity-details\">\n <span class=\"activity-name\">{{ item.IntegrationName }}</span>\n <span class=\"activity-meta\">\n {{ item.TotalRecords | number }} records &middot; {{ item.RelativeTime }}\n &middot; {{ item.RunByUser }}\n </span>\n </div>\n <span class=\"activity-status-chip\" [class]=\"'activity-' + item.StatusColor\">\n {{ item.Status }}\n </span>\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Performance Bar Chart -->\n <div class=\"performance-panel\">\n <h3 class=\"section-title\"><i class=\"fa-solid fa-chart-bar\"></i> Records Synced (7 days)</h3>\n @if (DailyCounts.length === 0) {\n <p class=\"empty-hint\">No data available.</p>\n } @else {\n <div class=\"bar-chart\">\n @for (day of DailyCounts; track day.Date) {\n <div class=\"bar-column\">\n <div class=\"bar-value\">{{ day.Records | number }}</div>\n <div class=\"bar-fill\" [style.height.%]=\"BarHeight(day.Records)\"></div>\n <div class=\"bar-label\">{{ day.Label }}</div>\n </div>\n }\n </div>\n }\n </div>\n </div>\n }\n</div>\n"]}
@@ -0,0 +1,43 @@
1
+ import { OnInit } from '@angular/core';
2
+ import { BaseResourceComponent } from '@memberjunction/ng-shared';
3
+ import { ResourceData } from '@memberjunction/core-entities';
4
+ import { IntegrationRow, IntegrationRunRow, EntityMapRow, FieldMapRow, RunDetailRow } from '../../services/integration-data.service';
5
+ import * as i0 from "@angular/core";
6
+ export declare class MappingWorkspaceComponent extends BaseResourceComponent implements OnInit {
7
+ /**
8
+ * The Provider @Input is inherited from BaseAngularComponent.
9
+ * Use this.RunViewToUse to get the appropriate IRunViewProvider
10
+ * for multi-MJ-instance support.
11
+ */
12
+ Integrations: IntegrationRow[];
13
+ EntityMaps: EntityMapRow[];
14
+ FieldMaps: FieldMapRow[];
15
+ RunEntityDetails: RunDetailRow[];
16
+ LatestRun: IntegrationRunRow | null;
17
+ SelectedIntegrationID: string;
18
+ SelectedEntityMapID: string | null;
19
+ EntityMapSearchText: string;
20
+ IsLoadingIntegrations: boolean;
21
+ IsLoadingEntityMaps: boolean;
22
+ IsLoadingFieldMaps: boolean;
23
+ IsLoadingRunDetails: boolean;
24
+ private dataService;
25
+ private cdr;
26
+ ngOnInit(): Promise<void>;
27
+ get FilteredEntityMaps(): EntityMapRow[];
28
+ LoadIntegrations(): Promise<void>;
29
+ OnIntegrationChange(integrationID: string): Promise<void>;
30
+ LoadEntityMapsForIntegration(integrationID: string): Promise<void>;
31
+ LoadLatestRunForIntegration(integrationID: string): Promise<void>;
32
+ OnEntityMapSelect(em: EntityMapRow): Promise<void>;
33
+ IsSelectedEntityMap(id: string): boolean;
34
+ OnToggleEntityMap(em: EntityMapRow): void;
35
+ get RunStatusColor(): string;
36
+ FormatDate(dateStr: string | null): string;
37
+ GetResourceDisplayName(_data: ResourceData): Promise<string>;
38
+ GetResourceIconClass(_data: ResourceData): Promise<string>;
39
+ static ɵfac: i0.ɵɵFactoryDeclaration<MappingWorkspaceComponent, never>;
40
+ static ɵcmp: i0.ɵɵComponentDeclaration<MappingWorkspaceComponent, "app-mapping-workspace", never, {}, {}, never, never, false, never>;
41
+ }
42
+ export declare function LoadMappingWorkspace(): void;
43
+ //# sourceMappingURL=mapping-workspace.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapping-workspace.component.d.ts","sourceRoot":"","sources":["../../../../src/Integration/components/mapping-workspace/mapping-workspace.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,MAAM,EAAU,MAAM,eAAe,CAAC;AAE7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAEL,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,YAAY,EACb,MAAM,yCAAyC,CAAC;;AAEjD,qBAoGa,yBAA0B,SAAQ,qBAAsB,YAAW,MAAM;IACpF;;;;OAIG;IAEH,YAAY,EAAE,cAAc,EAAE,CAAM;IACpC,UAAU,EAAE,YAAY,EAAE,CAAM;IAChC,SAAS,EAAE,WAAW,EAAE,CAAM;IAC9B,gBAAgB,EAAE,YAAY,EAAE,CAAM;IACtC,SAAS,EAAE,iBAAiB,GAAG,IAAI,CAAQ;IAE3C,qBAAqB,EAAE,MAAM,CAAM;IACnC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC1C,mBAAmB,EAAE,MAAM,CAAM;IAEjC,qBAAqB,UAAS;IAC9B,mBAAmB,UAAS;IAC5B,kBAAkB,UAAS;IAC3B,mBAAmB,UAAS;IAE5B,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,GAAG,CAA6B;IAElC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,IAAI,kBAAkB,IAAI,YAAY,EAAE,CAOvC;IAEK,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAYjC,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAezD,4BAA4B,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWlE,2BAA2B,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAejE,iBAAiB,CAAC,EAAE,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAYxD,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIxC,iBAAiB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI;IAIzC,IAAI,cAAc,IAAI,MAAM,CAK3B;IAED,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM;IAQpC,sBAAsB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAI5D,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;yCAlIrD,yBAAyB;2CAAzB,yBAAyB;CAqIrC;AAED,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C"}