@memberjunction/ng-dashboards 5.7.0 → 5.9.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.
- package/dist/AI/components/models/model-management.component.d.ts +2 -0
- package/dist/AI/components/models/model-management.component.d.ts.map +1 -1
- package/dist/AI/components/models/model-management.component.js +44 -2
- package/dist/AI/components/models/model-management.component.js.map +1 -1
- package/dist/DashboardBrowser/dashboard-browser-resource.component.d.ts.map +1 -1
- package/dist/DashboardBrowser/dashboard-browser-resource.component.js +5 -2
- package/dist/DashboardBrowser/dashboard-browser-resource.component.js.map +1 -1
- package/dist/Home/home-dashboard.component.d.ts.map +1 -1
- package/dist/Home/home-dashboard.component.js +10 -7
- package/dist/Home/home-dashboard.component.js.map +1 -1
- package/dist/Integration/components/activity/activity.component.d.ts +96 -0
- package/dist/Integration/components/activity/activity.component.d.ts.map +1 -0
- package/dist/Integration/components/activity/activity.component.js +961 -0
- package/dist/Integration/components/activity/activity.component.js.map +1 -0
- package/dist/Integration/components/connections/connections.component.d.ts +194 -0
- package/dist/Integration/components/connections/connections.component.d.ts.map +1 -0
- package/dist/Integration/components/connections/connections.component.js +2368 -0
- package/dist/Integration/components/connections/connections.component.js.map +1 -0
- package/dist/Integration/components/mapping-workspace/mapping-workspace.component.d.ts +241 -0
- package/dist/Integration/components/mapping-workspace/mapping-workspace.component.d.ts.map +1 -0
- package/dist/Integration/components/mapping-workspace/mapping-workspace.component.js +2373 -0
- package/dist/Integration/components/mapping-workspace/mapping-workspace.component.js.map +1 -0
- package/dist/Integration/components/overview/overview.component.d.ts +60 -0
- package/dist/Integration/components/overview/overview.component.d.ts.map +1 -0
- package/dist/Integration/components/overview/overview.component.js +628 -0
- package/dist/Integration/components/overview/overview.component.js.map +1 -0
- package/dist/Integration/components/pipelines/pipelines.component.d.ts +203 -0
- package/dist/Integration/components/pipelines/pipelines.component.d.ts.map +1 -0
- package/dist/Integration/components/pipelines/pipelines.component.js +2057 -0
- package/dist/Integration/components/pipelines/pipelines.component.js.map +1 -0
- package/dist/Integration/components/schedules/schedules.component.d.ts +110 -0
- package/dist/Integration/components/schedules/schedules.component.d.ts.map +1 -0
- package/dist/Integration/components/schedules/schedules.component.js +842 -0
- package/dist/Integration/components/schedules/schedules.component.js.map +1 -0
- package/dist/Integration/components/visual-editor/visual-editor.component.d.ts +141 -0
- package/dist/Integration/components/visual-editor/visual-editor.component.d.ts.map +1 -0
- package/dist/Integration/components/visual-editor/visual-editor.component.js +1538 -0
- package/dist/Integration/components/visual-editor/visual-editor.component.js.map +1 -0
- package/dist/Integration/components/widgets/integration-card.component.d.ts +22 -0
- package/dist/Integration/components/widgets/integration-card.component.d.ts.map +1 -0
- package/dist/Integration/components/widgets/integration-card.component.js +262 -0
- package/dist/Integration/components/widgets/integration-card.component.js.map +1 -0
- package/dist/Integration/components/widgets/run-history-panel.component.d.ts +29 -0
- package/dist/Integration/components/widgets/run-history-panel.component.d.ts.map +1 -0
- package/dist/Integration/components/widgets/run-history-panel.component.js +399 -0
- package/dist/Integration/components/widgets/run-history-panel.component.js.map +1 -0
- package/dist/Integration/index.d.ts +11 -0
- package/dist/Integration/index.d.ts.map +1 -0
- package/dist/Integration/index.js +20 -0
- package/dist/Integration/index.js.map +1 -0
- package/dist/Integration/integration.module.d.ts +26 -0
- package/dist/Integration/integration.module.d.ts.map +1 -0
- package/dist/Integration/integration.module.js +107 -0
- package/dist/Integration/integration.module.js.map +1 -0
- package/dist/Integration/services/integration-data.service.d.ts +268 -0
- package/dist/Integration/services/integration-data.service.d.ts.map +1 -0
- package/dist/Integration/services/integration-data.service.js +697 -0
- package/dist/Integration/services/integration-data.service.js.map +1 -0
- package/dist/QueryBrowser/query-browser-resource.component.d.ts +27 -4
- package/dist/QueryBrowser/query-browser-resource.component.d.ts.map +1 -1
- package/dist/QueryBrowser/query-browser-resource.component.js +338 -144
- package/dist/QueryBrowser/query-browser-resource.component.js.map +1 -1
- package/dist/__tests__/integration-data-service.test.d.ts +2 -0
- package/dist/__tests__/integration-data-service.test.d.ts.map +1 -0
- package/dist/__tests__/integration-data-service.test.js +131 -0
- package/dist/__tests__/integration-data-service.test.js.map +1 -0
- package/dist/__tests__/mapping-validation.test.d.ts +2 -0
- package/dist/__tests__/mapping-validation.test.d.ts.map +1 -0
- package/dist/__tests__/mapping-validation.test.js +170 -0
- package/dist/__tests__/mapping-validation.test.js.map +1 -0
- package/dist/module.d.ts +2 -1
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +17 -6
- package/dist/module.js.map +1 -1
- package/dist/public-api.d.ts +2 -1
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +3 -1
- package/dist/public-api.js.map +1 -1
- package/package.json +42 -39
|
@@ -0,0 +1,842 @@
|
|
|
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 { Metadata, RunView } from '@memberjunction/core';
|
|
10
|
+
import { BaseResourceComponent } from '@memberjunction/ng-shared';
|
|
11
|
+
import { IntegrationDataService, ResolveIntegrationIcon } from '../../services/integration-data.service';
|
|
12
|
+
import * as i0 from "@angular/core";
|
|
13
|
+
import * as i1 from "@memberjunction/ng-shared-generic";
|
|
14
|
+
function SchedulesComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
15
|
+
i0.ɵɵelementStart(0, "div", 0);
|
|
16
|
+
i0.ɵɵelement(1, "mj-loading", 2);
|
|
17
|
+
i0.ɵɵelementEnd();
|
|
18
|
+
} }
|
|
19
|
+
function SchedulesComponent_Conditional_1_Conditional_10_Template(rf, ctx) { if (rf & 1) {
|
|
20
|
+
i0.ɵɵelementStart(0, "span", 8);
|
|
21
|
+
i0.ɵɵelement(1, "i", 15);
|
|
22
|
+
i0.ɵɵtext(2);
|
|
23
|
+
i0.ɵɵelementEnd();
|
|
24
|
+
} if (rf & 2) {
|
|
25
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
26
|
+
i0.ɵɵadvance(2);
|
|
27
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.LockedCount, " running ");
|
|
28
|
+
} }
|
|
29
|
+
function SchedulesComponent_Conditional_1_Conditional_14_For_9_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
30
|
+
i0.ɵɵelementStart(0, "span", 24);
|
|
31
|
+
i0.ɵɵtext(1);
|
|
32
|
+
i0.ɵɵelementEnd();
|
|
33
|
+
} if (rf & 2) {
|
|
34
|
+
const hour_r3 = i0.ɵɵnextContext().$implicit;
|
|
35
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
36
|
+
i0.ɵɵstyleProp("left", ctx_r1.ComputeTimelinePosition(hour_r3, 0), "%");
|
|
37
|
+
i0.ɵɵadvance();
|
|
38
|
+
i0.ɵɵtextInterpolate1(" ", hour_r3, ":00 ");
|
|
39
|
+
} }
|
|
40
|
+
function SchedulesComponent_Conditional_1_Conditional_14_For_9_Template(rf, ctx) { if (rf & 1) {
|
|
41
|
+
i0.ɵɵconditionalCreate(0, SchedulesComponent_Conditional_1_Conditional_14_For_9_Conditional_0_Template, 2, 3, "span", 23);
|
|
42
|
+
} if (rf & 2) {
|
|
43
|
+
const hour_r3 = ctx.$implicit;
|
|
44
|
+
i0.ɵɵconditional(hour_r3 % 4 === 0 ? 0 : -1);
|
|
45
|
+
} }
|
|
46
|
+
function SchedulesComponent_Conditional_1_Conditional_14_For_11_For_5_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
47
|
+
i0.ɵɵelement(0, "div", 32);
|
|
48
|
+
} if (rf & 2) {
|
|
49
|
+
const hour_r4 = i0.ɵɵnextContext().$implicit;
|
|
50
|
+
const ctx_r1 = i0.ɵɵnextContext(4);
|
|
51
|
+
i0.ɵɵstyleProp("left", ctx_r1.ComputeTimelinePosition(hour_r4, 0), "%");
|
|
52
|
+
} }
|
|
53
|
+
function SchedulesComponent_Conditional_1_Conditional_14_For_11_For_5_Template(rf, ctx) { if (rf & 1) {
|
|
54
|
+
i0.ɵɵconditionalCreate(0, SchedulesComponent_Conditional_1_Conditional_14_For_11_For_5_Conditional_0_Template, 1, 2, "div", 31);
|
|
55
|
+
} if (rf & 2) {
|
|
56
|
+
const hour_r4 = ctx.$implicit;
|
|
57
|
+
i0.ɵɵconditional(hour_r4 % 4 === 0 ? 0 : -1);
|
|
58
|
+
} }
|
|
59
|
+
function SchedulesComponent_Conditional_1_Conditional_14_For_11_For_8_Template(rf, ctx) { if (rf & 1) {
|
|
60
|
+
i0.ɵɵelement(0, "div", 33);
|
|
61
|
+
} if (rf & 2) {
|
|
62
|
+
const marker_r5 = ctx.$implicit;
|
|
63
|
+
const ctx_r1 = i0.ɵɵnextContext(4);
|
|
64
|
+
i0.ɵɵstyleProp("left", ctx_r1.ComputeTimelinePosition(marker_r5.Hour, marker_r5.Minute), "%");
|
|
65
|
+
i0.ɵɵclassProp("timeline-marker-next", marker_r5.IsNext);
|
|
66
|
+
i0.ɵɵproperty("title", marker_r5.Hour + ":" + ("" + marker_r5.Minute).padStart(2, "0"));
|
|
67
|
+
} }
|
|
68
|
+
function SchedulesComponent_Conditional_1_Conditional_14_For_11_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
69
|
+
i0.ɵɵelementStart(0, "div", 29);
|
|
70
|
+
i0.ɵɵelement(1, "i", 15);
|
|
71
|
+
i0.ɵɵelementEnd();
|
|
72
|
+
} }
|
|
73
|
+
function SchedulesComponent_Conditional_1_Conditional_14_For_11_Template(rf, ctx) { if (rf & 1) {
|
|
74
|
+
i0.ɵɵelementStart(0, "div", 22)(1, "div", 25);
|
|
75
|
+
i0.ɵɵtext(2);
|
|
76
|
+
i0.ɵɵelementEnd();
|
|
77
|
+
i0.ɵɵelementStart(3, "div", 26);
|
|
78
|
+
i0.ɵɵrepeaterCreate(4, SchedulesComponent_Conditional_1_Conditional_14_For_11_For_5_Template, 1, 1, null, null, i0.ɵɵcomponentInstance().TrackHour, true);
|
|
79
|
+
i0.ɵɵelement(6, "div", 27);
|
|
80
|
+
i0.ɵɵrepeaterCreate(7, SchedulesComponent_Conditional_1_Conditional_14_For_11_For_8_Template, 1, 5, "div", 28, i0.ɵɵcomponentInstance().TrackMarker, true);
|
|
81
|
+
i0.ɵɵconditionalCreate(9, SchedulesComponent_Conditional_1_Conditional_14_For_11_Conditional_9_Template, 2, 0, "div", 29);
|
|
82
|
+
i0.ɵɵelement(10, "div", 30);
|
|
83
|
+
i0.ɵɵelementEnd()();
|
|
84
|
+
} if (rf & 2) {
|
|
85
|
+
const row_r6 = ctx.$implicit;
|
|
86
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
87
|
+
i0.ɵɵadvance();
|
|
88
|
+
i0.ɵɵproperty("title", row_r6.IntegrationName);
|
|
89
|
+
i0.ɵɵadvance();
|
|
90
|
+
i0.ɵɵtextInterpolate1(" ", row_r6.IntegrationName, " ");
|
|
91
|
+
i0.ɵɵadvance(2);
|
|
92
|
+
i0.ɵɵrepeater(ctx_r1.TimelineHours);
|
|
93
|
+
i0.ɵɵadvance(3);
|
|
94
|
+
i0.ɵɵrepeater(row_r6.Markers);
|
|
95
|
+
i0.ɵɵadvance(2);
|
|
96
|
+
i0.ɵɵconditional(row_r6.IsLocked ? 9 : -1);
|
|
97
|
+
} }
|
|
98
|
+
function SchedulesComponent_Conditional_1_Conditional_14_Template(rf, ctx) { if (rf & 1) {
|
|
99
|
+
i0.ɵɵelementStart(0, "div", 11)(1, "div", 16);
|
|
100
|
+
i0.ɵɵelement(2, "i", 17);
|
|
101
|
+
i0.ɵɵtext(3, " Next 24 Hours ");
|
|
102
|
+
i0.ɵɵelementEnd();
|
|
103
|
+
i0.ɵɵelementStart(4, "div", 18)(5, "div", 19);
|
|
104
|
+
i0.ɵɵelement(6, "div", 20);
|
|
105
|
+
i0.ɵɵelementStart(7, "div", 21);
|
|
106
|
+
i0.ɵɵrepeaterCreate(8, SchedulesComponent_Conditional_1_Conditional_14_For_9_Template, 1, 1, null, null, i0.ɵɵcomponentInstance().TrackHour, true);
|
|
107
|
+
i0.ɵɵelementEnd()();
|
|
108
|
+
i0.ɵɵrepeaterCreate(10, SchedulesComponent_Conditional_1_Conditional_14_For_11_Template, 11, 3, "div", 22, i0.ɵɵcomponentInstance().TrackTimelineByID, true);
|
|
109
|
+
i0.ɵɵelementEnd()();
|
|
110
|
+
} if (rf & 2) {
|
|
111
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
112
|
+
i0.ɵɵadvance(8);
|
|
113
|
+
i0.ɵɵrepeater(ctx_r1.TimelineHours);
|
|
114
|
+
i0.ɵɵadvance(2);
|
|
115
|
+
i0.ɵɵrepeater(ctx_r1.TimelineMarkers);
|
|
116
|
+
} }
|
|
117
|
+
function SchedulesComponent_Conditional_1_For_17_Conditional_10_Template(rf, ctx) { if (rf & 1) {
|
|
118
|
+
i0.ɵɵelementStart(0, "span", 41);
|
|
119
|
+
i0.ɵɵtext(1, "Active");
|
|
120
|
+
i0.ɵɵelementEnd();
|
|
121
|
+
} }
|
|
122
|
+
function SchedulesComponent_Conditional_1_For_17_Conditional_11_Template(rf, ctx) { if (rf & 1) {
|
|
123
|
+
i0.ɵɵelementStart(0, "span", 42);
|
|
124
|
+
i0.ɵɵtext(1, "Inactive");
|
|
125
|
+
i0.ɵɵelementEnd();
|
|
126
|
+
} }
|
|
127
|
+
function SchedulesComponent_Conditional_1_For_17_Conditional_12_Template(rf, ctx) { if (rf & 1) {
|
|
128
|
+
i0.ɵɵelementStart(0, "span", 43);
|
|
129
|
+
i0.ɵɵelement(1, "i", 15);
|
|
130
|
+
i0.ɵɵtext(2, " Running ");
|
|
131
|
+
i0.ɵɵelementEnd();
|
|
132
|
+
} }
|
|
133
|
+
function SchedulesComponent_Conditional_1_For_17_Conditional_19_Conditional_8_For_3_Template(rf, ctx) { if (rf & 1) {
|
|
134
|
+
const _r11 = i0.ɵɵgetCurrentView();
|
|
135
|
+
i0.ɵɵelementStart(0, "button", 66);
|
|
136
|
+
i0.ɵɵlistener("click", function SchedulesComponent_Conditional_1_For_17_Conditional_19_Conditional_8_For_3_Template_button_click_0_listener() { const preset_r12 = i0.ɵɵrestoreView(_r11).$implicit; const schedule_r8 = i0.ɵɵnextContext(3).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SetInterval(schedule_r8.ID, preset_r12.Minutes)); });
|
|
137
|
+
i0.ɵɵtext(1);
|
|
138
|
+
i0.ɵɵelementEnd();
|
|
139
|
+
} if (rf & 2) {
|
|
140
|
+
const preset_r12 = ctx.$implicit;
|
|
141
|
+
const schedule_r8 = i0.ɵɵnextContext(3).$implicit;
|
|
142
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
143
|
+
i0.ɵɵclassProp("preset-btn-active", ctx_r1.IsIntervalSelected(schedule_r8, preset_r12.Minutes));
|
|
144
|
+
i0.ɵɵadvance();
|
|
145
|
+
i0.ɵɵtextInterpolate1(" ", preset_r12.Label, " ");
|
|
146
|
+
} }
|
|
147
|
+
function SchedulesComponent_Conditional_1_For_17_Conditional_19_Conditional_8_Template(rf, ctx) { if (rf & 1) {
|
|
148
|
+
const _r10 = i0.ɵɵgetCurrentView();
|
|
149
|
+
i0.ɵɵelementStart(0, "div", 45)(1, "div", 61);
|
|
150
|
+
i0.ɵɵrepeaterCreate(2, SchedulesComponent_Conditional_1_For_17_Conditional_19_Conditional_8_For_3_Template, 2, 3, "button", 62, i0.ɵɵcomponentInstance().TrackPresetByMinutes, true);
|
|
151
|
+
i0.ɵɵelementEnd();
|
|
152
|
+
i0.ɵɵelementStart(4, "div", 63)(5, "input", 64);
|
|
153
|
+
i0.ɵɵlistener("change", function SchedulesComponent_Conditional_1_For_17_Conditional_19_Conditional_8_Template_input_change_5_listener($event) { i0.ɵɵrestoreView(_r10); const schedule_r8 = i0.ɵɵnextContext(2).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SetCustomInterval(schedule_r8.ID, $event)); });
|
|
154
|
+
i0.ɵɵelementEnd();
|
|
155
|
+
i0.ɵɵelementStart(6, "span", 65);
|
|
156
|
+
i0.ɵɵtext(7, "min");
|
|
157
|
+
i0.ɵɵelementEnd()()();
|
|
158
|
+
} if (rf & 2) {
|
|
159
|
+
const schedule_r8 = i0.ɵɵnextContext(2).$implicit;
|
|
160
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
161
|
+
i0.ɵɵadvance(2);
|
|
162
|
+
i0.ɵɵrepeater(ctx_r1.IntervalPresets);
|
|
163
|
+
i0.ɵɵadvance(3);
|
|
164
|
+
i0.ɵɵproperty("value", ctx_r1.GetEffectiveInterval(schedule_r8));
|
|
165
|
+
} }
|
|
166
|
+
function SchedulesComponent_Conditional_1_For_17_Conditional_19_Conditional_9_For_3_Template(rf, ctx) { if (rf & 1) {
|
|
167
|
+
const _r14 = i0.ɵɵgetCurrentView();
|
|
168
|
+
i0.ɵɵelementStart(0, "button", 66);
|
|
169
|
+
i0.ɵɵlistener("click", function SchedulesComponent_Conditional_1_For_17_Conditional_19_Conditional_9_For_3_Template_button_click_0_listener() { const preset_r15 = i0.ɵɵrestoreView(_r14).$implicit; const schedule_r8 = i0.ɵɵnextContext(3).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SetCronExpression(schedule_r8.ID, preset_r15.Expression)); });
|
|
170
|
+
i0.ɵɵtext(1);
|
|
171
|
+
i0.ɵɵelementEnd();
|
|
172
|
+
} if (rf & 2) {
|
|
173
|
+
const preset_r15 = ctx.$implicit;
|
|
174
|
+
const schedule_r8 = i0.ɵɵnextContext(3).$implicit;
|
|
175
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
176
|
+
i0.ɵɵclassProp("preset-btn-active", ctx_r1.GetEffectiveCron(schedule_r8) === preset_r15.Expression);
|
|
177
|
+
i0.ɵɵadvance();
|
|
178
|
+
i0.ɵɵtextInterpolate1(" ", preset_r15.Label, " ");
|
|
179
|
+
} }
|
|
180
|
+
function SchedulesComponent_Conditional_1_For_17_Conditional_19_Conditional_9_Conditional_6_Template(rf, ctx) { if (rf & 1) {
|
|
181
|
+
i0.ɵɵelementStart(0, "span", 70);
|
|
182
|
+
i0.ɵɵtext(1);
|
|
183
|
+
i0.ɵɵelementEnd();
|
|
184
|
+
} if (rf & 2) {
|
|
185
|
+
const schedule_r8 = i0.ɵɵnextContext(3).$implicit;
|
|
186
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
187
|
+
i0.ɵɵadvance();
|
|
188
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.GetCronDescription(ctx_r1.GetEffectiveCron(schedule_r8)), " ");
|
|
189
|
+
} }
|
|
190
|
+
function SchedulesComponent_Conditional_1_For_17_Conditional_19_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
191
|
+
const _r13 = i0.ɵɵgetCurrentView();
|
|
192
|
+
i0.ɵɵelementStart(0, "div", 45)(1, "div", 67);
|
|
193
|
+
i0.ɵɵrepeaterCreate(2, SchedulesComponent_Conditional_1_For_17_Conditional_19_Conditional_9_For_3_Template, 2, 3, "button", 62, i0.ɵɵcomponentInstance().TrackCronPresetByExpr, true);
|
|
194
|
+
i0.ɵɵelementEnd();
|
|
195
|
+
i0.ɵɵelementStart(4, "div", 68)(5, "input", 69);
|
|
196
|
+
i0.ɵɵlistener("change", function SchedulesComponent_Conditional_1_For_17_Conditional_19_Conditional_9_Template_input_change_5_listener($event) { i0.ɵɵrestoreView(_r13); const schedule_r8 = i0.ɵɵnextContext(2).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OnCronInputChange(schedule_r8.ID, $event)); });
|
|
197
|
+
i0.ɵɵelementEnd();
|
|
198
|
+
i0.ɵɵconditionalCreate(6, SchedulesComponent_Conditional_1_For_17_Conditional_19_Conditional_9_Conditional_6_Template, 2, 1, "span", 70);
|
|
199
|
+
i0.ɵɵelementEnd()();
|
|
200
|
+
} if (rf & 2) {
|
|
201
|
+
const schedule_r8 = i0.ɵɵnextContext(2).$implicit;
|
|
202
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
203
|
+
i0.ɵɵadvance(2);
|
|
204
|
+
i0.ɵɵrepeater(ctx_r1.CronPresets);
|
|
205
|
+
i0.ɵɵadvance(3);
|
|
206
|
+
i0.ɵɵproperty("value", ctx_r1.GetEffectiveCron(schedule_r8) ?? "");
|
|
207
|
+
i0.ɵɵadvance();
|
|
208
|
+
i0.ɵɵconditional(ctx_r1.GetEffectiveCron(schedule_r8) ? 6 : -1);
|
|
209
|
+
} }
|
|
210
|
+
function SchedulesComponent_Conditional_1_For_17_Conditional_19_Template(rf, ctx) { if (rf & 1) {
|
|
211
|
+
const _r9 = i0.ɵɵgetCurrentView();
|
|
212
|
+
i0.ɵɵelementStart(0, "div", 45)(1, "div", 59)(2, "button", 60);
|
|
213
|
+
i0.ɵɵlistener("click", function SchedulesComponent_Conditional_1_For_17_Conditional_19_Template_button_click_2_listener() { i0.ɵɵrestoreView(_r9); const schedule_r8 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SetScheduleType(schedule_r8.ID, "Manual")); });
|
|
214
|
+
i0.ɵɵtext(3, " Manual ");
|
|
215
|
+
i0.ɵɵelementEnd();
|
|
216
|
+
i0.ɵɵelementStart(4, "button", 60);
|
|
217
|
+
i0.ɵɵlistener("click", function SchedulesComponent_Conditional_1_For_17_Conditional_19_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r9); const schedule_r8 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SetScheduleType(schedule_r8.ID, "Interval")); });
|
|
218
|
+
i0.ɵɵtext(5, " Interval ");
|
|
219
|
+
i0.ɵɵelementEnd();
|
|
220
|
+
i0.ɵɵelementStart(6, "button", 60);
|
|
221
|
+
i0.ɵɵlistener("click", function SchedulesComponent_Conditional_1_For_17_Conditional_19_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r9); const schedule_r8 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SetScheduleType(schedule_r8.ID, "Cron")); });
|
|
222
|
+
i0.ɵɵtext(7, " Cron ");
|
|
223
|
+
i0.ɵɵelementEnd()()();
|
|
224
|
+
i0.ɵɵconditionalCreate(8, SchedulesComponent_Conditional_1_For_17_Conditional_19_Conditional_8_Template, 8, 1, "div", 45);
|
|
225
|
+
i0.ɵɵconditionalCreate(9, SchedulesComponent_Conditional_1_For_17_Conditional_19_Conditional_9_Template, 7, 2, "div", 45);
|
|
226
|
+
} if (rf & 2) {
|
|
227
|
+
const schedule_r8 = i0.ɵɵnextContext().$implicit;
|
|
228
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
229
|
+
i0.ɵɵadvance(2);
|
|
230
|
+
i0.ɵɵclassProp("type-pill-active", ctx_r1.GetEffectiveScheduleType(schedule_r8) === "Manual");
|
|
231
|
+
i0.ɵɵadvance(2);
|
|
232
|
+
i0.ɵɵclassProp("type-pill-active", ctx_r1.GetEffectiveScheduleType(schedule_r8) === "Interval");
|
|
233
|
+
i0.ɵɵadvance(2);
|
|
234
|
+
i0.ɵɵclassProp("type-pill-active", ctx_r1.GetEffectiveScheduleType(schedule_r8) === "Cron");
|
|
235
|
+
i0.ɵɵadvance(2);
|
|
236
|
+
i0.ɵɵconditional(ctx_r1.GetEffectiveScheduleType(schedule_r8) === "Interval" ? 8 : -1);
|
|
237
|
+
i0.ɵɵadvance();
|
|
238
|
+
i0.ɵɵconditional(ctx_r1.GetEffectiveScheduleType(schedule_r8) === "Cron" ? 9 : -1);
|
|
239
|
+
} }
|
|
240
|
+
function SchedulesComponent_Conditional_1_For_17_Conditional_35_Template(rf, ctx) { if (rf & 1) {
|
|
241
|
+
i0.ɵɵelementStart(0, "span", 54);
|
|
242
|
+
i0.ɵɵelement(1, "i", 15);
|
|
243
|
+
i0.ɵɵtext(2, " Running ");
|
|
244
|
+
i0.ɵɵelementEnd();
|
|
245
|
+
} }
|
|
246
|
+
function SchedulesComponent_Conditional_1_For_17_Conditional_36_Template(rf, ctx) { if (rf & 1) {
|
|
247
|
+
i0.ɵɵelementStart(0, "span", 55);
|
|
248
|
+
i0.ɵɵtext(1, "Idle");
|
|
249
|
+
i0.ɵɵelementEnd();
|
|
250
|
+
} }
|
|
251
|
+
function SchedulesComponent_Conditional_1_For_17_Conditional_38_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
252
|
+
i0.ɵɵelement(0, "i", 15);
|
|
253
|
+
i0.ɵɵtext(1, " Saving... ");
|
|
254
|
+
} }
|
|
255
|
+
function SchedulesComponent_Conditional_1_For_17_Conditional_38_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
256
|
+
i0.ɵɵelement(0, "i", 72);
|
|
257
|
+
i0.ɵɵtext(1, " Save ");
|
|
258
|
+
} }
|
|
259
|
+
function SchedulesComponent_Conditional_1_For_17_Conditional_38_Template(rf, ctx) { if (rf & 1) {
|
|
260
|
+
const _r16 = i0.ɵɵgetCurrentView();
|
|
261
|
+
i0.ɵɵelementStart(0, "button", 71);
|
|
262
|
+
i0.ɵɵlistener("click", function SchedulesComponent_Conditional_1_For_17_Conditional_38_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r16); const schedule_r8 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.SaveSchedule(schedule_r8.ID)); });
|
|
263
|
+
i0.ɵɵconditionalCreate(1, SchedulesComponent_Conditional_1_For_17_Conditional_38_Conditional_1_Template, 2, 0)(2, SchedulesComponent_Conditional_1_For_17_Conditional_38_Conditional_2_Template, 2, 0);
|
|
264
|
+
i0.ɵɵelementEnd();
|
|
265
|
+
} if (rf & 2) {
|
|
266
|
+
const schedule_r8 = i0.ɵɵnextContext().$implicit;
|
|
267
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
268
|
+
i0.ɵɵproperty("disabled", ctx_r1.IsSaving(schedule_r8.ID));
|
|
269
|
+
i0.ɵɵadvance();
|
|
270
|
+
i0.ɵɵconditional(ctx_r1.IsSaving(schedule_r8.ID) ? 1 : 2);
|
|
271
|
+
} }
|
|
272
|
+
function SchedulesComponent_Conditional_1_For_17_Conditional_40_Template(rf, ctx) { if (rf & 1) {
|
|
273
|
+
i0.ɵɵelement(0, "i", 15);
|
|
274
|
+
i0.ɵɵtext(1, " Running... ");
|
|
275
|
+
} }
|
|
276
|
+
function SchedulesComponent_Conditional_1_For_17_Conditional_41_Template(rf, ctx) { if (rf & 1) {
|
|
277
|
+
i0.ɵɵelement(0, "i", 73);
|
|
278
|
+
i0.ɵɵtext(1, " Run Now ");
|
|
279
|
+
} }
|
|
280
|
+
function SchedulesComponent_Conditional_1_For_17_Template(rf, ctx) { if (rf & 1) {
|
|
281
|
+
const _r7 = i0.ɵɵgetCurrentView();
|
|
282
|
+
i0.ɵɵelementStart(0, "div", 34)(1, "div", 35)(2, "div", 36);
|
|
283
|
+
i0.ɵɵelement(3, "i");
|
|
284
|
+
i0.ɵɵelementEnd();
|
|
285
|
+
i0.ɵɵelementStart(4, "div", 37)(5, "div", 38);
|
|
286
|
+
i0.ɵɵtext(6);
|
|
287
|
+
i0.ɵɵelementEnd();
|
|
288
|
+
i0.ɵɵelementStart(7, "div", 39);
|
|
289
|
+
i0.ɵɵtext(8);
|
|
290
|
+
i0.ɵɵelementEnd();
|
|
291
|
+
i0.ɵɵelementStart(9, "div", 40);
|
|
292
|
+
i0.ɵɵconditionalCreate(10, SchedulesComponent_Conditional_1_For_17_Conditional_10_Template, 2, 0, "span", 41)(11, SchedulesComponent_Conditional_1_For_17_Conditional_11_Template, 2, 0, "span", 42);
|
|
293
|
+
i0.ɵɵconditionalCreate(12, SchedulesComponent_Conditional_1_For_17_Conditional_12_Template, 3, 0, "span", 43);
|
|
294
|
+
i0.ɵɵelementEnd()()();
|
|
295
|
+
i0.ɵɵelementStart(13, "div", 44)(14, "div", 45)(15, "label", 46);
|
|
296
|
+
i0.ɵɵtext(16, " Auto-sync ");
|
|
297
|
+
i0.ɵɵelementStart(17, "button", 47);
|
|
298
|
+
i0.ɵɵlistener("click", function SchedulesComponent_Conditional_1_For_17_Template_button_click_17_listener() { const schedule_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.ToggleScheduleEnabled(schedule_r8.ID)); });
|
|
299
|
+
i0.ɵɵelement(18, "span", 48);
|
|
300
|
+
i0.ɵɵelementEnd()()();
|
|
301
|
+
i0.ɵɵconditionalCreate(19, SchedulesComponent_Conditional_1_For_17_Conditional_19_Template, 10, 8);
|
|
302
|
+
i0.ɵɵelementEnd();
|
|
303
|
+
i0.ɵɵelementStart(20, "div", 49)(21, "div", 50)(22, "div", 51)(23, "span", 52);
|
|
304
|
+
i0.ɵɵtext(24, "Next run");
|
|
305
|
+
i0.ɵɵelementEnd();
|
|
306
|
+
i0.ɵɵelementStart(25, "span", 53);
|
|
307
|
+
i0.ɵɵtext(26);
|
|
308
|
+
i0.ɵɵelementEnd()();
|
|
309
|
+
i0.ɵɵelementStart(27, "div", 51)(28, "span", 52);
|
|
310
|
+
i0.ɵɵtext(29, "Last run");
|
|
311
|
+
i0.ɵɵelementEnd();
|
|
312
|
+
i0.ɵɵelementStart(30, "span", 53);
|
|
313
|
+
i0.ɵɵtext(31);
|
|
314
|
+
i0.ɵɵelementEnd()();
|
|
315
|
+
i0.ɵɵelementStart(32, "div", 51)(33, "span", 52);
|
|
316
|
+
i0.ɵɵtext(34, "Status");
|
|
317
|
+
i0.ɵɵelementEnd();
|
|
318
|
+
i0.ɵɵconditionalCreate(35, SchedulesComponent_Conditional_1_For_17_Conditional_35_Template, 3, 0, "span", 54)(36, SchedulesComponent_Conditional_1_For_17_Conditional_36_Template, 2, 0, "span", 55);
|
|
319
|
+
i0.ɵɵelementEnd()();
|
|
320
|
+
i0.ɵɵelementStart(37, "div", 56);
|
|
321
|
+
i0.ɵɵconditionalCreate(38, SchedulesComponent_Conditional_1_For_17_Conditional_38_Template, 3, 2, "button", 57);
|
|
322
|
+
i0.ɵɵelementStart(39, "button", 58);
|
|
323
|
+
i0.ɵɵlistener("click", function SchedulesComponent_Conditional_1_For_17_Template_button_click_39_listener() { const schedule_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.RunNow(schedule_r8.ID)); });
|
|
324
|
+
i0.ɵɵconditionalCreate(40, SchedulesComponent_Conditional_1_For_17_Conditional_40_Template, 2, 0)(41, SchedulesComponent_Conditional_1_For_17_Conditional_41_Template, 2, 0);
|
|
325
|
+
i0.ɵɵelementEnd()()()();
|
|
326
|
+
} if (rf & 2) {
|
|
327
|
+
const schedule_r8 = ctx.$implicit;
|
|
328
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
329
|
+
i0.ɵɵclassProp("schedule-card-disabled", !schedule_r8.IsActive);
|
|
330
|
+
i0.ɵɵadvance(3);
|
|
331
|
+
i0.ɵɵclassMap(ctx_r1.GetIntegrationIcon(schedule_r8.Integration || schedule_r8.Name));
|
|
332
|
+
i0.ɵɵadvance(3);
|
|
333
|
+
i0.ɵɵtextInterpolate(schedule_r8.Integration || schedule_r8.Name);
|
|
334
|
+
i0.ɵɵadvance(2);
|
|
335
|
+
i0.ɵɵtextInterpolate(schedule_r8.Company);
|
|
336
|
+
i0.ɵɵadvance(2);
|
|
337
|
+
i0.ɵɵconditional(schedule_r8.IsActive ? 10 : 11);
|
|
338
|
+
i0.ɵɵadvance(2);
|
|
339
|
+
i0.ɵɵconditional(schedule_r8.IsLocked ? 12 : -1);
|
|
340
|
+
i0.ɵɵadvance(5);
|
|
341
|
+
i0.ɵɵclassProp("toggle-on", ctx_r1.GetEffectiveScheduleEnabled(schedule_r8));
|
|
342
|
+
i0.ɵɵattribute("aria-checked", ctx_r1.GetEffectiveScheduleEnabled(schedule_r8));
|
|
343
|
+
i0.ɵɵadvance(2);
|
|
344
|
+
i0.ɵɵconditional(ctx_r1.GetEffectiveScheduleEnabled(schedule_r8) ? 19 : -1);
|
|
345
|
+
i0.ɵɵadvance(7);
|
|
346
|
+
i0.ɵɵtextInterpolate(ctx_r1.GetNextRunRelative(schedule_r8));
|
|
347
|
+
i0.ɵɵadvance(5);
|
|
348
|
+
i0.ɵɵtextInterpolate(ctx_r1.GetLastRunRelative(schedule_r8));
|
|
349
|
+
i0.ɵɵadvance(4);
|
|
350
|
+
i0.ɵɵconditional(schedule_r8.IsLocked ? 35 : 36);
|
|
351
|
+
i0.ɵɵadvance(3);
|
|
352
|
+
i0.ɵɵconditional(ctx_r1.HasChanges(schedule_r8.ID) ? 38 : -1);
|
|
353
|
+
i0.ɵɵadvance();
|
|
354
|
+
i0.ɵɵproperty("disabled", ctx_r1.IsRunning(schedule_r8.ID) || schedule_r8.IsLocked);
|
|
355
|
+
i0.ɵɵadvance();
|
|
356
|
+
i0.ɵɵconditional(ctx_r1.IsRunning(schedule_r8.ID) ? 40 : 41);
|
|
357
|
+
} }
|
|
358
|
+
function SchedulesComponent_Conditional_1_Conditional_18_Template(rf, ctx) { if (rf & 1) {
|
|
359
|
+
i0.ɵɵelementStart(0, "div", 14);
|
|
360
|
+
i0.ɵɵelement(1, "i", 74);
|
|
361
|
+
i0.ɵɵelementStart(2, "p");
|
|
362
|
+
i0.ɵɵtext(3, "No integrations found");
|
|
363
|
+
i0.ɵɵelementEnd()();
|
|
364
|
+
} }
|
|
365
|
+
function SchedulesComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
366
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
367
|
+
i0.ɵɵelementStart(0, "div", 1)(1, "div", 3)(2, "div", 4)(3, "h2", 5);
|
|
368
|
+
i0.ɵɵelement(4, "i", 6);
|
|
369
|
+
i0.ɵɵtext(5, " Sync Schedules ");
|
|
370
|
+
i0.ɵɵelementEnd();
|
|
371
|
+
i0.ɵɵelementStart(6, "span", 7);
|
|
372
|
+
i0.ɵɵtext(7);
|
|
373
|
+
i0.ɵɵelementEnd();
|
|
374
|
+
i0.ɵɵelementStart(8, "span", 7);
|
|
375
|
+
i0.ɵɵtext(9);
|
|
376
|
+
i0.ɵɵelementEnd();
|
|
377
|
+
i0.ɵɵconditionalCreate(10, SchedulesComponent_Conditional_1_Conditional_10_Template, 3, 1, "span", 8);
|
|
378
|
+
i0.ɵɵelementEnd();
|
|
379
|
+
i0.ɵɵelementStart(11, "button", 9);
|
|
380
|
+
i0.ɵɵlistener("click", function SchedulesComponent_Conditional_1_Template_button_click_11_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.LoadData()); });
|
|
381
|
+
i0.ɵɵelement(12, "i", 10);
|
|
382
|
+
i0.ɵɵtext(13, " Refresh ");
|
|
383
|
+
i0.ɵɵelementEnd()();
|
|
384
|
+
i0.ɵɵconditionalCreate(14, SchedulesComponent_Conditional_1_Conditional_14_Template, 12, 0, "div", 11);
|
|
385
|
+
i0.ɵɵelementStart(15, "div", 12);
|
|
386
|
+
i0.ɵɵrepeaterCreate(16, SchedulesComponent_Conditional_1_For_17_Template, 42, 18, "div", 13, i0.ɵɵcomponentInstance().TrackByID, true);
|
|
387
|
+
i0.ɵɵconditionalCreate(18, SchedulesComponent_Conditional_1_Conditional_18_Template, 4, 0, "div", 14);
|
|
388
|
+
i0.ɵɵelementEnd()();
|
|
389
|
+
} if (rf & 2) {
|
|
390
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
391
|
+
i0.ɵɵadvance(7);
|
|
392
|
+
i0.ɵɵtextInterpolate2("", ctx_r1.Schedules.length, " integration", ctx_r1.Schedules.length === 1 ? "" : "s");
|
|
393
|
+
i0.ɵɵadvance(2);
|
|
394
|
+
i0.ɵɵtextInterpolate1("", ctx_r1.ScheduledCount, " scheduled");
|
|
395
|
+
i0.ɵɵadvance();
|
|
396
|
+
i0.ɵɵconditional(ctx_r1.LockedCount > 0 ? 10 : -1);
|
|
397
|
+
i0.ɵɵadvance(4);
|
|
398
|
+
i0.ɵɵconditional(ctx_r1.TimelineMarkers.length > 0 ? 14 : -1);
|
|
399
|
+
i0.ɵɵadvance(2);
|
|
400
|
+
i0.ɵɵrepeater(ctx_r1.Schedules);
|
|
401
|
+
i0.ɵɵadvance(2);
|
|
402
|
+
i0.ɵɵconditional(ctx_r1.Schedules.length === 0 ? 18 : -1);
|
|
403
|
+
} }
|
|
404
|
+
// ---------------------------------------------------------------------------
|
|
405
|
+
// Component
|
|
406
|
+
// ---------------------------------------------------------------------------
|
|
407
|
+
let SchedulesComponent = class SchedulesComponent extends BaseResourceComponent {
|
|
408
|
+
// ---------------------------------------------------------------------------
|
|
409
|
+
// Public state
|
|
410
|
+
// ---------------------------------------------------------------------------
|
|
411
|
+
Schedules = [];
|
|
412
|
+
IsLoading = true;
|
|
413
|
+
SavingID = null;
|
|
414
|
+
RunningID = null;
|
|
415
|
+
/** Track pending edits per integration ID */
|
|
416
|
+
PendingChanges = new Map();
|
|
417
|
+
/** Timeline data built from schedules */
|
|
418
|
+
TimelineMarkers = [];
|
|
419
|
+
TimelineHours = [];
|
|
420
|
+
/** Preset interval choices */
|
|
421
|
+
IntervalPresets = [
|
|
422
|
+
{ Label: '5m', Minutes: 5 },
|
|
423
|
+
{ Label: '15m', Minutes: 15 },
|
|
424
|
+
{ Label: '30m', Minutes: 30 },
|
|
425
|
+
{ Label: '1h', Minutes: 60 },
|
|
426
|
+
{ Label: '6h', Minutes: 360 },
|
|
427
|
+
{ Label: '12h', Minutes: 720 },
|
|
428
|
+
{ Label: '24h', Minutes: 1440 }
|
|
429
|
+
];
|
|
430
|
+
/** Preset cron expressions */
|
|
431
|
+
CronPresets = [
|
|
432
|
+
{ Label: 'Every hour', Expression: '0 * * * *' },
|
|
433
|
+
{ Label: 'Every 6 hours', Expression: '0 */6 * * *' },
|
|
434
|
+
{ Label: 'Daily midnight', Expression: '0 0 * * *' },
|
|
435
|
+
{ Label: 'Weekdays 9am', Expression: '0 9 * * 1-5' }
|
|
436
|
+
];
|
|
437
|
+
// ---------------------------------------------------------------------------
|
|
438
|
+
// Private
|
|
439
|
+
// ---------------------------------------------------------------------------
|
|
440
|
+
dataService = inject(IntegrationDataService);
|
|
441
|
+
cdr = inject(ChangeDetectorRef);
|
|
442
|
+
refreshTimer = null;
|
|
443
|
+
// ---------------------------------------------------------------------------
|
|
444
|
+
// Lifecycle
|
|
445
|
+
// ---------------------------------------------------------------------------
|
|
446
|
+
async ngOnInit() {
|
|
447
|
+
this.buildTimelineHours();
|
|
448
|
+
await this.LoadData();
|
|
449
|
+
// Refresh relative times every 60 seconds
|
|
450
|
+
this.refreshTimer = setInterval(() => this.cdr.detectChanges(), 60_000);
|
|
451
|
+
}
|
|
452
|
+
ngOnDestroy() {
|
|
453
|
+
if (this.refreshTimer) {
|
|
454
|
+
clearInterval(this.refreshTimer);
|
|
455
|
+
this.refreshTimer = null;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
// ---------------------------------------------------------------------------
|
|
459
|
+
// Resource overrides
|
|
460
|
+
// ---------------------------------------------------------------------------
|
|
461
|
+
async GetResourceDisplayName(_data) {
|
|
462
|
+
return 'Schedules';
|
|
463
|
+
}
|
|
464
|
+
async GetResourceIconClass(_data) {
|
|
465
|
+
return 'fa-solid fa-calendar-check';
|
|
466
|
+
}
|
|
467
|
+
// ---------------------------------------------------------------------------
|
|
468
|
+
// Data loading
|
|
469
|
+
// ---------------------------------------------------------------------------
|
|
470
|
+
async LoadData() {
|
|
471
|
+
this.IsLoading = true;
|
|
472
|
+
this.cdr.detectChanges();
|
|
473
|
+
try {
|
|
474
|
+
const provider = this.RunViewToUse;
|
|
475
|
+
const rv = new RunView(provider ?? null);
|
|
476
|
+
const result = await rv.RunView({
|
|
477
|
+
EntityName: 'MJ: Company Integrations',
|
|
478
|
+
ExtraFilter: '',
|
|
479
|
+
OrderBy: 'Name',
|
|
480
|
+
Fields: [
|
|
481
|
+
'ID', 'Name', 'Integration', 'Company', 'IsActive',
|
|
482
|
+
'ScheduleEnabled', 'ScheduleType', 'ScheduleIntervalMinutes',
|
|
483
|
+
'CronExpression', 'NextScheduledRunAt', 'LastScheduledRunAt',
|
|
484
|
+
'IsLocked', 'LockedAt'
|
|
485
|
+
],
|
|
486
|
+
ResultType: 'simple'
|
|
487
|
+
});
|
|
488
|
+
this.Schedules = result.Results;
|
|
489
|
+
this.BuildTimeline();
|
|
490
|
+
}
|
|
491
|
+
catch (err) {
|
|
492
|
+
console.error('[SchedulesComponent] Failed to load data:', err);
|
|
493
|
+
}
|
|
494
|
+
finally {
|
|
495
|
+
this.IsLoading = false;
|
|
496
|
+
this.cdr.detectChanges();
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
// ---------------------------------------------------------------------------
|
|
500
|
+
// Timeline
|
|
501
|
+
// ---------------------------------------------------------------------------
|
|
502
|
+
BuildTimeline() {
|
|
503
|
+
const now = new Date();
|
|
504
|
+
const rows = [];
|
|
505
|
+
for (const schedule of this.Schedules) {
|
|
506
|
+
if (!schedule.ScheduleEnabled)
|
|
507
|
+
continue;
|
|
508
|
+
const markers = this.computeMarkersForSchedule(schedule, now);
|
|
509
|
+
if (markers.length > 0 || schedule.IsLocked) {
|
|
510
|
+
rows.push({
|
|
511
|
+
IntegrationID: schedule.ID,
|
|
512
|
+
IntegrationName: schedule.Integration ?? schedule.Name,
|
|
513
|
+
Markers: markers,
|
|
514
|
+
IsLocked: schedule.IsLocked
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
this.TimelineMarkers = rows;
|
|
519
|
+
}
|
|
520
|
+
// ---------------------------------------------------------------------------
|
|
521
|
+
// Schedule editing
|
|
522
|
+
// ---------------------------------------------------------------------------
|
|
523
|
+
ToggleScheduleEnabled(integrationID) {
|
|
524
|
+
const schedule = this.findSchedule(integrationID);
|
|
525
|
+
if (!schedule)
|
|
526
|
+
return;
|
|
527
|
+
const current = this.getEffectiveValue(integrationID, 'ScheduleEnabled', schedule.ScheduleEnabled);
|
|
528
|
+
this.setPendingChange(integrationID, 'ScheduleEnabled', !current);
|
|
529
|
+
}
|
|
530
|
+
SetScheduleType(integrationID, type) {
|
|
531
|
+
this.setPendingChange(integrationID, 'ScheduleType', type);
|
|
532
|
+
}
|
|
533
|
+
SetInterval(integrationID, minutes) {
|
|
534
|
+
this.setPendingChange(integrationID, 'ScheduleIntervalMinutes', minutes);
|
|
535
|
+
this.setPendingChange(integrationID, 'ScheduleType', 'Interval');
|
|
536
|
+
}
|
|
537
|
+
SetCronExpression(integrationID, expression) {
|
|
538
|
+
this.setPendingChange(integrationID, 'CronExpression', expression);
|
|
539
|
+
}
|
|
540
|
+
OnCronInputChange(integrationID, event) {
|
|
541
|
+
const input = event.target;
|
|
542
|
+
this.SetCronExpression(integrationID, input.value);
|
|
543
|
+
}
|
|
544
|
+
SetCustomInterval(integrationID, event) {
|
|
545
|
+
const input = event.target;
|
|
546
|
+
const value = parseInt(input.value, 10);
|
|
547
|
+
if (!isNaN(value) && value > 0) {
|
|
548
|
+
this.SetInterval(integrationID, value);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
HasChanges(integrationID) {
|
|
552
|
+
return this.PendingChanges.has(integrationID);
|
|
553
|
+
}
|
|
554
|
+
// ---------------------------------------------------------------------------
|
|
555
|
+
// Save & Run
|
|
556
|
+
// ---------------------------------------------------------------------------
|
|
557
|
+
async SaveSchedule(integrationID) {
|
|
558
|
+
const changes = this.PendingChanges.get(integrationID);
|
|
559
|
+
if (!changes)
|
|
560
|
+
return;
|
|
561
|
+
this.SavingID = integrationID;
|
|
562
|
+
this.cdr.detectChanges();
|
|
563
|
+
try {
|
|
564
|
+
const md = new Metadata();
|
|
565
|
+
const entity = await md.GetEntityObject('MJ: Company Integrations');
|
|
566
|
+
await entity.Load(integrationID);
|
|
567
|
+
// Use Set() method since scheduling fields are not in the generated class yet
|
|
568
|
+
if (changes.ScheduleEnabled !== undefined)
|
|
569
|
+
entity.Set('ScheduleEnabled', changes.ScheduleEnabled);
|
|
570
|
+
if (changes.ScheduleType !== undefined)
|
|
571
|
+
entity.Set('ScheduleType', changes.ScheduleType);
|
|
572
|
+
if (changes.ScheduleIntervalMinutes !== undefined)
|
|
573
|
+
entity.Set('ScheduleIntervalMinutes', changes.ScheduleIntervalMinutes);
|
|
574
|
+
if (changes.CronExpression !== undefined)
|
|
575
|
+
entity.Set('CronExpression', changes.CronExpression);
|
|
576
|
+
const saved = await entity.Save();
|
|
577
|
+
if (saved) {
|
|
578
|
+
this.PendingChanges.delete(integrationID);
|
|
579
|
+
await this.LoadData();
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
console.error('[SchedulesComponent] Failed to save schedule:', entity.LatestResult?.CompleteMessage);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
catch (err) {
|
|
586
|
+
console.error('[SchedulesComponent] Error saving schedule:', err);
|
|
587
|
+
}
|
|
588
|
+
finally {
|
|
589
|
+
this.SavingID = null;
|
|
590
|
+
this.cdr.detectChanges();
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
async RunNow(integrationID) {
|
|
594
|
+
if (this.RunningID)
|
|
595
|
+
return;
|
|
596
|
+
this.RunningID = integrationID;
|
|
597
|
+
this.cdr.detectChanges();
|
|
598
|
+
try {
|
|
599
|
+
const result = await this.dataService.RunSync(integrationID);
|
|
600
|
+
if (!result.Success) {
|
|
601
|
+
console.error('[SchedulesComponent] RunSync failed:', result.Message);
|
|
602
|
+
}
|
|
603
|
+
await this.LoadData();
|
|
604
|
+
}
|
|
605
|
+
catch (err) {
|
|
606
|
+
console.error('[SchedulesComponent] RunNow error:', err);
|
|
607
|
+
}
|
|
608
|
+
finally {
|
|
609
|
+
this.RunningID = null;
|
|
610
|
+
this.cdr.detectChanges();
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
// ---------------------------------------------------------------------------
|
|
614
|
+
// Template helpers
|
|
615
|
+
// ---------------------------------------------------------------------------
|
|
616
|
+
GetEffectiveScheduleEnabled(schedule) {
|
|
617
|
+
return this.getEffectiveValue(schedule.ID, 'ScheduleEnabled', schedule.ScheduleEnabled);
|
|
618
|
+
}
|
|
619
|
+
GetEffectiveScheduleType(schedule) {
|
|
620
|
+
return this.getEffectiveValue(schedule.ID, 'ScheduleType', schedule.ScheduleType);
|
|
621
|
+
}
|
|
622
|
+
GetEffectiveInterval(schedule) {
|
|
623
|
+
return this.getEffectiveValue(schedule.ID, 'ScheduleIntervalMinutes', schedule.ScheduleIntervalMinutes);
|
|
624
|
+
}
|
|
625
|
+
GetEffectiveCron(schedule) {
|
|
626
|
+
return this.getEffectiveValue(schedule.ID, 'CronExpression', schedule.CronExpression);
|
|
627
|
+
}
|
|
628
|
+
IsIntervalSelected(schedule, minutes) {
|
|
629
|
+
return this.GetEffectiveInterval(schedule) === minutes;
|
|
630
|
+
}
|
|
631
|
+
GetNextRunRelative(schedule) {
|
|
632
|
+
if (!schedule.NextScheduledRunAt)
|
|
633
|
+
return 'Not scheduled';
|
|
634
|
+
const next = new Date(schedule.NextScheduledRunAt);
|
|
635
|
+
const diffMs = next.getTime() - Date.now();
|
|
636
|
+
if (diffMs < 0)
|
|
637
|
+
return 'Overdue';
|
|
638
|
+
return 'in ' + this.formatDurationMs(diffMs);
|
|
639
|
+
}
|
|
640
|
+
GetLastRunRelative(schedule) {
|
|
641
|
+
if (!schedule.LastScheduledRunAt)
|
|
642
|
+
return 'Never';
|
|
643
|
+
return this.dataService.ComputeRelativeTime(schedule.LastScheduledRunAt);
|
|
644
|
+
}
|
|
645
|
+
GetCronDescription(expression) {
|
|
646
|
+
if (!expression)
|
|
647
|
+
return '';
|
|
648
|
+
const parts = expression.split(' ');
|
|
649
|
+
if (parts.length !== 5)
|
|
650
|
+
return expression;
|
|
651
|
+
const [min, hour, , , dow] = parts;
|
|
652
|
+
if (min === '0' && hour === '*')
|
|
653
|
+
return 'Every hour';
|
|
654
|
+
if (min === '0' && hour.startsWith('*/'))
|
|
655
|
+
return `Every ${hour.slice(2)} hours`;
|
|
656
|
+
if (min === '0' && hour !== '*' && dow === '*')
|
|
657
|
+
return `Daily at ${hour}:00`;
|
|
658
|
+
if (min === '0' && hour !== '*' && dow === '1-5')
|
|
659
|
+
return `Weekdays at ${hour}:00`;
|
|
660
|
+
if (dow === '1-5')
|
|
661
|
+
return `Weekdays at ${hour}:${min.padStart(2, '0')}`;
|
|
662
|
+
return expression;
|
|
663
|
+
}
|
|
664
|
+
GetIntegrationIcon(name) {
|
|
665
|
+
return ResolveIntegrationIcon(name);
|
|
666
|
+
}
|
|
667
|
+
ComputeTimelinePosition(hour, minute) {
|
|
668
|
+
return ((hour * 60 + minute) / (24 * 60)) * 100;
|
|
669
|
+
}
|
|
670
|
+
IsSaving(integrationID) {
|
|
671
|
+
return UUIDsEqual(this.SavingID, integrationID);
|
|
672
|
+
}
|
|
673
|
+
IsRunning(integrationID) {
|
|
674
|
+
return UUIDsEqual(this.RunningID, integrationID);
|
|
675
|
+
}
|
|
676
|
+
get ScheduledCount() {
|
|
677
|
+
return this.Schedules.filter(s => s.ScheduleEnabled).length;
|
|
678
|
+
}
|
|
679
|
+
get LockedCount() {
|
|
680
|
+
return this.Schedules.filter(s => s.IsLocked).length;
|
|
681
|
+
}
|
|
682
|
+
TrackByID(_index, item) {
|
|
683
|
+
return item.ID;
|
|
684
|
+
}
|
|
685
|
+
TrackTimelineByID(_index, item) {
|
|
686
|
+
return item.IntegrationID;
|
|
687
|
+
}
|
|
688
|
+
TrackPresetByMinutes(_index, item) {
|
|
689
|
+
return item.Minutes;
|
|
690
|
+
}
|
|
691
|
+
TrackCronPresetByExpr(_index, item) {
|
|
692
|
+
return item.Expression;
|
|
693
|
+
}
|
|
694
|
+
TrackHour(_index, hour) {
|
|
695
|
+
return hour;
|
|
696
|
+
}
|
|
697
|
+
TrackMarker(index, _item) {
|
|
698
|
+
return index;
|
|
699
|
+
}
|
|
700
|
+
// ---------------------------------------------------------------------------
|
|
701
|
+
// Private helpers
|
|
702
|
+
// ---------------------------------------------------------------------------
|
|
703
|
+
findSchedule(integrationID) {
|
|
704
|
+
return this.Schedules.find(s => UUIDsEqual(s.ID, integrationID));
|
|
705
|
+
}
|
|
706
|
+
getEffectiveValue(integrationID, field, originalValue) {
|
|
707
|
+
const changes = this.PendingChanges.get(integrationID);
|
|
708
|
+
if (changes && field in changes) {
|
|
709
|
+
return changes[field];
|
|
710
|
+
}
|
|
711
|
+
return originalValue;
|
|
712
|
+
}
|
|
713
|
+
setPendingChange(integrationID, field, value) {
|
|
714
|
+
const existing = this.PendingChanges.get(integrationID) ?? {};
|
|
715
|
+
existing[field] = value;
|
|
716
|
+
this.PendingChanges.set(integrationID, existing);
|
|
717
|
+
this.cdr.detectChanges();
|
|
718
|
+
}
|
|
719
|
+
buildTimelineHours() {
|
|
720
|
+
this.TimelineHours = Array.from({ length: 24 }, (_, i) => i);
|
|
721
|
+
}
|
|
722
|
+
computeMarkersForSchedule(schedule, now) {
|
|
723
|
+
const markers = [];
|
|
724
|
+
const type = schedule.ScheduleType;
|
|
725
|
+
if (type === 'Interval' && schedule.ScheduleIntervalMinutes && schedule.ScheduleIntervalMinutes > 0) {
|
|
726
|
+
this.computeIntervalMarkers(schedule, now, markers);
|
|
727
|
+
}
|
|
728
|
+
else if (type === 'Cron' && schedule.CronExpression) {
|
|
729
|
+
this.computeCronMarkers(schedule, now, markers);
|
|
730
|
+
}
|
|
731
|
+
else if (schedule.NextScheduledRunAt) {
|
|
732
|
+
this.addNextRunMarker(schedule, now, markers);
|
|
733
|
+
}
|
|
734
|
+
return markers;
|
|
735
|
+
}
|
|
736
|
+
computeIntervalMarkers(schedule, now, markers) {
|
|
737
|
+
const intervalMs = schedule.ScheduleIntervalMinutes * 60_000;
|
|
738
|
+
const endTime = now.getTime() + 24 * 60 * 60_000;
|
|
739
|
+
// Start from the next scheduled run or from now
|
|
740
|
+
let cursor = schedule.NextScheduledRunAt
|
|
741
|
+
? new Date(schedule.NextScheduledRunAt).getTime()
|
|
742
|
+
: now.getTime();
|
|
743
|
+
// If cursor is in the past, advance to the next future occurrence
|
|
744
|
+
while (cursor < now.getTime()) {
|
|
745
|
+
cursor += intervalMs;
|
|
746
|
+
}
|
|
747
|
+
const nextRun = cursor;
|
|
748
|
+
while (cursor < endTime) {
|
|
749
|
+
const d = new Date(cursor);
|
|
750
|
+
markers.push({
|
|
751
|
+
Hour: d.getHours(),
|
|
752
|
+
Minute: d.getMinutes(),
|
|
753
|
+
IsNext: cursor === nextRun
|
|
754
|
+
});
|
|
755
|
+
cursor += intervalMs;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
computeCronMarkers(schedule, now, markers) {
|
|
759
|
+
// Simple cron approximation for timeline display
|
|
760
|
+
const times = this.approximateCronTimes(schedule.CronExpression);
|
|
761
|
+
const nextRunTime = schedule.NextScheduledRunAt ? new Date(schedule.NextScheduledRunAt) : null;
|
|
762
|
+
for (const time of times) {
|
|
763
|
+
const isNext = nextRunTime != null
|
|
764
|
+
&& time.Hour === nextRunTime.getHours()
|
|
765
|
+
&& time.Minute === nextRunTime.getMinutes();
|
|
766
|
+
markers.push({ Hour: time.Hour, Minute: time.Minute, IsNext: isNext });
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
addNextRunMarker(schedule, now, markers) {
|
|
770
|
+
const next = new Date(schedule.NextScheduledRunAt);
|
|
771
|
+
const diffMs = next.getTime() - now.getTime();
|
|
772
|
+
if (diffMs >= 0 && diffMs <= 24 * 60 * 60_000) {
|
|
773
|
+
markers.push({ Hour: next.getHours(), Minute: next.getMinutes(), IsNext: true });
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Approximate cron schedule times within a 24-hour window.
|
|
778
|
+
* Handles common patterns only -- not a full cron parser.
|
|
779
|
+
*/
|
|
780
|
+
approximateCronTimes(expression) {
|
|
781
|
+
const parts = expression.split(' ');
|
|
782
|
+
if (parts.length !== 5)
|
|
783
|
+
return [];
|
|
784
|
+
const [minPart, hourPart] = parts;
|
|
785
|
+
const minute = minPart === '*' ? 0 : parseInt(minPart, 10);
|
|
786
|
+
const times = [];
|
|
787
|
+
if (hourPart === '*') {
|
|
788
|
+
// Every hour
|
|
789
|
+
for (let h = 0; h < 24; h++) {
|
|
790
|
+
times.push({ Hour: h, Minute: isNaN(minute) ? 0 : minute });
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
else if (hourPart.startsWith('*/')) {
|
|
794
|
+
const step = parseInt(hourPart.slice(2), 10);
|
|
795
|
+
if (!isNaN(step) && step > 0) {
|
|
796
|
+
for (let h = 0; h < 24; h += step) {
|
|
797
|
+
times.push({ Hour: h, Minute: isNaN(minute) ? 0 : minute });
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
else {
|
|
802
|
+
// Specific hour(s) — comma-separated
|
|
803
|
+
const hours = hourPart.split(',').map(h => parseInt(h.trim(), 10)).filter(h => !isNaN(h));
|
|
804
|
+
for (const h of hours) {
|
|
805
|
+
times.push({ Hour: h, Minute: isNaN(minute) ? 0 : minute });
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
return times;
|
|
809
|
+
}
|
|
810
|
+
formatDurationMs(ms) {
|
|
811
|
+
const totalMinutes = Math.floor(ms / 60_000);
|
|
812
|
+
if (totalMinutes < 1)
|
|
813
|
+
return 'less than a minute';
|
|
814
|
+
if (totalMinutes < 60)
|
|
815
|
+
return `${totalMinutes} minute${totalMinutes === 1 ? '' : 's'}`;
|
|
816
|
+
const hours = Math.floor(totalMinutes / 60);
|
|
817
|
+
const minutes = totalMinutes % 60;
|
|
818
|
+
if (minutes === 0)
|
|
819
|
+
return `${hours} hour${hours === 1 ? '' : 's'}`;
|
|
820
|
+
return `${hours}h ${minutes}m`;
|
|
821
|
+
}
|
|
822
|
+
static ɵfac = /*@__PURE__*/ (() => { let ɵSchedulesComponent_BaseFactory; return function SchedulesComponent_Factory(__ngFactoryType__) { return (ɵSchedulesComponent_BaseFactory || (ɵSchedulesComponent_BaseFactory = i0.ɵɵgetInheritedFactory(SchedulesComponent)))(__ngFactoryType__ || SchedulesComponent); }; })();
|
|
823
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SchedulesComponent, selectors: [["app-integration-schedules"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 2, vars: 1, consts: [[1, "loading-container"], [1, "schedules-root"], ["text", "Loading schedules..."], [1, "section-header"], [1, "header-left"], [1, "section-title"], [1, "fa-solid", "fa-calendar-check"], [1, "header-meta"], [1, "header-meta", "running-badge"], [1, "btn", "btn-outline", 3, "click"], [1, "fa-solid", "fa-arrows-rotate"], [1, "timeline-section"], [1, "cards-section"], [1, "schedule-card", 3, "schedule-card-disabled"], [1, "empty-state"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "timeline-title"], [1, "fa-regular", "fa-clock"], [1, "timeline-container"], [1, "timeline-grid-labels"], [1, "timeline-label-spacer"], [1, "timeline-grid-track"], [1, "timeline-row"], [1, "timeline-hour-label", 3, "left"], [1, "timeline-hour-label"], [1, "timeline-row-label", 3, "title"], [1, "timeline-track-container"], [1, "timeline-track"], [1, "timeline-marker", 3, "timeline-marker-next", "left", "title"], [1, "timeline-locked-indicator"], [1, "timeline-now-line"], [1, "timeline-grid-line", 3, "left"], [1, "timeline-grid-line"], [1, "timeline-marker", 3, "title"], [1, "schedule-card"], [1, "card-info"], [1, "card-icon"], [1, "card-details"], [1, "card-name"], [1, "card-company"], [1, "card-badges"], [1, "badge", "badge-success"], [1, "badge", "badge-gray"], [1, "badge", "badge-running"], [1, "card-config"], [1, "config-row"], [1, "toggle-label"], ["role", "switch", 1, "toggle-switch", 3, "click"], [1, "toggle-knob"], [1, "card-status"], [1, "status-items"], [1, "status-item"], [1, "status-label"], [1, "status-value"], [1, "status-value", "status-running"], [1, "status-value", "status-idle"], [1, "card-actions"], [1, "btn", "btn-primary", "btn-save", 3, "disabled"], [1, "btn", "btn-outline", "btn-run", 3, "click", "disabled"], [1, "type-pills"], [1, "type-pill", 3, "click"], [1, "interval-presets"], [1, "preset-btn", 3, "preset-btn-active"], [1, "custom-interval"], ["type", "number", "placeholder", "Custom min", "min", "1", 1, "interval-input", 3, "change", "value"], [1, "interval-unit"], [1, "preset-btn", 3, "click"], [1, "cron-presets"], [1, "cron-input-row"], ["type", "text", "placeholder", "* * * * *", 1, "cron-input", 3, "change", "value"], [1, "cron-description"], [1, "btn", "btn-primary", "btn-save", 3, "click", "disabled"], [1, "fa-solid", "fa-check"], [1, "fa-solid", "fa-play"], [1, "fa-solid", "fa-calendar-xmark"]], template: function SchedulesComponent_Template(rf, ctx) { if (rf & 1) {
|
|
824
|
+
i0.ɵɵconditionalCreate(0, SchedulesComponent_Conditional_0_Template, 2, 0, "div", 0)(1, SchedulesComponent_Conditional_1_Template, 19, 6, "div", 1);
|
|
825
|
+
} if (rf & 2) {
|
|
826
|
+
i0.ɵɵconditional(ctx.IsLoading ? 0 : 1);
|
|
827
|
+
} }, dependencies: [i1.LoadingComponent], styles: ["\n\n\n\n\n[_nghost-%COMP%] {\n display: block;\n height: 100%;\n overflow-y: auto;\n background: var(--mj-bg-page);\n}\n\n.schedules-root[_ngcontent-%COMP%] {\n padding: 24px;\n max-width: 1400px;\n margin: 0 auto;\n}\n\n.loading-container[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 300px;\n}\n\n\n\n\n\n\n.section-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 24px;\n}\n\n.header-left[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n flex-wrap: wrap;\n}\n\n.section-title[_ngcontent-%COMP%] {\n font-size: 20px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.section-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.header-meta[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-muted);\n padding: 2px 10px;\n background: var(--mj-bg-surface-active);\n border-radius: 12px;\n}\n\n.running-badge[_ngcontent-%COMP%] {\n background: var(--mj-color-warning-100);\n color: var(--mj-color-warning-600);\n}\n\n.running-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n margin-right: 4px;\n}\n\n\n\n\n\n\n.btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n border: none;\n transition: all 0.15s ease;\n white-space: nowrap;\n}\n\n.btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.btn-primary[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-bg-surface);\n}\n\n.btn-primary[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-color-info-700);\n}\n\n.btn-outline[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n border: 1px solid var(--mj-border-strong);\n}\n\n.btn-outline[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-text-disabled);\n}\n\n\n\n\n\n\n.timeline-section[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n padding: 20px 24px;\n margin-bottom: 24px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n border: 1px solid var(--mj-border-default);\n}\n\n.timeline-title[_ngcontent-%COMP%] {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-color-neutral-700);\n margin-bottom: 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.timeline-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n\n.timeline-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n\n\n.timeline-grid-labels[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-end;\n height: 20px;\n margin-bottom: 4px;\n}\n\n.timeline-label-spacer[_ngcontent-%COMP%] {\n width: 200px;\n min-width: 200px;\n flex-shrink: 0;\n}\n\n.timeline-grid-track[_ngcontent-%COMP%] {\n flex: 1;\n position: relative;\n height: 20px;\n}\n\n.timeline-hour-label[_ngcontent-%COMP%] {\n position: absolute;\n font-size: 10px;\n color: var(--mj-text-disabled);\n transform: translateX(-50%);\n white-space: nowrap;\n}\n\n\n\n.timeline-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n height: 28px;\n}\n\n.timeline-row-label[_ngcontent-%COMP%] {\n width: 200px;\n min-width: 200px;\n flex-shrink: 0;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n padding-right: 16px;\n}\n\n.timeline-track-container[_ngcontent-%COMP%] {\n flex: 1;\n position: relative;\n height: 28px;\n display: flex;\n align-items: center;\n}\n\n.timeline-track[_ngcontent-%COMP%] {\n position: absolute;\n left: 0;\n right: 0;\n height: 4px;\n background: var(--mj-border-default);\n border-radius: 2px;\n}\n\n.timeline-grid-line[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 1px;\n background: var(--mj-border-subtle);\n z-index: 0;\n}\n\n\n\n.timeline-marker[_ngcontent-%COMP%] {\n position: absolute;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--mj-text-disabled);\n transform: translate(-50%, 0);\n z-index: 2;\n transition: all 0.2s ease;\n}\n\n.timeline-marker[_ngcontent-%COMP%]:hover {\n transform: translate(-50%, 0) scale(1.4);\n}\n\n.timeline-marker-next[_ngcontent-%COMP%] {\n width: 12px;\n height: 12px;\n background: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);\n}\n\n\n\n.timeline-now-line[_ngcontent-%COMP%] {\n position: absolute;\n left: 0;\n top: -2px;\n bottom: -2px;\n width: 2px;\n background: var(--mj-status-error);\n z-index: 3;\n border-radius: 1px;\n}\n\n\n\n.timeline-locked-indicator[_ngcontent-%COMP%] {\n position: absolute;\n left: -4px;\n font-size: 12px;\n color: var(--mj-color-warning-600);\n z-index: 4;\n animation: _ngcontent-%COMP%_pulse-opacity 1.5s ease-in-out infinite;\n}\n\n@keyframes _ngcontent-%COMP%_pulse-opacity {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.4; }\n}\n\n\n\n\n\n\n.cards-section[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.schedule-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n background: var(--mj-bg-surface);\n border-radius: 12px;\n padding: 24px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n border: 1px solid var(--mj-border-default);\n transition: box-shadow 0.2s ease;\n gap: 32px;\n}\n\n.schedule-card[_ngcontent-%COMP%]:hover {\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n}\n\n.schedule-card-disabled[_ngcontent-%COMP%] {\n opacity: 0.6;\n}\n\n\n\n.card-info[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 16px;\n min-width: 220px;\n flex-shrink: 0;\n}\n\n.card-icon[_ngcontent-%COMP%] {\n width: 48px;\n height: 48px;\n border-radius: 12px;\n background: var(--mj-status-info-bg);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.card-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 20px;\n color: var(--mj-brand-primary);\n}\n\n.card-details[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.card-name[_ngcontent-%COMP%] {\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.card-company[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-disabled);\n}\n\n.card-badges[_ngcontent-%COMP%] {\n display: flex;\n gap: 6px;\n margin-top: 4px;\n flex-wrap: wrap;\n}\n\n.badge[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 500;\n padding: 2px 8px;\n border-radius: 10px;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.badge-success[_ngcontent-%COMP%] {\n background: var(--mj-status-success-bg);\n color: var(--mj-color-success-600);\n}\n\n.badge-gray[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-muted);\n}\n\n.badge-running[_ngcontent-%COMP%] {\n background: var(--mj-color-warning-100);\n color: var(--mj-color-warning-600);\n}\n\n\n\n.card-config[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.config-row[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n\n\n.toggle-label[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.toggle-switch[_ngcontent-%COMP%] {\n position: relative;\n width: 40px;\n height: 22px;\n border-radius: 11px;\n background: var(--mj-color-neutral-300);\n border: none;\n cursor: pointer;\n transition: background 0.2s ease;\n padding: 0;\n flex-shrink: 0;\n}\n\n.toggle-switch.toggle-on[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n}\n\n.toggle-knob[_ngcontent-%COMP%] {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: var(--mj-bg-surface);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);\n transition: transform 0.2s ease;\n}\n\n.toggle-on[_ngcontent-%COMP%] .toggle-knob[_ngcontent-%COMP%] {\n transform: translateX(18px);\n}\n\n\n\n.type-pills[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n background: var(--mj-bg-surface-hover);\n border-radius: 8px;\n padding: 3px;\n width: fit-content;\n}\n\n.type-pill[_ngcontent-%COMP%] {\n padding: 5px 14px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n background: transparent;\n border: none;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.type-pill[_ngcontent-%COMP%]:hover {\n color: var(--mj-color-neutral-700);\n}\n\n.type-pill-active[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\n}\n\n\n\n.interval-presets[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.preset-btn[_ngcontent-%COMP%] {\n padding: 4px 12px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n background: var(--mj-bg-surface-hover);\n border: 1px solid var(--mj-border-default);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.preset-btn[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-text-disabled);\n color: var(--mj-color-neutral-700);\n}\n\n.preset-btn-active[_ngcontent-%COMP%] {\n background: var(--mj-status-info-bg);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n\n\n.custom-interval[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 4px;\n}\n\n.interval-input[_ngcontent-%COMP%] {\n width: 100px;\n padding: 5px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 12px;\n color: var(--mj-color-neutral-700);\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.interval-input[_ngcontent-%COMP%]:focus {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1);\n}\n\n.interval-unit[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-disabled);\n}\n\n\n\n.cron-presets[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.cron-input-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-top: 4px;\n flex-wrap: wrap;\n}\n\n.cron-input[_ngcontent-%COMP%] {\n width: 180px;\n padding: 5px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 12px;\n font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;\n color: var(--mj-color-neutral-700);\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.cron-input[_ngcontent-%COMP%]:focus {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1);\n}\n\n.cron-description[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-muted);\n font-style: italic;\n}\n\n\n\n.card-status[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 16px;\n min-width: 180px;\n flex-shrink: 0;\n}\n\n.status-items[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n text-align: right;\n}\n\n.status-item[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.status-label[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.status-value[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-color-neutral-700);\n}\n\n.status-running[_ngcontent-%COMP%] {\n color: var(--mj-color-warning-600);\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 6px;\n}\n\n.status-idle[_ngcontent-%COMP%] {\n color: var(--mj-color-success-600);\n}\n\n\n\n.card-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n}\n\n.btn-save[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_slideIn 0.2s ease;\n}\n\n@keyframes _ngcontent-%COMP%_slideIn {\n from {\n opacity: 0;\n transform: translateX(8px);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n}\n\n.btn-run[_ngcontent-%COMP%] {\n font-size: 12px;\n padding: 6px 12px;\n}\n\n\n\n\n\n\n.empty-state[_ngcontent-%COMP%] {\n text-align: center;\n padding: 60px 24px;\n color: var(--mj-text-disabled);\n}\n\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n display: block;\n}\n\n.empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n font-size: 15px;\n margin: 0;\n}\n\n\n\n\n\n\n@media (max-width: 900px) {\n .schedule-card[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 20px;\n }\n\n .card-info[_ngcontent-%COMP%] {\n min-width: 0;\n width: 100%;\n }\n\n .card-config[_ngcontent-%COMP%] {\n width: 100%;\n }\n\n .card-status[_ngcontent-%COMP%] {\n align-items: flex-start;\n width: 100%;\n min-width: 0;\n }\n\n .status-items[_ngcontent-%COMP%] {\n text-align: left;\n flex-direction: row;\n gap: 20px;\n flex-wrap: wrap;\n }\n\n .status-running[_ngcontent-%COMP%] {\n justify-content: flex-start;\n }\n\n .card-actions[_ngcontent-%COMP%] {\n width: 100%;\n }\n\n .card-actions[_ngcontent-%COMP%] .btn[_ngcontent-%COMP%] {\n flex: 1;\n justify-content: center;\n }\n}\n\n@media (max-width: 768px) {\n .timeline-section[_ngcontent-%COMP%] {\n display: none;\n }\n\n .schedules-root[_ngcontent-%COMP%] {\n padding: 16px;\n }\n\n .section-header[_ngcontent-%COMP%] {\n flex-direction: column;\n align-items: flex-start;\n gap: 12px;\n }\n\n .header-left[_ngcontent-%COMP%] {\n gap: 8px;\n }\n}"] });
|
|
828
|
+
};
|
|
829
|
+
SchedulesComponent = __decorate([
|
|
830
|
+
RegisterClass(BaseResourceComponent, 'IntegrationSchedules')
|
|
831
|
+
], SchedulesComponent);
|
|
832
|
+
export { SchedulesComponent };
|
|
833
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SchedulesComponent, [{
|
|
834
|
+
type: Component,
|
|
835
|
+
args: [{ standalone: false, selector: 'app-integration-schedules', template: "<!-- Loading state -->\n@if (IsLoading) {\n <div class=\"loading-container\">\n <mj-loading text=\"Loading schedules...\"></mj-loading>\n </div>\n} @else {\n <div class=\"schedules-root\">\n\n <!-- ================================================================= -->\n <!-- HEADER -->\n <!-- ================================================================= -->\n <div class=\"section-header\">\n <div class=\"header-left\">\n <h2 class=\"section-title\">\n <i class=\"fa-solid fa-calendar-check\"></i>\n Sync Schedules\n </h2>\n <span class=\"header-meta\">{{ Schedules.length }} integration{{ Schedules.length === 1 ? '' : 's' }}</span>\n <span class=\"header-meta\">{{ ScheduledCount }} scheduled</span>\n @if (LockedCount > 0) {\n <span class=\"header-meta running-badge\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n {{ LockedCount }} running\n </span>\n }\n </div>\n <button class=\"btn btn-outline\" (click)=\"LoadData()\">\n <i class=\"fa-solid fa-arrows-rotate\"></i>\n Refresh\n </button>\n </div>\n\n <!-- ================================================================= -->\n <!-- TIMELINE (24-hour visual) -->\n <!-- ================================================================= -->\n @if (TimelineMarkers.length > 0) {\n <div class=\"timeline-section\">\n <div class=\"timeline-title\">\n <i class=\"fa-regular fa-clock\"></i>\n Next 24 Hours\n </div>\n <div class=\"timeline-container\">\n <!-- Hour grid labels -->\n <div class=\"timeline-grid-labels\">\n <div class=\"timeline-label-spacer\"></div>\n <div class=\"timeline-grid-track\">\n @for (hour of TimelineHours; track TrackHour($index, hour)) {\n @if (hour % 4 === 0) {\n <span class=\"timeline-hour-label\"\n [style.left.%]=\"ComputeTimelinePosition(hour, 0)\">\n {{ hour }}:00\n </span>\n }\n }\n </div>\n </div>\n\n <!-- Integration rows -->\n @for (row of TimelineMarkers; track TrackTimelineByID($index, row)) {\n <div class=\"timeline-row\">\n <div class=\"timeline-row-label\" [title]=\"row.IntegrationName\">\n {{ row.IntegrationName }}\n </div>\n <div class=\"timeline-track-container\">\n <!-- Hour grid lines -->\n @for (hour of TimelineHours; track TrackHour($index, hour)) {\n @if (hour % 4 === 0) {\n <div class=\"timeline-grid-line\"\n [style.left.%]=\"ComputeTimelinePosition(hour, 0)\">\n </div>\n }\n }\n\n <!-- Track bar -->\n <div class=\"timeline-track\"></div>\n\n <!-- Markers -->\n @for (marker of row.Markers; track TrackMarker($index, marker)) {\n <div class=\"timeline-marker\"\n [class.timeline-marker-next]=\"marker.IsNext\"\n [style.left.%]=\"ComputeTimelinePosition(marker.Hour, marker.Minute)\"\n [title]=\"marker.Hour + ':' + ('' + marker.Minute).padStart(2, '0')\">\n </div>\n }\n\n <!-- Locked indicator -->\n @if (row.IsLocked) {\n <div class=\"timeline-locked-indicator\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n </div>\n }\n\n <!-- Current time line -->\n <div class=\"timeline-now-line\"></div>\n </div>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- ================================================================= -->\n <!-- SCHEDULE CARDS -->\n <!-- ================================================================= -->\n <div class=\"cards-section\">\n @for (schedule of Schedules; track TrackByID($index, schedule)) {\n <div class=\"schedule-card\" [class.schedule-card-disabled]=\"!schedule.IsActive\">\n\n <!-- LEFT: Icon + Info -->\n <div class=\"card-info\">\n <div class=\"card-icon\">\n <i [class]=\"GetIntegrationIcon(schedule.Integration || schedule.Name)\"></i>\n </div>\n <div class=\"card-details\">\n <div class=\"card-name\">{{ schedule.Integration || schedule.Name }}</div>\n <div class=\"card-company\">{{ schedule.Company }}</div>\n <div class=\"card-badges\">\n @if (schedule.IsActive) {\n <span class=\"badge badge-success\">Active</span>\n } @else {\n <span class=\"badge badge-gray\">Inactive</span>\n }\n @if (schedule.IsLocked) {\n <span class=\"badge badge-running\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Running\n </span>\n }\n </div>\n </div>\n </div>\n\n <!-- CENTER: Schedule Configuration -->\n <div class=\"card-config\">\n <!-- Schedule toggle -->\n <div class=\"config-row\">\n <label class=\"toggle-label\">\n Auto-sync\n <button class=\"toggle-switch\"\n [class.toggle-on]=\"GetEffectiveScheduleEnabled(schedule)\"\n (click)=\"ToggleScheduleEnabled(schedule.ID)\"\n [attr.aria-checked]=\"GetEffectiveScheduleEnabled(schedule)\"\n role=\"switch\">\n <span class=\"toggle-knob\"></span>\n </button>\n </label>\n </div>\n\n @if (GetEffectiveScheduleEnabled(schedule)) {\n <!-- Schedule type pills -->\n <div class=\"config-row\">\n <div class=\"type-pills\">\n <button class=\"type-pill\"\n [class.type-pill-active]=\"GetEffectiveScheduleType(schedule) === 'Manual'\"\n (click)=\"SetScheduleType(schedule.ID, 'Manual')\">\n Manual\n </button>\n <button class=\"type-pill\"\n [class.type-pill-active]=\"GetEffectiveScheduleType(schedule) === 'Interval'\"\n (click)=\"SetScheduleType(schedule.ID, 'Interval')\">\n Interval\n </button>\n <button class=\"type-pill\"\n [class.type-pill-active]=\"GetEffectiveScheduleType(schedule) === 'Cron'\"\n (click)=\"SetScheduleType(schedule.ID, 'Cron')\">\n Cron\n </button>\n </div>\n </div>\n\n <!-- Interval config -->\n @if (GetEffectiveScheduleType(schedule) === 'Interval') {\n <div class=\"config-row\">\n <div class=\"interval-presets\">\n @for (preset of IntervalPresets; track TrackPresetByMinutes($index, preset)) {\n <button class=\"preset-btn\"\n [class.preset-btn-active]=\"IsIntervalSelected(schedule, preset.Minutes)\"\n (click)=\"SetInterval(schedule.ID, preset.Minutes)\">\n {{ preset.Label }}\n </button>\n }\n </div>\n <div class=\"custom-interval\">\n <input type=\"number\"\n class=\"interval-input\"\n placeholder=\"Custom min\"\n min=\"1\"\n [value]=\"GetEffectiveInterval(schedule)\"\n (change)=\"SetCustomInterval(schedule.ID, $event)\" />\n <span class=\"interval-unit\">min</span>\n </div>\n </div>\n }\n\n <!-- Cron config -->\n @if (GetEffectiveScheduleType(schedule) === 'Cron') {\n <div class=\"config-row\">\n <div class=\"cron-presets\">\n @for (preset of CronPresets; track TrackCronPresetByExpr($index, preset)) {\n <button class=\"preset-btn\"\n [class.preset-btn-active]=\"GetEffectiveCron(schedule) === preset.Expression\"\n (click)=\"SetCronExpression(schedule.ID, preset.Expression)\">\n {{ preset.Label }}\n </button>\n }\n </div>\n <div class=\"cron-input-row\">\n <input type=\"text\"\n class=\"cron-input\"\n placeholder=\"* * * * *\"\n [value]=\"GetEffectiveCron(schedule) ?? ''\"\n (change)=\"OnCronInputChange(schedule.ID, $event)\" />\n @if (GetEffectiveCron(schedule)) {\n <span class=\"cron-description\">\n {{ GetCronDescription(GetEffectiveCron(schedule)) }}\n </span>\n }\n </div>\n </div>\n }\n }\n </div>\n\n <!-- RIGHT: Status + Actions -->\n <div class=\"card-status\">\n <div class=\"status-items\">\n <div class=\"status-item\">\n <span class=\"status-label\">Next run</span>\n <span class=\"status-value\">{{ GetNextRunRelative(schedule) }}</span>\n </div>\n <div class=\"status-item\">\n <span class=\"status-label\">Last run</span>\n <span class=\"status-value\">{{ GetLastRunRelative(schedule) }}</span>\n </div>\n <div class=\"status-item\">\n <span class=\"status-label\">Status</span>\n @if (schedule.IsLocked) {\n <span class=\"status-value status-running\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Running\n </span>\n } @else {\n <span class=\"status-value status-idle\">Idle</span>\n }\n </div>\n </div>\n\n <div class=\"card-actions\">\n @if (HasChanges(schedule.ID)) {\n <button class=\"btn btn-primary btn-save\"\n [disabled]=\"IsSaving(schedule.ID)\"\n (click)=\"SaveSchedule(schedule.ID)\">\n @if (IsSaving(schedule.ID)) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Saving...\n } @else {\n <i class=\"fa-solid fa-check\"></i>\n Save\n }\n </button>\n }\n <button class=\"btn btn-outline btn-run\"\n [disabled]=\"IsRunning(schedule.ID) || schedule.IsLocked\"\n (click)=\"RunNow(schedule.ID)\">\n @if (IsRunning(schedule.ID)) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n Running...\n } @else {\n <i class=\"fa-solid fa-play\"></i>\n Run Now\n }\n </button>\n </div>\n </div>\n </div>\n }\n\n @if (Schedules.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-calendar-xmark\"></i>\n <p>No integrations found</p>\n </div>\n }\n </div>\n </div>\n}\n", styles: ["/* ==========================================================================\n Schedules Component\n ========================================================================== */\n\n:host {\n display: block;\n height: 100%;\n overflow-y: auto;\n background: var(--mj-bg-page);\n}\n\n.schedules-root {\n padding: 24px;\n max-width: 1400px;\n margin: 0 auto;\n}\n\n.loading-container {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 300px;\n}\n\n/* --------------------------------------------------------------------------\n Header\n -------------------------------------------------------------------------- */\n\n.section-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 24px;\n}\n\n.header-left {\n display: flex;\n align-items: center;\n gap: 16px;\n flex-wrap: wrap;\n}\n\n.section-title {\n font-size: 20px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.section-title i {\n color: var(--mj-brand-primary);\n}\n\n.header-meta {\n font-size: 13px;\n color: var(--mj-text-muted);\n padding: 2px 10px;\n background: var(--mj-bg-surface-active);\n border-radius: 12px;\n}\n\n.running-badge {\n background: var(--mj-color-warning-100);\n color: var(--mj-color-warning-600);\n}\n\n.running-badge i {\n margin-right: 4px;\n}\n\n/* --------------------------------------------------------------------------\n Buttons (shared)\n -------------------------------------------------------------------------- */\n\n.btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n border: none;\n transition: all 0.15s ease;\n white-space: nowrap;\n}\n\n.btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.btn-primary {\n background: var(--mj-brand-primary);\n color: var(--mj-bg-surface);\n}\n\n.btn-primary:hover:not(:disabled) {\n background: var(--mj-color-info-700);\n}\n\n.btn-outline {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n border: 1px solid var(--mj-border-strong);\n}\n\n.btn-outline:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-text-disabled);\n}\n\n/* --------------------------------------------------------------------------\n Timeline Section\n -------------------------------------------------------------------------- */\n\n.timeline-section {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n padding: 20px 24px;\n margin-bottom: 24px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n border: 1px solid var(--mj-border-default);\n}\n\n.timeline-title {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-color-neutral-700);\n margin-bottom: 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.timeline-title i {\n color: var(--mj-text-muted);\n}\n\n.timeline-container {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* Grid labels row */\n.timeline-grid-labels {\n display: flex;\n align-items: flex-end;\n height: 20px;\n margin-bottom: 4px;\n}\n\n.timeline-label-spacer {\n width: 200px;\n min-width: 200px;\n flex-shrink: 0;\n}\n\n.timeline-grid-track {\n flex: 1;\n position: relative;\n height: 20px;\n}\n\n.timeline-hour-label {\n position: absolute;\n font-size: 10px;\n color: var(--mj-text-disabled);\n transform: translateX(-50%);\n white-space: nowrap;\n}\n\n/* Individual timeline rows */\n.timeline-row {\n display: flex;\n align-items: center;\n height: 28px;\n}\n\n.timeline-row-label {\n width: 200px;\n min-width: 200px;\n flex-shrink: 0;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n padding-right: 16px;\n}\n\n.timeline-track-container {\n flex: 1;\n position: relative;\n height: 28px;\n display: flex;\n align-items: center;\n}\n\n.timeline-track {\n position: absolute;\n left: 0;\n right: 0;\n height: 4px;\n background: var(--mj-border-default);\n border-radius: 2px;\n}\n\n.timeline-grid-line {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 1px;\n background: var(--mj-border-subtle);\n z-index: 0;\n}\n\n/* Markers */\n.timeline-marker {\n position: absolute;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--mj-text-disabled);\n transform: translate(-50%, 0);\n z-index: 2;\n transition: all 0.2s ease;\n}\n\n.timeline-marker:hover {\n transform: translate(-50%, 0) scale(1.4);\n}\n\n.timeline-marker-next {\n width: 12px;\n height: 12px;\n background: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);\n}\n\n/* Now line */\n.timeline-now-line {\n position: absolute;\n left: 0;\n top: -2px;\n bottom: -2px;\n width: 2px;\n background: var(--mj-status-error);\n z-index: 3;\n border-radius: 1px;\n}\n\n/* Locked indicator */\n.timeline-locked-indicator {\n position: absolute;\n left: -4px;\n font-size: 12px;\n color: var(--mj-color-warning-600);\n z-index: 4;\n animation: pulse-opacity 1.5s ease-in-out infinite;\n}\n\n@keyframes pulse-opacity {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.4; }\n}\n\n/* --------------------------------------------------------------------------\n Schedule Cards\n -------------------------------------------------------------------------- */\n\n.cards-section {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.schedule-card {\n display: flex;\n align-items: flex-start;\n background: var(--mj-bg-surface);\n border-radius: 12px;\n padding: 24px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);\n border: 1px solid var(--mj-border-default);\n transition: box-shadow 0.2s ease;\n gap: 32px;\n}\n\n.schedule-card:hover {\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);\n}\n\n.schedule-card-disabled {\n opacity: 0.6;\n}\n\n/* Card: Left (info) */\n.card-info {\n display: flex;\n align-items: flex-start;\n gap: 16px;\n min-width: 220px;\n flex-shrink: 0;\n}\n\n.card-icon {\n width: 48px;\n height: 48px;\n border-radius: 12px;\n background: var(--mj-status-info-bg);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.card-icon i {\n font-size: 20px;\n color: var(--mj-brand-primary);\n}\n\n.card-details {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.card-name {\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.card-company {\n font-size: 12px;\n color: var(--mj-text-disabled);\n}\n\n.card-badges {\n display: flex;\n gap: 6px;\n margin-top: 4px;\n flex-wrap: wrap;\n}\n\n.badge {\n font-size: 11px;\n font-weight: 500;\n padding: 2px 8px;\n border-radius: 10px;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.badge-success {\n background: var(--mj-status-success-bg);\n color: var(--mj-color-success-600);\n}\n\n.badge-gray {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-muted);\n}\n\n.badge-running {\n background: var(--mj-color-warning-100);\n color: var(--mj-color-warning-600);\n}\n\n/* Card: Center (config) */\n.card-config {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.config-row {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* Toggle switch */\n.toggle-label {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.toggle-switch {\n position: relative;\n width: 40px;\n height: 22px;\n border-radius: 11px;\n background: var(--mj-color-neutral-300);\n border: none;\n cursor: pointer;\n transition: background 0.2s ease;\n padding: 0;\n flex-shrink: 0;\n}\n\n.toggle-switch.toggle-on {\n background: var(--mj-brand-primary);\n}\n\n.toggle-knob {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: var(--mj-bg-surface);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);\n transition: transform 0.2s ease;\n}\n\n.toggle-on .toggle-knob {\n transform: translateX(18px);\n}\n\n/* Type pills */\n.type-pills {\n display: flex;\n gap: 4px;\n background: var(--mj-bg-surface-hover);\n border-radius: 8px;\n padding: 3px;\n width: fit-content;\n}\n\n.type-pill {\n padding: 5px 14px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n background: transparent;\n border: none;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.type-pill:hover {\n color: var(--mj-color-neutral-700);\n}\n\n.type-pill-active {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\n}\n\n/* Interval presets */\n.interval-presets {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.preset-btn {\n padding: 4px 12px;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n background: var(--mj-bg-surface-hover);\n border: 1px solid var(--mj-border-default);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.preset-btn:hover {\n border-color: var(--mj-text-disabled);\n color: var(--mj-color-neutral-700);\n}\n\n.preset-btn-active {\n background: var(--mj-status-info-bg);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n/* Custom interval input */\n.custom-interval {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 4px;\n}\n\n.interval-input {\n width: 100px;\n padding: 5px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 12px;\n color: var(--mj-color-neutral-700);\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.interval-input:focus {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1);\n}\n\n.interval-unit {\n font-size: 12px;\n color: var(--mj-text-disabled);\n}\n\n/* Cron config */\n.cron-presets {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.cron-input-row {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-top: 4px;\n flex-wrap: wrap;\n}\n\n.cron-input {\n width: 180px;\n padding: 5px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 12px;\n font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;\n color: var(--mj-color-neutral-700);\n outline: none;\n transition: border-color 0.15s ease;\n}\n\n.cron-input:focus {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1);\n}\n\n.cron-description {\n font-size: 12px;\n color: var(--mj-text-muted);\n font-style: italic;\n}\n\n/* Card: Right (status) */\n.card-status {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 16px;\n min-width: 180px;\n flex-shrink: 0;\n}\n\n.status-items {\n display: flex;\n flex-direction: column;\n gap: 8px;\n text-align: right;\n}\n\n.status-item {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.status-label {\n font-size: 11px;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.status-value {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-color-neutral-700);\n}\n\n.status-running {\n color: var(--mj-color-warning-600);\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 6px;\n}\n\n.status-idle {\n color: var(--mj-color-success-600);\n}\n\n/* Card actions */\n.card-actions {\n display: flex;\n gap: 8px;\n}\n\n.btn-save {\n animation: slideIn 0.2s ease;\n}\n\n@keyframes slideIn {\n from {\n opacity: 0;\n transform: translateX(8px);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n}\n\n.btn-run {\n font-size: 12px;\n padding: 6px 12px;\n}\n\n/* --------------------------------------------------------------------------\n Empty State\n -------------------------------------------------------------------------- */\n\n.empty-state {\n text-align: center;\n padding: 60px 24px;\n color: var(--mj-text-disabled);\n}\n\n.empty-state i {\n font-size: 48px;\n margin-bottom: 16px;\n display: block;\n}\n\n.empty-state p {\n font-size: 15px;\n margin: 0;\n}\n\n/* --------------------------------------------------------------------------\n Responsive\n -------------------------------------------------------------------------- */\n\n@media (max-width: 900px) {\n .schedule-card {\n flex-direction: column;\n gap: 20px;\n }\n\n .card-info {\n min-width: 0;\n width: 100%;\n }\n\n .card-config {\n width: 100%;\n }\n\n .card-status {\n align-items: flex-start;\n width: 100%;\n min-width: 0;\n }\n\n .status-items {\n text-align: left;\n flex-direction: row;\n gap: 20px;\n flex-wrap: wrap;\n }\n\n .status-running {\n justify-content: flex-start;\n }\n\n .card-actions {\n width: 100%;\n }\n\n .card-actions .btn {\n flex: 1;\n justify-content: center;\n }\n}\n\n@media (max-width: 768px) {\n .timeline-section {\n display: none;\n }\n\n .schedules-root {\n padding: 16px;\n }\n\n .section-header {\n flex-direction: column;\n align-items: flex-start;\n gap: 12px;\n }\n\n .header-left {\n gap: 8px;\n }\n}\n"] }]
|
|
836
|
+
}], null, null); })();
|
|
837
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SchedulesComponent, { className: "SchedulesComponent", filePath: "src/Integration/components/schedules/schedules.component.ts", lineNumber: 62 }); })();
|
|
838
|
+
export function LoadSchedulesComponent() {
|
|
839
|
+
// Tree-shaking prevention: importing this module causes
|
|
840
|
+
// @RegisterClass decorators to fire, registering components.
|
|
841
|
+
}
|
|
842
|
+
//# sourceMappingURL=schedules.component.js.map
|