@memberjunction/ng-record-process-studio 0.0.1 → 5.43.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/README.md +50 -28
- package/dist/lib/record-process-editor/record-process-editor.component.d.ts +61 -0
- package/dist/lib/record-process-editor/record-process-editor.component.d.ts.map +1 -0
- package/dist/lib/record-process-editor/record-process-editor.component.js +485 -0
- package/dist/lib/record-process-editor/record-process-editor.component.js.map +1 -0
- package/dist/lib/record-process-history/record-process-history.component.d.ts +69 -0
- package/dist/lib/record-process-history/record-process-history.component.d.ts.map +1 -0
- package/dist/lib/record-process-history/record-process-history.component.js +450 -0
- package/dist/lib/record-process-history/record-process-history.component.js.map +1 -0
- package/dist/lib/record-process-studio/record-process-studio.component.d.ts +55 -0
- package/dist/lib/record-process-studio/record-process-studio.component.d.ts.map +1 -0
- package/dist/lib/record-process-studio/record-process-studio.component.js +304 -0
- package/dist/lib/record-process-studio/record-process-studio.component.js.map +1 -0
- package/dist/lib/run-detail.d.ts +23 -0
- package/dist/lib/run-detail.d.ts.map +1 -0
- package/dist/lib/run-detail.js +34 -0
- package/dist/lib/run-detail.js.map +1 -0
- package/dist/public-api.d.ts +9 -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 +48 -6
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Run history for Bulk Operations: every Record Process run (who/when/scope/status/counts),
|
|
3
|
+
* drilling into the per-record outcome (the field diff persisted on each Process Run Detail). Read-only
|
|
4
|
+
* audit surface. Generic + reusable.
|
|
5
|
+
* @module @memberjunction/ng-record-process-studio
|
|
6
|
+
*/
|
|
7
|
+
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, inject } from '@angular/core';
|
|
8
|
+
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
9
|
+
import { BaseEntity, RunView } from '@memberjunction/core';
|
|
10
|
+
import { MJEventType, MJGlobal, NormalizeUUID } from '@memberjunction/global';
|
|
11
|
+
import { MJButtonDirective } from '@memberjunction/ng-ui-components';
|
|
12
|
+
import { parseAppliedRunDetailChanges, displayRunValue } from '../run-detail';
|
|
13
|
+
import * as i0 from "@angular/core";
|
|
14
|
+
const _forTrack0 = ($index, $item) => $item.ID;
|
|
15
|
+
const _forTrack1 = ($index, $item) => $item.RecordID;
|
|
16
|
+
const _forTrack2 = ($index, $item) => $item.Field;
|
|
17
|
+
function RecordProcessHistoryComponent_Conditional_0_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
18
|
+
i0.ɵɵelementStart(0, "div", 5);
|
|
19
|
+
i0.ɵɵelement(1, "i", 7);
|
|
20
|
+
i0.ɵɵtext(2, " Loading\u2026");
|
|
21
|
+
i0.ɵɵelementEnd();
|
|
22
|
+
} }
|
|
23
|
+
function RecordProcessHistoryComponent_Conditional_0_Conditional_8_Template(rf, ctx) { if (rf & 1) {
|
|
24
|
+
i0.ɵɵelementStart(0, "div", 5);
|
|
25
|
+
i0.ɵɵtext(1, "No runs yet.");
|
|
26
|
+
i0.ɵɵelementEnd();
|
|
27
|
+
} }
|
|
28
|
+
function RecordProcessHistoryComponent_Conditional_0_Conditional_9_For_15_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
29
|
+
i0.ɵɵelementStart(0, "span", 15);
|
|
30
|
+
i0.ɵɵtext(1, "Dry Run");
|
|
31
|
+
i0.ɵɵelementEnd();
|
|
32
|
+
} }
|
|
33
|
+
function RecordProcessHistoryComponent_Conditional_0_Conditional_9_For_15_Conditional_17_Template(rf, ctx) { if (rf & 1) {
|
|
34
|
+
i0.ɵɵelementStart(0, "span", 18);
|
|
35
|
+
i0.ɵɵtext(1);
|
|
36
|
+
i0.ɵɵelementEnd();
|
|
37
|
+
} if (rf & 2) {
|
|
38
|
+
const r_r4 = i0.ɵɵnextContext().$implicit;
|
|
39
|
+
i0.ɵɵadvance();
|
|
40
|
+
i0.ɵɵtextInterpolate1("", r_r4.ErrorCount, " err");
|
|
41
|
+
} }
|
|
42
|
+
function RecordProcessHistoryComponent_Conditional_0_Conditional_9_For_15_Conditional_18_Template(rf, ctx) { if (rf & 1) {
|
|
43
|
+
i0.ɵɵelementStart(0, "span", 19);
|
|
44
|
+
i0.ɵɵtext(1);
|
|
45
|
+
i0.ɵɵelementEnd();
|
|
46
|
+
} if (rf & 2) {
|
|
47
|
+
const r_r4 = i0.ɵɵnextContext().$implicit;
|
|
48
|
+
i0.ɵɵadvance();
|
|
49
|
+
i0.ɵɵtextInterpolate1("", r_r4.SkippedCount, " skip");
|
|
50
|
+
} }
|
|
51
|
+
function RecordProcessHistoryComponent_Conditional_0_Conditional_9_For_15_Template(rf, ctx) { if (rf & 1) {
|
|
52
|
+
const _r3 = i0.ɵɵgetCurrentView();
|
|
53
|
+
i0.ɵɵelementStart(0, "div", 10);
|
|
54
|
+
i0.ɵɵlistener("click", function RecordProcessHistoryComponent_Conditional_0_Conditional_9_For_15_Template_div_click_0_listener() { const r_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.openRun(r_r4)); });
|
|
55
|
+
i0.ɵɵelementStart(1, "div", 11);
|
|
56
|
+
i0.ɵɵtext(2);
|
|
57
|
+
i0.ɵɵelementEnd();
|
|
58
|
+
i0.ɵɵelementStart(3, "div", 12);
|
|
59
|
+
i0.ɵɵtext(4);
|
|
60
|
+
i0.ɵɵelementEnd();
|
|
61
|
+
i0.ɵɵelementStart(5, "div");
|
|
62
|
+
i0.ɵɵtext(6);
|
|
63
|
+
i0.ɵɵelementEnd();
|
|
64
|
+
i0.ɵɵelementStart(7, "div", 11);
|
|
65
|
+
i0.ɵɵtext(8);
|
|
66
|
+
i0.ɵɵelementEnd();
|
|
67
|
+
i0.ɵɵelementStart(9, "div")(10, "span", 13);
|
|
68
|
+
i0.ɵɵelement(11, "span", 14);
|
|
69
|
+
i0.ɵɵtext(12);
|
|
70
|
+
i0.ɵɵelementEnd();
|
|
71
|
+
i0.ɵɵconditionalCreate(13, RecordProcessHistoryComponent_Conditional_0_Conditional_9_For_15_Conditional_13_Template, 2, 0, "span", 15);
|
|
72
|
+
i0.ɵɵelementEnd();
|
|
73
|
+
i0.ɵɵelementStart(14, "div", 16)(15, "span", 17);
|
|
74
|
+
i0.ɵɵtext(16);
|
|
75
|
+
i0.ɵɵelementEnd();
|
|
76
|
+
i0.ɵɵconditionalCreate(17, RecordProcessHistoryComponent_Conditional_0_Conditional_9_For_15_Conditional_17_Template, 2, 1, "span", 18);
|
|
77
|
+
i0.ɵɵconditionalCreate(18, RecordProcessHistoryComponent_Conditional_0_Conditional_9_For_15_Conditional_18_Template, 2, 1, "span", 19);
|
|
78
|
+
i0.ɵɵelementEnd()();
|
|
79
|
+
} if (rf & 2) {
|
|
80
|
+
const r_r4 = ctx.$implicit;
|
|
81
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
82
|
+
i0.ɵɵadvance(2);
|
|
83
|
+
i0.ɵɵtextInterpolate(ctx_r1.fmt(r_r4.StartTime));
|
|
84
|
+
i0.ɵɵadvance(2);
|
|
85
|
+
i0.ɵɵtextInterpolate(r_r4.ProcessName);
|
|
86
|
+
i0.ɵɵadvance(2);
|
|
87
|
+
i0.ɵɵtextInterpolate(r_r4.EntityName);
|
|
88
|
+
i0.ɵɵadvance(2);
|
|
89
|
+
i0.ɵɵtextInterpolate(r_r4.TriggeredBy || "\u2014");
|
|
90
|
+
i0.ɵɵadvance(2);
|
|
91
|
+
i0.ɵɵclassProp("ok", r_r4.Status === "Completed")("err", r_r4.Status === "Failed");
|
|
92
|
+
i0.ɵɵadvance(2);
|
|
93
|
+
i0.ɵɵtextInterpolate1(" ", r_r4.Status);
|
|
94
|
+
i0.ɵɵadvance();
|
|
95
|
+
i0.ɵɵconditional(r_r4.DryRun ? 13 : -1);
|
|
96
|
+
i0.ɵɵadvance(3);
|
|
97
|
+
i0.ɵɵtextInterpolate1("", r_r4.SuccessCount || 0, " ok");
|
|
98
|
+
i0.ɵɵadvance();
|
|
99
|
+
i0.ɵɵconditional(r_r4.ErrorCount ? 17 : -1);
|
|
100
|
+
i0.ɵɵadvance();
|
|
101
|
+
i0.ɵɵconditional(r_r4.SkippedCount ? 18 : -1);
|
|
102
|
+
} }
|
|
103
|
+
function RecordProcessHistoryComponent_Conditional_0_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
104
|
+
i0.ɵɵelementStart(0, "div", 6)(1, "div", 8)(2, "div");
|
|
105
|
+
i0.ɵɵtext(3, "When");
|
|
106
|
+
i0.ɵɵelementEnd();
|
|
107
|
+
i0.ɵɵelementStart(4, "div");
|
|
108
|
+
i0.ɵɵtext(5, "Operation");
|
|
109
|
+
i0.ɵɵelementEnd();
|
|
110
|
+
i0.ɵɵelementStart(6, "div");
|
|
111
|
+
i0.ɵɵtext(7, "Entity");
|
|
112
|
+
i0.ɵɵelementEnd();
|
|
113
|
+
i0.ɵɵelementStart(8, "div");
|
|
114
|
+
i0.ɵɵtext(9, "By");
|
|
115
|
+
i0.ɵɵelementEnd();
|
|
116
|
+
i0.ɵɵelementStart(10, "div");
|
|
117
|
+
i0.ɵɵtext(11, "Status");
|
|
118
|
+
i0.ɵɵelementEnd();
|
|
119
|
+
i0.ɵɵelementStart(12, "div");
|
|
120
|
+
i0.ɵɵtext(13, "Counts");
|
|
121
|
+
i0.ɵɵelementEnd()();
|
|
122
|
+
i0.ɵɵrepeaterCreate(14, RecordProcessHistoryComponent_Conditional_0_Conditional_9_For_15_Template, 19, 13, "div", 9, _forTrack0);
|
|
123
|
+
i0.ɵɵelementEnd();
|
|
124
|
+
} if (rf & 2) {
|
|
125
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
126
|
+
i0.ɵɵadvance(14);
|
|
127
|
+
i0.ɵɵrepeater(ctx_r1.Runs);
|
|
128
|
+
} }
|
|
129
|
+
function RecordProcessHistoryComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
130
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
131
|
+
i0.ɵɵelementStart(0, "div", 0)(1, "h3", 1);
|
|
132
|
+
i0.ɵɵtext(2, "Run history");
|
|
133
|
+
i0.ɵɵelementEnd();
|
|
134
|
+
i0.ɵɵelement(3, "span", 2);
|
|
135
|
+
i0.ɵɵelementStart(4, "button", 3);
|
|
136
|
+
i0.ɵɵlistener("click", function RecordProcessHistoryComponent_Conditional_0_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.reload()); });
|
|
137
|
+
i0.ɵɵelement(5, "i", 4);
|
|
138
|
+
i0.ɵɵtext(6, " Refresh");
|
|
139
|
+
i0.ɵɵelementEnd()();
|
|
140
|
+
i0.ɵɵconditionalCreate(7, RecordProcessHistoryComponent_Conditional_0_Conditional_7_Template, 3, 0, "div", 5)(8, RecordProcessHistoryComponent_Conditional_0_Conditional_8_Template, 2, 0, "div", 5)(9, RecordProcessHistoryComponent_Conditional_0_Conditional_9_Template, 16, 0, "div", 6);
|
|
141
|
+
} if (rf & 2) {
|
|
142
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
143
|
+
i0.ɵɵadvance(7);
|
|
144
|
+
i0.ɵɵconditional(ctx_r1.Loading ? 7 : ctx_r1.Runs.length === 0 ? 8 : 9);
|
|
145
|
+
} }
|
|
146
|
+
function RecordProcessHistoryComponent_Conditional_1_Conditional_6_Template(rf, ctx) { if (rf & 1) {
|
|
147
|
+
i0.ɵɵelementStart(0, "span", 15);
|
|
148
|
+
i0.ɵɵtext(1, "Dry Run");
|
|
149
|
+
i0.ɵɵelementEnd();
|
|
150
|
+
} }
|
|
151
|
+
function RecordProcessHistoryComponent_Conditional_1_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
152
|
+
i0.ɵɵelementStart(0, "div", 5);
|
|
153
|
+
i0.ɵɵelement(1, "i", 7);
|
|
154
|
+
i0.ɵɵtext(2, " Loading details\u2026");
|
|
155
|
+
i0.ɵɵelementEnd();
|
|
156
|
+
} }
|
|
157
|
+
function RecordProcessHistoryComponent_Conditional_1_Conditional_8_Template(rf, ctx) { if (rf & 1) {
|
|
158
|
+
i0.ɵɵelementStart(0, "div", 5);
|
|
159
|
+
i0.ɵɵtext(1, "No per-record details recorded for this run.");
|
|
160
|
+
i0.ɵɵelementEnd();
|
|
161
|
+
} }
|
|
162
|
+
function RecordProcessHistoryComponent_Conditional_1_Conditional_9_For_2_For_9_Template(rf, ctx) { if (rf & 1) {
|
|
163
|
+
i0.ɵɵelementStart(0, "div", 27)(1, "span", 29);
|
|
164
|
+
i0.ɵɵtext(2);
|
|
165
|
+
i0.ɵɵelementEnd();
|
|
166
|
+
i0.ɵɵelementStart(3, "span", 30);
|
|
167
|
+
i0.ɵɵtext(4);
|
|
168
|
+
i0.ɵɵelementEnd();
|
|
169
|
+
i0.ɵɵelement(5, "i", 31);
|
|
170
|
+
i0.ɵɵelementStart(6, "span", 32);
|
|
171
|
+
i0.ɵɵtext(7);
|
|
172
|
+
i0.ɵɵelementEnd()();
|
|
173
|
+
} if (rf & 2) {
|
|
174
|
+
const c_r6 = ctx.$implicit;
|
|
175
|
+
const ctx_r1 = i0.ɵɵnextContext(4);
|
|
176
|
+
i0.ɵɵadvance(2);
|
|
177
|
+
i0.ɵɵtextInterpolate(c_r6.Field);
|
|
178
|
+
i0.ɵɵadvance(2);
|
|
179
|
+
i0.ɵɵtextInterpolate(ctx_r1.disp(c_r6.OldValue));
|
|
180
|
+
i0.ɵɵadvance(3);
|
|
181
|
+
i0.ɵɵtextInterpolate(ctx_r1.disp(c_r6.NewValue));
|
|
182
|
+
} }
|
|
183
|
+
function RecordProcessHistoryComponent_Conditional_1_Conditional_9_For_2_Conditional_10_Template(rf, ctx) { if (rf & 1) {
|
|
184
|
+
i0.ɵɵelementStart(0, "div", 28);
|
|
185
|
+
i0.ɵɵtext(1);
|
|
186
|
+
i0.ɵɵelementEnd();
|
|
187
|
+
} if (rf & 2) {
|
|
188
|
+
const d_r7 = i0.ɵɵnextContext().$implicit;
|
|
189
|
+
i0.ɵɵadvance();
|
|
190
|
+
i0.ɵɵtextInterpolate(d_r7.ErrorMessage);
|
|
191
|
+
} }
|
|
192
|
+
function RecordProcessHistoryComponent_Conditional_1_Conditional_9_For_2_Conditional_11_Template(rf, ctx) { if (rf & 1) {
|
|
193
|
+
i0.ɵɵelementStart(0, "span", 11);
|
|
194
|
+
i0.ɵɵtext(1, "no changes");
|
|
195
|
+
i0.ɵɵelementEnd();
|
|
196
|
+
} }
|
|
197
|
+
function RecordProcessHistoryComponent_Conditional_1_Conditional_9_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
198
|
+
i0.ɵɵelementStart(0, "div", 22)(1, "div", 23)(2, "span", 24);
|
|
199
|
+
i0.ɵɵelement(3, "span", 14);
|
|
200
|
+
i0.ɵɵtext(4);
|
|
201
|
+
i0.ɵɵelementEnd();
|
|
202
|
+
i0.ɵɵelementStart(5, "div", 25);
|
|
203
|
+
i0.ɵɵtext(6);
|
|
204
|
+
i0.ɵɵelementEnd()();
|
|
205
|
+
i0.ɵɵelementStart(7, "div", 26);
|
|
206
|
+
i0.ɵɵrepeaterCreate(8, RecordProcessHistoryComponent_Conditional_1_Conditional_9_For_2_For_9_Template, 8, 3, "div", 27, _forTrack2);
|
|
207
|
+
i0.ɵɵconditionalCreate(10, RecordProcessHistoryComponent_Conditional_1_Conditional_9_For_2_Conditional_10_Template, 2, 1, "div", 28);
|
|
208
|
+
i0.ɵɵconditionalCreate(11, RecordProcessHistoryComponent_Conditional_1_Conditional_9_For_2_Conditional_11_Template, 2, 0, "span", 11);
|
|
209
|
+
i0.ɵɵelementEnd()();
|
|
210
|
+
} if (rf & 2) {
|
|
211
|
+
const d_r7 = ctx.$implicit;
|
|
212
|
+
i0.ɵɵadvance(2);
|
|
213
|
+
i0.ɵɵclassProp("ok", d_r7.Status === "Succeeded")("err", d_r7.Status === "Failed")("muted", d_r7.Status === "Skipped");
|
|
214
|
+
i0.ɵɵadvance(2);
|
|
215
|
+
i0.ɵɵtextInterpolate1(" ", d_r7.Status);
|
|
216
|
+
i0.ɵɵadvance(2);
|
|
217
|
+
i0.ɵɵtextInterpolate(d_r7.RecordID);
|
|
218
|
+
i0.ɵɵadvance(2);
|
|
219
|
+
i0.ɵɵrepeater(d_r7.Changes);
|
|
220
|
+
i0.ɵɵadvance(2);
|
|
221
|
+
i0.ɵɵconditional(d_r7.ErrorMessage ? 10 : -1);
|
|
222
|
+
i0.ɵɵadvance();
|
|
223
|
+
i0.ɵɵconditional(d_r7.Changes.length === 0 && !d_r7.ErrorMessage ? 11 : -1);
|
|
224
|
+
} }
|
|
225
|
+
function RecordProcessHistoryComponent_Conditional_1_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
226
|
+
i0.ɵɵelementStart(0, "div", 21);
|
|
227
|
+
i0.ɵɵrepeaterCreate(1, RecordProcessHistoryComponent_Conditional_1_Conditional_9_For_2_Template, 12, 10, "div", 22, _forTrack1);
|
|
228
|
+
i0.ɵɵelementEnd();
|
|
229
|
+
} if (rf & 2) {
|
|
230
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
231
|
+
i0.ɵɵadvance();
|
|
232
|
+
i0.ɵɵrepeater(ctx_r1.Details);
|
|
233
|
+
} }
|
|
234
|
+
function RecordProcessHistoryComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
235
|
+
const _r5 = i0.ɵɵgetCurrentView();
|
|
236
|
+
i0.ɵɵelementStart(0, "div", 0)(1, "button", 3);
|
|
237
|
+
i0.ɵɵlistener("click", function RecordProcessHistoryComponent_Conditional_1_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.backToList()); });
|
|
238
|
+
i0.ɵɵelement(2, "i", 20);
|
|
239
|
+
i0.ɵɵtext(3, " Back");
|
|
240
|
+
i0.ɵɵelementEnd();
|
|
241
|
+
i0.ɵɵelementStart(4, "h3", 1);
|
|
242
|
+
i0.ɵɵtext(5);
|
|
243
|
+
i0.ɵɵelementEnd();
|
|
244
|
+
i0.ɵɵconditionalCreate(6, RecordProcessHistoryComponent_Conditional_1_Conditional_6_Template, 2, 0, "span", 15);
|
|
245
|
+
i0.ɵɵelementEnd();
|
|
246
|
+
i0.ɵɵconditionalCreate(7, RecordProcessHistoryComponent_Conditional_1_Conditional_7_Template, 3, 0, "div", 5)(8, RecordProcessHistoryComponent_Conditional_1_Conditional_8_Template, 2, 0, "div", 5)(9, RecordProcessHistoryComponent_Conditional_1_Conditional_9_Template, 3, 0, "div", 21);
|
|
247
|
+
} if (rf & 2) {
|
|
248
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
249
|
+
i0.ɵɵadvance(5);
|
|
250
|
+
i0.ɵɵtextInterpolate2("", ctx_r1.OpenRunRow == null ? null : ctx_r1.OpenRunRow.ProcessName, " \u00B7 ", ctx_r1.fmt(ctx_r1.OpenRunRow == null ? null : ctx_r1.OpenRunRow.StartTime));
|
|
251
|
+
i0.ɵɵadvance();
|
|
252
|
+
i0.ɵɵconditional((ctx_r1.OpenRunRow == null ? null : ctx_r1.OpenRunRow.DryRun) ? 6 : -1);
|
|
253
|
+
i0.ɵɵadvance();
|
|
254
|
+
i0.ɵɵconditional(ctx_r1.DetailLoading ? 7 : ctx_r1.Details.length === 0 ? 8 : 9);
|
|
255
|
+
} }
|
|
256
|
+
export class RecordProcessHistoryComponent extends BaseAngularComponent {
|
|
257
|
+
cdr = inject(ChangeDetectorRef);
|
|
258
|
+
/** Optional: show runs for one Record Process only. */
|
|
259
|
+
RecordProcessID = null;
|
|
260
|
+
Mode = 'list';
|
|
261
|
+
Loading = true;
|
|
262
|
+
DetailLoading = false;
|
|
263
|
+
Runs = [];
|
|
264
|
+
Details = [];
|
|
265
|
+
OpenRunRow = null;
|
|
266
|
+
/**
|
|
267
|
+
* There is no RecordProcess BaseEngine to observe — Process Runs is an unbounded log table, so we don't
|
|
268
|
+
* bulk-cache it. Instead we re-query on demand AND keep the list live by listening for BaseEntity save/
|
|
269
|
+
* delete events on the run tables: a run started/finished anywhere (this view, the editor preview, a grid
|
|
270
|
+
* runner, the scheduler) fires an event, and we debounce-reload so history never goes stale behind a run.
|
|
271
|
+
*/
|
|
272
|
+
eventSub;
|
|
273
|
+
pendingReload = false;
|
|
274
|
+
reloadTimer = null;
|
|
275
|
+
async ngOnInit() {
|
|
276
|
+
await this.reload();
|
|
277
|
+
this.subscribeToRunChanges();
|
|
278
|
+
}
|
|
279
|
+
ngOnDestroy() {
|
|
280
|
+
this.eventSub?.unsubscribe();
|
|
281
|
+
if (this.reloadTimer) {
|
|
282
|
+
clearTimeout(this.reloadTimer);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/** Subscribe to BaseEntity events and debounce-reload whenever a Process Run (or its details) changes. */
|
|
286
|
+
subscribeToRunChanges() {
|
|
287
|
+
this.eventSub = MJGlobal.Instance.GetEventListener(false).subscribe((event) => {
|
|
288
|
+
if (event.event !== MJEventType.ComponentEvent || event.eventCode !== BaseEntity.BaseEventCode) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
const e = event.args;
|
|
292
|
+
if (e.type !== 'save' && e.type !== 'delete' && e.type !== 'remote-invalidate') {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const name = e.baseEntity?.EntityInfo?.Name ?? e.entityName;
|
|
296
|
+
if (name === 'MJ: Process Runs' || name === 'MJ: Process Run Details') {
|
|
297
|
+
this.scheduleReload();
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
/** Debounce a burst of run/detail/checkpoint saves into a single reload; defer if the user is drilled into a detail. */
|
|
302
|
+
scheduleReload() {
|
|
303
|
+
if (this.Mode !== 'list') {
|
|
304
|
+
this.pendingReload = true; // refresh when they return to the list — don't yank them out of a drill-in
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
if (this.reloadTimer) {
|
|
308
|
+
clearTimeout(this.reloadTimer);
|
|
309
|
+
}
|
|
310
|
+
this.reloadTimer = setTimeout(() => {
|
|
311
|
+
this.reloadTimer = null;
|
|
312
|
+
void this.reload();
|
|
313
|
+
}, 400);
|
|
314
|
+
}
|
|
315
|
+
async reload() {
|
|
316
|
+
this.Loading = true;
|
|
317
|
+
this.cdr.detectChanges();
|
|
318
|
+
const rv = this.Provider ? RunView.FromMetadataProvider(this.Provider) : new RunView();
|
|
319
|
+
const filter = this.RecordProcessID ? `RecordProcessID='${this.RecordProcessID}'` : '';
|
|
320
|
+
const [runs, procs] = await rv.RunViews([
|
|
321
|
+
{
|
|
322
|
+
EntityName: 'MJ: Process Runs',
|
|
323
|
+
Fields: ['ID', 'RecordProcessID', 'EntityID', 'Status', 'StartTime', 'SuccessCount', 'ErrorCount', 'SkippedCount', 'TotalItemCount', 'TriggeredBy', 'SourceType', 'DryRun'],
|
|
324
|
+
ExtraFilter: filter, OrderBy: 'StartTime DESC', MaxRows: 200, ResultType: 'simple',
|
|
325
|
+
},
|
|
326
|
+
{ EntityName: 'MJ: Record Processes', Fields: ['ID', 'Name'], ResultType: 'simple' },
|
|
327
|
+
]);
|
|
328
|
+
// Normalized-UUID keys → O(1), case-safe across SQL Server (upper) / PostgreSQL (lower).
|
|
329
|
+
const nameByID = new Map();
|
|
330
|
+
if (procs.Success)
|
|
331
|
+
for (const p of procs.Results)
|
|
332
|
+
nameByID.set(NormalizeUUID(p.ID), p.Name);
|
|
333
|
+
const rawRuns = runs.Success ? runs.Results : [];
|
|
334
|
+
this.Runs = rawRuns.map((r) => ({
|
|
335
|
+
...r,
|
|
336
|
+
ProcessName: nameByID.get(NormalizeUUID(r.RecordProcessID)) ?? '(deleted)',
|
|
337
|
+
EntityName: this.ProviderToUse.EntityByID(r.EntityID)?.Name ?? r.EntityID,
|
|
338
|
+
}));
|
|
339
|
+
this.Loading = false;
|
|
340
|
+
this.cdr.detectChanges();
|
|
341
|
+
}
|
|
342
|
+
async openRun(run) {
|
|
343
|
+
this.OpenRunRow = run;
|
|
344
|
+
this.Mode = 'detail';
|
|
345
|
+
this.DetailLoading = true;
|
|
346
|
+
this.Details = [];
|
|
347
|
+
this.cdr.detectChanges();
|
|
348
|
+
const rv = this.Provider ? RunView.FromMetadataProvider(this.Provider) : new RunView();
|
|
349
|
+
const result = await rv.RunView({
|
|
350
|
+
EntityName: 'MJ: Process Run Details',
|
|
351
|
+
Fields: ['RecordID', 'Status', 'ResultPayload', 'ErrorMessage'],
|
|
352
|
+
ExtraFilter: `ProcessRunID='${run.ID}'`, OrderBy: 'Status, RecordID', MaxRows: 1000, ResultType: 'simple',
|
|
353
|
+
});
|
|
354
|
+
this.Details = (result.Success ? (result.Results ?? []) : []).map((d) => ({
|
|
355
|
+
RecordID: d.RecordID, Status: d.Status, Changes: parseAppliedRunDetailChanges(d.ResultPayload), ErrorMessage: d.ErrorMessage,
|
|
356
|
+
}));
|
|
357
|
+
this.DetailLoading = false;
|
|
358
|
+
this.cdr.detectChanges();
|
|
359
|
+
}
|
|
360
|
+
backToList() {
|
|
361
|
+
this.Mode = 'list';
|
|
362
|
+
this.OpenRunRow = null;
|
|
363
|
+
this.cdr.detectChanges();
|
|
364
|
+
if (this.pendingReload) {
|
|
365
|
+
this.pendingReload = false;
|
|
366
|
+
void this.reload(); // a run changed while we were drilled into a detail — refresh now
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
fmt(value) {
|
|
370
|
+
if (!value)
|
|
371
|
+
return '—';
|
|
372
|
+
const d = value instanceof Date ? value : new Date(value);
|
|
373
|
+
return isNaN(d.getTime()) ? String(value) : d.toLocaleString();
|
|
374
|
+
}
|
|
375
|
+
disp(value) { return displayRunValue(value); }
|
|
376
|
+
static ɵfac = /*@__PURE__*/ (() => { let ɵRecordProcessHistoryComponent_BaseFactory; return function RecordProcessHistoryComponent_Factory(__ngFactoryType__) { return (ɵRecordProcessHistoryComponent_BaseFactory || (ɵRecordProcessHistoryComponent_BaseFactory = i0.ɵɵgetInheritedFactory(RecordProcessHistoryComponent)))(__ngFactoryType__ || RecordProcessHistoryComponent); }; })();
|
|
377
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RecordProcessHistoryComponent, selectors: [["mj-record-process-history"]], inputs: { RecordProcessID: "RecordProcessID" }, features: [i0.ɵɵInheritDefinitionFeature], decls: 2, vars: 1, consts: [[1, "rph-bar"], [1, "rph-title"], [1, "rph-spacer"], ["mjButton", "", "variant", "flat", 3, "click"], [1, "fa-solid", "fa-arrows-rotate"], [1, "rph-msg"], [1, "rph-table"], [1, "fa-solid", "fa-circle-notch", "fa-spin"], [1, "rph-tr", "rph-head"], [1, "rph-tr"], [1, "rph-tr", 3, "click"], [1, "rph-sub"], [1, "rph-nm"], [1, "st"], [1, "dot"], ["title", "Compute-only preview \u2014 no changes were written", 1, "dryrun"], [1, "counts"], [1, "cbadge", "ok"], [1, "cbadge", "err"], [1, "cbadge"], [1, "fa-solid", "fa-arrow-left"], [1, "rph-details"], [1, "rph-detrec"], [1, "rph-detkey"], [1, "st", "sm"], [1, "rph-rk"], [1, "rph-changes"], [1, "rph-chg"], [1, "rph-chg", "err-msg"], [1, "f"], [1, "o"], [1, "fa-solid", "fa-arrow-right-long", "ar"], [1, "nw"]], template: function RecordProcessHistoryComponent_Template(rf, ctx) { if (rf & 1) {
|
|
378
|
+
i0.ɵɵconditionalCreate(0, RecordProcessHistoryComponent_Conditional_0_Template, 10, 1)(1, RecordProcessHistoryComponent_Conditional_1_Template, 10, 4);
|
|
379
|
+
} if (rf & 2) {
|
|
380
|
+
i0.ɵɵconditional(ctx.Mode === "list" ? 0 : 1);
|
|
381
|
+
} }, dependencies: [MJButtonDirective], styles: ["[_nghost-%COMP%]{display:block;color:var(--mj-text-primary)}\n .rph-bar[_ngcontent-%COMP%]{display:flex;align-items:center;gap:14px;margin-bottom:16px}\n .rph-title[_ngcontent-%COMP%]{margin:0;font-size:16px;font-weight:700}\n .rph-spacer[_ngcontent-%COMP%]{flex:1}\n .rph-bar[_ngcontent-%COMP%] button[_ngcontent-%COMP%] i[_ngcontent-%COMP%]{margin-right:6px}\n .rph-msg[_ngcontent-%COMP%]{padding:36px;text-align:center;color:var(--mj-text-muted)}\n .rph-table[_ngcontent-%COMP%]{border:1px solid var(--mj-border-subtle);border-radius:var(--mj-radius-md,10px);overflow:hidden}\n .rph-tr[_ngcontent-%COMP%]{display:grid;grid-template-columns:1.3fr 1.6fr 1.2fr .9fr 1fr 1.4fr;gap:14px;align-items:center;padding:12px 16px;border-bottom:1px solid var(--mj-border-subtle);font-size:13px;cursor:pointer}\n .rph-tr[_ngcontent-%COMP%]:last-child{border-bottom:none}\n .rph-tr[_ngcontent-%COMP%]:not(.rph-head):hover{background:var(--mj-bg-surface-hover)}\n .rph-head[_ngcontent-%COMP%]{background:var(--mj-bg-surface-sunken);font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--mj-text-muted);font-weight:700;cursor:default}\n .rph-nm[_ngcontent-%COMP%]{font-weight:600}\n .rph-sub[_ngcontent-%COMP%]{color:var(--mj-text-muted);font-size:12px}\n .st[_ngcontent-%COMP%]{display:inline-flex;align-items:center;gap:7px;font-weight:600;font-size:12.5px}\n .st[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%]{width:8px;height:8px;border-radius:50%;background:var(--mj-text-disabled)}\n .st.ok[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%]{background:var(--mj-status-success)} .st.ok[_ngcontent-%COMP%]{color:var(--mj-status-success-text)}\n .st.err[_ngcontent-%COMP%] .dot[_ngcontent-%COMP%]{background:var(--mj-status-error)} .st.err[_ngcontent-%COMP%]{color:var(--mj-status-error-text)}\n .st.muted[_ngcontent-%COMP%]{color:var(--mj-text-muted)}\n .st.sm[_ngcontent-%COMP%]{font-size:11.5px}\n .counts[_ngcontent-%COMP%]{display:flex;gap:6px;flex-wrap:wrap}\n .cbadge[_ngcontent-%COMP%]{font-size:11px;font-weight:700;padding:2px 8px;border-radius:6px;background:var(--mj-bg-surface-sunken);color:var(--mj-text-secondary)}\n .dryrun[_ngcontent-%COMP%]{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;padding:2px 7px;border-radius:6px;margin-left:6px;background:var(--mj-status-info-bg);color:var(--mj-status-info-text);border:1px solid var(--mj-status-info-border)}\n .cbadge.ok[_ngcontent-%COMP%]{background:var(--mj-status-success-bg);color:var(--mj-status-success-text)}\n .cbadge.err[_ngcontent-%COMP%]{background:var(--mj-status-error-bg);color:var(--mj-status-error-text)}\n .rph-details[_ngcontent-%COMP%]{border:1px solid var(--mj-border-subtle);border-radius:var(--mj-radius-md,10px);overflow:hidden;max-height:560px;overflow-y:auto}\n .rph-detrec[_ngcontent-%COMP%]{display:grid;grid-template-columns:200px 1fr;gap:14px;padding:11px 16px;border-bottom:1px solid var(--mj-border-subtle)}\n .rph-detrec[_ngcontent-%COMP%]:last-child{border-bottom:none}\n .rph-detkey[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:5px}\n .rph-rk[_ngcontent-%COMP%]{font-family:var(--mj-font-mono,monospace);font-size:11px;color:var(--mj-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n .rph-chg[_ngcontent-%COMP%]{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:13px;padding:2px 0}\n .rph-chg[_ngcontent-%COMP%] .f[_ngcontent-%COMP%]{font-weight:700;min-width:120px}\n .rph-chg[_ngcontent-%COMP%] .o[_ngcontent-%COMP%]{color:var(--mj-text-muted);text-decoration:line-through}\n .rph-chg[_ngcontent-%COMP%] .nw[_ngcontent-%COMP%]{color:var(--mj-status-success-text);font-weight:600}\n .rph-chg[_ngcontent-%COMP%] .ar[_ngcontent-%COMP%]{color:var(--mj-text-disabled);font-size:11px}\n .rph-chg.err-msg[_ngcontent-%COMP%]{color:var(--mj-status-error-text)}"], changeDetection: 0 });
|
|
382
|
+
}
|
|
383
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RecordProcessHistoryComponent, [{
|
|
384
|
+
type: Component,
|
|
385
|
+
args: [{ selector: 'mj-record-process-history', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [MJButtonDirective], template: `
|
|
386
|
+
@if (Mode === 'list') {
|
|
387
|
+
<div class="rph-bar">
|
|
388
|
+
<h3 class="rph-title">Run history</h3><span class="rph-spacer"></span>
|
|
389
|
+
<button mjButton variant="flat" (click)="reload()"><i class="fa-solid fa-arrows-rotate"></i> Refresh</button>
|
|
390
|
+
</div>
|
|
391
|
+
@if (Loading) {
|
|
392
|
+
<div class="rph-msg"><i class="fa-solid fa-circle-notch fa-spin"></i> Loading…</div>
|
|
393
|
+
} @else if (Runs.length === 0) {
|
|
394
|
+
<div class="rph-msg">No runs yet.</div>
|
|
395
|
+
} @else {
|
|
396
|
+
<div class="rph-table">
|
|
397
|
+
<div class="rph-tr rph-head"><div>When</div><div>Operation</div><div>Entity</div><div>By</div><div>Status</div><div>Counts</div></div>
|
|
398
|
+
@for (r of Runs; track r.ID) {
|
|
399
|
+
<div class="rph-tr" (click)="openRun(r)">
|
|
400
|
+
<div class="rph-sub">{{ fmt(r.StartTime) }}</div>
|
|
401
|
+
<div class="rph-nm">{{ r.ProcessName }}</div>
|
|
402
|
+
<div>{{ r.EntityName }}</div>
|
|
403
|
+
<div class="rph-sub">{{ r.TriggeredBy || '—' }}</div>
|
|
404
|
+
<div><span class="st" [class.ok]="r.Status==='Completed'" [class.err]="r.Status==='Failed'"><span class="dot"></span> {{ r.Status }}</span>@if (r.DryRun) { <span class="dryrun" title="Compute-only preview — no changes were written">Dry Run</span> }</div>
|
|
405
|
+
<div class="counts">
|
|
406
|
+
<span class="cbadge ok">{{ r.SuccessCount || 0 }} ok</span>
|
|
407
|
+
@if (r.ErrorCount) { <span class="cbadge err">{{ r.ErrorCount }} err</span> }
|
|
408
|
+
@if (r.SkippedCount) { <span class="cbadge">{{ r.SkippedCount }} skip</span> }
|
|
409
|
+
</div>
|
|
410
|
+
</div>
|
|
411
|
+
}
|
|
412
|
+
</div>
|
|
413
|
+
}
|
|
414
|
+
} @else {
|
|
415
|
+
<div class="rph-bar">
|
|
416
|
+
<button mjButton variant="flat" (click)="backToList()"><i class="fa-solid fa-arrow-left"></i> Back</button>
|
|
417
|
+
<h3 class="rph-title">{{ OpenRunRow?.ProcessName }} · {{ fmt(OpenRunRow?.StartTime) }}</h3>
|
|
418
|
+
@if (OpenRunRow?.DryRun) { <span class="dryrun" title="Compute-only preview — no changes were written">Dry Run</span> }
|
|
419
|
+
</div>
|
|
420
|
+
@if (DetailLoading) {
|
|
421
|
+
<div class="rph-msg"><i class="fa-solid fa-circle-notch fa-spin"></i> Loading details…</div>
|
|
422
|
+
} @else if (Details.length === 0) {
|
|
423
|
+
<div class="rph-msg">No per-record details recorded for this run.</div>
|
|
424
|
+
} @else {
|
|
425
|
+
<div class="rph-details">
|
|
426
|
+
@for (d of Details; track d.RecordID) {
|
|
427
|
+
<div class="rph-detrec">
|
|
428
|
+
<div class="rph-detkey">
|
|
429
|
+
<span class="st sm" [class.ok]="d.Status==='Succeeded'" [class.err]="d.Status==='Failed'" [class.muted]="d.Status==='Skipped'"><span class="dot"></span> {{ d.Status }}</span>
|
|
430
|
+
<div class="rph-rk">{{ d.RecordID }}</div>
|
|
431
|
+
</div>
|
|
432
|
+
<div class="rph-changes">
|
|
433
|
+
@for (c of d.Changes; track c.Field) {
|
|
434
|
+
<div class="rph-chg"><span class="f">{{ c.Field }}</span>
|
|
435
|
+
<span class="o">{{ disp(c.OldValue) }}</span><i class="fa-solid fa-arrow-right-long ar"></i><span class="nw">{{ disp(c.NewValue) }}</span></div>
|
|
436
|
+
}
|
|
437
|
+
@if (d.ErrorMessage) { <div class="rph-chg err-msg">{{ d.ErrorMessage }}</div> }
|
|
438
|
+
@if (d.Changes.length === 0 && !d.ErrorMessage) { <span class="rph-sub">no changes</span> }
|
|
439
|
+
</div>
|
|
440
|
+
</div>
|
|
441
|
+
}
|
|
442
|
+
</div>
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
`, styles: ["\n :host{display:block;color:var(--mj-text-primary)}\n .rph-bar{display:flex;align-items:center;gap:14px;margin-bottom:16px}\n .rph-title{margin:0;font-size:16px;font-weight:700}\n .rph-spacer{flex:1}\n .rph-bar button i{margin-right:6px}\n .rph-msg{padding:36px;text-align:center;color:var(--mj-text-muted)}\n .rph-table{border:1px solid var(--mj-border-subtle);border-radius:var(--mj-radius-md,10px);overflow:hidden}\n .rph-tr{display:grid;grid-template-columns:1.3fr 1.6fr 1.2fr .9fr 1fr 1.4fr;gap:14px;align-items:center;padding:12px 16px;border-bottom:1px solid var(--mj-border-subtle);font-size:13px;cursor:pointer}\n .rph-tr:last-child{border-bottom:none}\n .rph-tr:not(.rph-head):hover{background:var(--mj-bg-surface-hover)}\n .rph-head{background:var(--mj-bg-surface-sunken);font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--mj-text-muted);font-weight:700;cursor:default}\n .rph-nm{font-weight:600}\n .rph-sub{color:var(--mj-text-muted);font-size:12px}\n .st{display:inline-flex;align-items:center;gap:7px;font-weight:600;font-size:12.5px}\n .st .dot{width:8px;height:8px;border-radius:50%;background:var(--mj-text-disabled)}\n .st.ok .dot{background:var(--mj-status-success)} .st.ok{color:var(--mj-status-success-text)}\n .st.err .dot{background:var(--mj-status-error)} .st.err{color:var(--mj-status-error-text)}\n .st.muted{color:var(--mj-text-muted)}\n .st.sm{font-size:11.5px}\n .counts{display:flex;gap:6px;flex-wrap:wrap}\n .cbadge{font-size:11px;font-weight:700;padding:2px 8px;border-radius:6px;background:var(--mj-bg-surface-sunken);color:var(--mj-text-secondary)}\n .dryrun{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;padding:2px 7px;border-radius:6px;margin-left:6px;background:var(--mj-status-info-bg);color:var(--mj-status-info-text);border:1px solid var(--mj-status-info-border)}\n .cbadge.ok{background:var(--mj-status-success-bg);color:var(--mj-status-success-text)}\n .cbadge.err{background:var(--mj-status-error-bg);color:var(--mj-status-error-text)}\n .rph-details{border:1px solid var(--mj-border-subtle);border-radius:var(--mj-radius-md,10px);overflow:hidden;max-height:560px;overflow-y:auto}\n .rph-detrec{display:grid;grid-template-columns:200px 1fr;gap:14px;padding:11px 16px;border-bottom:1px solid var(--mj-border-subtle)}\n .rph-detrec:last-child{border-bottom:none}\n .rph-detkey{display:flex;flex-direction:column;gap:5px}\n .rph-rk{font-family:var(--mj-font-mono,monospace);font-size:11px;color:var(--mj-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n .rph-chg{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:13px;padding:2px 0}\n .rph-chg .f{font-weight:700;min-width:120px}\n .rph-chg .o{color:var(--mj-text-muted);text-decoration:line-through}\n .rph-chg .nw{color:var(--mj-status-success-text);font-weight:600}\n .rph-chg .ar{color:var(--mj-text-disabled);font-size:11px}\n .rph-chg.err-msg{color:var(--mj-status-error-text)}\n "] }]
|
|
446
|
+
}], null, { RecordProcessID: [{
|
|
447
|
+
type: Input
|
|
448
|
+
}] }); })();
|
|
449
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RecordProcessHistoryComponent, { className: "RecordProcessHistoryComponent", filePath: "lib/record-process-history/record-process-history.component.ts", lineNumber: 129 }); })();
|
|
450
|
+
//# sourceMappingURL=record-process-history.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record-process-history.component.js","sourceRoot":"","sources":["../../../src/lib/record-process-history/record-process-history.component.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,EAAqB,MAAM,EAAE,MAAM,eAAe,CAAC;AAExH,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,UAAU,EAAmB,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAW,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvF,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,4BAA4B,EAAE,eAAe,EAAwB,MAAM,eAAe,CAAC;;;;;;IAwBpF,8BAAqB;IAAA,uBAAgD;IAAC,8BAAQ;IAAA,iBAAM;;;IAEpF,8BAAqB;IAAA,4BAAY;IAAA,iBAAM;;;IAUiI,gCAA4E;IAAA,uBAAO;IAAA,iBAAO;;;IAG7N,gCAAyB;IAAA,YAAsB;IAAA,iBAAO;;;IAA7B,cAAsB;IAAtB,kDAAsB;;;IAC7C,gCAAqB;IAAA,YAAyB;IAAA,iBAAO;;;IAAhC,cAAyB;IAAzB,qDAAyB;;;;IAT7E,+BAAyC;IAArB,2OAAS,oBAAU,KAAC;IACpC,+BAAqB;IAAA,YAAsB;IAAA,iBAAM;IACjD,+BAAoB;IAAA,YAAmB;IAAA,iBAAM;IAC7C,2BAAK;IAAA,YAAkB;IAAA,iBAAM;IAC7B,+BAAqB;IAAA,YAA0B;IAAA,iBAAM;IAChD,AAAL,2BAAK,gBAAuF;IAAA,4BAAyB;IAAC,aAAc;IAAA,iBAAO;IAAA,sIAAgB;IAA6F,iBAAM;IAE1P,AADJ,gCAAoB,gBACQ;IAAA,aAA4B;IAAA,iBAAO;IAC3D,sIAAoB;IACpB,sIAAsB;IAE9B,AADI,iBAAM,EACJ;;;;IAVmB,eAAsB;IAAtB,gDAAsB;IACvB,eAAmB;IAAnB,sCAAmB;IAClC,eAAkB;IAAlB,qCAAkB;IACF,eAA0B;IAA1B,kDAA0B;IACzB,eAAmC;IAAC,AAApC,iDAAmC,iCAAkC;IAA2B,eAAc;IAAd,uCAAc;IAAO,cAA6G;IAA7G,uCAA6G;IAE5N,eAA4B;IAA5B,wDAA4B;IACpD,cAA6E;IAA7E,2CAA6E;IAC7E,cAA8E;IAA9E,6CAA8E;;;IAX7D,AAA7B,AADJ,8BAAuB,aACU,UAAK;IAAA,oBAAI;IAAA,iBAAM;IAAA,2BAAK;IAAA,yBAAS;IAAA,iBAAM;IAAA,2BAAK;IAAA,sBAAM;IAAA,iBAAM;IAAA,2BAAK;IAAA,kBAAE;IAAA,iBAAM;IAAA,4BAAK;IAAA,uBAAM;IAAA,iBAAM;IAAA,4BAAK;IAAA,uBAAM;IAAM,AAAN,iBAAM,EAAM;IACtI,gIAaC;IACL,iBAAM;;;IAdF,gBAaC;IAbD,0BAaC;;;;IAvBL,AADJ,8BAAqB,YACK;IAAA,2BAAW;IAAA,iBAAK;IAAA,0BAAgC;IACtE,iCAAmD;IAAnB,iMAAS,eAAQ,KAAC;IAAC,uBAAyC;IAAC,wBAAO;IACxG,AADwG,iBAAS,EAC3G;IAKJ,AAFA,AAFF,6GAAe,uFAEiB,wFAEvB;;;IAJT,eAsBC;IAtBD,uEAsBC;;;IAK8B,gCAA4E;IAAA,uBAAO;IAAA,iBAAO;;;IAGrH,8BAAqB;IAAA,uBAAgD;IAAC,sCAAgB;IAAA,iBAAM;;;IAE5F,8BAAqB;IAAA,4DAA4C;IAAA,iBAAM;;;IAW9B,AAArB,+BAAqB,eAAgB;IAAA,YAAa;IAAA,iBAAO;IACrD,gCAAgB;IAAA,YAAsB;IAAA,iBAAO;IAAA,wBAA+C;IAAA,gCAAiB;IAAA,YAAsB;IAAO,AAAP,iBAAO,EAAM;;;;IAD/G,eAAa;IAAb,gCAAa;IAC9B,eAAsB;IAAtB,gDAAsB;IAAuE,eAAsB;IAAtB,gDAAsB;;;IAEpH,+BAA6B;IAAA,YAAoB;IAAA,iBAAM;;;IAA1B,cAAoB;IAApB,uCAAoB;;;IACtB,gCAAsB;IAAA,0BAAU;IAAA,iBAAO;;;IATzF,AADJ,AADJ,+BAAwB,cACI,eAC2G;IAAA,2BAAyB;IAAC,YAAc;IAAA,iBAAO;IAC9K,+BAAoB;IAAA,YAAgB;IACxC,AADwC,iBAAM,EACxC;IACN,+BAAyB;IACrB,mIAGC;IACD,oIAAsB;IACtB,qIAAiD;IAEzD,AADI,iBAAM,EACJ;;;IAXsB,eAAmC;IAAmC,AAAlC,AAApC,iDAAmC,iCAAkC,oCAAqC;IAA2B,eAAc;IAAd,uCAAc;IACnJ,eAAgB;IAAhB,mCAAgB;IAGpC,eAGC;IAHD,2BAGC;IACD,eAAgF;IAAhF,6CAAgF;IAChF,cAA2F;IAA3F,2EAA2F;;;IAb3G,+BAAyB;IACrB,+HAeC;IACL,iBAAM;;;IAhBF,cAeC;IAfD,6BAeC;;;;IAzBL,AADJ,8BAAqB,gBACsC;IAAvB,iMAAS,mBAAY,KAAC;IAAC,wBAAsC;IAAC,qBAAI;IAAA,iBAAS;IAC3G,6BAAsB;IAAA,YAAgE;IAAA,iBAAK;IAC3F,+GAA0B;IAC9B,iBAAM;IAKJ,AAFA,AAFF,6GAAqB,uFAEc,wFAE1B;;;IAPiB,eAAgE;IAAhE,mLAAgE;IACtF,cAAuH;IAAvH,wFAAuH;IAE3H,cAuBC;IAvBD,gFAuBC;;AAyCb,MAAM,OAAO,6BAA8B,SAAQ,oBAAoB;IAC3D,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAExC,uDAAuD;IAC9C,eAAe,GAAkB,IAAI,CAAC;IAExC,IAAI,GAAsB,MAAM,CAAC;IACjC,OAAO,GAAG,IAAI,CAAC;IACf,aAAa,GAAG,KAAK,CAAC;IACtB,IAAI,GAAa,EAAE,CAAC;IACpB,OAAO,GAAgB,EAAE,CAAC;IAC1B,UAAU,GAAkB,IAAI,CAAC;IAExC;;;;;OAKG;IACK,QAAQ,CAAgB;IACxB,aAAa,GAAG,KAAK,CAAC;IACtB,WAAW,GAAyC,IAAI,CAAC;IAEjE,KAAK,CAAC,QAAQ;QACV,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACpB,IAAI,CAAC,qBAAqB,EAAE,CAAC;IACjC,CAAC;IAED,WAAW;QACP,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,CAAC;QAC7B,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACnC,CAAC;IACL,CAAC;IAED,0GAA0G;IAClG,qBAAqB;QACzB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,KAAc,EAAE,EAAE;YACnF,IAAI,KAAK,CAAC,KAAK,KAAK,WAAW,CAAC,cAAc,IAAI,KAAK,CAAC,SAAS,KAAK,UAAU,CAAC,aAAa,EAAE,CAAC;gBAC7F,OAAO;YACX,CAAC;YACD,MAAM,CAAC,GAAG,KAAK,CAAC,IAAuB,CAAC;YACxC,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;gBAC7E,OAAO;YACX,CAAC;YACD,MAAM,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,IAAI,CAAC,CAAC,UAAU,CAAC;YAC5D,IAAI,IAAI,KAAK,kBAAkB,IAAI,IAAI,KAAK,yBAAyB,EAAE,CAAC;gBACpE,IAAI,CAAC,cAAc,EAAE,CAAC;YAC1B,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;IAED,wHAAwH;IAChH,cAAc;QAClB,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACvB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC,2EAA2E;YACtG,OAAO;QACX,CAAC;QACD,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACnC,CAAC;QACD,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC,GAAG,EAAE;YAC/B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YACxB,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;QACvB,CAAC,EAAE,GAAG,CAAC,CAAC;IACZ,CAAC;IAED,KAAK,CAAC,MAAM;QACR,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAC9C,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;QACvF,MAAM,MAAM,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,oBAAoB,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACvF,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC;YACpC;gBACI,UAAU,EAAE,kBAAkB;gBAC9B,MAAM,EAAE,CAAC,IAAI,EAAE,iBAAiB,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,YAAY,EAAE,QAAQ,CAAC;gBAC3K,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,QAAQ;aACrF;YACD,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE;SACvF,CAAC,CAAC;QACH,yFAAyF;QACzF,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC3C,IAAI,KAAK,CAAC,OAAO;YAAE,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,OAA8C;gBAAE,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACnI,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAE,IAAI,CAAC,OAAoB,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC5B,GAAG,CAAC;YACJ,WAAW,EAAE,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,IAAI,WAAW;YAC1E,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,IAAI,IAAI,CAAC,CAAC,QAAQ;SAC5E,CAAC,CAAC,CAAC;QACJ,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAW;QACrB,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC;QAAC,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAC;QAAC,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QACpH,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;QACvF,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAAY;YACvC,UAAU,EAAE,yBAAyB;YACrC,MAAM,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,eAAe,EAAE,cAAc,CAAC;YAC/D,WAAW,EAAE,iBAAiB,GAAG,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ;SAC5G,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACtE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,4BAA4B,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,YAAY;SAC/H,CAAC,CAAC,CAAC;QACJ,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAC7B,CAAC;IAED,UAAU;QACN,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC;QACnB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;YAC3B,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,kEAAkE;QAC1F,CAAC;IACL,CAAC;IAED,GAAG,CAAC,KAAqB;QACrB,IAAI,CAAC,KAAK;YAAE,OAAO,GAAG,CAAC;QACvB,MAAM,CAAC,GAAG,KAAK,YAAY,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1D,OAAO,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC;IACnE,CAAC;IACD,IAAI,CAAC,KAAc,IAAY,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;iSA1HtD,6BAA6B,yBAA7B,6BAA6B;6DAA7B,6BAA6B;YAtEhC,AA5BF,sFAAuB,gEA4Bd;;YA5BT,6CA0DC;4BA5DK,iBAAiB;;iFAoGlB,6BAA6B;cAxGzC,SAAS;2BACI,2BAA2B,cACzB,IAAI,mBACC,uBAAuB,CAAC,MAAM,WACtC,CAAC,iBAAiB,CAAC,YAClB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA4DT;;kBA2CA,KAAK;;kFAJG,6BAA6B","sourcesContent":["/**\n * @fileoverview Run history for Bulk Operations: every Record Process run (who/when/scope/status/counts),\n * drilling into the per-record outcome (the field diff persisted on each Process Run Detail). Read-only\n * audit surface. Generic + reusable.\n * @module @memberjunction/ng-record-process-studio\n */\nimport { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, inject } from '@angular/core';\nimport { Subscription } from 'rxjs';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { BaseEntity, BaseEntityEvent, RunView } from '@memberjunction/core';\nimport { MJEvent, MJEventType, MJGlobal, NormalizeUUID } from '@memberjunction/global';\nimport { MJButtonDirective } from '@memberjunction/ng-ui-components';\nimport { parseAppliedRunDetailChanges, displayRunValue, type RunDetailChange } from '../run-detail';\n\ninterface RawRun {\n ID: string; RecordProcessID: string; EntityID: string; Status: string;\n StartTime?: string | Date; SuccessCount?: number; ErrorCount?: number; SkippedCount?: number;\n TotalItemCount?: number; TriggeredBy?: string; SourceType?: string; DryRun?: boolean;\n}\ninterface RunRow extends RawRun { ProcessName: string; EntityName: string; }\n\ninterface RawDetail { RecordID: string; Status: string; ResultPayload?: string; ErrorMessage?: string; }\ninterface DetailRow { RecordID: string; Status: string; Changes: RunDetailChange[]; ErrorMessage?: string; }\n\n@Component({\n selector: 'mj-record-process-history',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [MJButtonDirective],\n template: `\n @if (Mode === 'list') {\n <div class=\"rph-bar\">\n <h3 class=\"rph-title\">Run history</h3><span class=\"rph-spacer\"></span>\n <button mjButton variant=\"flat\" (click)=\"reload()\"><i class=\"fa-solid fa-arrows-rotate\"></i> Refresh</button>\n </div>\n @if (Loading) {\n <div class=\"rph-msg\"><i class=\"fa-solid fa-circle-notch fa-spin\"></i> Loading…</div>\n } @else if (Runs.length === 0) {\n <div class=\"rph-msg\">No runs yet.</div>\n } @else {\n <div class=\"rph-table\">\n <div class=\"rph-tr rph-head\"><div>When</div><div>Operation</div><div>Entity</div><div>By</div><div>Status</div><div>Counts</div></div>\n @for (r of Runs; track r.ID) {\n <div class=\"rph-tr\" (click)=\"openRun(r)\">\n <div class=\"rph-sub\">{{ fmt(r.StartTime) }}</div>\n <div class=\"rph-nm\">{{ r.ProcessName }}</div>\n <div>{{ r.EntityName }}</div>\n <div class=\"rph-sub\">{{ r.TriggeredBy || '—' }}</div>\n <div><span class=\"st\" [class.ok]=\"r.Status==='Completed'\" [class.err]=\"r.Status==='Failed'\"><span class=\"dot\"></span> {{ r.Status }}</span>@if (r.DryRun) { <span class=\"dryrun\" title=\"Compute-only preview — no changes were written\">Dry Run</span> }</div>\n <div class=\"counts\">\n <span class=\"cbadge ok\">{{ r.SuccessCount || 0 }} ok</span>\n @if (r.ErrorCount) { <span class=\"cbadge err\">{{ r.ErrorCount }} err</span> }\n @if (r.SkippedCount) { <span class=\"cbadge\">{{ r.SkippedCount }} skip</span> }\n </div>\n </div>\n }\n </div>\n }\n } @else {\n <div class=\"rph-bar\">\n <button mjButton variant=\"flat\" (click)=\"backToList()\"><i class=\"fa-solid fa-arrow-left\"></i> Back</button>\n <h3 class=\"rph-title\">{{ OpenRunRow?.ProcessName }} · {{ fmt(OpenRunRow?.StartTime) }}</h3>\n @if (OpenRunRow?.DryRun) { <span class=\"dryrun\" title=\"Compute-only preview — no changes were written\">Dry Run</span> }\n </div>\n @if (DetailLoading) {\n <div class=\"rph-msg\"><i class=\"fa-solid fa-circle-notch fa-spin\"></i> Loading details…</div>\n } @else if (Details.length === 0) {\n <div class=\"rph-msg\">No per-record details recorded for this run.</div>\n } @else {\n <div class=\"rph-details\">\n @for (d of Details; track d.RecordID) {\n <div class=\"rph-detrec\">\n <div class=\"rph-detkey\">\n <span class=\"st sm\" [class.ok]=\"d.Status==='Succeeded'\" [class.err]=\"d.Status==='Failed'\" [class.muted]=\"d.Status==='Skipped'\"><span class=\"dot\"></span> {{ d.Status }}</span>\n <div class=\"rph-rk\">{{ d.RecordID }}</div>\n </div>\n <div class=\"rph-changes\">\n @for (c of d.Changes; track c.Field) {\n <div class=\"rph-chg\"><span class=\"f\">{{ c.Field }}</span>\n <span class=\"o\">{{ disp(c.OldValue) }}</span><i class=\"fa-solid fa-arrow-right-long ar\"></i><span class=\"nw\">{{ disp(c.NewValue) }}</span></div>\n }\n @if (d.ErrorMessage) { <div class=\"rph-chg err-msg\">{{ d.ErrorMessage }}</div> }\n @if (d.Changes.length === 0 && !d.ErrorMessage) { <span class=\"rph-sub\">no changes</span> }\n </div>\n </div>\n }\n </div>\n }\n }\n `,\n styles: [`\n :host{display:block;color:var(--mj-text-primary)}\n .rph-bar{display:flex;align-items:center;gap:14px;margin-bottom:16px}\n .rph-title{margin:0;font-size:16px;font-weight:700}\n .rph-spacer{flex:1}\n .rph-bar button i{margin-right:6px}\n .rph-msg{padding:36px;text-align:center;color:var(--mj-text-muted)}\n .rph-table{border:1px solid var(--mj-border-subtle);border-radius:var(--mj-radius-md,10px);overflow:hidden}\n .rph-tr{display:grid;grid-template-columns:1.3fr 1.6fr 1.2fr .9fr 1fr 1.4fr;gap:14px;align-items:center;padding:12px 16px;border-bottom:1px solid var(--mj-border-subtle);font-size:13px;cursor:pointer}\n .rph-tr:last-child{border-bottom:none}\n .rph-tr:not(.rph-head):hover{background:var(--mj-bg-surface-hover)}\n .rph-head{background:var(--mj-bg-surface-sunken);font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--mj-text-muted);font-weight:700;cursor:default}\n .rph-nm{font-weight:600}\n .rph-sub{color:var(--mj-text-muted);font-size:12px}\n .st{display:inline-flex;align-items:center;gap:7px;font-weight:600;font-size:12.5px}\n .st .dot{width:8px;height:8px;border-radius:50%;background:var(--mj-text-disabled)}\n .st.ok .dot{background:var(--mj-status-success)} .st.ok{color:var(--mj-status-success-text)}\n .st.err .dot{background:var(--mj-status-error)} .st.err{color:var(--mj-status-error-text)}\n .st.muted{color:var(--mj-text-muted)}\n .st.sm{font-size:11.5px}\n .counts{display:flex;gap:6px;flex-wrap:wrap}\n .cbadge{font-size:11px;font-weight:700;padding:2px 8px;border-radius:6px;background:var(--mj-bg-surface-sunken);color:var(--mj-text-secondary)}\n .dryrun{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;padding:2px 7px;border-radius:6px;margin-left:6px;background:var(--mj-status-info-bg);color:var(--mj-status-info-text);border:1px solid var(--mj-status-info-border)}\n .cbadge.ok{background:var(--mj-status-success-bg);color:var(--mj-status-success-text)}\n .cbadge.err{background:var(--mj-status-error-bg);color:var(--mj-status-error-text)}\n .rph-details{border:1px solid var(--mj-border-subtle);border-radius:var(--mj-radius-md,10px);overflow:hidden;max-height:560px;overflow-y:auto}\n .rph-detrec{display:grid;grid-template-columns:200px 1fr;gap:14px;padding:11px 16px;border-bottom:1px solid var(--mj-border-subtle)}\n .rph-detrec:last-child{border-bottom:none}\n .rph-detkey{display:flex;flex-direction:column;gap:5px}\n .rph-rk{font-family:var(--mj-font-mono,monospace);font-size:11px;color:var(--mj-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n .rph-chg{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:13px;padding:2px 0}\n .rph-chg .f{font-weight:700;min-width:120px}\n .rph-chg .o{color:var(--mj-text-muted);text-decoration:line-through}\n .rph-chg .nw{color:var(--mj-status-success-text);font-weight:600}\n .rph-chg .ar{color:var(--mj-text-disabled);font-size:11px}\n .rph-chg.err-msg{color:var(--mj-status-error-text)}\n `],\n})\nexport class RecordProcessHistoryComponent extends BaseAngularComponent implements OnInit, OnDestroy {\n private cdr = inject(ChangeDetectorRef);\n\n /** Optional: show runs for one Record Process only. */\n @Input() RecordProcessID: string | null = null;\n\n public Mode: 'list' | 'detail' = 'list';\n public Loading = true;\n public DetailLoading = false;\n public Runs: RunRow[] = [];\n public Details: DetailRow[] = [];\n public OpenRunRow: RunRow | null = null;\n\n /**\n * There is no RecordProcess BaseEngine to observe — Process Runs is an unbounded log table, so we don't\n * bulk-cache it. Instead we re-query on demand AND keep the list live by listening for BaseEntity save/\n * delete events on the run tables: a run started/finished anywhere (this view, the editor preview, a grid\n * runner, the scheduler) fires an event, and we debounce-reload so history never goes stale behind a run.\n */\n private eventSub?: Subscription;\n private pendingReload = false;\n private reloadTimer: ReturnType<typeof setTimeout> | null = null;\n\n async ngOnInit(): Promise<void> {\n await this.reload();\n this.subscribeToRunChanges();\n }\n\n ngOnDestroy(): void {\n this.eventSub?.unsubscribe();\n if (this.reloadTimer) {\n clearTimeout(this.reloadTimer);\n }\n }\n\n /** Subscribe to BaseEntity events and debounce-reload whenever a Process Run (or its details) changes. */\n private subscribeToRunChanges(): void {\n this.eventSub = MJGlobal.Instance.GetEventListener(false).subscribe((event: MJEvent) => {\n if (event.event !== MJEventType.ComponentEvent || event.eventCode !== BaseEntity.BaseEventCode) {\n return;\n }\n const e = event.args as BaseEntityEvent;\n if (e.type !== 'save' && e.type !== 'delete' && e.type !== 'remote-invalidate') {\n return;\n }\n const name = e.baseEntity?.EntityInfo?.Name ?? e.entityName;\n if (name === 'MJ: Process Runs' || name === 'MJ: Process Run Details') {\n this.scheduleReload();\n }\n });\n }\n\n /** Debounce a burst of run/detail/checkpoint saves into a single reload; defer if the user is drilled into a detail. */\n private scheduleReload(): void {\n if (this.Mode !== 'list') {\n this.pendingReload = true; // refresh when they return to the list — don't yank them out of a drill-in\n return;\n }\n if (this.reloadTimer) {\n clearTimeout(this.reloadTimer);\n }\n this.reloadTimer = setTimeout(() => {\n this.reloadTimer = null;\n void this.reload();\n }, 400);\n }\n\n async reload(): Promise<void> {\n this.Loading = true; this.cdr.detectChanges();\n const rv = this.Provider ? RunView.FromMetadataProvider(this.Provider) : new RunView();\n const filter = this.RecordProcessID ? `RecordProcessID='${this.RecordProcessID}'` : '';\n const [runs, procs] = await rv.RunViews([\n {\n EntityName: 'MJ: Process Runs',\n Fields: ['ID', 'RecordProcessID', 'EntityID', 'Status', 'StartTime', 'SuccessCount', 'ErrorCount', 'SkippedCount', 'TotalItemCount', 'TriggeredBy', 'SourceType', 'DryRun'],\n ExtraFilter: filter, OrderBy: 'StartTime DESC', MaxRows: 200, ResultType: 'simple',\n },\n { EntityName: 'MJ: Record Processes', Fields: ['ID', 'Name'], ResultType: 'simple' },\n ]);\n // Normalized-UUID keys → O(1), case-safe across SQL Server (upper) / PostgreSQL (lower).\n const nameByID = new Map<string, string>();\n if (procs.Success) for (const p of procs.Results as Array<{ ID: string; Name: string }>) nameByID.set(NormalizeUUID(p.ID), p.Name);\n const rawRuns = runs.Success ? (runs.Results as RawRun[]) : [];\n this.Runs = rawRuns.map((r) => ({\n ...r,\n ProcessName: nameByID.get(NormalizeUUID(r.RecordProcessID)) ?? '(deleted)',\n EntityName: this.ProviderToUse.EntityByID(r.EntityID)?.Name ?? r.EntityID,\n }));\n this.Loading = false;\n this.cdr.detectChanges();\n }\n\n async openRun(run: RunRow): Promise<void> {\n this.OpenRunRow = run; this.Mode = 'detail'; this.DetailLoading = true; this.Details = []; this.cdr.detectChanges();\n const rv = this.Provider ? RunView.FromMetadataProvider(this.Provider) : new RunView();\n const result = await rv.RunView<RawDetail>({\n EntityName: 'MJ: Process Run Details',\n Fields: ['RecordID', 'Status', 'ResultPayload', 'ErrorMessage'],\n ExtraFilter: `ProcessRunID='${run.ID}'`, OrderBy: 'Status, RecordID', MaxRows: 1000, ResultType: 'simple',\n });\n this.Details = (result.Success ? (result.Results ?? []) : []).map((d) => ({\n RecordID: d.RecordID, Status: d.Status, Changes: parseAppliedRunDetailChanges(d.ResultPayload), ErrorMessage: d.ErrorMessage,\n }));\n this.DetailLoading = false;\n this.cdr.detectChanges();\n }\n\n backToList(): void {\n this.Mode = 'list';\n this.OpenRunRow = null;\n this.cdr.detectChanges();\n if (this.pendingReload) {\n this.pendingReload = false;\n void this.reload(); // a run changed while we were drilled into a detail — refresh now\n }\n }\n\n fmt(value?: string | Date): string {\n if (!value) return '—';\n const d = value instanceof Date ? value : new Date(value);\n return isNaN(d.getTime()) ? String(value) : d.toLocaleString();\n }\n disp(value: unknown): string { return displayRunValue(value); }\n}\n"]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The Bulk Operations hub: lists the org's Record Processes, lets a user create / edit one
|
|
3
|
+
* (via the embedded {@link RecordProcessEditorComponent}), and run any of them on demand (mounting the
|
|
4
|
+
* same dry-run → confirm runner used from a grid). Generic + reusable — Explorer's Bulk Operations app
|
|
5
|
+
* hosts it, but any MJ app can drop it in.
|
|
6
|
+
* @module @memberjunction/ng-record-process-studio
|
|
7
|
+
*/
|
|
8
|
+
import { OnInit } from '@angular/core';
|
|
9
|
+
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
10
|
+
import { type EntityActionUXContext, type EntityActionUXResult } from '@memberjunction/ng-entity-action-ux';
|
|
11
|
+
import * as i0 from "@angular/core";
|
|
12
|
+
/** A row of the process list (read-only projection). */
|
|
13
|
+
interface ProcessRow {
|
|
14
|
+
ID: string;
|
|
15
|
+
Name: string;
|
|
16
|
+
Description?: string;
|
|
17
|
+
Entity: string;
|
|
18
|
+
EntityID: string;
|
|
19
|
+
WorkType: string;
|
|
20
|
+
Status: string;
|
|
21
|
+
ScopeType: string;
|
|
22
|
+
ScopeFilter?: string;
|
|
23
|
+
ScopeViewID?: string;
|
|
24
|
+
ScopeListID?: string;
|
|
25
|
+
}
|
|
26
|
+
export declare class RecordProcessStudioComponent extends BaseAngularComponent implements OnInit {
|
|
27
|
+
private cdr;
|
|
28
|
+
/** Optional: restrict the hub to processes for one entity (e.g. when embedded on an entity page). */
|
|
29
|
+
EntityID: string | null;
|
|
30
|
+
Mode: 'list' | 'edit';
|
|
31
|
+
EditingID: string | null;
|
|
32
|
+
Loading: boolean;
|
|
33
|
+
Search: string;
|
|
34
|
+
Processes: ProcessRow[];
|
|
35
|
+
RunDriver: {
|
|
36
|
+
DriverClass: string;
|
|
37
|
+
Context: EntityActionUXContext;
|
|
38
|
+
} | null;
|
|
39
|
+
ngOnInit(): Promise<void>;
|
|
40
|
+
get Filtered(): ProcessRow[];
|
|
41
|
+
reload(): Promise<void>;
|
|
42
|
+
onSearch(event: Event): void;
|
|
43
|
+
New(): void;
|
|
44
|
+
Edit(p: ProcessRow): void;
|
|
45
|
+
backToList(): void;
|
|
46
|
+
onSaved(): Promise<void>;
|
|
47
|
+
/** Runs a process on demand against its default scope, via the dry-run → confirm runner. */
|
|
48
|
+
Run(p: ProcessRow): void;
|
|
49
|
+
onRunDone(_r: EntityActionUXResult): void;
|
|
50
|
+
workLabel(workType: string): string;
|
|
51
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<RecordProcessStudioComponent, never>;
|
|
52
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<RecordProcessStudioComponent, "mj-record-process-studio", never, { "EntityID": { "alias": "EntityID"; "required": false; }; }, {}, never, never, true, never>;
|
|
53
|
+
}
|
|
54
|
+
export {};
|
|
55
|
+
//# sourceMappingURL=record-process-studio.component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record-process-studio.component.d.ts","sourceRoot":"","sources":["../../../src/lib/record-process-studio/record-process-studio.component.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAgE,MAAM,EAAU,MAAM,eAAe,CAAC;AAC7G,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAErE,OAAO,EAA+B,KAAK,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;;AAIzI,wDAAwD;AACxD,UAAU,UAAU;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBA+Fa,4BAA6B,SAAQ,oBAAqB,YAAW,MAAM;IACpF,OAAO,CAAC,GAAG,CAA6B;IAExC,qGAAqG;IAC5F,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEjC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAU;IAC/B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAQ;IAChC,OAAO,UAAQ;IACf,MAAM,SAAM;IACZ,SAAS,EAAE,UAAU,EAAE,CAAM;IAC7B,SAAS,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,qBAAqB,CAAA;KAAE,GAAG,IAAI,CAAQ;IAElF,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,IAAI,QAAQ,IAAI,UAAU,EAAE,CAI3B;IAEK,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB7B,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAE5B,GAAG,IAAI,IAAI;IACX,IAAI,CAAC,CAAC,EAAE,UAAU,GAAG,IAAI;IACzB,UAAU,IAAI,IAAI;IACZ,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAE9B,4FAA4F;IAC5F,GAAG,CAAC,CAAC,EAAE,UAAU,GAAG,IAAI;IAmBxB,SAAS,CAAC,EAAE,EAAE,oBAAoB,GAAG,IAAI;IAEzC,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;yCApE1B,4BAA4B;2CAA5B,4BAA4B;CAuExC"}
|