@memberjunction/ng-user-routines 0.0.0 → 5.45.1
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/lib/my-routines-list.component.d.ts +119 -0
- package/dist/lib/my-routines-list.component.d.ts.map +1 -0
- package/dist/lib/my-routines-list.component.js +574 -0
- package/dist/lib/my-routines-list.component.js.map +1 -0
- package/dist/lib/new-routine.component.d.ts +182 -0
- package/dist/lib/new-routine.component.d.ts.map +1 -0
- package/dist/lib/new-routine.component.js +1070 -0
- package/dist/lib/new-routine.component.js.map +1 -0
- package/dist/lib/routine-history.component.d.ts +65 -0
- package/dist/lib/routine-history.component.d.ts.map +1 -0
- package/dist/lib/routine-history.component.js +290 -0
- package/dist/lib/routine-history.component.js.map +1 -0
- package/dist/lib/routine-target-catalog.d.ts +48 -0
- package/dist/lib/routine-target-catalog.d.ts.map +1 -0
- package/dist/lib/routine-target-catalog.js +59 -0
- package/dist/lib/routine-target-catalog.js.map +1 -0
- package/dist/lib/routine-ui-helpers.d.ts +49 -0
- package/dist/lib/routine-ui-helpers.d.ts.map +1 -0
- package/dist/lib/routine-ui-helpers.js +174 -0
- package/dist/lib/routine-ui-helpers.js.map +1 -0
- package/dist/lib/user-routines-command-center.component.d.ts +88 -0
- package/dist/lib/user-routines-command-center.component.d.ts.map +1 -0
- package/dist/lib/user-routines-command-center.component.js +372 -0
- package/dist/lib/user-routines-command-center.component.js.map +1 -0
- package/dist/lib/user-routines-events.d.ts +88 -0
- package/dist/lib/user-routines-events.d.ts.map +1 -0
- package/dist/lib/user-routines-events.js +97 -0
- package/dist/lib/user-routines-events.js.map +1 -0
- package/dist/lib/user-routines-slide-in.component.d.ts +57 -0
- package/dist/lib/user-routines-slide-in.component.d.ts.map +1 -0
- package/dist/lib/user-routines-slide-in.component.js +170 -0
- package/dist/lib/user-routines-slide-in.component.js.map +1 -0
- package/dist/lib/user-routines.module.d.ts +37 -0
- package/dist/lib/user-routines.module.d.ts.map +1 -0
- package/dist/lib/user-routines.module.js +113 -0
- package/dist/lib/user-routines.module.js.map +1 -0
- package/dist/public-api.d.ts +10 -0
- package/dist/public-api.d.ts.map +1 -0
- package/dist/public-api.js +19 -0
- package/dist/public-api.js.map +1 -0
- package/package.json +49 -7
- package/README.md +0 -45
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
import { ChangeDetectorRef, Component, EventEmitter, Input, Output, inject } from '@angular/core';
|
|
2
|
+
import { Subject } from 'rxjs';
|
|
3
|
+
import { takeUntil } from 'rxjs/operators';
|
|
4
|
+
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
5
|
+
import { UserRoutineEngine } from '@memberjunction/core-entities';
|
|
6
|
+
import { NormalizeUUID } from '@memberjunction/global';
|
|
7
|
+
import { MJNotificationService } from '@memberjunction/ng-notifications';
|
|
8
|
+
import { MJConfirmService } from '@memberjunction/ng-ui-components';
|
|
9
|
+
import { DescribeCronExpression } from '@memberjunction/global';
|
|
10
|
+
import { FormatRelativeTime, RoutineStatusVariant, RunStatusVariant } from './routine-ui-helpers';
|
|
11
|
+
import { LoadRoutineTargetCatalog } from './routine-target-catalog';
|
|
12
|
+
import { ConversationOpenedEventArgs, AfterRoutineDeletedEventArgs, AfterRoutinePausedEventArgs, AfterRoutineRunNowEventArgs, BeforeRoutineDeletedEventArgs, BeforeRoutinePausedEventArgs, BeforeRoutineRunNowEventArgs, RoutineSelectedEventArgs, } from './user-routines-events';
|
|
13
|
+
import * as i0 from "@angular/core";
|
|
14
|
+
import * as i1 from "@memberjunction/ng-ui-components";
|
|
15
|
+
import * as i2 from "@memberjunction/ng-shared-generic";
|
|
16
|
+
const _forTrack0 = ($index, $item) => $item.ID;
|
|
17
|
+
function MyRoutinesListComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
18
|
+
i0.ɵɵelementStart(0, "div", 0);
|
|
19
|
+
i0.ɵɵelement(1, "mj-loading", 4);
|
|
20
|
+
i0.ɵɵelementEnd();
|
|
21
|
+
} }
|
|
22
|
+
function MyRoutinesListComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
23
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
24
|
+
i0.ɵɵelementStart(0, "mj-empty-state", 5);
|
|
25
|
+
i0.ɵɵlistener("Action", function MyRoutinesListComponent_Conditional_1_Template_mj_empty_state_Action_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.CreateRequested.emit()); });
|
|
26
|
+
i0.ɵɵelementEnd();
|
|
27
|
+
} }
|
|
28
|
+
function MyRoutinesListComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
29
|
+
i0.ɵɵelement(0, "mj-empty-state", 2);
|
|
30
|
+
} }
|
|
31
|
+
function MyRoutinesListComponent_Conditional_3_For_2_Conditional_11_Template(rf, ctx) { if (rf & 1) {
|
|
32
|
+
i0.ɵɵelementStart(0, "p", 12);
|
|
33
|
+
i0.ɵɵtext(1);
|
|
34
|
+
i0.ɵɵelementEnd();
|
|
35
|
+
} if (rf & 2) {
|
|
36
|
+
const routine_r4 = i0.ɵɵnextContext().$implicit;
|
|
37
|
+
i0.ɵɵproperty("title", routine_r4.Description);
|
|
38
|
+
i0.ɵɵadvance();
|
|
39
|
+
i0.ɵɵtextInterpolate(routine_r4.Description);
|
|
40
|
+
} }
|
|
41
|
+
function MyRoutinesListComponent_Conditional_3_For_2_Conditional_25_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
42
|
+
i0.ɵɵelementStart(0, "span");
|
|
43
|
+
i0.ɵɵtext(1);
|
|
44
|
+
i0.ɵɵelementEnd();
|
|
45
|
+
} if (rf & 2) {
|
|
46
|
+
const routine_r4 = i0.ɵɵnextContext(2).$implicit;
|
|
47
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
48
|
+
i0.ɵɵclassMap(ctx_r1.ChipClass(ctx_r1.LastRunVariant(routine_r4.LastRunStatus)));
|
|
49
|
+
i0.ɵɵadvance();
|
|
50
|
+
i0.ɵɵtextInterpolate(routine_r4.LastRunStatus);
|
|
51
|
+
} }
|
|
52
|
+
function MyRoutinesListComponent_Conditional_3_For_2_Conditional_25_Template(rf, ctx) { if (rf & 1) {
|
|
53
|
+
i0.ɵɵelementStart(0, "span");
|
|
54
|
+
i0.ɵɵtext(1);
|
|
55
|
+
i0.ɵɵelementEnd();
|
|
56
|
+
i0.ɵɵconditionalCreate(2, MyRoutinesListComponent_Conditional_3_For_2_Conditional_25_Conditional_2_Template, 2, 3, "span", 33);
|
|
57
|
+
} if (rf & 2) {
|
|
58
|
+
const routine_r4 = i0.ɵɵnextContext().$implicit;
|
|
59
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
60
|
+
i0.ɵɵadvance();
|
|
61
|
+
i0.ɵɵtextInterpolate(ctx_r1.LastRunText(routine_r4));
|
|
62
|
+
i0.ɵɵadvance();
|
|
63
|
+
i0.ɵɵconditional(routine_r4.LastRunStatus ? 2 : -1);
|
|
64
|
+
} }
|
|
65
|
+
function MyRoutinesListComponent_Conditional_3_For_2_Conditional_26_Template(rf, ctx) { if (rf & 1) {
|
|
66
|
+
i0.ɵɵelementStart(0, "span", 20);
|
|
67
|
+
i0.ɵɵtext(1, "never");
|
|
68
|
+
i0.ɵɵelementEnd();
|
|
69
|
+
} }
|
|
70
|
+
function MyRoutinesListComponent_Conditional_3_For_2_Conditional_31_Template(rf, ctx) { if (rf & 1) {
|
|
71
|
+
i0.ɵɵelementStart(0, "span");
|
|
72
|
+
i0.ɵɵtext(1);
|
|
73
|
+
i0.ɵɵelementEnd();
|
|
74
|
+
} if (rf & 2) {
|
|
75
|
+
const routine_r4 = i0.ɵɵnextContext().$implicit;
|
|
76
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
77
|
+
i0.ɵɵadvance();
|
|
78
|
+
i0.ɵɵtextInterpolate(ctx_r1.NextRunText(routine_r4));
|
|
79
|
+
} }
|
|
80
|
+
function MyRoutinesListComponent_Conditional_3_For_2_Conditional_32_Template(rf, ctx) { if (rf & 1) {
|
|
81
|
+
i0.ɵɵelementStart(0, "span", 20);
|
|
82
|
+
i0.ɵɵtext(1, "not scheduled");
|
|
83
|
+
i0.ɵɵelementEnd();
|
|
84
|
+
} }
|
|
85
|
+
function MyRoutinesListComponent_Conditional_3_For_2_Conditional_45_Template(rf, ctx) { if (rf & 1) {
|
|
86
|
+
const _r5 = i0.ɵɵgetCurrentView();
|
|
87
|
+
i0.ɵɵelementStart(0, "button", 34);
|
|
88
|
+
i0.ɵɵlistener("click", function MyRoutinesListComponent_Conditional_3_For_2_Conditional_45_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r5); const routine_r4 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OpenConversation(routine_r4)); });
|
|
89
|
+
i0.ɵɵelement(1, "i", 35);
|
|
90
|
+
i0.ɵɵelementEnd();
|
|
91
|
+
} }
|
|
92
|
+
function MyRoutinesListComponent_Conditional_3_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
93
|
+
const _r3 = i0.ɵɵgetCurrentView();
|
|
94
|
+
i0.ɵɵelementStart(0, "div", 6)(1, "div", 7)(2, "div", 8);
|
|
95
|
+
i0.ɵɵelement(3, "i");
|
|
96
|
+
i0.ɵɵelementEnd();
|
|
97
|
+
i0.ɵɵelementStart(4, "div", 9)(5, "h3", 10);
|
|
98
|
+
i0.ɵɵtext(6);
|
|
99
|
+
i0.ɵɵelementEnd();
|
|
100
|
+
i0.ɵɵelementStart(7, "div", 11);
|
|
101
|
+
i0.ɵɵtext(8);
|
|
102
|
+
i0.ɵɵelementEnd()();
|
|
103
|
+
i0.ɵɵelementStart(9, "span");
|
|
104
|
+
i0.ɵɵtext(10);
|
|
105
|
+
i0.ɵɵelementEnd()();
|
|
106
|
+
i0.ɵɵconditionalCreate(11, MyRoutinesListComponent_Conditional_3_For_2_Conditional_11_Template, 2, 2, "p", 12);
|
|
107
|
+
i0.ɵɵelementStart(12, "div", 13)(13, "div", 14);
|
|
108
|
+
i0.ɵɵelement(14, "i", 15);
|
|
109
|
+
i0.ɵɵelementStart(15, "span", 16);
|
|
110
|
+
i0.ɵɵtext(16);
|
|
111
|
+
i0.ɵɵelementEnd();
|
|
112
|
+
i0.ɵɵelementStart(17, "span", 17);
|
|
113
|
+
i0.ɵɵtext(18);
|
|
114
|
+
i0.ɵɵelementEnd();
|
|
115
|
+
i0.ɵɵelementStart(19, "span", 18);
|
|
116
|
+
i0.ɵɵtext(20);
|
|
117
|
+
i0.ɵɵelementEnd()();
|
|
118
|
+
i0.ɵɵelementStart(21, "div", 14);
|
|
119
|
+
i0.ɵɵelement(22, "i", 19);
|
|
120
|
+
i0.ɵɵelementStart(23, "span");
|
|
121
|
+
i0.ɵɵtext(24, "Last run:");
|
|
122
|
+
i0.ɵɵelementEnd();
|
|
123
|
+
i0.ɵɵconditionalCreate(25, MyRoutinesListComponent_Conditional_3_For_2_Conditional_25_Template, 3, 2)(26, MyRoutinesListComponent_Conditional_3_For_2_Conditional_26_Template, 2, 0, "span", 20);
|
|
124
|
+
i0.ɵɵelementEnd();
|
|
125
|
+
i0.ɵɵelementStart(27, "div", 14);
|
|
126
|
+
i0.ɵɵelement(28, "i", 21);
|
|
127
|
+
i0.ɵɵelementStart(29, "span");
|
|
128
|
+
i0.ɵɵtext(30, "Next run:");
|
|
129
|
+
i0.ɵɵelementEnd();
|
|
130
|
+
i0.ɵɵconditionalCreate(31, MyRoutinesListComponent_Conditional_3_For_2_Conditional_31_Template, 2, 1, "span")(32, MyRoutinesListComponent_Conditional_3_For_2_Conditional_32_Template, 2, 0, "span", 20);
|
|
131
|
+
i0.ɵɵelementEnd()();
|
|
132
|
+
i0.ɵɵelementStart(33, "div", 22)(34, "button", 23);
|
|
133
|
+
i0.ɵɵlistener("click", function MyRoutinesListComponent_Conditional_3_For_2_Template_button_click_34_listener() { const routine_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.RunNow(routine_r4)); });
|
|
134
|
+
i0.ɵɵelement(35, "i", 24);
|
|
135
|
+
i0.ɵɵtext(36, " Run now ");
|
|
136
|
+
i0.ɵɵelementEnd();
|
|
137
|
+
i0.ɵɵelementStart(37, "button", 23);
|
|
138
|
+
i0.ɵɵlistener("click", function MyRoutinesListComponent_Conditional_3_For_2_Template_button_click_37_listener() { const routine_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.ToggleStatus(routine_r4)); });
|
|
139
|
+
i0.ɵɵelement(38, "i", 15);
|
|
140
|
+
i0.ɵɵtext(39);
|
|
141
|
+
i0.ɵɵelementEnd();
|
|
142
|
+
i0.ɵɵelement(40, "span", 25);
|
|
143
|
+
i0.ɵɵelementStart(41, "button", 26);
|
|
144
|
+
i0.ɵɵlistener("click", function MyRoutinesListComponent_Conditional_3_For_2_Template_button_click_41_listener() { const routine_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.EditRequested.emit(routine_r4)); });
|
|
145
|
+
i0.ɵɵelement(42, "i", 27);
|
|
146
|
+
i0.ɵɵelementEnd();
|
|
147
|
+
i0.ɵɵelementStart(43, "button", 28);
|
|
148
|
+
i0.ɵɵlistener("click", function MyRoutinesListComponent_Conditional_3_For_2_Template_button_click_43_listener() { const routine_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.OpenHistory(routine_r4)); });
|
|
149
|
+
i0.ɵɵelement(44, "i", 29);
|
|
150
|
+
i0.ɵɵelementEnd();
|
|
151
|
+
i0.ɵɵconditionalCreate(45, MyRoutinesListComponent_Conditional_3_For_2_Conditional_45_Template, 2, 0, "button", 30);
|
|
152
|
+
i0.ɵɵelementStart(46, "button", 31);
|
|
153
|
+
i0.ɵɵlistener("click", function MyRoutinesListComponent_Conditional_3_For_2_Template_button_click_46_listener() { const routine_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.DeleteRoutine(routine_r4)); });
|
|
154
|
+
i0.ɵɵelement(47, "i", 32);
|
|
155
|
+
i0.ɵɵelementEnd()()();
|
|
156
|
+
} if (rf & 2) {
|
|
157
|
+
const routine_r4 = ctx.$implicit;
|
|
158
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
159
|
+
i0.ɵɵadvance(2);
|
|
160
|
+
i0.ɵɵproperty("title", ctx_r1.AgentName(routine_r4));
|
|
161
|
+
i0.ɵɵadvance();
|
|
162
|
+
i0.ɵɵclassMap(ctx_r1.AgentIcon(routine_r4));
|
|
163
|
+
i0.ɵɵadvance(2);
|
|
164
|
+
i0.ɵɵproperty("title", routine_r4.Name);
|
|
165
|
+
i0.ɵɵadvance();
|
|
166
|
+
i0.ɵɵtextInterpolate(routine_r4.Name);
|
|
167
|
+
i0.ɵɵadvance();
|
|
168
|
+
i0.ɵɵproperty("title", ctx_r1.AgentName(routine_r4));
|
|
169
|
+
i0.ɵɵadvance();
|
|
170
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.AgentName(routine_r4), " ");
|
|
171
|
+
i0.ɵɵadvance();
|
|
172
|
+
i0.ɵɵclassMap(ctx_r1.ChipClass(ctx_r1.StatusVariant(routine_r4.Status)));
|
|
173
|
+
i0.ɵɵadvance();
|
|
174
|
+
i0.ɵɵtextInterpolate(routine_r4.Status);
|
|
175
|
+
i0.ɵɵadvance();
|
|
176
|
+
i0.ɵɵconditional(routine_r4.Description ? 11 : -1);
|
|
177
|
+
i0.ɵɵadvance(3);
|
|
178
|
+
i0.ɵɵclassProp("fa-clock", routine_r4.RoutineType === "Scheduled")("fa-eye", routine_r4.RoutineType === "Monitoring");
|
|
179
|
+
i0.ɵɵadvance(2);
|
|
180
|
+
i0.ɵɵtextInterpolate(routine_r4.RoutineType);
|
|
181
|
+
i0.ɵɵadvance();
|
|
182
|
+
i0.ɵɵproperty("title", routine_r4.CronExpression);
|
|
183
|
+
i0.ɵɵadvance();
|
|
184
|
+
i0.ɵɵtextInterpolate(ctx_r1.DescribeCron(routine_r4.CronExpression));
|
|
185
|
+
i0.ɵɵadvance(2);
|
|
186
|
+
i0.ɵɵtextInterpolate1("(", routine_r4.Timezone, ")");
|
|
187
|
+
i0.ɵɵadvance(5);
|
|
188
|
+
i0.ɵɵconditional(routine_r4.LastRunAt ? 25 : 26);
|
|
189
|
+
i0.ɵɵadvance(6);
|
|
190
|
+
i0.ɵɵconditional(routine_r4.NextRunAt ? 31 : 32);
|
|
191
|
+
i0.ɵɵadvance(3);
|
|
192
|
+
i0.ɵɵproperty("disabled", ctx_r1.RunNowDisabled(routine_r4))("title", routine_r4.Status === "Active" ? "Queue a run now (dispatcher picks it up within a minute)" : "Only Active routines can be run");
|
|
193
|
+
i0.ɵɵadvance(3);
|
|
194
|
+
i0.ɵɵproperty("disabled", ctx_r1.BusyRoutineIDs.has(routine_r4.ID))("title", routine_r4.Status === "Active" ? "Pause this routine" : "Resume this routine");
|
|
195
|
+
i0.ɵɵadvance();
|
|
196
|
+
i0.ɵɵclassProp("fa-circle-pause", routine_r4.Status === "Active")("fa-circle-play", routine_r4.Status !== "Active");
|
|
197
|
+
i0.ɵɵadvance();
|
|
198
|
+
i0.ɵɵtextInterpolate1(" ", routine_r4.Status === "Active" ? "Pause" : "Resume", " ");
|
|
199
|
+
i0.ɵɵadvance(6);
|
|
200
|
+
i0.ɵɵconditional(routine_r4.ConversationID ? 45 : -1);
|
|
201
|
+
i0.ɵɵadvance();
|
|
202
|
+
i0.ɵɵproperty("disabled", ctx_r1.BusyRoutineIDs.has(routine_r4.ID));
|
|
203
|
+
} }
|
|
204
|
+
function MyRoutinesListComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
205
|
+
i0.ɵɵelementStart(0, "div", 3);
|
|
206
|
+
i0.ɵɵrepeaterCreate(1, MyRoutinesListComponent_Conditional_3_For_2_Template, 48, 32, "div", 6, _forTrack0);
|
|
207
|
+
i0.ɵɵelementEnd();
|
|
208
|
+
} if (rf & 2) {
|
|
209
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
210
|
+
i0.ɵɵadvance();
|
|
211
|
+
i0.ɵɵrepeater(ctx_r1.FilteredRoutines);
|
|
212
|
+
} }
|
|
213
|
+
/**
|
|
214
|
+
* "My Routines" — card list of the CURRENT USER's routines with per-card quick actions
|
|
215
|
+
* (run now / pause-resume / edit / history / delete). Data comes from the shared
|
|
216
|
+
* {@link UserRoutineEngine} cache — no ad-hoc RunViews.
|
|
217
|
+
*
|
|
218
|
+
* Reusable (Generic) component: no Router, no page chrome. Mutating quick actions raise
|
|
219
|
+
* cancelable `Before*` events (set `Cancel = true` to veto) followed by informational
|
|
220
|
+
* `After*` events, per the standard MJ Before/After event idiom.
|
|
221
|
+
*
|
|
222
|
+
* Run-now v1 semantics: sets `NextRunAt = now` and saves — the User Routine Dispatcher
|
|
223
|
+
* job claims due routines on its next sweep (within a minute).
|
|
224
|
+
*/
|
|
225
|
+
export class MyRoutinesListComponent extends BaseAngularComponent {
|
|
226
|
+
cdr = inject(ChangeDetectorRef);
|
|
227
|
+
notifications = inject(MJNotificationService);
|
|
228
|
+
confirm = inject(MJConfirmService);
|
|
229
|
+
destroy$ = new Subject();
|
|
230
|
+
// ---------------------------------------------------------------
|
|
231
|
+
// Inputs (getter/setter pattern — re-filter on change)
|
|
232
|
+
// ---------------------------------------------------------------
|
|
233
|
+
_searchText = '';
|
|
234
|
+
/** Free-text filter over routine name, description, and agent name. */
|
|
235
|
+
get SearchText() {
|
|
236
|
+
return this._searchText;
|
|
237
|
+
}
|
|
238
|
+
set SearchText(value) {
|
|
239
|
+
if (value !== this._searchText) {
|
|
240
|
+
this._searchText = value ?? '';
|
|
241
|
+
this.applyFilters();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
_statusFilter = 'all';
|
|
245
|
+
/** Status facet filter. */
|
|
246
|
+
get StatusFilter() {
|
|
247
|
+
return this._statusFilter;
|
|
248
|
+
}
|
|
249
|
+
set StatusFilter(value) {
|
|
250
|
+
if (value !== this._statusFilter) {
|
|
251
|
+
this._statusFilter = value ?? 'all';
|
|
252
|
+
this.applyFilters();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// ---------------------------------------------------------------
|
|
256
|
+
// Outputs — navigation intents
|
|
257
|
+
// ---------------------------------------------------------------
|
|
258
|
+
/** User asked to create a new routine (empty-state CTA). The host opens the editor. */
|
|
259
|
+
CreateRequested = new EventEmitter();
|
|
260
|
+
/** User asked to edit a routine. The host opens the editor. */
|
|
261
|
+
EditRequested = new EventEmitter();
|
|
262
|
+
/** User asked to view a routine's run history. The host opens the history view. */
|
|
263
|
+
HistoryRequested = new EventEmitter();
|
|
264
|
+
/** Informational: a routine was selected (history/detail opened). */
|
|
265
|
+
RoutineSelected = new EventEmitter();
|
|
266
|
+
/** Loaded/filtered counts changed (load, filter, refresh). */
|
|
267
|
+
CountsChanged = new EventEmitter();
|
|
268
|
+
// ---------------------------------------------------------------
|
|
269
|
+
// Outputs — cancelable Before/After event pairs
|
|
270
|
+
// ---------------------------------------------------------------
|
|
271
|
+
/** Cancelable: fires before a pause/resume Status toggle is saved. */
|
|
272
|
+
BeforeRoutinePaused = new EventEmitter();
|
|
273
|
+
/** Informational: fires after a pause/resume Status toggle was saved. */
|
|
274
|
+
AfterRoutinePaused = new EventEmitter();
|
|
275
|
+
/** Cancelable: fires before a routine is queued to run now. */
|
|
276
|
+
BeforeRoutineRunNow = new EventEmitter();
|
|
277
|
+
/** Informational: fires after a routine was queued to run now. */
|
|
278
|
+
AfterRoutineRunNow = new EventEmitter();
|
|
279
|
+
/** Cancelable: fires before a routine (and its recipients) is deleted. */
|
|
280
|
+
BeforeRoutineDeleted = new EventEmitter();
|
|
281
|
+
/** Informational: fires after a routine was deleted. */
|
|
282
|
+
AfterRoutineDeleted = new EventEmitter();
|
|
283
|
+
// ---------------------------------------------------------------
|
|
284
|
+
// State
|
|
285
|
+
// ---------------------------------------------------------------
|
|
286
|
+
Routines = [];
|
|
287
|
+
FilteredRoutines = [];
|
|
288
|
+
IsLoading = false;
|
|
289
|
+
/** IDs of routines with an in-flight save/delete (disables that card's action buttons). */
|
|
290
|
+
BusyRoutineIDs = new Set();
|
|
291
|
+
agentNameByID = new Map();
|
|
292
|
+
agentIconByID = new Map();
|
|
293
|
+
// Template helpers (bound, so keep as instance members)
|
|
294
|
+
DescribeCron = DescribeCronExpression;
|
|
295
|
+
RelativeTime = FormatRelativeTime;
|
|
296
|
+
StatusVariant = RoutineStatusVariant;
|
|
297
|
+
LastRunVariant = RunStatusVariant;
|
|
298
|
+
async ngOnInit() {
|
|
299
|
+
this.IsLoading = true;
|
|
300
|
+
this.cdr.markForCheck();
|
|
301
|
+
try {
|
|
302
|
+
const engine = this.engine();
|
|
303
|
+
const p = this.ProviderToUse;
|
|
304
|
+
const [, catalog] = await Promise.all([
|
|
305
|
+
engine.Config(false, p.CurrentUser, p),
|
|
306
|
+
LoadRoutineTargetCatalog(p),
|
|
307
|
+
]);
|
|
308
|
+
this.agentNameByID = catalog.NameByID;
|
|
309
|
+
this.agentIconByID = catalog.IconByID;
|
|
310
|
+
// Reactive: re-render whenever the engine's routines cache mutates
|
|
311
|
+
engine.Routines$.pipe(takeUntil(this.destroy$)).subscribe(() => {
|
|
312
|
+
this.Routines = engine.Routines;
|
|
313
|
+
this.applyFilters();
|
|
314
|
+
});
|
|
315
|
+
this.Routines = engine.Routines;
|
|
316
|
+
this.applyFilters();
|
|
317
|
+
}
|
|
318
|
+
finally {
|
|
319
|
+
this.IsLoading = false;
|
|
320
|
+
this.cdr.markForCheck();
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Snapshot of the time-relative strings, keyed by routine ID. Templates must NOT
|
|
325
|
+
* call FormatRelativeTime directly — its value can change between check passes at
|
|
326
|
+
* a minute boundary (NG0100). Rebuilt on every data change and on a 30s timer.
|
|
327
|
+
*/
|
|
328
|
+
relativeText = new Map();
|
|
329
|
+
relativeTimer = null;
|
|
330
|
+
/** Stable-within-a-pass "Last run" text for a routine. */
|
|
331
|
+
LastRunText(routine) {
|
|
332
|
+
return this.relativeText.get(routine.ID)?.Last ?? '';
|
|
333
|
+
}
|
|
334
|
+
/** Stable-within-a-pass "Next run" text for a routine. */
|
|
335
|
+
NextRunText(routine) {
|
|
336
|
+
return this.relativeText.get(routine.ID)?.Next ?? '';
|
|
337
|
+
}
|
|
338
|
+
rebuildRelativeText() {
|
|
339
|
+
this.relativeText = new Map(this.Routines.map((r) => [r.ID, {
|
|
340
|
+
Last: r.LastRunAt ? FormatRelativeTime(r.LastRunAt) : '',
|
|
341
|
+
Next: r.NextRunAt ? FormatRelativeTime(r.NextRunAt) : '',
|
|
342
|
+
}]));
|
|
343
|
+
if (this.relativeTimer == null && this.Routines.length > 0) {
|
|
344
|
+
this.relativeTimer = setInterval(() => {
|
|
345
|
+
this.rebuildRelativeText();
|
|
346
|
+
this.cdr.markForCheck();
|
|
347
|
+
}, 30_000);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
ngOnDestroy() {
|
|
351
|
+
if (this.relativeTimer != null) {
|
|
352
|
+
clearInterval(this.relativeTimer);
|
|
353
|
+
}
|
|
354
|
+
this.destroy$.next();
|
|
355
|
+
this.destroy$.complete();
|
|
356
|
+
}
|
|
357
|
+
/** Force-refreshes the engine cache (and this list). */
|
|
358
|
+
async Refresh() {
|
|
359
|
+
this.IsLoading = true;
|
|
360
|
+
this.cdr.markForCheck();
|
|
361
|
+
try {
|
|
362
|
+
await this.engine().Refresh(this.ProviderToUse.CurrentUser);
|
|
363
|
+
this.Routines = this.engine().Routines;
|
|
364
|
+
this.applyFilters();
|
|
365
|
+
}
|
|
366
|
+
finally {
|
|
367
|
+
this.IsLoading = false;
|
|
368
|
+
this.cdr.markForCheck();
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
/** Display name of a routine's target agent (falls back when the agent no longer exists). */
|
|
372
|
+
AgentName(routine) {
|
|
373
|
+
return this.agentNameByID.get(NormalizeUUID(routine.TargetID)) ?? '(unknown agent)';
|
|
374
|
+
}
|
|
375
|
+
/** The agent's own IconClass, falling back to the standard robot mark. */
|
|
376
|
+
AgentIcon(routine) {
|
|
377
|
+
return this.agentIconByID.get(NormalizeUUID(routine.TargetID)) ?? 'fa-solid fa-robot';
|
|
378
|
+
}
|
|
379
|
+
// ---------------------------------------------------------------
|
|
380
|
+
// Quick actions (Before → save → After)
|
|
381
|
+
// ---------------------------------------------------------------
|
|
382
|
+
/** Toggles Active ⇄ Paused (Disabled routines resume to Active as well). */
|
|
383
|
+
async ToggleStatus(routine) {
|
|
384
|
+
if (this.BusyRoutineIDs.has(routine.ID)) {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
const previous = routine.Status;
|
|
388
|
+
const newStatus = previous === 'Active' ? 'Paused' : 'Active';
|
|
389
|
+
const beforeArgs = new BeforeRoutinePausedEventArgs(routine, newStatus);
|
|
390
|
+
this.BeforeRoutinePaused.emit(beforeArgs);
|
|
391
|
+
if (beforeArgs.Cancel) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
routine.Status = newStatus;
|
|
395
|
+
const saved = await this.saveRoutine(routine, newStatus === 'Active' ? `'${routine.Name}' resumed` : `'${routine.Name}' paused`, () => (routine.Status = previous));
|
|
396
|
+
if (saved) {
|
|
397
|
+
this.AfterRoutinePaused.emit(new AfterRoutinePausedEventArgs(routine, newStatus));
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Run-now v1: stamps NextRunAt = now and saves; the dispatcher job claims due
|
|
402
|
+
* routines on its next sweep. Only meaningful for Active routines.
|
|
403
|
+
*/
|
|
404
|
+
async RunNow(routine) {
|
|
405
|
+
if (this.BusyRoutineIDs.has(routine.ID) || routine.Status !== 'Active') {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
const beforeArgs = new BeforeRoutineRunNowEventArgs(routine);
|
|
409
|
+
this.BeforeRoutineRunNow.emit(beforeArgs);
|
|
410
|
+
if (beforeArgs.Cancel) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
const previous = routine.NextRunAt;
|
|
414
|
+
routine.NextRunAt = new Date();
|
|
415
|
+
const saved = await this.saveRoutine(routine, `'${routine.Name}' queued to run — the dispatcher picks it up within the next minute.`, () => (routine.NextRunAt = previous));
|
|
416
|
+
if (saved) {
|
|
417
|
+
this.AfterRoutineRunNow.emit(new AfterRoutineRunNowEventArgs(routine));
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
/** Deletes a routine (after user confirmation), removing its recipients first. */
|
|
421
|
+
async DeleteRoutine(routine) {
|
|
422
|
+
if (this.BusyRoutineIDs.has(routine.ID)) {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
const beforeArgs = new BeforeRoutineDeletedEventArgs(routine);
|
|
426
|
+
this.BeforeRoutineDeleted.emit(beforeArgs);
|
|
427
|
+
if (beforeArgs.Cancel) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
const confirmed = await this.confirm.ConfirmDelete({
|
|
431
|
+
title: 'Delete Routine',
|
|
432
|
+
message: `Delete '${routine.Name}'? Its recipients and run bookkeeping are removed too — the underlying agent/prompt/action run records remain.`,
|
|
433
|
+
});
|
|
434
|
+
if (!confirmed) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
this.BusyRoutineIDs.add(routine.ID);
|
|
438
|
+
this.cdr.markForCheck();
|
|
439
|
+
try {
|
|
440
|
+
const engine = this.engine();
|
|
441
|
+
// The server-side entity subclass cascades recipients + run bookkeeping
|
|
442
|
+
// before the routine row (FK cleanup) — one Delete() is the whole story.
|
|
443
|
+
const deleted = await routine.Delete();
|
|
444
|
+
if (!deleted) {
|
|
445
|
+
this.notifications.CreateSimpleNotification(`Delete failed: ${routine.LatestResult?.CompleteMessage ?? 'unknown error'}`, 'error', 6000);
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
this.notifications.CreateSimpleNotification(`'${routine.Name}' deleted`, 'success', 3000);
|
|
449
|
+
// Full refresh so the dependent recipient/run configs re-scope
|
|
450
|
+
await engine.Refresh(this.ProviderToUse.CurrentUser);
|
|
451
|
+
this.Routines = engine.Routines;
|
|
452
|
+
this.applyFilters();
|
|
453
|
+
this.AfterRoutineDeleted.emit(new AfterRoutineDeletedEventArgs(routine));
|
|
454
|
+
}
|
|
455
|
+
finally {
|
|
456
|
+
this.BusyRoutineIDs.delete(routine.ID);
|
|
457
|
+
this.cdr.markForCheck();
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
/** The routine's dedicated conversation was requested (chat icon on the card). */
|
|
461
|
+
ConversationOpened = new EventEmitter();
|
|
462
|
+
/** Raises ConversationOpened for hosts to present the routine's conversation. */
|
|
463
|
+
OpenConversation(routine) {
|
|
464
|
+
if (routine.ConversationID) {
|
|
465
|
+
this.ConversationOpened.emit(new ConversationOpenedEventArgs(routine.ConversationID, routine));
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
/** Opens a routine's history (raises RoutineSelected + HistoryRequested). */
|
|
469
|
+
OpenHistory(routine) {
|
|
470
|
+
this.RoutineSelected.emit(new RoutineSelectedEventArgs(routine));
|
|
471
|
+
this.HistoryRequested.emit(routine);
|
|
472
|
+
}
|
|
473
|
+
/** True when the run-now button should be disabled for this routine. */
|
|
474
|
+
RunNowDisabled(routine) {
|
|
475
|
+
return routine.Status !== 'Active' || this.BusyRoutineIDs.has(routine.ID);
|
|
476
|
+
}
|
|
477
|
+
/** Chip variant helper narrowed for the template. */
|
|
478
|
+
ChipClass(variant) {
|
|
479
|
+
return `routine-chip routine-chip--${variant}`;
|
|
480
|
+
}
|
|
481
|
+
// ---------------------------------------------------------------
|
|
482
|
+
// Internals
|
|
483
|
+
// ---------------------------------------------------------------
|
|
484
|
+
engine() {
|
|
485
|
+
return UserRoutineEngine.GetProviderInstance(this.ProviderToUse, UserRoutineEngine);
|
|
486
|
+
}
|
|
487
|
+
/** Saves a routine, surfacing LatestResult.CompleteMessage and reverting on failure. */
|
|
488
|
+
async saveRoutine(routine, successMessage, revert) {
|
|
489
|
+
this.BusyRoutineIDs.add(routine.ID);
|
|
490
|
+
this.cdr.markForCheck();
|
|
491
|
+
try {
|
|
492
|
+
const saved = await routine.Save();
|
|
493
|
+
if (saved) {
|
|
494
|
+
this.notifications.CreateSimpleNotification(successMessage, 'success', 3500);
|
|
495
|
+
this.applyFilters();
|
|
496
|
+
return true;
|
|
497
|
+
}
|
|
498
|
+
revert();
|
|
499
|
+
this.notifications.CreateSimpleNotification(`Save failed: ${routine.LatestResult?.CompleteMessage ?? 'unknown error'}`, 'error', 6000);
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
finally {
|
|
503
|
+
this.BusyRoutineIDs.delete(routine.ID);
|
|
504
|
+
this.cdr.markForCheck();
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
applyFilters() {
|
|
508
|
+
this.rebuildRelativeText();
|
|
509
|
+
const search = this._searchText.trim().toLowerCase();
|
|
510
|
+
this.FilteredRoutines = this.Routines.filter((r) => {
|
|
511
|
+
if (this._statusFilter !== 'all' && r.Status !== this._statusFilter) {
|
|
512
|
+
return false;
|
|
513
|
+
}
|
|
514
|
+
if (search.length > 0) {
|
|
515
|
+
const haystack = `${r.Name ?? ''} ${r.Description ?? ''} ${this.AgentName(r)}`.toLowerCase();
|
|
516
|
+
if (!haystack.includes(search)) {
|
|
517
|
+
return false;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return true;
|
|
521
|
+
});
|
|
522
|
+
this.CountsChanged.emit({ Filtered: this.FilteredRoutines.length, Total: this.Routines.length });
|
|
523
|
+
this.cdr.markForCheck();
|
|
524
|
+
}
|
|
525
|
+
static ɵfac = /*@__PURE__*/ (() => { let ɵMyRoutinesListComponent_BaseFactory; return function MyRoutinesListComponent_Factory(__ngFactoryType__) { return (ɵMyRoutinesListComponent_BaseFactory || (ɵMyRoutinesListComponent_BaseFactory = i0.ɵɵgetInheritedFactory(MyRoutinesListComponent)))(__ngFactoryType__ || MyRoutinesListComponent); }; })();
|
|
526
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MyRoutinesListComponent, selectors: [["mj-my-routines-list"]], inputs: { SearchText: "SearchText", StatusFilter: "StatusFilter" }, outputs: { CreateRequested: "CreateRequested", EditRequested: "EditRequested", HistoryRequested: "HistoryRequested", RoutineSelected: "RoutineSelected", CountsChanged: "CountsChanged", BeforeRoutinePaused: "BeforeRoutinePaused", AfterRoutinePaused: "AfterRoutinePaused", BeforeRoutineRunNow: "BeforeRoutineRunNow", AfterRoutineRunNow: "AfterRoutineRunNow", BeforeRoutineDeleted: "BeforeRoutineDeleted", AfterRoutineDeleted: "AfterRoutineDeleted", ConversationOpened: "ConversationOpened" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 4, vars: 4, consts: [[1, "routines-loading"], ["Size", "large", "Icon", "fa-solid fa-business-time", "Title", "No Routines Yet", "Message", "Routines run an AI agent for you on a schedule \u2014 and can watch for changes and notify you.", "ActionText", "Create Your First Routine", "ActionIcon", "fa-solid fa-plus"], ["Variant", "no-results", "Icon", "fa-solid fa-filter-circle-xmark", "Title", "No Matching Routines", "Message", "No routines match your current search or filters."], [1, "routines-grid"], ["text", "Loading routines...", "size", "medium"], ["Size", "large", "Icon", "fa-solid fa-business-time", "Title", "No Routines Yet", "Message", "Routines run an AI agent for you on a schedule \u2014 and can watch for changes and notify you.", "ActionText", "Create Your First Routine", "ActionIcon", "fa-solid fa-plus", 3, "Action"], [1, "routine-card"], [1, "routine-card-header"], [1, "routine-target-icon", 3, "title"], [1, "routine-card-title"], [1, "routine-name", 3, "title"], [1, "routine-target-line", 3, "title"], [1, "routine-description", 3, "title"], [1, "routine-meta"], [1, "routine-meta-row"], [1, "fa-solid"], [1, "routine-type-badge"], [1, "routine-cron", 3, "title"], [1, "routine-tz"], [1, "fa-solid", "fa-backward-step"], [1, "routine-meta-muted"], [1, "fa-solid", "fa-forward-step"], [1, "routine-card-actions"], ["mjButton", "", "variant", "outline", "size", "sm", 3, "click", "disabled", "title"], [1, "fa-solid", "fa-play"], [1, "routine-actions-spacer"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Edit routine", 3, "click"], [1, "fa-solid", "fa-pen"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "View run history", 3, "click"], [1, "fa-solid", "fa-timeline"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Open this routine's conversation"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Delete routine", 3, "click", "disabled"], [1, "fa-solid", "fa-trash-can"], [3, "class"], ["mjButton", "", "variant", "flat", "size", "sm", "title", "Open this routine's conversation", 3, "click"], [1, "fa-solid", "fa-comments"]], template: function MyRoutinesListComponent_Template(rf, ctx) { if (rf & 1) {
|
|
527
|
+
i0.ɵɵconditionalCreate(0, MyRoutinesListComponent_Conditional_0_Template, 2, 0, "div", 0);
|
|
528
|
+
i0.ɵɵconditionalCreate(1, MyRoutinesListComponent_Conditional_1_Template, 1, 0, "mj-empty-state", 1);
|
|
529
|
+
i0.ɵɵconditionalCreate(2, MyRoutinesListComponent_Conditional_2_Template, 1, 0, "mj-empty-state", 2);
|
|
530
|
+
i0.ɵɵconditionalCreate(3, MyRoutinesListComponent_Conditional_3_Template, 3, 0, "div", 3);
|
|
531
|
+
} if (rf & 2) {
|
|
532
|
+
i0.ɵɵconditional(ctx.IsLoading ? 0 : -1);
|
|
533
|
+
i0.ɵɵadvance();
|
|
534
|
+
i0.ɵɵconditional(!ctx.IsLoading && ctx.Routines.length === 0 ? 1 : -1);
|
|
535
|
+
i0.ɵɵadvance();
|
|
536
|
+
i0.ɵɵconditional(!ctx.IsLoading && ctx.Routines.length > 0 && ctx.FilteredRoutines.length === 0 ? 2 : -1);
|
|
537
|
+
i0.ɵɵadvance();
|
|
538
|
+
i0.ɵɵconditional(!ctx.IsLoading && ctx.FilteredRoutines.length > 0 ? 3 : -1);
|
|
539
|
+
} }, dependencies: [i1.MJButtonDirective, i1.MJEmptyStateComponent, i2.LoadingComponent], styles: ["[_nghost-%COMP%] {\n display: block;\n}\n\n.routines-loading[_ngcontent-%COMP%] {\n display: flex;\n justify-content: center;\n padding: 48px 0;\n}\n\n.routines-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));\n gap: 16px;\n}\n\n.routine-card[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 16px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 10px);\n transition: box-shadow 0.15s ease, border-color 0.15s ease;\n}\n\n.routine-card[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-border-strong);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);\n}\n\n.routine-card-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.routine-target-icon[_ngcontent-%COMP%] {\n flex: 0 0 auto;\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--mj-radius-md, 8px);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 16px;\n}\n\n.routine-card-title[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.routine-name[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.routine-target-line[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.routine-description[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 13px;\n color: var(--mj-text-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.routine-meta[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n}\n\n.routine-meta-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n}\n\n.routine-meta-row[_ngcontent-%COMP%] > i[_ngcontent-%COMP%] {\n width: 14px;\n text-align: center;\n color: var(--mj-text-muted);\n}\n\n.routine-type-badge[_ngcontent-%COMP%] {\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.routine-cron[_ngcontent-%COMP%] {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.routine-tz[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n}\n\n.routine-meta-muted[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-style: italic;\n}\n\n.routine-card-actions[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: auto;\n padding-top: 8px;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.routine-actions-spacer[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n}\n\n\n\n.routine-chip[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n border-radius: var(--mj-radius-full, 999px);\n font-size: 11px;\n font-weight: 600;\n line-height: 1.5;\n white-space: nowrap;\n}\n\n.routine-chip--success[_ngcontent-%COMP%] {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n border: 1px solid var(--mj-status-success-border);\n}\n\n.routine-chip--warning[_ngcontent-%COMP%] {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning-border);\n}\n\n.routine-chip--error[_ngcontent-%COMP%] {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n border: 1px solid var(--mj-status-error-border);\n}\n\n.routine-chip--info[_ngcontent-%COMP%] {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n border: 1px solid var(--mj-status-info-border);\n}\n\n.routine-chip--muted[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n border: 1px solid var(--mj-border-default);\n}"] });
|
|
540
|
+
}
|
|
541
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyRoutinesListComponent, [{
|
|
542
|
+
type: Component,
|
|
543
|
+
args: [{ standalone: false, selector: 'mj-my-routines-list', template: "<!-- Loading -->\n@if (IsLoading) {\n <div class=\"routines-loading\">\n <mj-loading text=\"Loading routines...\" size=\"medium\"></mj-loading>\n </div>\n}\n\n<!-- Empty: no routines at all -->\n@if (!IsLoading && Routines.length === 0) {\n <mj-empty-state\n Size=\"large\"\n Icon=\"fa-solid fa-business-time\"\n Title=\"No Routines Yet\"\n Message=\"Routines run an AI agent for you on a schedule \u2014 and can watch for changes and notify you.\"\n ActionText=\"Create Your First Routine\"\n ActionIcon=\"fa-solid fa-plus\"\n (Action)=\"CreateRequested.emit()\">\n </mj-empty-state>\n}\n\n<!-- Empty: filters exclude everything -->\n@if (!IsLoading && Routines.length > 0 && FilteredRoutines.length === 0) {\n <mj-empty-state\n Variant=\"no-results\"\n Icon=\"fa-solid fa-filter-circle-xmark\"\n Title=\"No Matching Routines\"\n Message=\"No routines match your current search or filters.\">\n </mj-empty-state>\n}\n\n<!-- Card list -->\n@if (!IsLoading && FilteredRoutines.length > 0) {\n <div class=\"routines-grid\">\n @for (routine of FilteredRoutines; track routine.ID) {\n <div class=\"routine-card\">\n <div class=\"routine-card-header\">\n <div class=\"routine-target-icon\" [title]=\"AgentName(routine)\">\n <i [class]=\"AgentIcon(routine)\"></i>\n </div>\n <div class=\"routine-card-title\">\n <h3 class=\"routine-name\" [title]=\"routine.Name\">{{ routine.Name }}</h3>\n <div class=\"routine-target-line\" [title]=\"AgentName(routine)\">\n {{ AgentName(routine) }}\n </div>\n </div>\n <span [class]=\"ChipClass(StatusVariant(routine.Status))\">{{ routine.Status }}</span>\n </div>\n\n @if (routine.Description) {\n <p class=\"routine-description\" [title]=\"routine.Description\">{{ routine.Description }}</p>\n }\n\n <div class=\"routine-meta\">\n <div class=\"routine-meta-row\">\n <i class=\"fa-solid\" [class.fa-clock]=\"routine.RoutineType === 'Scheduled'\" [class.fa-eye]=\"routine.RoutineType === 'Monitoring'\"></i>\n <span class=\"routine-type-badge\">{{ routine.RoutineType }}</span>\n <span class=\"routine-cron\" [title]=\"routine.CronExpression\">{{ DescribeCron(routine.CronExpression) }}</span>\n <span class=\"routine-tz\">({{ routine.Timezone }})</span>\n </div>\n <div class=\"routine-meta-row\">\n <i class=\"fa-solid fa-backward-step\"></i>\n <span>Last run:</span>\n @if (routine.LastRunAt) {\n <span>{{ LastRunText(routine) }}</span>\n @if (routine.LastRunStatus) {\n <span [class]=\"ChipClass(LastRunVariant(routine.LastRunStatus))\">{{ routine.LastRunStatus }}</span>\n }\n } @else {\n <span class=\"routine-meta-muted\">never</span>\n }\n </div>\n <div class=\"routine-meta-row\">\n <i class=\"fa-solid fa-forward-step\"></i>\n <span>Next run:</span>\n @if (routine.NextRunAt) {\n <span>{{ NextRunText(routine) }}</span>\n } @else {\n <span class=\"routine-meta-muted\">not scheduled</span>\n }\n </div>\n </div>\n\n <div class=\"routine-card-actions\">\n <button mjButton variant=\"outline\" size=\"sm\"\n [disabled]=\"RunNowDisabled(routine)\"\n [title]=\"routine.Status === 'Active' ? 'Queue a run now (dispatcher picks it up within a minute)' : 'Only Active routines can be run'\"\n (click)=\"RunNow(routine)\">\n <i class=\"fa-solid fa-play\"></i> Run now\n </button>\n <button mjButton variant=\"outline\" size=\"sm\"\n [disabled]=\"BusyRoutineIDs.has(routine.ID)\"\n [title]=\"routine.Status === 'Active' ? 'Pause this routine' : 'Resume this routine'\"\n (click)=\"ToggleStatus(routine)\">\n <i class=\"fa-solid\" [class.fa-circle-pause]=\"routine.Status === 'Active'\" [class.fa-circle-play]=\"routine.Status !== 'Active'\"></i>\n {{ routine.Status === 'Active' ? 'Pause' : 'Resume' }}\n </button>\n <span class=\"routine-actions-spacer\"></span>\n <button mjButton variant=\"flat\" size=\"sm\" title=\"Edit routine\" (click)=\"EditRequested.emit(routine)\">\n <i class=\"fa-solid fa-pen\"></i>\n </button>\n <button mjButton variant=\"flat\" size=\"sm\" title=\"View run history\" (click)=\"OpenHistory(routine)\">\n <i class=\"fa-solid fa-timeline\"></i>\n </button>\n @if (routine.ConversationID) {\n <button mjButton variant=\"flat\" size=\"sm\" title=\"Open this routine's conversation\" (click)=\"OpenConversation(routine)\">\n <i class=\"fa-solid fa-comments\"></i>\n </button>\n }\n <button mjButton variant=\"flat\" size=\"sm\" title=\"Delete routine\"\n [disabled]=\"BusyRoutineIDs.has(routine.ID)\"\n (click)=\"DeleteRoutine(routine)\">\n <i class=\"fa-solid fa-trash-can\"></i>\n </button>\n </div>\n </div>\n }\n </div>\n}\n", styles: [":host {\n display: block;\n}\n\n.routines-loading {\n display: flex;\n justify-content: center;\n padding: 48px 0;\n}\n\n.routines-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));\n gap: 16px;\n}\n\n.routine-card {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 16px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 10px);\n transition: box-shadow 0.15s ease, border-color 0.15s ease;\n}\n\n.routine-card:hover {\n border-color: var(--mj-border-strong);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);\n}\n\n.routine-card-header {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n}\n\n.routine-target-icon {\n flex: 0 0 auto;\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--mj-radius-md, 8px);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 16px;\n}\n\n.routine-card-title {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.routine-name {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.routine-target-line {\n font-size: 12px;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.routine-description {\n margin: 0;\n font-size: 13px;\n color: var(--mj-text-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.routine-meta {\n display: flex;\n flex-direction: column;\n gap: 4px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n}\n\n.routine-meta-row {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n}\n\n.routine-meta-row > i {\n width: 14px;\n text-align: center;\n color: var(--mj-text-muted);\n}\n\n.routine-type-badge {\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.routine-cron {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.routine-tz {\n color: var(--mj-text-muted);\n}\n\n.routine-meta-muted {\n color: var(--mj-text-muted);\n font-style: italic;\n}\n\n.routine-card-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: auto;\n padding-top: 8px;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.routine-actions-spacer {\n flex: 1 1 auto;\n}\n\n/* Status / run chips */\n.routine-chip {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n border-radius: var(--mj-radius-full, 999px);\n font-size: 11px;\n font-weight: 600;\n line-height: 1.5;\n white-space: nowrap;\n}\n\n.routine-chip--success {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n border: 1px solid var(--mj-status-success-border);\n}\n\n.routine-chip--warning {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning-border);\n}\n\n.routine-chip--error {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n border: 1px solid var(--mj-status-error-border);\n}\n\n.routine-chip--info {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n border: 1px solid var(--mj-status-info-border);\n}\n\n.routine-chip--muted {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n border: 1px solid var(--mj-border-default);\n}\n"] }]
|
|
544
|
+
}], null, { SearchText: [{
|
|
545
|
+
type: Input
|
|
546
|
+
}], StatusFilter: [{
|
|
547
|
+
type: Input
|
|
548
|
+
}], CreateRequested: [{
|
|
549
|
+
type: Output
|
|
550
|
+
}], EditRequested: [{
|
|
551
|
+
type: Output
|
|
552
|
+
}], HistoryRequested: [{
|
|
553
|
+
type: Output
|
|
554
|
+
}], RoutineSelected: [{
|
|
555
|
+
type: Output
|
|
556
|
+
}], CountsChanged: [{
|
|
557
|
+
type: Output
|
|
558
|
+
}], BeforeRoutinePaused: [{
|
|
559
|
+
type: Output
|
|
560
|
+
}], AfterRoutinePaused: [{
|
|
561
|
+
type: Output
|
|
562
|
+
}], BeforeRoutineRunNow: [{
|
|
563
|
+
type: Output
|
|
564
|
+
}], AfterRoutineRunNow: [{
|
|
565
|
+
type: Output
|
|
566
|
+
}], BeforeRoutineDeleted: [{
|
|
567
|
+
type: Output
|
|
568
|
+
}], AfterRoutineDeleted: [{
|
|
569
|
+
type: Output
|
|
570
|
+
}], ConversationOpened: [{
|
|
571
|
+
type: Output
|
|
572
|
+
}] }); })();
|
|
573
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MyRoutinesListComponent, { className: "MyRoutinesListComponent", filePath: "src/lib/my-routines-list.component.ts", lineNumber: 49 }); })();
|
|
574
|
+
//# sourceMappingURL=my-routines-list.component.js.map
|