@memberjunction/ng-record-process-studio 0.0.1 → 5.44.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.
@@ -0,0 +1,485 @@
1
+ /**
2
+ * @fileoverview The authoring surface for a single Record Process (a reusable bulk operation). Edits the
3
+ * basics + target entity + scope, and — for the FieldRules work type — embeds the visual
4
+ * `<mj-field-rules-builder>` so a business user defines rules without touching JSON. An inline "Preview"
5
+ * runs the same server dry-run + diff runner used at invocation time.
6
+ *
7
+ * Reused in two places: standalone in the Bulk Operations hub (owns its Save/Preview toolbar), and inside
8
+ * the custom Record Process entity form (toolbar hidden — the form owns Save).
9
+ * @module @memberjunction/ng-record-process-studio
10
+ */
11
+ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, inject } from '@angular/core';
12
+ import { BaseAngularComponent } from '@memberjunction/ng-base-types';
13
+ import { SafeJSONParse } from '@memberjunction/global';
14
+ import { FieldRulesBuilderComponent, EntityActionUXHostComponent } from '@memberjunction/ng-entity-action-ux';
15
+ import { MJButtonDirective } from '@memberjunction/ng-ui-components';
16
+ import * as i0 from "@angular/core";
17
+ const _forTrack0 = ($index, $item) => $item.ID;
18
+ function RecordProcessEditorComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
19
+ i0.ɵɵelementStart(0, "div", 0);
20
+ i0.ɵɵelement(1, "i", 1);
21
+ i0.ɵɵtext(2, " Loading\u2026");
22
+ i0.ɵɵelementEnd();
23
+ } }
24
+ function RecordProcessEditorComponent_Conditional_1_For_40_Template(rf, ctx) { if (rf & 1) {
25
+ i0.ɵɵelementStart(0, "option", 17);
26
+ i0.ɵɵtext(1);
27
+ i0.ɵɵelementEnd();
28
+ } if (rf & 2) {
29
+ const e_r3 = ctx.$implicit;
30
+ i0.ɵɵproperty("value", e_r3.ID);
31
+ i0.ɵɵadvance();
32
+ i0.ɵɵtextInterpolate(e_r3.DisplayName || e_r3.Name);
33
+ } }
34
+ function RecordProcessEditorComponent_Conditional_1_Conditional_57_Template(rf, ctx) { if (rf & 1) {
35
+ const _r4 = i0.ɵɵgetCurrentView();
36
+ i0.ɵɵelementStart(0, "div", 13)(1, "label");
37
+ i0.ɵɵtext(2, "Filter ");
38
+ i0.ɵɵelementStart(3, "span", 24);
39
+ i0.ɵɵtext(4, "\u2014 overridden by the grid selection at run time");
40
+ i0.ɵɵelementEnd()();
41
+ i0.ɵɵelementStart(5, "input", 25);
42
+ i0.ɵɵlistener("input", function RecordProcessEditorComponent_Conditional_1_Conditional_57_Template_input_input_5_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.set("ScopeFilter", $event)); });
43
+ i0.ɵɵelementEnd()();
44
+ } if (rf & 2) {
45
+ const ctx_r1 = i0.ɵɵnextContext(2);
46
+ i0.ɵɵadvance(5);
47
+ i0.ɵɵproperty("value", ctx_r1.Record.ScopeFilter || "");
48
+ } }
49
+ function RecordProcessEditorComponent_Conditional_1_Conditional_58_Conditional_6_Template(rf, ctx) { if (rf & 1) {
50
+ i0.ɵɵelementStart(0, "span", 26);
51
+ i0.ɵɵtext(1, "choose an entity first");
52
+ i0.ɵɵelementEnd();
53
+ } }
54
+ function RecordProcessEditorComponent_Conditional_1_Conditional_58_Conditional_7_Template(rf, ctx) { if (rf & 1) {
55
+ const _r5 = i0.ɵɵgetCurrentView();
56
+ i0.ɵɵelementStart(0, "mj-field-rules-builder", 28);
57
+ i0.ɵɵlistener("ValueChange", function RecordProcessEditorComponent_Conditional_1_Conditional_58_Conditional_7_Template_mj_field_rules_builder_ValueChange_0_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.onRulesChange($event)); })("ValidChange", function RecordProcessEditorComponent_Conditional_1_Conditional_58_Conditional_7_Template_mj_field_rules_builder_ValidChange_0_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.RulesValid = $event); });
58
+ i0.ɵɵelementEnd();
59
+ } if (rf & 2) {
60
+ const ctx_r1 = i0.ɵɵnextContext(3);
61
+ i0.ɵɵproperty("EntityName", ctx_r1.SelectedEntityName)("Value", ctx_r1.RuleSet)("Provider", ctx_r1.Provider);
62
+ } }
63
+ function RecordProcessEditorComponent_Conditional_1_Conditional_58_Template(rf, ctx) { if (rf & 1) {
64
+ i0.ɵɵelementStart(0, "section", 3)(1, "div", 4)(2, "span", 5);
65
+ i0.ɵɵtext(3, "3");
66
+ i0.ɵɵelementEnd();
67
+ i0.ɵɵelementStart(4, "h3");
68
+ i0.ɵɵtext(5, "Rules");
69
+ i0.ɵɵelementEnd();
70
+ i0.ɵɵconditionalCreate(6, RecordProcessEditorComponent_Conditional_1_Conditional_58_Conditional_6_Template, 2, 0, "span", 26);
71
+ i0.ɵɵelementEnd();
72
+ i0.ɵɵconditionalCreate(7, RecordProcessEditorComponent_Conditional_1_Conditional_58_Conditional_7_Template, 1, 3, "mj-field-rules-builder", 27);
73
+ i0.ɵɵelementEnd();
74
+ } if (rf & 2) {
75
+ const ctx_r1 = i0.ɵɵnextContext(2);
76
+ i0.ɵɵadvance(6);
77
+ i0.ɵɵconditional(!ctx_r1.SelectedEntityName ? 6 : -1);
78
+ i0.ɵɵadvance();
79
+ i0.ɵɵconditional(ctx_r1.SelectedEntityName ? 7 : -1);
80
+ } }
81
+ function RecordProcessEditorComponent_Conditional_1_Conditional_59_Conditional_9_Template(rf, ctx) { if (rf & 1) {
82
+ i0.ɵɵelementStart(0, "span", 34);
83
+ i0.ɵɵelement(1, "i", 35);
84
+ i0.ɵɵtext(2);
85
+ i0.ɵɵelementEnd();
86
+ } if (rf & 2) {
87
+ const ctx_r1 = i0.ɵɵnextContext(3);
88
+ i0.ɵɵadvance(2);
89
+ i0.ɵɵtextInterpolate1(" ", ctx_r1.ErrorMessage);
90
+ } }
91
+ function RecordProcessEditorComponent_Conditional_1_Conditional_59_Template(rf, ctx) { if (rf & 1) {
92
+ const _r6 = i0.ɵɵgetCurrentView();
93
+ i0.ɵɵelementStart(0, "div", 22)(1, "button", 29);
94
+ i0.ɵɵlistener("click", function RecordProcessEditorComponent_Conditional_1_Conditional_59_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.Save()); });
95
+ i0.ɵɵelement(2, "i", 30);
96
+ i0.ɵɵtext(3, " Save ");
97
+ i0.ɵɵelementEnd();
98
+ i0.ɵɵelementStart(4, "button", 31);
99
+ i0.ɵɵlistener("click", function RecordProcessEditorComponent_Conditional_1_Conditional_59_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.Preview()); });
100
+ i0.ɵɵelement(5, "i", 32);
101
+ i0.ɵɵtext(6, " Preview changes");
102
+ i0.ɵɵelementEnd();
103
+ i0.ɵɵelementStart(7, "button", 33);
104
+ i0.ɵɵlistener("click", function RecordProcessEditorComponent_Conditional_1_Conditional_59_Template_button_click_7_listener() { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.Cancel()); });
105
+ i0.ɵɵtext(8, "Cancel");
106
+ i0.ɵɵelementEnd();
107
+ i0.ɵɵconditionalCreate(9, RecordProcessEditorComponent_Conditional_1_Conditional_59_Conditional_9_Template, 3, 1, "span", 34);
108
+ i0.ɵɵelementEnd();
109
+ } if (rf & 2) {
110
+ const ctx_r1 = i0.ɵɵnextContext(2);
111
+ i0.ɵɵadvance();
112
+ i0.ɵɵproperty("disabled", !ctx_r1.CanSave || ctx_r1.Saving);
113
+ i0.ɵɵadvance();
114
+ i0.ɵɵclassProp("fa-floppy-disk", !ctx_r1.Saving)("fa-circle-notch", ctx_r1.Saving)("fa-spin", ctx_r1.Saving);
115
+ i0.ɵɵadvance(2);
116
+ i0.ɵɵproperty("disabled", !ctx_r1.CanPreview || ctx_r1.Saving);
117
+ i0.ɵɵadvance(5);
118
+ i0.ɵɵconditional(ctx_r1.ErrorMessage ? 9 : -1);
119
+ } }
120
+ function RecordProcessEditorComponent_Conditional_1_Conditional_60_Template(rf, ctx) { if (rf & 1) {
121
+ const _r7 = i0.ɵɵgetCurrentView();
122
+ i0.ɵɵelementStart(0, "mj-entity-action-ux-host", 36);
123
+ i0.ɵɵlistener("Completed", function RecordProcessEditorComponent_Conditional_1_Conditional_60_Template_mj_entity_action_ux_host_Completed_0_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onPreviewDone($event)); })("Cancelled", function RecordProcessEditorComponent_Conditional_1_Conditional_60_Template_mj_entity_action_ux_host_Cancelled_0_listener() { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.PreviewDriver = null); });
124
+ i0.ɵɵelementEnd();
125
+ } if (rf & 2) {
126
+ const ctx_r1 = i0.ɵɵnextContext(2);
127
+ i0.ɵɵproperty("DriverClass", ctx_r1.PreviewDriver.DriverClass)("Context", ctx_r1.PreviewDriver.Context);
128
+ } }
129
+ function RecordProcessEditorComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
130
+ const _r1 = i0.ɵɵgetCurrentView();
131
+ i0.ɵɵelementStart(0, "div", 2)(1, "section", 3)(2, "div", 4)(3, "span", 5);
132
+ i0.ɵɵtext(4, "1");
133
+ i0.ɵɵelementEnd();
134
+ i0.ɵɵelementStart(5, "h3");
135
+ i0.ɵɵtext(6, "Basics");
136
+ i0.ɵɵelementEnd()();
137
+ i0.ɵɵelementStart(7, "div", 6)(8, "div", 7)(9, "label");
138
+ i0.ɵɵtext(10, "Name");
139
+ i0.ɵɵelementEnd();
140
+ i0.ɵɵelementStart(11, "input", 8);
141
+ i0.ɵɵlistener("input", function RecordProcessEditorComponent_Conditional_1_Template_input_input_11_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.set("Name", $event)); });
142
+ i0.ɵɵelementEnd()();
143
+ i0.ɵɵelementStart(12, "div", 7)(13, "label");
144
+ i0.ɵɵtext(14, "Status");
145
+ i0.ɵɵelementEnd();
146
+ i0.ɵɵelementStart(15, "select", 9);
147
+ i0.ɵɵlistener("change", function RecordProcessEditorComponent_Conditional_1_Template_select_change_15_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.set("Status", $event)); });
148
+ i0.ɵɵelementStart(16, "option", 10);
149
+ i0.ɵɵtext(17, "Draft");
150
+ i0.ɵɵelementEnd();
151
+ i0.ɵɵelementStart(18, "option", 11);
152
+ i0.ɵɵtext(19, "Active");
153
+ i0.ɵɵelementEnd();
154
+ i0.ɵɵelementStart(20, "option", 12);
155
+ i0.ɵɵtext(21, "Disabled");
156
+ i0.ɵɵelementEnd()()()();
157
+ i0.ɵɵelementStart(22, "div", 13)(23, "label");
158
+ i0.ɵɵtext(24, "Description");
159
+ i0.ɵɵelementEnd();
160
+ i0.ɵɵelementStart(25, "input", 14);
161
+ i0.ɵɵlistener("input", function RecordProcessEditorComponent_Conditional_1_Template_input_input_25_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.set("Description", $event)); });
162
+ i0.ɵɵelementEnd()()();
163
+ i0.ɵɵelementStart(26, "section", 3)(27, "div", 4)(28, "span", 5);
164
+ i0.ɵɵtext(29, "2");
165
+ i0.ɵɵelementEnd();
166
+ i0.ɵɵelementStart(30, "h3");
167
+ i0.ɵɵtext(31, "What it acts on");
168
+ i0.ɵɵelementEnd()();
169
+ i0.ɵɵelementStart(32, "div", 15)(33, "div", 7)(34, "label");
170
+ i0.ɵɵtext(35, "Entity");
171
+ i0.ɵɵelementEnd();
172
+ i0.ɵɵelementStart(36, "select", 9);
173
+ i0.ɵɵlistener("change", function RecordProcessEditorComponent_Conditional_1_Template_select_change_36_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onEntityChange($event)); });
174
+ i0.ɵɵelementStart(37, "option", 16);
175
+ i0.ɵɵtext(38, "\u2014 choose an entity \u2014");
176
+ i0.ɵɵelementEnd();
177
+ i0.ɵɵrepeaterCreate(39, RecordProcessEditorComponent_Conditional_1_For_40_Template, 2, 2, "option", 17, _forTrack0);
178
+ i0.ɵɵelementEnd()();
179
+ i0.ɵɵelementStart(41, "div", 7)(42, "label");
180
+ i0.ɵɵtext(43, "Default scope");
181
+ i0.ɵɵelementEnd();
182
+ i0.ɵɵelementStart(44, "select", 9);
183
+ i0.ɵɵlistener("change", function RecordProcessEditorComponent_Conditional_1_Template_select_change_44_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.set("ScopeType", $event)); });
184
+ i0.ɵɵelementStart(45, "option", 18);
185
+ i0.ɵɵtext(46, "Filter");
186
+ i0.ɵɵelementEnd();
187
+ i0.ɵɵelementStart(47, "option", 19);
188
+ i0.ɵɵtext(48, "A view");
189
+ i0.ɵɵelementEnd();
190
+ i0.ɵɵelementStart(49, "option", 20);
191
+ i0.ɵɵtext(50, "A list");
192
+ i0.ɵɵelementEnd()()();
193
+ i0.ɵɵelementStart(51, "div", 7)(52, "label");
194
+ i0.ɵɵtext(53, "Work type");
195
+ i0.ɵɵelementEnd();
196
+ i0.ɵɵelementStart(54, "select", 9);
197
+ i0.ɵɵlistener("change", function RecordProcessEditorComponent_Conditional_1_Template_select_change_54_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.set("WorkType", $event)); });
198
+ i0.ɵɵelementStart(55, "option", 21);
199
+ i0.ɵɵtext(56, "Field Rules");
200
+ i0.ɵɵelementEnd()()()();
201
+ i0.ɵɵconditionalCreate(57, RecordProcessEditorComponent_Conditional_1_Conditional_57_Template, 6, 1, "div", 13);
202
+ i0.ɵɵelementEnd();
203
+ i0.ɵɵconditionalCreate(58, RecordProcessEditorComponent_Conditional_1_Conditional_58_Template, 8, 2, "section", 3);
204
+ i0.ɵɵconditionalCreate(59, RecordProcessEditorComponent_Conditional_1_Conditional_59_Template, 10, 9, "div", 22);
205
+ i0.ɵɵelementEnd();
206
+ i0.ɵɵconditionalCreate(60, RecordProcessEditorComponent_Conditional_1_Conditional_60_Template, 1, 2, "mj-entity-action-ux-host", 23);
207
+ } if (rf & 2) {
208
+ const ctx_r1 = i0.ɵɵnextContext();
209
+ i0.ɵɵadvance(11);
210
+ i0.ɵɵproperty("value", ctx_r1.Record.Name || "");
211
+ i0.ɵɵadvance(4);
212
+ i0.ɵɵproperty("value", ctx_r1.Record.Status || "Active");
213
+ i0.ɵɵadvance(10);
214
+ i0.ɵɵproperty("value", ctx_r1.Record.Description || "");
215
+ i0.ɵɵadvance(11);
216
+ i0.ɵɵproperty("value", ctx_r1.Record.EntityID || "");
217
+ i0.ɵɵadvance(3);
218
+ i0.ɵɵrepeater(ctx_r1.Entities);
219
+ i0.ɵɵadvance(5);
220
+ i0.ɵɵproperty("value", ctx_r1.Record.ScopeType || "Filter");
221
+ i0.ɵɵadvance(10);
222
+ i0.ɵɵproperty("value", ctx_r1.Record.WorkType || "FieldRules");
223
+ i0.ɵɵadvance(3);
224
+ i0.ɵɵconditional(ctx_r1.Record.ScopeType === "Filter" ? 57 : -1);
225
+ i0.ɵɵadvance();
226
+ i0.ɵɵconditional(ctx_r1.Record.WorkType === "FieldRules" ? 58 : -1);
227
+ i0.ɵɵadvance();
228
+ i0.ɵɵconditional(ctx_r1.ShowToolbar ? 59 : -1);
229
+ i0.ɵɵadvance();
230
+ i0.ɵɵconditional(ctx_r1.PreviewDriver ? 60 : -1);
231
+ } }
232
+ export class RecordProcessEditorComponent extends BaseAngularComponent {
233
+ cdr = inject(ChangeDetectorRef);
234
+ /** The process being edited. Set this OR `RecordProcessID` (or neither, for a new draft). */
235
+ Record = null;
236
+ /** Load an existing process by ID (alternative to passing `Record`). */
237
+ RecordProcessID = null;
238
+ /** Default EntityID for a brand-new process (e.g. the grid the user came from). */
239
+ DefaultEntityID = null;
240
+ /** Show the built-in Save / Preview / Cancel toolbar. Set false when an outer form owns Save. */
241
+ ShowToolbar = true;
242
+ /** Emitted after a successful Save (the persisted record). */
243
+ Saved = new EventEmitter();
244
+ /** Emitted when the user cancels. */
245
+ Cancelled = new EventEmitter();
246
+ Entities = [];
247
+ SelectedEntityName = '';
248
+ RuleSet = { Rules: [] };
249
+ RulesValid = true;
250
+ Saving = false;
251
+ ErrorMessage = '';
252
+ PreviewDriver = null;
253
+ get CanSave() { return !!this.Record?.Name && !!this.Record?.EntityID && this.RulesValid; }
254
+ get CanPreview() { return this.CanSave && this.RuleSet.Rules.length > 0; }
255
+ async ngOnInit() {
256
+ const provider = this.ProviderToUse;
257
+ this.Entities = [...provider.Entities].sort((a, b) => (a.DisplayName || a.Name).localeCompare(b.DisplayName || b.Name));
258
+ await this.loadRecord();
259
+ }
260
+ /** Resolves the record to edit: passed instance → by ID → a fresh draft. */
261
+ async loadRecord() {
262
+ const provider = this.ProviderToUse;
263
+ if (!this.Record) {
264
+ this.Record = await provider.GetEntityObject('MJ: Record Processes', provider.CurrentUser);
265
+ if (this.RecordProcessID) {
266
+ await this.Record.Load(this.RecordProcessID);
267
+ }
268
+ else {
269
+ this.Record.NewRecord();
270
+ this.Record.WorkType = 'FieldRules';
271
+ this.Record.ScopeType = 'Filter';
272
+ this.Record.Status = 'Draft';
273
+ if (this.DefaultEntityID)
274
+ this.Record.EntityID = this.DefaultEntityID;
275
+ }
276
+ }
277
+ this.RuleSet = this.Record.Configuration ? (SafeJSONParse(this.Record.Configuration) ?? { Rules: [] }) : { Rules: [] };
278
+ this.resolveEntityName();
279
+ this.cdr.detectChanges();
280
+ }
281
+ /** Generic setter for the simple string/enum fields, driven by the template inputs. */
282
+ set(field, event) {
283
+ if (!this.Record)
284
+ return;
285
+ const value = event.target.value;
286
+ // Strongly-typed assignment per field (no dynamic Set()).
287
+ switch (field) {
288
+ case 'Name':
289
+ this.Record.Name = value;
290
+ break;
291
+ case 'Description':
292
+ this.Record.Description = value;
293
+ break;
294
+ case 'Status':
295
+ this.Record.Status = value;
296
+ break;
297
+ case 'ScopeType':
298
+ this.Record.ScopeType = value;
299
+ break;
300
+ case 'ScopeFilter':
301
+ this.Record.ScopeFilter = value;
302
+ break;
303
+ case 'WorkType':
304
+ this.Record.WorkType = value;
305
+ break;
306
+ }
307
+ this.cdr.detectChanges();
308
+ }
309
+ onEntityChange(event) {
310
+ if (!this.Record)
311
+ return;
312
+ this.Record.EntityID = event.target.value;
313
+ this.resolveEntityName();
314
+ this.cdr.detectChanges();
315
+ }
316
+ onRulesChange(ruleSet) {
317
+ this.RuleSet = ruleSet;
318
+ if (this.Record)
319
+ this.Record.Configuration = JSON.stringify(ruleSet);
320
+ this.cdr.detectChanges();
321
+ }
322
+ /** Saves the record (and emits). Returns success. */
323
+ async Save() {
324
+ if (!this.Record || !this.CanSave)
325
+ return false;
326
+ this.Saving = true;
327
+ this.ErrorMessage = '';
328
+ this.cdr.detectChanges();
329
+ this.Record.Configuration = JSON.stringify(this.RuleSet);
330
+ const ok = await this.Record.Save();
331
+ this.Saving = false;
332
+ if (ok) {
333
+ this.Saved.emit(this.Record);
334
+ }
335
+ else {
336
+ this.ErrorMessage = this.Record.LatestResult?.CompleteMessage ?? 'Save failed';
337
+ }
338
+ this.cdr.detectChanges();
339
+ return ok;
340
+ }
341
+ /** Saves (if needed) then mounts the dry-run runner against the process's default scope. */
342
+ async Preview() {
343
+ if (!this.Record)
344
+ return;
345
+ if (this.Record.Dirty || !this.Record.IsSaved) {
346
+ const ok = await this.Save();
347
+ if (!ok)
348
+ return;
349
+ }
350
+ const entity = this.ProviderToUse.EntityByID(this.Record.EntityID);
351
+ if (!entity) {
352
+ this.ErrorMessage = 'Target entity not found';
353
+ this.cdr.detectChanges();
354
+ return;
355
+ }
356
+ this.PreviewDriver = {
357
+ DriverClass: 'RecordProcessRunnerUX',
358
+ Context: {
359
+ EntityInfo: entity,
360
+ ScopeKind: this.Record.ScopeType?.toLowerCase() || 'filter',
361
+ Filter: this.Record.ScopeFilter ?? undefined,
362
+ ViewID: this.Record.ScopeViewID ?? undefined,
363
+ ListID: this.Record.ScopeListID ?? undefined,
364
+ Config: { RecordProcessID: this.Record.ID },
365
+ Provider: this.ProviderToUse,
366
+ ActionLabel: `Preview: ${this.Record.Name}`,
367
+ },
368
+ };
369
+ this.cdr.detectChanges();
370
+ }
371
+ onPreviewDone(_result) {
372
+ this.PreviewDriver = null;
373
+ this.cdr.detectChanges();
374
+ }
375
+ Cancel() { this.Cancelled.emit(); }
376
+ resolveEntityName() {
377
+ this.SelectedEntityName = this.Record?.EntityID ? (this.ProviderToUse.EntityByID(this.Record.EntityID)?.Name ?? '') : '';
378
+ }
379
+ static ɵfac = /*@__PURE__*/ (() => { let ɵRecordProcessEditorComponent_BaseFactory; return function RecordProcessEditorComponent_Factory(__ngFactoryType__) { return (ɵRecordProcessEditorComponent_BaseFactory || (ɵRecordProcessEditorComponent_BaseFactory = i0.ɵɵgetInheritedFactory(RecordProcessEditorComponent)))(__ngFactoryType__ || RecordProcessEditorComponent); }; })();
380
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RecordProcessEditorComponent, selectors: [["mj-record-process-editor"]], inputs: { Record: "Record", RecordProcessID: "RecordProcessID", DefaultEntityID: "DefaultEntityID", ShowToolbar: "ShowToolbar" }, outputs: { Saved: "Saved", Cancelled: "Cancelled" }, features: [i0.ɵɵInheritDefinitionFeature], decls: 2, vars: 1, consts: [[1, "rpe-loading"], [1, "fa-solid", "fa-circle-notch", "fa-spin"], [1, "rpe"], [1, "rpe-sec"], [1, "rpe-sec-h"], [1, "num"], [1, "rpe-grid2"], [1, "field"], ["placeholder", "e.g. Mark idle prompts for review", 1, "mj-input", 3, "input", "value"], [1, "mj-input", 3, "change", "value"], ["value", "Draft"], ["value", "Active"], ["value", "Disabled"], [1, "field", "rpe-mt"], ["placeholder", "What does this process do?", 1, "mj-input", 3, "input", "value"], [1, "rpe-grid3"], ["value", "", "disabled", ""], [3, "value"], ["value", "Filter"], ["value", "View"], ["value", "List"], ["value", "FieldRules"], [1, "rpe-bar"], [3, "DriverClass", "Context"], [1, "muted"], ["placeholder", "e.g. Status = 'Active'", 1, "mj-input", "mono", 3, "input", "value"], [1, "muted", "rpe-h-note"], [3, "EntityName", "Value", "Provider"], [3, "ValueChange", "ValidChange", "EntityName", "Value", "Provider"], ["mjButton", "", "variant", "primary", 3, "click", "disabled"], [1, "fa-solid"], ["mjButton", "", 3, "click", "disabled"], [1, "fa-solid", "fa-flask"], ["mjButton", "", "variant", "flat", 3, "click"], [1, "rpe-err"], [1, "fa-solid", "fa-triangle-exclamation"], [3, "Completed", "Cancelled", "DriverClass", "Context"]], template: function RecordProcessEditorComponent_Template(rf, ctx) { if (rf & 1) {
381
+ i0.ɵɵconditionalCreate(0, RecordProcessEditorComponent_Conditional_0_Template, 3, 0, "div", 0)(1, RecordProcessEditorComponent_Conditional_1_Template, 61, 10);
382
+ } if (rf & 2) {
383
+ i0.ɵɵconditional(!ctx.Record ? 0 : 1);
384
+ } }, dependencies: [FieldRulesBuilderComponent, EntityActionUXHostComponent, MJButtonDirective], styles: [".rpe-loading[_ngcontent-%COMP%]{padding:30px;color:var(--mj-text-muted);display:flex;align-items:center;gap:10px}\n .rpe[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:22px;color:var(--mj-text-primary)}\n .rpe-sec[_ngcontent-%COMP%]{background:var(--mj-bg-surface-card);border:1px solid var(--mj-border-subtle);border-radius:var(--mj-radius-md,10px);padding:18px 20px}\n .rpe-sec-h[_ngcontent-%COMP%]{display:flex;align-items:center;gap:11px;margin-bottom:16px}\n .rpe-sec-h[_ngcontent-%COMP%] h3[_ngcontent-%COMP%]{margin:0;font-size:16px;font-weight:700}\n .rpe-sec-h[_ngcontent-%COMP%] .num[_ngcontent-%COMP%]{width:23px;height:23px;border-radius:50%;background:var(--mj-brand-primary);color:var(--mj-text-inverse);display:grid;place-items:center;font-size:12px;font-weight:800}\n .rpe-h-note[_ngcontent-%COMP%]{margin-left:6px;font-size:12.5px}\n .rpe-grid2[_ngcontent-%COMP%]{display:grid;grid-template-columns:1fr 220px;gap:16px}\n .rpe-grid3[_ngcontent-%COMP%]{display:grid;grid-template-columns:1.4fr 1fr 1fr;gap:16px}\n .field[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:6px}\n .field[_ngcontent-%COMP%] label[_ngcontent-%COMP%]{font-size:12.5px;font-weight:600;color:var(--mj-text-secondary)}\n .rpe-mt[_ngcontent-%COMP%]{margin-top:16px}\n .mono[_ngcontent-%COMP%]{font-family:var(--mj-font-mono,monospace);font-size:12.5px}\n .muted[_ngcontent-%COMP%]{color:var(--mj-text-muted);font-weight:400}\n .rpe-bar[_ngcontent-%COMP%]{display:flex;align-items:center;gap:10px;padding-top:4px}\n .rpe-bar[_ngcontent-%COMP%] button[_ngcontent-%COMP%] i[_ngcontent-%COMP%]{margin-right:6px}\n .rpe-err[_ngcontent-%COMP%]{color:var(--mj-status-error-text);font-size:13px;display:flex;align-items:center;gap:7px}\n .rpe-err[_ngcontent-%COMP%] i[_ngcontent-%COMP%]{color:var(--mj-status-error)}"], changeDetection: 0 });
385
+ }
386
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RecordProcessEditorComponent, [{
387
+ type: Component,
388
+ args: [{ selector: 'mj-record-process-editor', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [FieldRulesBuilderComponent, EntityActionUXHostComponent, MJButtonDirective], template: `
389
+ @if (!Record) {
390
+ <div class="rpe-loading"><i class="fa-solid fa-circle-notch fa-spin"></i> Loading…</div>
391
+ } @else {
392
+ <div class="rpe">
393
+ <!-- BASICS -->
394
+ <section class="rpe-sec">
395
+ <div class="rpe-sec-h"><span class="num">1</span><h3>Basics</h3></div>
396
+ <div class="rpe-grid2">
397
+ <div class="field"><label>Name</label>
398
+ <input class="mj-input" [value]="Record.Name || ''" (input)="set('Name', $event)" placeholder="e.g. Mark idle prompts for review"></div>
399
+ <div class="field"><label>Status</label>
400
+ <select class="mj-input" [value]="Record.Status || 'Active'" (change)="set('Status', $event)">
401
+ <option value="Draft">Draft</option><option value="Active">Active</option><option value="Disabled">Disabled</option>
402
+ </select></div>
403
+ </div>
404
+ <div class="field rpe-mt"><label>Description</label>
405
+ <input class="mj-input" [value]="Record.Description || ''" (input)="set('Description', $event)" placeholder="What does this process do?"></div>
406
+ </section>
407
+
408
+ <!-- TARGET + SCOPE -->
409
+ <section class="rpe-sec">
410
+ <div class="rpe-sec-h"><span class="num">2</span><h3>What it acts on</h3></div>
411
+ <div class="rpe-grid3">
412
+ <div class="field"><label>Entity</label>
413
+ <select class="mj-input" [value]="Record.EntityID || ''" (change)="onEntityChange($event)">
414
+ <option value="" disabled>— choose an entity —</option>
415
+ @for (e of Entities; track e.ID) { <option [value]="e.ID">{{ e.DisplayName || e.Name }}</option> }
416
+ </select></div>
417
+ <div class="field"><label>Default scope</label>
418
+ <select class="mj-input" [value]="Record.ScopeType || 'Filter'" (change)="set('ScopeType', $event)">
419
+ <option value="Filter">Filter</option><option value="View">A view</option><option value="List">A list</option>
420
+ </select></div>
421
+ <div class="field"><label>Work type</label>
422
+ <select class="mj-input" [value]="Record.WorkType || 'FieldRules'" (change)="set('WorkType', $event)">
423
+ <option value="FieldRules">Field Rules</option>
424
+ </select></div>
425
+ </div>
426
+ @if (Record.ScopeType === 'Filter') {
427
+ <div class="field rpe-mt"><label>Filter <span class="muted">— overridden by the grid selection at run time</span></label>
428
+ <input class="mj-input mono" [value]="Record.ScopeFilter || ''" (input)="set('ScopeFilter', $event)" placeholder="e.g. Status = 'Active'"></div>
429
+ }
430
+ </section>
431
+
432
+ <!-- RULES -->
433
+ @if (Record.WorkType === 'FieldRules') {
434
+ <section class="rpe-sec">
435
+ <div class="rpe-sec-h"><span class="num">3</span><h3>Rules</h3>
436
+ @if (!SelectedEntityName) { <span class="muted rpe-h-note">choose an entity first</span> }
437
+ </div>
438
+ @if (SelectedEntityName) {
439
+ <mj-field-rules-builder
440
+ [EntityName]="SelectedEntityName"
441
+ [Value]="RuleSet"
442
+ [Provider]="Provider"
443
+ (ValueChange)="onRulesChange($event)"
444
+ (ValidChange)="RulesValid = $event">
445
+ </mj-field-rules-builder>
446
+ }
447
+ </section>
448
+ }
449
+
450
+ <!-- TOOLBAR -->
451
+ @if (ShowToolbar) {
452
+ <div class="rpe-bar">
453
+ <button mjButton variant="primary" [disabled]="!CanSave || Saving" (click)="Save()">
454
+ <i class="fa-solid" [class.fa-floppy-disk]="!Saving" [class.fa-circle-notch]="Saving" [class.fa-spin]="Saving"></i> Save
455
+ </button>
456
+ <button mjButton [disabled]="!CanPreview || Saving" (click)="Preview()"><i class="fa-solid fa-flask"></i> Preview changes</button>
457
+ <button mjButton variant="flat" (click)="Cancel()">Cancel</button>
458
+ @if (ErrorMessage) { <span class="rpe-err"><i class="fa-solid fa-triangle-exclamation"></i> {{ ErrorMessage }}</span> }
459
+ </div>
460
+ }
461
+ </div>
462
+
463
+ @if (PreviewDriver) {
464
+ <mj-entity-action-ux-host
465
+ [DriverClass]="PreviewDriver.DriverClass" [Context]="PreviewDriver.Context"
466
+ (Completed)="onPreviewDone($event)" (Cancelled)="PreviewDriver = null">
467
+ </mj-entity-action-ux-host>
468
+ }
469
+ }
470
+ `, styles: ["\n .rpe-loading{padding:30px;color:var(--mj-text-muted);display:flex;align-items:center;gap:10px}\n .rpe{display:flex;flex-direction:column;gap:22px;color:var(--mj-text-primary)}\n .rpe-sec{background:var(--mj-bg-surface-card);border:1px solid var(--mj-border-subtle);border-radius:var(--mj-radius-md,10px);padding:18px 20px}\n .rpe-sec-h{display:flex;align-items:center;gap:11px;margin-bottom:16px}\n .rpe-sec-h h3{margin:0;font-size:16px;font-weight:700}\n .rpe-sec-h .num{width:23px;height:23px;border-radius:50%;background:var(--mj-brand-primary);color:var(--mj-text-inverse);display:grid;place-items:center;font-size:12px;font-weight:800}\n .rpe-h-note{margin-left:6px;font-size:12.5px}\n .rpe-grid2{display:grid;grid-template-columns:1fr 220px;gap:16px}\n .rpe-grid3{display:grid;grid-template-columns:1.4fr 1fr 1fr;gap:16px}\n .field{display:flex;flex-direction:column;gap:6px}\n .field label{font-size:12.5px;font-weight:600;color:var(--mj-text-secondary)}\n .rpe-mt{margin-top:16px}\n .mono{font-family:var(--mj-font-mono,monospace);font-size:12.5px}\n .muted{color:var(--mj-text-muted);font-weight:400}\n .rpe-bar{display:flex;align-items:center;gap:10px;padding-top:4px}\n .rpe-bar button i{margin-right:6px}\n .rpe-err{color:var(--mj-status-error-text);font-size:13px;display:flex;align-items:center;gap:7px}\n .rpe-err i{color:var(--mj-status-error)}\n "] }]
471
+ }], null, { Record: [{
472
+ type: Input
473
+ }], RecordProcessID: [{
474
+ type: Input
475
+ }], DefaultEntityID: [{
476
+ type: Input
477
+ }], ShowToolbar: [{
478
+ type: Input
479
+ }], Saved: [{
480
+ type: Output
481
+ }], Cancelled: [{
482
+ type: Output
483
+ }] }); })();
484
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RecordProcessEditorComponent, { className: "RecordProcessEditorComponent", filePath: "lib/record-process-editor/record-process-editor.component.ts", lineNumber: 130 }); })();
485
+ //# sourceMappingURL=record-process-editor.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record-process-editor.component.js","sourceRoot":"","sources":["../../../src/lib/record-process-editor/record-process-editor.component.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview The authoring surface for a single Record Process (a reusable bulk operation). Edits the\n * basics + target entity + scope, and — for the FieldRules work type — embeds the visual\n * `<mj-field-rules-builder>` so a business user defines rules without touching JSON. An inline \"Preview\"\n * runs the same server dry-run + diff runner used at invocation time.\n *\n * Reused in two places: standalone in the Bulk Operations hub (owns its Save/Preview toolbar), and inside\n * the custom Record Process entity form (toolbar hidden — the form owns Save).\n * @module @memberjunction/ng-record-process-studio\n */\nimport { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, inject } from '@angular/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { type EntityInfo } from '@memberjunction/core';\nimport { SafeJSONParse, type FieldRuleSet } from '@memberjunction/global';\nimport { MJRecordProcessEntity } from '@memberjunction/core-entities';\nimport { FieldRulesBuilderComponent, EntityActionUXHostComponent, type EntityActionUXContext, type EntityActionUXResult } from '@memberjunction/ng-entity-action-ux';\nimport { MJButtonDirective } from '@memberjunction/ng-ui-components';\n\ntype ScopeKind = 'Filter' | 'View' | 'List';\n\n@Component({\n selector: 'mj-record-process-editor',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [FieldRulesBuilderComponent, EntityActionUXHostComponent, MJButtonDirective],\n template: `\n @if (!Record) {\n <div class=\"rpe-loading\"><i class=\"fa-solid fa-circle-notch fa-spin\"></i> Loading…</div>\n } @else {\n <div class=\"rpe\">\n <!-- BASICS -->\n <section class=\"rpe-sec\">\n <div class=\"rpe-sec-h\"><span class=\"num\">1</span><h3>Basics</h3></div>\n <div class=\"rpe-grid2\">\n <div class=\"field\"><label>Name</label>\n <input class=\"mj-input\" [value]=\"Record.Name || ''\" (input)=\"set('Name', $event)\" placeholder=\"e.g. Mark idle prompts for review\"></div>\n <div class=\"field\"><label>Status</label>\n <select class=\"mj-input\" [value]=\"Record.Status || 'Active'\" (change)=\"set('Status', $event)\">\n <option value=\"Draft\">Draft</option><option value=\"Active\">Active</option><option value=\"Disabled\">Disabled</option>\n </select></div>\n </div>\n <div class=\"field rpe-mt\"><label>Description</label>\n <input class=\"mj-input\" [value]=\"Record.Description || ''\" (input)=\"set('Description', $event)\" placeholder=\"What does this process do?\"></div>\n </section>\n\n <!-- TARGET + SCOPE -->\n <section class=\"rpe-sec\">\n <div class=\"rpe-sec-h\"><span class=\"num\">2</span><h3>What it acts on</h3></div>\n <div class=\"rpe-grid3\">\n <div class=\"field\"><label>Entity</label>\n <select class=\"mj-input\" [value]=\"Record.EntityID || ''\" (change)=\"onEntityChange($event)\">\n <option value=\"\" disabled>— choose an entity —</option>\n @for (e of Entities; track e.ID) { <option [value]=\"e.ID\">{{ e.DisplayName || e.Name }}</option> }\n </select></div>\n <div class=\"field\"><label>Default scope</label>\n <select class=\"mj-input\" [value]=\"Record.ScopeType || 'Filter'\" (change)=\"set('ScopeType', $event)\">\n <option value=\"Filter\">Filter</option><option value=\"View\">A view</option><option value=\"List\">A list</option>\n </select></div>\n <div class=\"field\"><label>Work type</label>\n <select class=\"mj-input\" [value]=\"Record.WorkType || 'FieldRules'\" (change)=\"set('WorkType', $event)\">\n <option value=\"FieldRules\">Field Rules</option>\n </select></div>\n </div>\n @if (Record.ScopeType === 'Filter') {\n <div class=\"field rpe-mt\"><label>Filter <span class=\"muted\">— overridden by the grid selection at run time</span></label>\n <input class=\"mj-input mono\" [value]=\"Record.ScopeFilter || ''\" (input)=\"set('ScopeFilter', $event)\" placeholder=\"e.g. Status = 'Active'\"></div>\n }\n </section>\n\n <!-- RULES -->\n @if (Record.WorkType === 'FieldRules') {\n <section class=\"rpe-sec\">\n <div class=\"rpe-sec-h\"><span class=\"num\">3</span><h3>Rules</h3>\n @if (!SelectedEntityName) { <span class=\"muted rpe-h-note\">choose an entity first</span> }\n </div>\n @if (SelectedEntityName) {\n <mj-field-rules-builder\n [EntityName]=\"SelectedEntityName\"\n [Value]=\"RuleSet\"\n [Provider]=\"Provider\"\n (ValueChange)=\"onRulesChange($event)\"\n (ValidChange)=\"RulesValid = $event\">\n </mj-field-rules-builder>\n }\n </section>\n }\n\n <!-- TOOLBAR -->\n @if (ShowToolbar) {\n <div class=\"rpe-bar\">\n <button mjButton variant=\"primary\" [disabled]=\"!CanSave || Saving\" (click)=\"Save()\">\n <i class=\"fa-solid\" [class.fa-floppy-disk]=\"!Saving\" [class.fa-circle-notch]=\"Saving\" [class.fa-spin]=\"Saving\"></i> Save\n </button>\n <button mjButton [disabled]=\"!CanPreview || Saving\" (click)=\"Preview()\"><i class=\"fa-solid fa-flask\"></i> Preview changes</button>\n <button mjButton variant=\"flat\" (click)=\"Cancel()\">Cancel</button>\n @if (ErrorMessage) { <span class=\"rpe-err\"><i class=\"fa-solid fa-triangle-exclamation\"></i> {{ ErrorMessage }}</span> }\n </div>\n }\n </div>\n\n @if (PreviewDriver) {\n <mj-entity-action-ux-host\n [DriverClass]=\"PreviewDriver.DriverClass\" [Context]=\"PreviewDriver.Context\"\n (Completed)=\"onPreviewDone($event)\" (Cancelled)=\"PreviewDriver = null\">\n </mj-entity-action-ux-host>\n }\n }\n `,\n styles: [`\n .rpe-loading{padding:30px;color:var(--mj-text-muted);display:flex;align-items:center;gap:10px}\n .rpe{display:flex;flex-direction:column;gap:22px;color:var(--mj-text-primary)}\n .rpe-sec{background:var(--mj-bg-surface-card);border:1px solid var(--mj-border-subtle);border-radius:var(--mj-radius-md,10px);padding:18px 20px}\n .rpe-sec-h{display:flex;align-items:center;gap:11px;margin-bottom:16px}\n .rpe-sec-h h3{margin:0;font-size:16px;font-weight:700}\n .rpe-sec-h .num{width:23px;height:23px;border-radius:50%;background:var(--mj-brand-primary);color:var(--mj-text-inverse);display:grid;place-items:center;font-size:12px;font-weight:800}\n .rpe-h-note{margin-left:6px;font-size:12.5px}\n .rpe-grid2{display:grid;grid-template-columns:1fr 220px;gap:16px}\n .rpe-grid3{display:grid;grid-template-columns:1.4fr 1fr 1fr;gap:16px}\n .field{display:flex;flex-direction:column;gap:6px}\n .field label{font-size:12.5px;font-weight:600;color:var(--mj-text-secondary)}\n .rpe-mt{margin-top:16px}\n .mono{font-family:var(--mj-font-mono,monospace);font-size:12.5px}\n .muted{color:var(--mj-text-muted);font-weight:400}\n .rpe-bar{display:flex;align-items:center;gap:10px;padding-top:4px}\n .rpe-bar button i{margin-right:6px}\n .rpe-err{color:var(--mj-status-error-text);font-size:13px;display:flex;align-items:center;gap:7px}\n .rpe-err i{color:var(--mj-status-error)}\n `],\n})\nexport class RecordProcessEditorComponent extends BaseAngularComponent implements OnInit {\n private cdr = inject(ChangeDetectorRef);\n\n /** The process being edited. Set this OR `RecordProcessID` (or neither, for a new draft). */\n @Input() Record: MJRecordProcessEntity | null = null;\n /** Load an existing process by ID (alternative to passing `Record`). */\n @Input() RecordProcessID: string | null = null;\n /** Default EntityID for a brand-new process (e.g. the grid the user came from). */\n @Input() DefaultEntityID: string | null = null;\n /** Show the built-in Save / Preview / Cancel toolbar. Set false when an outer form owns Save. */\n @Input() ShowToolbar = true;\n\n /** Emitted after a successful Save (the persisted record). */\n @Output() Saved = new EventEmitter<MJRecordProcessEntity>();\n /** Emitted when the user cancels. */\n @Output() Cancelled = new EventEmitter<void>();\n\n public Entities: EntityInfo[] = [];\n public SelectedEntityName = '';\n public RuleSet: FieldRuleSet = { Rules: [] };\n public RulesValid = true;\n public Saving = false;\n public ErrorMessage = '';\n public PreviewDriver: { DriverClass: string; Context: EntityActionUXContext } | null = null;\n\n get CanSave(): boolean { return !!this.Record?.Name && !!this.Record?.EntityID && this.RulesValid; }\n get CanPreview(): boolean { return this.CanSave && this.RuleSet.Rules.length > 0; }\n\n async ngOnInit(): Promise<void> {\n const provider = this.ProviderToUse;\n this.Entities = [...provider.Entities].sort((a, b) => (a.DisplayName || a.Name).localeCompare(b.DisplayName || b.Name));\n await this.loadRecord();\n }\n\n /** Resolves the record to edit: passed instance → by ID → a fresh draft. */\n private async loadRecord(): Promise<void> {\n const provider = this.ProviderToUse;\n if (!this.Record) {\n this.Record = await provider.GetEntityObject<MJRecordProcessEntity>('MJ: Record Processes', provider.CurrentUser);\n if (this.RecordProcessID) {\n await this.Record.Load(this.RecordProcessID);\n } else {\n this.Record.NewRecord();\n this.Record.WorkType = 'FieldRules';\n this.Record.ScopeType = 'Filter';\n this.Record.Status = 'Draft';\n if (this.DefaultEntityID) this.Record.EntityID = this.DefaultEntityID;\n }\n }\n this.RuleSet = this.Record.Configuration ? (SafeJSONParse<FieldRuleSet>(this.Record.Configuration) ?? { Rules: [] }) : { Rules: [] };\n this.resolveEntityName();\n this.cdr.detectChanges();\n }\n\n /** Generic setter for the simple string/enum fields, driven by the template inputs. */\n set(field: 'Name' | 'Description' | 'Status' | 'ScopeType' | 'ScopeFilter' | 'WorkType', event: Event): void {\n if (!this.Record) return;\n const value = (event.target as HTMLInputElement | HTMLSelectElement).value;\n // Strongly-typed assignment per field (no dynamic Set()).\n switch (field) {\n case 'Name': this.Record.Name = value; break;\n case 'Description': this.Record.Description = value; break;\n case 'Status': this.Record.Status = value as MJRecordProcessEntity['Status']; break;\n case 'ScopeType': this.Record.ScopeType = value as MJRecordProcessEntity['ScopeType']; break;\n case 'ScopeFilter': this.Record.ScopeFilter = value; break;\n case 'WorkType': this.Record.WorkType = value as MJRecordProcessEntity['WorkType']; break;\n }\n this.cdr.detectChanges();\n }\n\n onEntityChange(event: Event): void {\n if (!this.Record) return;\n this.Record.EntityID = (event.target as HTMLSelectElement).value;\n this.resolveEntityName();\n this.cdr.detectChanges();\n }\n\n onRulesChange(ruleSet: FieldRuleSet): void {\n this.RuleSet = ruleSet;\n if (this.Record) this.Record.Configuration = JSON.stringify(ruleSet);\n this.cdr.detectChanges();\n }\n\n /** Saves the record (and emits). Returns success. */\n async Save(): Promise<boolean> {\n if (!this.Record || !this.CanSave) return false;\n this.Saving = true; this.ErrorMessage = ''; this.cdr.detectChanges();\n this.Record.Configuration = JSON.stringify(this.RuleSet);\n const ok = await this.Record.Save();\n this.Saving = false;\n if (ok) {\n this.Saved.emit(this.Record);\n } else {\n this.ErrorMessage = this.Record.LatestResult?.CompleteMessage ?? 'Save failed';\n }\n this.cdr.detectChanges();\n return ok;\n }\n\n /** Saves (if needed) then mounts the dry-run runner against the process's default scope. */\n async Preview(): Promise<void> {\n if (!this.Record) return;\n if (this.Record.Dirty || !this.Record.IsSaved) {\n const ok = await this.Save();\n if (!ok) return;\n }\n const entity = this.ProviderToUse.EntityByID(this.Record.EntityID);\n if (!entity) { this.ErrorMessage = 'Target entity not found'; this.cdr.detectChanges(); return; }\n this.PreviewDriver = {\n DriverClass: 'RecordProcessRunnerUX',\n Context: {\n EntityInfo: entity,\n ScopeKind: (this.Record.ScopeType?.toLowerCase() as EntityActionUXContext['ScopeKind']) || 'filter',\n Filter: this.Record.ScopeFilter ?? undefined,\n ViewID: this.Record.ScopeViewID ?? undefined,\n ListID: this.Record.ScopeListID ?? undefined,\n Config: { RecordProcessID: this.Record.ID },\n Provider: this.ProviderToUse,\n ActionLabel: `Preview: ${this.Record.Name}`,\n },\n };\n this.cdr.detectChanges();\n }\n\n onPreviewDone(_result: EntityActionUXResult): void {\n this.PreviewDriver = null;\n this.cdr.detectChanges();\n }\n\n Cancel(): void { this.Cancelled.emit(); }\n\n private resolveEntityName(): void {\n this.SelectedEntityName = this.Record?.EntityID ? (this.ProviderToUse.EntityByID(this.Record.EntityID)?.Name ?? '') : '';\n }\n}\n"]}
@@ -0,0 +1,69 @@
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 { OnDestroy, OnInit } from '@angular/core';
8
+ import { BaseAngularComponent } from '@memberjunction/ng-base-types';
9
+ import { type RunDetailChange } from '../run-detail';
10
+ import * as i0 from "@angular/core";
11
+ interface RawRun {
12
+ ID: string;
13
+ RecordProcessID: string;
14
+ EntityID: string;
15
+ Status: string;
16
+ StartTime?: string | Date;
17
+ SuccessCount?: number;
18
+ ErrorCount?: number;
19
+ SkippedCount?: number;
20
+ TotalItemCount?: number;
21
+ TriggeredBy?: string;
22
+ SourceType?: string;
23
+ DryRun?: boolean;
24
+ }
25
+ interface RunRow extends RawRun {
26
+ ProcessName: string;
27
+ EntityName: string;
28
+ }
29
+ interface DetailRow {
30
+ RecordID: string;
31
+ Status: string;
32
+ Changes: RunDetailChange[];
33
+ ErrorMessage?: string;
34
+ }
35
+ export declare class RecordProcessHistoryComponent extends BaseAngularComponent implements OnInit, OnDestroy {
36
+ private cdr;
37
+ /** Optional: show runs for one Record Process only. */
38
+ RecordProcessID: string | null;
39
+ Mode: 'list' | 'detail';
40
+ Loading: boolean;
41
+ DetailLoading: boolean;
42
+ Runs: RunRow[];
43
+ Details: DetailRow[];
44
+ OpenRunRow: RunRow | null;
45
+ /**
46
+ * There is no RecordProcess BaseEngine to observe — Process Runs is an unbounded log table, so we don't
47
+ * bulk-cache it. Instead we re-query on demand AND keep the list live by listening for BaseEntity save/
48
+ * delete events on the run tables: a run started/finished anywhere (this view, the editor preview, a grid
49
+ * runner, the scheduler) fires an event, and we debounce-reload so history never goes stale behind a run.
50
+ */
51
+ private eventSub?;
52
+ private pendingReload;
53
+ private reloadTimer;
54
+ ngOnInit(): Promise<void>;
55
+ ngOnDestroy(): void;
56
+ /** Subscribe to BaseEntity events and debounce-reload whenever a Process Run (or its details) changes. */
57
+ private subscribeToRunChanges;
58
+ /** Debounce a burst of run/detail/checkpoint saves into a single reload; defer if the user is drilled into a detail. */
59
+ private scheduleReload;
60
+ reload(): Promise<void>;
61
+ openRun(run: RunRow): Promise<void>;
62
+ backToList(): void;
63
+ fmt(value?: string | Date): string;
64
+ disp(value: unknown): string;
65
+ static ɵfac: i0.ɵɵFactoryDeclaration<RecordProcessHistoryComponent, never>;
66
+ static ɵcmp: i0.ɵɵComponentDeclaration<RecordProcessHistoryComponent, "mj-record-process-history", never, { "RecordProcessID": { "alias": "RecordProcessID"; "required": false; }; }, {}, never, never, true, never>;
67
+ }
68
+ export {};
69
+ //# sourceMappingURL=record-process-history.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record-process-history.component.d.ts","sourceRoot":"","sources":["../../../src/lib/record-process-history/record-process-history.component.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAgE,SAAS,EAAE,MAAM,EAAU,MAAM,eAAe,CAAC;AAExH,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAIrE,OAAO,EAAiD,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;;AAEpG,UAAU,MAAM;IACZ,EAAE,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IACtE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC7F,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CACxF;AACD,UAAU,MAAO,SAAQ,MAAM;IAAG,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;CAAE;AAG5E,UAAU,SAAS;IAAG,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,eAAe,EAAE,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAAE;AAE5G,qBAwGa,6BAA8B,SAAQ,oBAAqB,YAAW,MAAM,EAAE,SAAS;IAChG,OAAO,CAAC,GAAG,CAA6B;IAExC,uDAAuD;IAC9C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAQ;IAExC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAU;IACjC,OAAO,UAAQ;IACf,aAAa,UAAS;IACtB,IAAI,EAAE,MAAM,EAAE,CAAM;IACpB,OAAO,EAAE,SAAS,EAAE,CAAM;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IAExC;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,CAAe;IAChC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,WAAW,CAA8C;IAE3D,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAK/B,WAAW,IAAI,IAAI;IAOnB,0GAA0G;IAC1G,OAAO,CAAC,qBAAqB;IAgB7B,wHAAwH;IACxH,OAAO,CAAC,cAAc;IAchB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBvB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAezC,UAAU,IAAI,IAAI;IAUlB,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM;IAKlC,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM;yCA1HnB,6BAA6B;2CAA7B,6BAA6B;CA2HzC"}