@memberjunction/ng-entity-action-ux 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.
Files changed (42) hide show
  1. package/README.md +175 -43
  2. package/dist/lib/ai-prompt-selector/ai-prompt-selector.component.d.ts +41 -0
  3. package/dist/lib/ai-prompt-selector/ai-prompt-selector.component.d.ts.map +1 -0
  4. package/dist/lib/ai-prompt-selector/ai-prompt-selector.component.js +231 -0
  5. package/dist/lib/ai-prompt-selector/ai-prompt-selector.component.js.map +1 -0
  6. package/dist/lib/base-entity-action-runtime-ux.d.ts +31 -0
  7. package/dist/lib/base-entity-action-runtime-ux.d.ts.map +1 -0
  8. package/dist/lib/base-entity-action-runtime-ux.js +34 -0
  9. package/dist/lib/base-entity-action-runtime-ux.js.map +1 -0
  10. package/dist/lib/entity-action-ux-host.component.d.ts +32 -0
  11. package/dist/lib/entity-action-ux-host.component.d.ts.map +1 -0
  12. package/dist/lib/entity-action-ux-host.component.js +89 -0
  13. package/dist/lib/entity-action-ux-host.component.js.map +1 -0
  14. package/dist/lib/field-rules-builder/field-rules-builder.component.d.ts +62 -0
  15. package/dist/lib/field-rules-builder/field-rules-builder.component.d.ts.map +1 -0
  16. package/dist/lib/field-rules-builder/field-rules-builder.component.js +406 -0
  17. package/dist/lib/field-rules-builder/field-rules-builder.component.js.map +1 -0
  18. package/dist/lib/field-rules-model.d.ts +32 -0
  19. package/dist/lib/field-rules-model.d.ts.map +1 -0
  20. package/dist/lib/field-rules-model.js +76 -0
  21. package/dist/lib/field-rules-model.js.map +1 -0
  22. package/dist/lib/prompt-grouping.d.ts +20 -0
  23. package/dist/lib/prompt-grouping.d.ts.map +1 -0
  24. package/dist/lib/prompt-grouping.js +30 -0
  25. package/dist/lib/prompt-grouping.js.map +1 -0
  26. package/dist/lib/record-process-runner/record-process-runner-ux.component.d.ts +76 -0
  27. package/dist/lib/record-process-runner/record-process-runner-ux.component.d.ts.map +1 -0
  28. package/dist/lib/record-process-runner/record-process-runner-ux.component.js +506 -0
  29. package/dist/lib/record-process-runner/record-process-runner-ux.component.js.map +1 -0
  30. package/dist/lib/runtime-ux-context.d.ts +52 -0
  31. package/dist/lib/runtime-ux-context.d.ts.map +1 -0
  32. package/dist/lib/runtime-ux-context.js +2 -0
  33. package/dist/lib/runtime-ux-context.js.map +1 -0
  34. package/dist/lib/scope.d.ts +12 -0
  35. package/dist/lib/scope.d.ts.map +1 -0
  36. package/dist/lib/scope.js +25 -0
  37. package/dist/lib/scope.js.map +1 -0
  38. package/dist/public-api.d.ts +15 -0
  39. package/dist/public-api.d.ts.map +1 -0
  40. package/dist/public-api.js +27 -0
  41. package/dist/public-api.js.map +1 -0
  42. package/package.json +47 -6
@@ -0,0 +1,506 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var RecordProcessRunnerUXComponent_1;
8
+ /**
9
+ * @fileoverview The flagship runtime-UX driver: runs a `MJ: Record Processes` (typically a FieldRules
10
+ * bulk-update) as a safe, two-step flow — a server dry-run computes the exact per-record diff, the user
11
+ * reviews it, and only on confirm does the real write happen. Registered as `RecordProcessRunnerUX`; an
12
+ * entity action points its `RuntimeUXDriverClass` here and the host mounts it. All server work goes through
13
+ * the typed {@link GraphQLRecordProcessClient}; the rich diff is read with `RunView` over
14
+ * `MJ: Process Run Details`.
15
+ * @module @memberjunction/ng-entity-action-ux
16
+ */
17
+ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject } from '@angular/core';
18
+ import { RunView } from '@memberjunction/core';
19
+ import { RegisterClass, SafeJSONParse } from '@memberjunction/global';
20
+ import { RecordProcessRunNowOperation, } from '@memberjunction/core-entities';
21
+ import { MJDialogComponent, MJDialogActionsComponent, MJButtonDirective, MJProgressBarComponent, MJEmptyStateComponent } from '@memberjunction/ng-ui-components';
22
+ import { BaseEntityActionRuntimeUX } from '../base-entity-action-runtime-ux';
23
+ import { buildRecordProcessScope, displayValue } from '../scope';
24
+ import * as i0 from "@angular/core";
25
+ const _forTrack0 = ($index, $item) => $item.RecordID;
26
+ const _forTrack1 = ($index, $item) => $item.Field;
27
+ function RecordProcessRunnerUXComponent_Case_2_Template(rf, ctx) { if (rf & 1) {
28
+ i0.ɵɵelementStart(0, "div", 2);
29
+ i0.ɵɵelement(1, "mj-progress-bar", 7);
30
+ i0.ɵɵelementStart(2, "p");
31
+ i0.ɵɵtext(3, "Computing a safe preview \u2014 no changes have been made yet\u2026");
32
+ i0.ɵɵelementEnd()();
33
+ } }
34
+ function RecordProcessRunnerUXComponent_Case_3_Template(rf, ctx) { if (rf & 1) {
35
+ i0.ɵɵelementStart(0, "div", 2);
36
+ i0.ɵɵelement(1, "mj-progress-bar", 7);
37
+ i0.ɵɵelementStart(2, "p");
38
+ i0.ɵɵtext(3);
39
+ i0.ɵɵelementEnd()();
40
+ } if (rf & 2) {
41
+ const ctx_r0 = i0.ɵɵnextContext();
42
+ i0.ɵɵadvance(3);
43
+ i0.ɵɵtextInterpolate2("Applying changes to ", ctx_r0.ChangedRecordCount, " ", ctx_r0.EntityLabel, "\u2026");
44
+ } }
45
+ function RecordProcessRunnerUXComponent_Case_4_Template(rf, ctx) { if (rf & 1) {
46
+ i0.ɵɵelementStart(0, "div", 3);
47
+ i0.ɵɵelement(1, "i", 8);
48
+ i0.ɵɵelementStart(2, "div")(3, "strong");
49
+ i0.ɵɵtext(4, "Couldn't run this process");
50
+ i0.ɵɵelementEnd();
51
+ i0.ɵɵelementStart(5, "p");
52
+ i0.ɵɵtext(6);
53
+ i0.ɵɵelementEnd()()();
54
+ } if (rf & 2) {
55
+ const ctx_r0 = i0.ɵɵnextContext();
56
+ i0.ɵɵadvance(6);
57
+ i0.ɵɵtextInterpolate(ctx_r0.ErrorMessage);
58
+ } }
59
+ function RecordProcessRunnerUXComponent_Case_5_Conditional_5_Template(rf, ctx) { if (rf & 1) {
60
+ i0.ɵɵelementStart(0, "p", 10);
61
+ i0.ɵɵtext(1);
62
+ i0.ɵɵelementEnd();
63
+ } if (rf & 2) {
64
+ const ctx_r0 = i0.ɵɵnextContext(2);
65
+ i0.ɵɵadvance();
66
+ i0.ɵɵtextInterpolate1("", ctx_r0.AppliedResult == null ? null : ctx_r0.AppliedResult.Errored, " record(s) reported an error.");
67
+ } }
68
+ function RecordProcessRunnerUXComponent_Case_5_Template(rf, ctx) { if (rf & 1) {
69
+ i0.ɵɵelementStart(0, "div", 4);
70
+ i0.ɵɵelement(1, "i", 9);
71
+ i0.ɵɵelementStart(2, "div")(3, "strong");
72
+ i0.ɵɵtext(4);
73
+ i0.ɵɵelementEnd();
74
+ i0.ɵɵconditionalCreate(5, RecordProcessRunnerUXComponent_Case_5_Conditional_5_Template, 2, 1, "p", 10);
75
+ i0.ɵɵelementEnd()();
76
+ } if (rf & 2) {
77
+ const ctx_r0 = i0.ɵɵnextContext();
78
+ i0.ɵɵadvance(4);
79
+ i0.ɵɵtextInterpolate(ctx_r0.DoneMessage);
80
+ i0.ɵɵadvance();
81
+ i0.ɵɵconditional((ctx_r0.AppliedResult == null ? null : ctx_r0.AppliedResult.Errored) ? 5 : -1);
82
+ } }
83
+ function RecordProcessRunnerUXComponent_Case_6_Conditional_5_Template(rf, ctx) { if (rf & 1) {
84
+ i0.ɵɵelementStart(0, "span", 14);
85
+ i0.ɵɵtext(1);
86
+ i0.ɵɵelementEnd();
87
+ } if (rf & 2) {
88
+ const ctx_r0 = i0.ɵɵnextContext(2);
89
+ i0.ɵɵadvance();
90
+ i0.ɵɵtextInterpolate1("", ctx_r0.SkippedCount, " unchanged");
91
+ } }
92
+ function RecordProcessRunnerUXComponent_Case_6_Conditional_6_Template(rf, ctx) { if (rf & 1) {
93
+ i0.ɵɵelementStart(0, "span", 15);
94
+ i0.ɵɵtext(1);
95
+ i0.ɵɵelementEnd();
96
+ } if (rf & 2) {
97
+ const ctx_r0 = i0.ɵɵnextContext(2);
98
+ i0.ɵɵadvance();
99
+ i0.ɵɵtextInterpolate1("", ctx_r0.ErroredCount, " error(s)");
100
+ } }
101
+ function RecordProcessRunnerUXComponent_Case_6_Conditional_7_Template(rf, ctx) { if (rf & 1) {
102
+ i0.ɵɵelement(0, "mj-empty-state", 16);
103
+ } if (rf & 2) {
104
+ const ctx_r0 = i0.ɵɵnextContext(2);
105
+ i0.ɵɵproperty("Message", "The rules did not match any of the selected " + ctx_r0.EntityLabel + ".");
106
+ } }
107
+ function RecordProcessRunnerUXComponent_Case_6_Conditional_8_For_2_For_5_Template(rf, ctx) { if (rf & 1) {
108
+ i0.ɵɵelementStart(0, "div", 22)(1, "span", 24);
109
+ i0.ɵɵtext(2);
110
+ i0.ɵɵelementEnd();
111
+ i0.ɵɵelementStart(3, "span", 25);
112
+ i0.ɵɵtext(4);
113
+ i0.ɵɵelementEnd();
114
+ i0.ɵɵelement(5, "i", 26);
115
+ i0.ɵɵelementStart(6, "span", 27);
116
+ i0.ɵɵtext(7);
117
+ i0.ɵɵelementEnd()();
118
+ } if (rf & 2) {
119
+ const c_r2 = ctx.$implicit;
120
+ const ctx_r0 = i0.ɵɵnextContext(4);
121
+ i0.ɵɵadvance(2);
122
+ i0.ɵɵtextInterpolate(c_r2.Field);
123
+ i0.ɵɵadvance(2);
124
+ i0.ɵɵtextInterpolate(ctx_r0.Display(c_r2.OldValue));
125
+ i0.ɵɵadvance(3);
126
+ i0.ɵɵtextInterpolate(ctx_r0.Display(c_r2.NewValue));
127
+ } }
128
+ function RecordProcessRunnerUXComponent_Case_6_Conditional_8_For_2_Conditional_6_Template(rf, ctx) { if (rf & 1) {
129
+ i0.ɵɵelementStart(0, "div", 23);
130
+ i0.ɵɵtext(1);
131
+ i0.ɵɵelementEnd();
132
+ } if (rf & 2) {
133
+ const row_r3 = i0.ɵɵnextContext().$implicit;
134
+ i0.ɵɵadvance();
135
+ i0.ɵɵtextInterpolate(row_r3.ErrorMessage);
136
+ } }
137
+ function RecordProcessRunnerUXComponent_Case_6_Conditional_8_For_2_Template(rf, ctx) { if (rf & 1) {
138
+ i0.ɵɵelementStart(0, "div", 18)(1, "div", 20);
139
+ i0.ɵɵtext(2);
140
+ i0.ɵɵelementEnd();
141
+ i0.ɵɵelementStart(3, "div", 21);
142
+ i0.ɵɵrepeaterCreate(4, RecordProcessRunnerUXComponent_Case_6_Conditional_8_For_2_For_5_Template, 8, 3, "div", 22, _forTrack1);
143
+ i0.ɵɵconditionalCreate(6, RecordProcessRunnerUXComponent_Case_6_Conditional_8_For_2_Conditional_6_Template, 2, 1, "div", 23);
144
+ i0.ɵɵelementEnd()();
145
+ } if (rf & 2) {
146
+ const row_r3 = ctx.$implicit;
147
+ i0.ɵɵadvance();
148
+ i0.ɵɵproperty("title", row_r3.RecordID);
149
+ i0.ɵɵadvance();
150
+ i0.ɵɵtextInterpolate(row_r3.RecordID);
151
+ i0.ɵɵadvance(2);
152
+ i0.ɵɵrepeater(row_r3.Changes);
153
+ i0.ɵɵadvance(2);
154
+ i0.ɵɵconditional(row_r3.ErrorMessage ? 6 : -1);
155
+ } }
156
+ function RecordProcessRunnerUXComponent_Case_6_Conditional_8_Conditional_3_Template(rf, ctx) { if (rf & 1) {
157
+ i0.ɵɵelementStart(0, "p", 19);
158
+ i0.ɵɵtext(1);
159
+ i0.ɵɵelementEnd();
160
+ } if (rf & 2) {
161
+ const ctx_r0 = i0.ɵɵnextContext(3);
162
+ i0.ɵɵadvance();
163
+ i0.ɵɵtextInterpolate1("Showing the first ", ctx_r0.DiffRows.length, " changed records. The apply step covers the full scope.");
164
+ } }
165
+ function RecordProcessRunnerUXComponent_Case_6_Conditional_8_Template(rf, ctx) { if (rf & 1) {
166
+ i0.ɵɵelementStart(0, "div", 17);
167
+ i0.ɵɵrepeaterCreate(1, RecordProcessRunnerUXComponent_Case_6_Conditional_8_For_2_Template, 7, 3, "div", 18, _forTrack0);
168
+ i0.ɵɵelementEnd();
169
+ i0.ɵɵconditionalCreate(3, RecordProcessRunnerUXComponent_Case_6_Conditional_8_Conditional_3_Template, 2, 1, "p", 19);
170
+ } if (rf & 2) {
171
+ const ctx_r0 = i0.ɵɵnextContext(2);
172
+ i0.ɵɵadvance();
173
+ i0.ɵɵrepeater(ctx_r0.DiffRows);
174
+ i0.ɵɵadvance(2);
175
+ i0.ɵɵconditional(ctx_r0.Truncated ? 3 : -1);
176
+ } }
177
+ function RecordProcessRunnerUXComponent_Case_6_Template(rf, ctx) { if (rf & 1) {
178
+ i0.ɵɵelementStart(0, "div", 11)(1, "span", 12);
179
+ i0.ɵɵtext(2);
180
+ i0.ɵɵelementEnd();
181
+ i0.ɵɵelementStart(3, "span", 13);
182
+ i0.ɵɵtext(4);
183
+ i0.ɵɵelementEnd();
184
+ i0.ɵɵconditionalCreate(5, RecordProcessRunnerUXComponent_Case_6_Conditional_5_Template, 2, 1, "span", 14);
185
+ i0.ɵɵconditionalCreate(6, RecordProcessRunnerUXComponent_Case_6_Conditional_6_Template, 2, 1, "span", 15);
186
+ i0.ɵɵelementEnd();
187
+ i0.ɵɵconditionalCreate(7, RecordProcessRunnerUXComponent_Case_6_Conditional_7_Template, 1, 1, "mj-empty-state", 16)(8, RecordProcessRunnerUXComponent_Case_6_Conditional_8_Template, 4, 1);
188
+ } if (rf & 2) {
189
+ const ctx_r0 = i0.ɵɵnextContext();
190
+ i0.ɵɵadvance(2);
191
+ i0.ɵɵtextInterpolate3("", ctx_r0.ChangedRecordCount, " of ", (ctx_r0.PreviewResult == null ? null : ctx_r0.PreviewResult.Processed) ?? 0, " ", ctx_r0.EntityLabel, " will change");
192
+ i0.ɵɵadvance(2);
193
+ i0.ɵɵtextInterpolate1("", ctx_r0.TotalChangeCount, " field update(s)");
194
+ i0.ɵɵadvance();
195
+ i0.ɵɵconditional(ctx_r0.SkippedCount ? 5 : -1);
196
+ i0.ɵɵadvance();
197
+ i0.ɵɵconditional(ctx_r0.ErroredCount ? 6 : -1);
198
+ i0.ɵɵadvance();
199
+ i0.ɵɵconditional(ctx_r0.DiffRows.length === 0 ? 7 : 8);
200
+ } }
201
+ function RecordProcessRunnerUXComponent_Case_8_Conditional_0_Template(rf, ctx) { if (rf & 1) {
202
+ const _r5 = i0.ɵɵgetCurrentView();
203
+ i0.ɵɵelementStart(0, "button", 29);
204
+ i0.ɵɵlistener("click", function RecordProcessRunnerUXComponent_Case_8_Conditional_0_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r5); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.Apply()); });
205
+ i0.ɵɵelement(1, "i", 30);
206
+ i0.ɵɵtext(2);
207
+ i0.ɵɵelementEnd();
208
+ } if (rf & 2) {
209
+ const ctx_r0 = i0.ɵɵnextContext(2);
210
+ i0.ɵɵadvance(2);
211
+ i0.ɵɵtextInterpolate1(" Apply ", ctx_r0.TotalChangeCount, " change(s) ");
212
+ } }
213
+ function RecordProcessRunnerUXComponent_Case_8_Template(rf, ctx) { if (rf & 1) {
214
+ const _r4 = i0.ɵɵgetCurrentView();
215
+ i0.ɵɵconditionalCreate(0, RecordProcessRunnerUXComponent_Case_8_Conditional_0_Template, 3, 1, "button", 5);
216
+ i0.ɵɵelementStart(1, "button", 28);
217
+ i0.ɵɵlistener("click", function RecordProcessRunnerUXComponent_Case_8_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnCancel()); });
218
+ i0.ɵɵtext(2, "Cancel");
219
+ i0.ɵɵelementEnd();
220
+ } if (rf & 2) {
221
+ const ctx_r0 = i0.ɵɵnextContext();
222
+ i0.ɵɵconditional(ctx_r0.HasChanges ? 0 : -1);
223
+ } }
224
+ function RecordProcessRunnerUXComponent_Case_9_Template(rf, ctx) { if (rf & 1) {
225
+ const _r6 = i0.ɵɵgetCurrentView();
226
+ i0.ɵɵelementStart(0, "button", 29);
227
+ i0.ɵɵlistener("click", function RecordProcessRunnerUXComponent_Case_9_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r6); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.Finish()); });
228
+ i0.ɵɵtext(1, "Done");
229
+ i0.ɵɵelementEnd();
230
+ } }
231
+ function RecordProcessRunnerUXComponent_Case_10_Template(rf, ctx) { if (rf & 1) {
232
+ const _r7 = i0.ɵɵgetCurrentView();
233
+ i0.ɵɵelementStart(0, "button", 28);
234
+ i0.ɵɵlistener("click", function RecordProcessRunnerUXComponent_Case_10_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r7); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnCancel()); });
235
+ i0.ɵɵtext(1, "Close");
236
+ i0.ɵɵelementEnd();
237
+ } }
238
+ let RecordProcessRunnerUXComponent = class RecordProcessRunnerUXComponent extends BaseEntityActionRuntimeUX {
239
+ static { RecordProcessRunnerUXComponent_1 = this; }
240
+ cdr = inject(ChangeDetectorRef);
241
+ /** Cap on preview rows we render (the apply step always covers the full scope). */
242
+ static MAX_PREVIEW_ROWS = 500;
243
+ State = 'loading';
244
+ ErrorMessage = '';
245
+ DiffRows = [];
246
+ PreviewResult;
247
+ AppliedResult;
248
+ ChangedRecordCount = 0;
249
+ TotalChangeCount = 0;
250
+ Truncated = false;
251
+ get Title() { return this.Context?.ActionLabel || 'Run Record Process'; }
252
+ get EntityLabel() { return this.Context?.EntityInfo?.DisplayName || this.Context?.EntityInfo?.Name || 'records'; }
253
+ get HasChanges() { return this.ChangedRecordCount > 0; }
254
+ get SkippedCount() { return this.PreviewResult?.Skipped ?? 0; }
255
+ get ErroredCount() { return this.PreviewResult?.Errored ?? 0; }
256
+ get DoneMessage() {
257
+ const r = this.AppliedResult;
258
+ return r ? `Updated ${r.Succeeded} ${this.EntityLabel}.` : 'Done.';
259
+ }
260
+ /** Step 1: run a dry-run on the server and load the resulting per-record diff. */
261
+ async Start() {
262
+ const recordProcessID = this.recordProcessID();
263
+ if (!recordProcessID) {
264
+ this.toError('This action has no RecordProcessID configured.');
265
+ return;
266
+ }
267
+ try {
268
+ const preview = await this.runOp(recordProcessID, true);
269
+ this.PreviewResult = preview;
270
+ if (!preview.Success) {
271
+ this.toError(preview.ErrorMessage || 'The preview could not be computed.');
272
+ return;
273
+ }
274
+ if (preview.ProcessRunID) {
275
+ await this.loadDiff(preview.ProcessRunID);
276
+ }
277
+ this.State = 'preview';
278
+ this.cdr.detectChanges();
279
+ }
280
+ catch (e) {
281
+ this.toError(e instanceof Error ? e.message : String(e));
282
+ }
283
+ }
284
+ /** Step 2: the user confirmed — run for real. */
285
+ async Apply() {
286
+ const recordProcessID = this.recordProcessID();
287
+ if (!recordProcessID)
288
+ return;
289
+ this.State = 'applying';
290
+ this.cdr.detectChanges();
291
+ try {
292
+ const result = await this.runOp(recordProcessID, false);
293
+ this.AppliedResult = result;
294
+ if (!result.Success) {
295
+ this.toError(result.ErrorMessage || 'The changes could not be applied.');
296
+ return;
297
+ }
298
+ this.State = 'done';
299
+ this.cdr.detectChanges();
300
+ }
301
+ catch (e) {
302
+ this.toError(e instanceof Error ? e.message : String(e));
303
+ }
304
+ }
305
+ /** Closes after a successful apply, telling the host to refresh. */
306
+ Finish() {
307
+ this.Completed.emit({ Completed: true, RefreshData: true, Message: this.DoneMessage });
308
+ }
309
+ /** Dismiss without applying (also used for the error/close button). */
310
+ OnCancel() {
311
+ if (this.State === 'applying')
312
+ return; // don't allow dismiss mid-write
313
+ this.Cancelled.emit();
314
+ }
315
+ /** Renders a value for the diff table (delegates to the pure helper). */
316
+ Display(value) {
317
+ return displayValue(value);
318
+ }
319
+ /**
320
+ * Runs the `RecordProcess.RunNow` Remote Operation and maps its typed output to a {@link RunSummary}.
321
+ * `Execute` routes through the host's provider — marshalled over the generic `ExecuteRemoteOperation`
322
+ * transport on the client, in-process on the server — so there is no bespoke resolver/client.
323
+ */
324
+ async runOp(recordProcessID, dryRun) {
325
+ const input = { recordProcessID, scope: this.buildScope(), dryRun };
326
+ const result = await new RecordProcessRunNowOperation().Execute(input, { provider: this.Context?.Provider ?? undefined });
327
+ const o = result.Output;
328
+ const completed = result.Success && o?.status === 'Completed';
329
+ return {
330
+ Success: completed,
331
+ ProcessRunID: o?.processRunID,
332
+ Processed: o?.processed ?? 0,
333
+ Succeeded: o?.success ?? 0,
334
+ Errored: o?.error ?? 0,
335
+ Skipped: o?.skipped ?? 0,
336
+ Status: o?.status,
337
+ ErrorMessage: result.ErrorMessage ?? o?.errorMessage ?? (o && o.status !== 'Completed' ? `Run ended with status '${o.status}'` : undefined),
338
+ };
339
+ }
340
+ /** Reads `MJ: Process Run Details` for the dry-run and builds the per-record diff rows. */
341
+ async loadDiff(processRunID) {
342
+ const rv = await (this.Context?.Provider ? RunView.FromMetadataProvider(this.Context.Provider) : new RunView()).RunView({
343
+ EntityName: 'MJ: Process Run Details',
344
+ ExtraFilter: `ProcessRunID='${processRunID}'`,
345
+ Fields: ['RecordID', 'Status', 'ResultPayload', 'ErrorMessage'],
346
+ OrderBy: 'RecordID',
347
+ MaxRows: RecordProcessRunnerUXComponent_1.MAX_PREVIEW_ROWS,
348
+ ResultType: 'simple',
349
+ });
350
+ if (!rv.Success)
351
+ return;
352
+ const rows = [];
353
+ let changedRecords = 0;
354
+ let totalChanges = 0;
355
+ for (const detail of rv.Results) {
356
+ const payload = detail.ResultPayload ? SafeJSONParse(detail.ResultPayload) : undefined;
357
+ const changes = (payload?.Changes ?? []).filter((c) => c.Applied && c.Changed && !c.Error);
358
+ if (changes.length > 0) {
359
+ changedRecords++;
360
+ totalChanges += changes.length;
361
+ }
362
+ if (changes.length > 0 || detail.ErrorMessage) {
363
+ rows.push({ RecordID: detail.RecordID, Status: detail.Status, Changes: changes, ErrorMessage: detail.ErrorMessage });
364
+ }
365
+ }
366
+ this.DiffRows = rows;
367
+ this.ChangedRecordCount = changedRecords;
368
+ this.TotalChangeCount = totalChanges;
369
+ this.Truncated = (this.PreviewResult?.Processed ?? 0) > RecordProcessRunnerUXComponent_1.MAX_PREVIEW_ROWS;
370
+ }
371
+ /** Builds the engine scope from the host-supplied context (delegates to the pure helper). */
372
+ buildScope() {
373
+ return buildRecordProcessScope(this.Context);
374
+ }
375
+ recordProcessID() {
376
+ const id = this.Context?.Config?.['RecordProcessID'];
377
+ return typeof id === 'string' && id.length > 0 ? id : undefined;
378
+ }
379
+ toError(message) {
380
+ this.ErrorMessage = message;
381
+ this.State = 'error';
382
+ this.cdr.detectChanges();
383
+ }
384
+ static ɵfac = /*@__PURE__*/ (() => { let ɵRecordProcessRunnerUXComponent_BaseFactory; return function RecordProcessRunnerUXComponent_Factory(__ngFactoryType__) { return (ɵRecordProcessRunnerUXComponent_BaseFactory || (ɵRecordProcessRunnerUXComponent_BaseFactory = i0.ɵɵgetInheritedFactory(RecordProcessRunnerUXComponent)))(__ngFactoryType__ || RecordProcessRunnerUXComponent); }; })();
385
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RecordProcessRunnerUXComponent, selectors: [["mj-record-process-runner-ux"]], features: [i0.ɵɵInheritDefinitionFeature], decls: 11, vars: 6, consts: [[3, "Close", "Visible", "Width", "Title", "Closeable"], [1, "rp-runner"], [1, "rp-status"], [1, "rp-error"], [1, "rp-done"], ["mjButton", "", "variant", "primary"], ["mjButton", ""], ["Type", "infinite"], [1, "fa-solid", "fa-triangle-exclamation"], [1, "fa-solid", "fa-circle-check"], [1, "rp-warn"], [1, "rp-summary"], [1, "rp-pill", "rp-pill--primary"], [1, "rp-pill"], [1, "rp-pill", "rp-pill--muted"], [1, "rp-pill", "rp-pill--error"], ["Size", "compact", "Variant", "success", "Icon", "fa-regular fa-circle-check", "Title", "Nothing to change", 1, "rp-empty", 3, "Message"], ["role", "table", 1, "rp-diff"], ["role", "row", 1, "rp-diff-record"], [1, "rp-truncated"], [1, "rp-diff-key", 3, "title"], [1, "rp-diff-changes"], [1, "rp-change"], [1, "rp-change", "rp-change--error"], [1, "rp-field"], [1, "rp-old"], [1, "fa-solid", "fa-arrow-right-long", "rp-arrow"], [1, "rp-new"], ["mjButton", "", 3, "click"], ["mjButton", "", "variant", "primary", 3, "click"], [1, "fa-solid", "fa-check"]], template: function RecordProcessRunnerUXComponent_Template(rf, ctx) { if (rf & 1) {
386
+ i0.ɵɵelementStart(0, "mj-dialog", 0);
387
+ i0.ɵɵlistener("Close", function RecordProcessRunnerUXComponent_Template_mj_dialog_Close_0_listener() { return ctx.OnCancel(); });
388
+ i0.ɵɵelementStart(1, "div", 1);
389
+ i0.ɵɵconditionalCreate(2, RecordProcessRunnerUXComponent_Case_2_Template, 4, 0, "div", 2)(3, RecordProcessRunnerUXComponent_Case_3_Template, 4, 2, "div", 2)(4, RecordProcessRunnerUXComponent_Case_4_Template, 7, 1, "div", 3)(5, RecordProcessRunnerUXComponent_Case_5_Template, 6, 2, "div", 4)(6, RecordProcessRunnerUXComponent_Case_6_Template, 9, 7);
390
+ i0.ɵɵelementEnd();
391
+ i0.ɵɵelementStart(7, "mj-dialog-actions");
392
+ i0.ɵɵconditionalCreate(8, RecordProcessRunnerUXComponent_Case_8_Template, 3, 1)(9, RecordProcessRunnerUXComponent_Case_9_Template, 2, 0, "button", 5)(10, RecordProcessRunnerUXComponent_Case_10_Template, 2, 0, "button", 6);
393
+ i0.ɵɵelementEnd()();
394
+ } if (rf & 2) {
395
+ let tmp_4_0;
396
+ let tmp_5_0;
397
+ i0.ɵɵproperty("Visible", true)("Width", 760)("Title", ctx.Title)("Closeable", ctx.State !== "applying");
398
+ i0.ɵɵadvance(2);
399
+ i0.ɵɵconditional((tmp_4_0 = ctx.State) === "loading" ? 2 : tmp_4_0 === "applying" ? 3 : tmp_4_0 === "error" ? 4 : tmp_4_0 === "done" ? 5 : tmp_4_0 === "preview" ? 6 : -1);
400
+ i0.ɵɵadvance(6);
401
+ i0.ɵɵconditional((tmp_5_0 = ctx.State) === "preview" ? 8 : tmp_5_0 === "done" ? 9 : tmp_5_0 === "error" ? 10 : -1);
402
+ } }, dependencies: [MJDialogComponent, MJDialogActionsComponent, MJButtonDirective, MJProgressBarComponent, MJEmptyStateComponent], styles: [".rp-runner[_ngcontent-%COMP%] { min-height: 120px; color: var(--mj-text-primary); }\n .rp-status[_ngcontent-%COMP%] { display: flex; flex-direction: column; align-items: center; gap: 16px; padding: 32px 12px; color: var(--mj-text-secondary); }\n .rp-status[_ngcontent-%COMP%] mj-progress-bar[_ngcontent-%COMP%] { width: 60%; }\n .rp-error[_ngcontent-%COMP%], .rp-done[_ngcontent-%COMP%] { display: flex; gap: 14px; align-items: flex-start; padding: 16px; border-radius: var(--mj-radius-md, 8px); }\n .rp-error[_ngcontent-%COMP%] { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n .rp-done[_ngcontent-%COMP%] { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n .rp-error[_ngcontent-%COMP%] i[_ngcontent-%COMP%], .rp-done[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { font-size: 22px; margin-top: 2px; }\n .rp-error[_ngcontent-%COMP%] p[_ngcontent-%COMP%], .rp-done[_ngcontent-%COMP%] p[_ngcontent-%COMP%] { margin: 4px 0 0; }\n .rp-warn[_ngcontent-%COMP%] { color: var(--mj-status-warning-text); }\n .rp-summary[_ngcontent-%COMP%] { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }\n .rp-pill[_ngcontent-%COMP%] { padding: 4px 12px; border-radius: 999px; font-size: 13px; font-weight: 600; background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n .rp-pill--primary[_ngcontent-%COMP%] { background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface)); color: var(--mj-brand-primary); }\n .rp-pill--error[_ngcontent-%COMP%] { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n .rp-pill--muted[_ngcontent-%COMP%] { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n .rp-empty[_ngcontent-%COMP%] { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 28px; color: var(--mj-text-muted); }\n .rp-empty[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { font-size: 26px; color: var(--mj-status-success); }\n .rp-diff[_ngcontent-%COMP%] { max-height: 420px; overflow-y: auto; border: 1px solid var(--mj-border-default); border-radius: var(--mj-radius-md, 8px); }\n .rp-diff-record[_ngcontent-%COMP%] { display: grid; grid-template-columns: 180px 1fr; gap: 12px; padding: 10px 14px; border-bottom: 1px solid var(--mj-border-subtle); }\n .rp-diff-record[_ngcontent-%COMP%]:last-child { border-bottom: none; }\n .rp-diff-key[_ngcontent-%COMP%] { font-family: var(--mj-font-mono, monospace); font-size: 12px; color: var(--mj-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .rp-change[_ngcontent-%COMP%] { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 2px 0; font-size: 13px; }\n .rp-change--error[_ngcontent-%COMP%] { color: var(--mj-status-error-text); }\n .rp-field[_ngcontent-%COMP%] { font-weight: 600; min-width: 120px; }\n .rp-old[_ngcontent-%COMP%] { color: var(--mj-text-muted); text-decoration: line-through; }\n .rp-new[_ngcontent-%COMP%] { color: var(--mj-status-success-text); font-weight: 600; }\n .rp-arrow[_ngcontent-%COMP%] { color: var(--mj-text-disabled); font-size: 11px; }\n .rp-truncated[_ngcontent-%COMP%] { margin-top: 10px; font-size: 12px; color: var(--mj-text-muted); }\n button[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { margin-right: 6px; }"], changeDetection: 0 });
403
+ };
404
+ RecordProcessRunnerUXComponent = RecordProcessRunnerUXComponent_1 = __decorate([
405
+ RegisterClass(BaseEntityActionRuntimeUX, 'RecordProcessRunnerUX')
406
+ ], RecordProcessRunnerUXComponent);
407
+ export { RecordProcessRunnerUXComponent };
408
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RecordProcessRunnerUXComponent, [{
409
+ type: Component,
410
+ args: [{ selector: 'mj-record-process-runner-ux', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [MJDialogComponent, MJDialogActionsComponent, MJButtonDirective, MJProgressBarComponent, MJEmptyStateComponent], template: `
411
+ <mj-dialog [Visible]="true" [Width]="760" [Title]="Title" [Closeable]="State !== 'applying'" (Close)="OnCancel()">
412
+ <div class="rp-runner">
413
+ @switch (State) {
414
+ @case ('loading') {
415
+ <div class="rp-status">
416
+ <mj-progress-bar Type="infinite"></mj-progress-bar>
417
+ <p>Computing a safe preview — no changes have been made yet…</p>
418
+ </div>
419
+ }
420
+ @case ('applying') {
421
+ <div class="rp-status">
422
+ <mj-progress-bar Type="infinite"></mj-progress-bar>
423
+ <p>Applying changes to {{ ChangedRecordCount }} {{ EntityLabel }}…</p>
424
+ </div>
425
+ }
426
+ @case ('error') {
427
+ <div class="rp-error">
428
+ <i class="fa-solid fa-triangle-exclamation"></i>
429
+ <div>
430
+ <strong>Couldn't run this process</strong>
431
+ <p>{{ ErrorMessage }}</p>
432
+ </div>
433
+ </div>
434
+ }
435
+ @case ('done') {
436
+ <div class="rp-done">
437
+ <i class="fa-solid fa-circle-check"></i>
438
+ <div>
439
+ <strong>{{ DoneMessage }}</strong>
440
+ @if (AppliedResult?.Errored) {
441
+ <p class="rp-warn">{{ AppliedResult?.Errored }} record(s) reported an error.</p>
442
+ }
443
+ </div>
444
+ </div>
445
+ }
446
+ @case ('preview') {
447
+ <div class="rp-summary">
448
+ <span class="rp-pill rp-pill--primary">{{ ChangedRecordCount }} of {{ PreviewResult?.Processed ?? 0 }} {{ EntityLabel }} will change</span>
449
+ <span class="rp-pill">{{ TotalChangeCount }} field update(s)</span>
450
+ @if (SkippedCount) { <span class="rp-pill rp-pill--muted">{{ SkippedCount }} unchanged</span> }
451
+ @if (ErroredCount) { <span class="rp-pill rp-pill--error">{{ ErroredCount }} error(s)</span> }
452
+ </div>
453
+
454
+ @if (DiffRows.length === 0) {
455
+ <mj-empty-state class="rp-empty" Size="compact" Variant="success"
456
+ Icon="fa-regular fa-circle-check"
457
+ Title="Nothing to change"
458
+ [Message]="'The rules did not match any of the selected ' + EntityLabel + '.'" />
459
+ } @else {
460
+ <div class="rp-diff" role="table">
461
+ @for (row of DiffRows; track row.RecordID) {
462
+ <div class="rp-diff-record" role="row">
463
+ <div class="rp-diff-key" [title]="row.RecordID">{{ row.RecordID }}</div>
464
+ <div class="rp-diff-changes">
465
+ @for (c of row.Changes; track c.Field) {
466
+ <div class="rp-change">
467
+ <span class="rp-field">{{ c.Field }}</span>
468
+ <span class="rp-old">{{ Display(c.OldValue) }}</span>
469
+ <i class="fa-solid fa-arrow-right-long rp-arrow"></i>
470
+ <span class="rp-new">{{ Display(c.NewValue) }}</span>
471
+ </div>
472
+ }
473
+ @if (row.ErrorMessage) {
474
+ <div class="rp-change rp-change--error">{{ row.ErrorMessage }}</div>
475
+ }
476
+ </div>
477
+ </div>
478
+ }
479
+ </div>
480
+ @if (Truncated) {
481
+ <p class="rp-truncated">Showing the first {{ DiffRows.length }} changed records. The apply step covers the full scope.</p>
482
+ }
483
+ }
484
+ }
485
+ }
486
+ </div>
487
+
488
+ <mj-dialog-actions>
489
+ @switch (State) {
490
+ @case ('preview') {
491
+ @if (HasChanges) {
492
+ <button mjButton variant="primary" (click)="Apply()">
493
+ <i class="fa-solid fa-check"></i> Apply {{ TotalChangeCount }} change(s)
494
+ </button>
495
+ }
496
+ <button mjButton (click)="OnCancel()">Cancel</button>
497
+ }
498
+ @case ('done') { <button mjButton variant="primary" (click)="Finish()">Done</button> }
499
+ @case ('error') { <button mjButton (click)="OnCancel()">Close</button> }
500
+ }
501
+ </mj-dialog-actions>
502
+ </mj-dialog>
503
+ `, styles: ["\n .rp-runner { min-height: 120px; color: var(--mj-text-primary); }\n .rp-status { display: flex; flex-direction: column; align-items: center; gap: 16px; padding: 32px 12px; color: var(--mj-text-secondary); }\n .rp-status mj-progress-bar { width: 60%; }\n .rp-error, .rp-done { display: flex; gap: 14px; align-items: flex-start; padding: 16px; border-radius: var(--mj-radius-md, 8px); }\n .rp-error { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n .rp-done { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n .rp-error i, .rp-done i { font-size: 22px; margin-top: 2px; }\n .rp-error p, .rp-done p { margin: 4px 0 0; }\n .rp-warn { color: var(--mj-status-warning-text); }\n .rp-summary { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }\n .rp-pill { padding: 4px 12px; border-radius: 999px; font-size: 13px; font-weight: 600; background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n .rp-pill--primary { background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface)); color: var(--mj-brand-primary); }\n .rp-pill--error { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n .rp-pill--muted { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n .rp-empty { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 28px; color: var(--mj-text-muted); }\n .rp-empty i { font-size: 26px; color: var(--mj-status-success); }\n .rp-diff { max-height: 420px; overflow-y: auto; border: 1px solid var(--mj-border-default); border-radius: var(--mj-radius-md, 8px); }\n .rp-diff-record { display: grid; grid-template-columns: 180px 1fr; gap: 12px; padding: 10px 14px; border-bottom: 1px solid var(--mj-border-subtle); }\n .rp-diff-record:last-child { border-bottom: none; }\n .rp-diff-key { font-family: var(--mj-font-mono, monospace); font-size: 12px; color: var(--mj-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .rp-change { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 2px 0; font-size: 13px; }\n .rp-change--error { color: var(--mj-status-error-text); }\n .rp-field { font-weight: 600; min-width: 120px; }\n .rp-old { color: var(--mj-text-muted); text-decoration: line-through; }\n .rp-new { color: var(--mj-status-success-text); font-weight: 600; }\n .rp-arrow { color: var(--mj-text-disabled); font-size: 11px; }\n .rp-truncated { margin-top: 10px; font-size: 12px; color: var(--mj-text-muted); }\n button i { margin-right: 6px; }\n "] }]
504
+ }], null, null); })();
505
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RecordProcessRunnerUXComponent, { className: "RecordProcessRunnerUXComponent", filePath: "lib/record-process-runner/record-process-runner-ux.component.ts", lineNumber: 203 }); })();
506
+ //# sourceMappingURL=record-process-runner-ux.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record-process-runner-ux.component.js","sourceRoot":"","sources":["../../../src/lib/record-process-runner/record-process-runner-ux.component.ts"],"names":[],"mappings":";;;;;;;AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC9F,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EACH,4BAA4B,GAI/B,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACjK,OAAO,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;;;;;IA6DzC,8BAAuB;IACnB,qCAAmD;IACnD,yBAAG;IAAA,mFAAyD;IAChE,AADgE,iBAAI,EAC9D;;;IAGN,8BAAuB;IACnB,qCAAmD;IACnD,yBAAG;IAAA,YAA+D;IACtE,AADsE,iBAAI,EACpE;;;IADC,eAA+D;IAA/D,2GAA+D;;;IAItE,8BAAsB;IAClB,uBAAgD;IAE5C,AADJ,2BAAK,aACO;IAAA,yCAAyB;IAAA,iBAAS;IAC1C,yBAAG;IAAA,YAAkB;IAE7B,AADI,AADyB,iBAAI,EACvB,EACJ;;;IAFK,eAAkB;IAAlB,yCAAkB;;;IAUjB,6BAAmB;IAAA,YAAyD;IAAA,iBAAI;;;IAA7D,cAAyD;IAAzD,8HAAyD;;;IALxF,8BAAqB;IACjB,uBAAwC;IAEpC,AADJ,2BAAK,aACO;IAAA,YAAiB;IAAA,iBAAS;IAClC,sGAA8B;IAItC,AADI,iBAAM,EACJ;;;IALU,eAAiB;IAAjB,wCAAiB;IACzB,cAEC;IAFD,+FAEC;;;IAQgB,gCAAqC;IAAA,YAA4B;IAAA,iBAAO;;;IAAnC,cAA4B;IAA5B,4DAA4B;;;IACjE,gCAAqC;IAAA,YAA2B;IAAA,iBAAO;;;IAAlC,cAA2B;IAA3B,2DAA2B;;;IAIrF,qCAGqF;;;IAAjF,mGAA8E;;;IAS1D,AADJ,+BAAuB,eACI;IAAA,YAAa;IAAA,iBAAO;IAC3C,gCAAqB;IAAA,YAAyB;IAAA,iBAAO;IACrD,wBAAqD;IACrD,gCAAqB;IAAA,YAAyB;IAClD,AADkD,iBAAO,EACnD;;;;IAJqB,eAAa;IAAb,gCAAa;IACf,eAAyB;IAAzB,mDAAyB;IAEzB,eAAyB;IAAzB,mDAAyB;;;IAIlD,+BAAwC;IAAA,YAAsB;IAAA,iBAAM;;;IAA5B,cAAsB;IAAtB,yCAAsB;;;IAXtE,AADJ,+BAAuC,cACa;IAAA,YAAkB;IAAA,iBAAM;IACxE,+BAA6B;IACzB,6HAOC;IACD,4HAAwB;IAIhC,AADI,iBAAM,EACJ;;;IAduB,cAAsB;IAAtB,uCAAsB;IAAC,cAAkB;IAAlB,qCAAkB;IAE9D,eAOC;IAPD,6BAOC;IACD,eAEC;IAFD,8CAEC;;;IAMb,6BAAwB;IAAA,YAA8F;IAAA,iBAAI;;;IAAlG,cAA8F;IAA9F,8HAA8F;;;IArB1H,+BAAkC;IAC9B,uHAiBC;IACL,iBAAM;IACN,oHAAiB;;;IAnBb,cAiBC;IAjBD,8BAiBC;IAEL,eAEC;IAFD,2CAEC;;;IAlCD,AADJ,+BAAwB,eACmB;IAAA,YAA6F;IAAA,iBAAO;IAC3I,gCAAsB;IAAA,YAAsC;IAAA,iBAAO;IACnE,yGAAoB;IACpB,yGAAoB;IACxB,iBAAM;IAOJ,AALF,mHAA6B,uEAKpB;;;IAXkC,eAA6F;IAA7F,kLAA6F;IAC9G,eAAsC;IAAtC,sEAAsC;IAC5D,cAA+F;IAA/F,8CAA+F;IAC/F,cAA8F;IAA9F,8CAA8F;IAGlG,cA6BC;IA7BD,sDA6BC;;;;IASG,kCAAqD;IAAlB,0MAAS,cAAO,KAAC;IAChD,wBAAiC;IAAC,YACtC;IAAA,iBAAS;;;IAD6B,eACtC;IADsC,wEACtC;;;;IAHJ,0GAAkB;IAKlB,kCAAsC;IAArB,2LAAS,iBAAU,KAAC;IAAC,sBAAM;IAAA,iBAAS;;;IALrD,4CAIC;;;;IAGY,kCAAsD;IAAnB,2LAAS,eAAQ,KAAC;IAAC,oBAAI;IAAA,iBAAS;;;;IAClE,kCAAsC;IAArB,4LAAS,iBAAU,KAAC;IAAC,qBAAK;IAAA,iBAAS;;AAoCnF,IAAM,8BAA8B,GAApC,MAAM,8BAA+B,SAAQ,yBAAyB;;IACjE,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAExC,mFAAmF;IAC3E,MAAM,CAAU,gBAAgB,GAAG,GAAG,CAAC;IAExC,KAAK,GAAgB,SAAS,CAAC;IAC/B,YAAY,GAAG,EAAE,CAAC;IAClB,QAAQ,GAAoB,EAAE,CAAC;IAC/B,aAAa,CAAc;IAC3B,aAAa,CAAc;IAC3B,kBAAkB,GAAG,CAAC,CAAC;IACvB,gBAAgB,GAAG,CAAC,CAAC;IACrB,SAAS,GAAG,KAAK,CAAC;IAEzB,IAAI,KAAK,KAAa,OAAO,IAAI,CAAC,OAAO,EAAE,WAAW,IAAI,oBAAoB,CAAC,CAAC,CAAC;IACjF,IAAI,WAAW,KAAa,OAAO,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,WAAW,IAAI,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,IAAI,SAAS,CAAC,CAAC,CAAC;IAC1H,IAAI,UAAU,KAAc,OAAO,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC,CAAC,CAAC;IACjE,IAAI,YAAY,KAAa,OAAO,IAAI,CAAC,aAAa,EAAE,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;IACvE,IAAI,YAAY,KAAa,OAAO,IAAI,CAAC,aAAa,EAAE,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;IACvE,IAAI,WAAW;QACX,MAAM,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC;QAC7B,OAAO,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;IACvE,CAAC;IAED,kFAAkF;IAClF,KAAK,CAAC,KAAK;QACP,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QAC/C,IAAI,CAAC,eAAe,EAAE,CAAC;YACnB,IAAI,CAAC,OAAO,CAAC,gDAAgD,CAAC,CAAC;YAC/D,OAAO;QACX,CAAC;QACD,IAAI,CAAC;YACD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;YACxD,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;YAC7B,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;gBACnB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,IAAI,oCAAoC,CAAC,CAAC;gBAC3E,OAAO;YACX,CAAC;YACD,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;gBACvB,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YAC9C,CAAC;YACD,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAC7B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,IAAI,CAAC,OAAO,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7D,CAAC;IACL,CAAC;IAED,iDAAiD;IACjD,KAAK,CAAC,KAAK;QACP,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QAC/C,IAAI,CAAC,eAAe;YAAE,OAAO;QAC7B,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC;QACxB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QACzB,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;YACxD,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC;YAC5B,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAClB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,IAAI,mCAAmC,CAAC,CAAC;gBACzE,OAAO;YACX,CAAC;YACD,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC;YACpB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAC7B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,IAAI,CAAC,OAAO,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7D,CAAC;IACL,CAAC;IAED,oEAAoE;IACpE,MAAM;QACF,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAC3F,CAAC;IAED,uEAAuE;IACvE,QAAQ;QACJ,IAAI,IAAI,CAAC,KAAK,KAAK,UAAU;YAAE,OAAO,CAAC,gCAAgC;QACvE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IAC1B,CAAC;IAED,yEAAyE;IACzE,OAAO,CAAC,KAAc;QAClB,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,KAAK,CAAC,eAAuB,EAAE,MAAe;QACxD,MAAM,KAAK,GAA6B,EAAE,eAAe,EAAE,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,CAAC;QAC9F,MAAM,MAAM,GACR,MAAM,IAAI,4BAA4B,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,IAAI,SAAS,EAAE,CAAC,CAAC;QAC/G,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;QACxB,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,IAAI,CAAC,EAAE,MAAM,KAAK,WAAW,CAAC;QAC9D,OAAO;YACH,OAAO,EAAE,SAAS;YAClB,YAAY,EAAE,CAAC,EAAE,YAAY;YAC7B,SAAS,EAAE,CAAC,EAAE,SAAS,IAAI,CAAC;YAC5B,SAAS,EAAE,CAAC,EAAE,OAAO,IAAI,CAAC;YAC1B,OAAO,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC;YACtB,OAAO,EAAE,CAAC,EAAE,OAAO,IAAI,CAAC;YACxB,MAAM,EAAE,CAAC,EAAE,MAAM;YACjB,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,CAAC,EAAE,YAAY,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;SAC9I,CAAC;IACN,CAAC;IAED,2FAA2F;IACnF,KAAK,CAAC,QAAQ,CAAC,YAAoB;QACvC,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC;YACpH,UAAU,EAAE,yBAAyB;YACrC,WAAW,EAAE,iBAAiB,YAAY,GAAG;YAC7C,MAAM,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,eAAe,EAAE,cAAc,CAAC;YAC/D,OAAO,EAAE,UAAU;YACnB,OAAO,EAAE,gCAA8B,CAAC,gBAAgB;YACxD,UAAU,EAAE,QAAQ;SACvB,CAAC,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,OAAO;YAAE,OAAO;QAExB,MAAM,IAAI,GAAoB,EAAE,CAAC;QACjC,IAAI,cAAc,GAAG,CAAC,CAAC;QACvB,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,KAAK,MAAM,MAAM,IAAI,EAAE,CAAC,OAAsB,EAAE,CAAC;YAC7C,MAAM,OAAO,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa,CAAc,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACpG,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YAC3F,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACrB,cAAc,EAAE,CAAC;gBACjB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;YACnC,CAAC;YACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;gBAC5C,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;YACzH,CAAC;QACL,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,kBAAkB,GAAG,cAAc,CAAC;QACzC,IAAI,CAAC,gBAAgB,GAAG,YAAY,CAAC;QACrC,IAAI,CAAC,SAAS,GAAG,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,IAAI,CAAC,CAAC,GAAG,gCAA8B,CAAC,gBAAgB,CAAC;IAC5G,CAAC;IAED,6FAA6F;IACrF,UAAU;QACd,OAAO,uBAAuB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IAEO,eAAe;QACnB,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,iBAAiB,CAAC,CAAC;QACrD,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACpE,CAAC;IAEO,OAAO,CAAC,OAAe;QAC3B,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC;QAC5B,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC;QACrB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAC7B,CAAC;qSA1JQ,8BAA8B,yBAA9B,8BAA8B;6DAA9B,8BAA8B;YA5HnC,oCAAkH;YAArB,8GAAS,cAAU,IAAC;YAC7G,8BAAuB;YAkCf,AAXA,AATA,AANA,AANA,yFAAmB,mEAMC,mEAMH,mEASD,yDAWG;YAwC3B,iBAAM;YAEN,yCAAmB;YAWX,AADA,AARA,+EAAmB,sEAQH,wEACC;YAG7B,AADI,iBAAoB,EACZ;;;;YA3F8C,AAAhB,AAAd,AAAjB,8BAAgB,cAAc,oBAAgB,uCAAmC;YAEpF,eAwEC;YAxED,2CAAA,SAAS,mBAAT,UAAU,mBAAV,OAAO,mBAAP,MAAM,mBAAN,SAAS,UAwER;YAID,eAWC;YAXD,2CAAA,SAAS,mBAAT,MAAM,mBAAN,OAAO,WAWN;4BA3FH,iBAAiB,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,qBAAqB;;AA8H9G,8BAA8B;IAnI1C,aAAa,CAAC,yBAAyB,EAAE,uBAAuB,CAAC;GAmIrD,8BAA8B,CA2J1C;;iFA3JY,8BAA8B;cAlI1C,SAAS;2BACI,6BAA6B,cAC3B,IAAI,mBACC,uBAAuB,CAAC,MAAM,WACtC,CAAC,iBAAiB,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,qBAAqB,CAAC,YAC9G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA6FT;;kFAgCQ,8BAA8B","sourcesContent":["/**\n * @fileoverview The flagship runtime-UX driver: runs a `MJ: Record Processes` (typically a FieldRules\n * bulk-update) as a safe, two-step flow — a server dry-run computes the exact per-record diff, the user\n * reviews it, and only on confirm does the real write happen. Registered as `RecordProcessRunnerUX`; an\n * entity action points its `RuntimeUXDriverClass` here and the host mounts it. All server work goes through\n * the typed {@link GraphQLRecordProcessClient}; the rich diff is read with `RunView` over\n * `MJ: Process Run Details`.\n * @module @memberjunction/ng-entity-action-ux\n */\nimport { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject } from '@angular/core';\nimport { RunView } from '@memberjunction/core';\nimport { RegisterClass, SafeJSONParse } from '@memberjunction/global';\nimport {\n RecordProcessRunNowOperation,\n type RecordProcessRunNowInput,\n type RecordProcessRunNowOutput,\n type RecordProcessScopeOverride,\n} from '@memberjunction/core-entities';\nimport type { RemoteOpResult } from '@memberjunction/core';\nimport { MJDialogComponent, MJDialogActionsComponent, MJButtonDirective, MJProgressBarComponent, MJEmptyStateComponent } from '@memberjunction/ng-ui-components';\nimport { BaseEntityActionRuntimeUX } from '../base-entity-action-runtime-ux';\nimport { buildRecordProcessScope, displayValue } from '../scope';\n\n/** The run summary the runner renders, mapped from the operation's typed output. */\ninterface RunSummary {\n Success: boolean;\n ProcessRunID?: string;\n Processed: number;\n Succeeded: number;\n Errored: number;\n Skipped: number;\n Status?: string;\n ErrorMessage?: string;\n}\n\n/** The driver's flow state. */\ntype RunnerState = 'loading' | 'preview' | 'applying' | 'done' | 'error';\n\n/** One computed field change (mirrors the engine's `FieldChange` for the bits the UI shows). */\ninterface DiffChange {\n Field: string;\n OldValue: unknown;\n NewValue: unknown;\n Applied: boolean;\n Changed: boolean;\n Error?: string;\n}\n\n/** The JSON payload persisted on each `MJ: Process Run Details.ResultPayload` for a FieldRules run. */\ninterface DiffPayload {\n DryRun: boolean;\n Changes: DiffChange[];\n ChangedFields: string[];\n}\n\n/** A per-record row of the preview table. */\ninterface RecordDiffRow {\n RecordID: string;\n Status: string;\n Changes: DiffChange[];\n ErrorMessage?: string;\n}\n\n/** Raw `MJ: Process Run Details` projection used to build the diff. */\ninterface RawDetail {\n RecordID: string;\n Status: string;\n ResultPayload?: string;\n ErrorMessage?: string;\n}\n\n@RegisterClass(BaseEntityActionRuntimeUX, 'RecordProcessRunnerUX')\n@Component({\n selector: 'mj-record-process-runner-ux',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [MJDialogComponent, MJDialogActionsComponent, MJButtonDirective, MJProgressBarComponent, MJEmptyStateComponent],\n template: `\n <mj-dialog [Visible]=\"true\" [Width]=\"760\" [Title]=\"Title\" [Closeable]=\"State !== 'applying'\" (Close)=\"OnCancel()\">\n <div class=\"rp-runner\">\n @switch (State) {\n @case ('loading') {\n <div class=\"rp-status\">\n <mj-progress-bar Type=\"infinite\"></mj-progress-bar>\n <p>Computing a safe preview — no changes have been made yet…</p>\n </div>\n }\n @case ('applying') {\n <div class=\"rp-status\">\n <mj-progress-bar Type=\"infinite\"></mj-progress-bar>\n <p>Applying changes to {{ ChangedRecordCount }} {{ EntityLabel }}…</p>\n </div>\n }\n @case ('error') {\n <div class=\"rp-error\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <div>\n <strong>Couldn't run this process</strong>\n <p>{{ ErrorMessage }}</p>\n </div>\n </div>\n }\n @case ('done') {\n <div class=\"rp-done\">\n <i class=\"fa-solid fa-circle-check\"></i>\n <div>\n <strong>{{ DoneMessage }}</strong>\n @if (AppliedResult?.Errored) {\n <p class=\"rp-warn\">{{ AppliedResult?.Errored }} record(s) reported an error.</p>\n }\n </div>\n </div>\n }\n @case ('preview') {\n <div class=\"rp-summary\">\n <span class=\"rp-pill rp-pill--primary\">{{ ChangedRecordCount }} of {{ PreviewResult?.Processed ?? 0 }} {{ EntityLabel }} will change</span>\n <span class=\"rp-pill\">{{ TotalChangeCount }} field update(s)</span>\n @if (SkippedCount) { <span class=\"rp-pill rp-pill--muted\">{{ SkippedCount }} unchanged</span> }\n @if (ErroredCount) { <span class=\"rp-pill rp-pill--error\">{{ ErroredCount }} error(s)</span> }\n </div>\n\n @if (DiffRows.length === 0) {\n <mj-empty-state class=\"rp-empty\" Size=\"compact\" Variant=\"success\"\n Icon=\"fa-regular fa-circle-check\"\n Title=\"Nothing to change\"\n [Message]=\"'The rules did not match any of the selected ' + EntityLabel + '.'\" />\n } @else {\n <div class=\"rp-diff\" role=\"table\">\n @for (row of DiffRows; track row.RecordID) {\n <div class=\"rp-diff-record\" role=\"row\">\n <div class=\"rp-diff-key\" [title]=\"row.RecordID\">{{ row.RecordID }}</div>\n <div class=\"rp-diff-changes\">\n @for (c of row.Changes; track c.Field) {\n <div class=\"rp-change\">\n <span class=\"rp-field\">{{ c.Field }}</span>\n <span class=\"rp-old\">{{ Display(c.OldValue) }}</span>\n <i class=\"fa-solid fa-arrow-right-long rp-arrow\"></i>\n <span class=\"rp-new\">{{ Display(c.NewValue) }}</span>\n </div>\n }\n @if (row.ErrorMessage) {\n <div class=\"rp-change rp-change--error\">{{ row.ErrorMessage }}</div>\n }\n </div>\n </div>\n }\n </div>\n @if (Truncated) {\n <p class=\"rp-truncated\">Showing the first {{ DiffRows.length }} changed records. The apply step covers the full scope.</p>\n }\n }\n }\n }\n </div>\n\n <mj-dialog-actions>\n @switch (State) {\n @case ('preview') {\n @if (HasChanges) {\n <button mjButton variant=\"primary\" (click)=\"Apply()\">\n <i class=\"fa-solid fa-check\"></i> Apply {{ TotalChangeCount }} change(s)\n </button>\n }\n <button mjButton (click)=\"OnCancel()\">Cancel</button>\n }\n @case ('done') { <button mjButton variant=\"primary\" (click)=\"Finish()\">Done</button> }\n @case ('error') { <button mjButton (click)=\"OnCancel()\">Close</button> }\n }\n </mj-dialog-actions>\n </mj-dialog>\n `,\n styles: [`\n .rp-runner { min-height: 120px; color: var(--mj-text-primary); }\n .rp-status { display: flex; flex-direction: column; align-items: center; gap: 16px; padding: 32px 12px; color: var(--mj-text-secondary); }\n .rp-status mj-progress-bar { width: 60%; }\n .rp-error, .rp-done { display: flex; gap: 14px; align-items: flex-start; padding: 16px; border-radius: var(--mj-radius-md, 8px); }\n .rp-error { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n .rp-done { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n .rp-error i, .rp-done i { font-size: 22px; margin-top: 2px; }\n .rp-error p, .rp-done p { margin: 4px 0 0; }\n .rp-warn { color: var(--mj-status-warning-text); }\n .rp-summary { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }\n .rp-pill { padding: 4px 12px; border-radius: 999px; font-size: 13px; font-weight: 600; background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n .rp-pill--primary { background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface)); color: var(--mj-brand-primary); }\n .rp-pill--error { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n .rp-pill--muted { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n .rp-empty { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 28px; color: var(--mj-text-muted); }\n .rp-empty i { font-size: 26px; color: var(--mj-status-success); }\n .rp-diff { max-height: 420px; overflow-y: auto; border: 1px solid var(--mj-border-default); border-radius: var(--mj-radius-md, 8px); }\n .rp-diff-record { display: grid; grid-template-columns: 180px 1fr; gap: 12px; padding: 10px 14px; border-bottom: 1px solid var(--mj-border-subtle); }\n .rp-diff-record:last-child { border-bottom: none; }\n .rp-diff-key { font-family: var(--mj-font-mono, monospace); font-size: 12px; color: var(--mj-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .rp-change { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 2px 0; font-size: 13px; }\n .rp-change--error { color: var(--mj-status-error-text); }\n .rp-field { font-weight: 600; min-width: 120px; }\n .rp-old { color: var(--mj-text-muted); text-decoration: line-through; }\n .rp-new { color: var(--mj-status-success-text); font-weight: 600; }\n .rp-arrow { color: var(--mj-text-disabled); font-size: 11px; }\n .rp-truncated { margin-top: 10px; font-size: 12px; color: var(--mj-text-muted); }\n button i { margin-right: 6px; }\n `],\n})\nexport class RecordProcessRunnerUXComponent extends BaseEntityActionRuntimeUX {\n private cdr = inject(ChangeDetectorRef);\n\n /** Cap on preview rows we render (the apply step always covers the full scope). */\n private static readonly MAX_PREVIEW_ROWS = 500;\n\n public State: RunnerState = 'loading';\n public ErrorMessage = '';\n public DiffRows: RecordDiffRow[] = [];\n public PreviewResult?: RunSummary;\n public AppliedResult?: RunSummary;\n public ChangedRecordCount = 0;\n public TotalChangeCount = 0;\n public Truncated = false;\n\n get Title(): string { return this.Context?.ActionLabel || 'Run Record Process'; }\n get EntityLabel(): string { return this.Context?.EntityInfo?.DisplayName || this.Context?.EntityInfo?.Name || 'records'; }\n get HasChanges(): boolean { return this.ChangedRecordCount > 0; }\n get SkippedCount(): number { return this.PreviewResult?.Skipped ?? 0; }\n get ErroredCount(): number { return this.PreviewResult?.Errored ?? 0; }\n get DoneMessage(): string {\n const r = this.AppliedResult;\n return r ? `Updated ${r.Succeeded} ${this.EntityLabel}.` : 'Done.';\n }\n\n /** Step 1: run a dry-run on the server and load the resulting per-record diff. */\n async Start(): Promise<void> {\n const recordProcessID = this.recordProcessID();\n if (!recordProcessID) {\n this.toError('This action has no RecordProcessID configured.');\n return;\n }\n try {\n const preview = await this.runOp(recordProcessID, true);\n this.PreviewResult = preview;\n if (!preview.Success) {\n this.toError(preview.ErrorMessage || 'The preview could not be computed.');\n return;\n }\n if (preview.ProcessRunID) {\n await this.loadDiff(preview.ProcessRunID);\n }\n this.State = 'preview';\n this.cdr.detectChanges();\n } catch (e) {\n this.toError(e instanceof Error ? e.message : String(e));\n }\n }\n\n /** Step 2: the user confirmed — run for real. */\n async Apply(): Promise<void> {\n const recordProcessID = this.recordProcessID();\n if (!recordProcessID) return;\n this.State = 'applying';\n this.cdr.detectChanges();\n try {\n const result = await this.runOp(recordProcessID, false);\n this.AppliedResult = result;\n if (!result.Success) {\n this.toError(result.ErrorMessage || 'The changes could not be applied.');\n return;\n }\n this.State = 'done';\n this.cdr.detectChanges();\n } catch (e) {\n this.toError(e instanceof Error ? e.message : String(e));\n }\n }\n\n /** Closes after a successful apply, telling the host to refresh. */\n Finish(): void {\n this.Completed.emit({ Completed: true, RefreshData: true, Message: this.DoneMessage });\n }\n\n /** Dismiss without applying (also used for the error/close button). */\n OnCancel(): void {\n if (this.State === 'applying') return; // don't allow dismiss mid-write\n this.Cancelled.emit();\n }\n\n /** Renders a value for the diff table (delegates to the pure helper). */\n Display(value: unknown): string {\n return displayValue(value);\n }\n\n /**\n * Runs the `RecordProcess.RunNow` Remote Operation and maps its typed output to a {@link RunSummary}.\n * `Execute` routes through the host's provider — marshalled over the generic `ExecuteRemoteOperation`\n * transport on the client, in-process on the server — so there is no bespoke resolver/client.\n */\n private async runOp(recordProcessID: string, dryRun: boolean): Promise<RunSummary> {\n const input: RecordProcessRunNowInput = { recordProcessID, scope: this.buildScope(), dryRun };\n const result: RemoteOpResult<RecordProcessRunNowOutput> =\n await new RecordProcessRunNowOperation().Execute(input, { provider: this.Context?.Provider ?? undefined });\n const o = result.Output;\n const completed = result.Success && o?.status === 'Completed';\n return {\n Success: completed,\n ProcessRunID: o?.processRunID,\n Processed: o?.processed ?? 0,\n Succeeded: o?.success ?? 0,\n Errored: o?.error ?? 0,\n Skipped: o?.skipped ?? 0,\n Status: o?.status,\n ErrorMessage: result.ErrorMessage ?? o?.errorMessage ?? (o && o.status !== 'Completed' ? `Run ended with status '${o.status}'` : undefined),\n };\n }\n\n /** Reads `MJ: Process Run Details` for the dry-run and builds the per-record diff rows. */\n private async loadDiff(processRunID: string): Promise<void> {\n const rv = await (this.Context?.Provider ? RunView.FromMetadataProvider(this.Context.Provider) : new RunView()).RunView({\n EntityName: 'MJ: Process Run Details',\n ExtraFilter: `ProcessRunID='${processRunID}'`,\n Fields: ['RecordID', 'Status', 'ResultPayload', 'ErrorMessage'],\n OrderBy: 'RecordID',\n MaxRows: RecordProcessRunnerUXComponent.MAX_PREVIEW_ROWS,\n ResultType: 'simple',\n });\n if (!rv.Success) return;\n\n const rows: RecordDiffRow[] = [];\n let changedRecords = 0;\n let totalChanges = 0;\n for (const detail of rv.Results as RawDetail[]) {\n const payload = detail.ResultPayload ? SafeJSONParse<DiffPayload>(detail.ResultPayload) : undefined;\n const changes = (payload?.Changes ?? []).filter((c) => c.Applied && c.Changed && !c.Error);\n if (changes.length > 0) {\n changedRecords++;\n totalChanges += changes.length;\n }\n if (changes.length > 0 || detail.ErrorMessage) {\n rows.push({ RecordID: detail.RecordID, Status: detail.Status, Changes: changes, ErrorMessage: detail.ErrorMessage });\n }\n }\n this.DiffRows = rows;\n this.ChangedRecordCount = changedRecords;\n this.TotalChangeCount = totalChanges;\n this.Truncated = (this.PreviewResult?.Processed ?? 0) > RecordProcessRunnerUXComponent.MAX_PREVIEW_ROWS;\n }\n\n /** Builds the engine scope from the host-supplied context (delegates to the pure helper). */\n private buildScope(): RecordProcessScopeOverride {\n return buildRecordProcessScope(this.Context);\n }\n\n private recordProcessID(): string | undefined {\n const id = this.Context?.Config?.['RecordProcessID'];\n return typeof id === 'string' && id.length > 0 ? id : undefined;\n }\n\n private toError(message: string): void {\n this.ErrorMessage = message;\n this.State = 'error';\n this.cdr.detectChanges();\n }\n}\n"]}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @fileoverview The contract a host passes to a runtime-UX driver. Framework-agnostic data only — the
3
+ * entity being acted on, the scope of rows, and a driver-specific config bag — so a driver never needs to
4
+ * know how the grid/list above it works.
5
+ * @module @memberjunction/ng-entity-action-ux
6
+ */
7
+ import type { EntityInfo, IMetadataProvider, UserInfo } from '@memberjunction/core';
8
+ /** How a driver's work is scoped — mirrors the engine's `RecordProcessScopeOverride`. */
9
+ export type EntityActionUXScopeKind = 'records' | 'view' | 'list' | 'filter';
10
+ /**
11
+ * Everything a runtime-UX driver needs to do its job, assembled by the host from the grid/list state and
12
+ * the entity action's configured params.
13
+ */
14
+ export interface EntityActionUXContext {
15
+ /** The entity being acted on. */
16
+ EntityInfo: EntityInfo;
17
+ /** How the target rows are scoped. */
18
+ ScopeKind: EntityActionUXScopeKind;
19
+ /** Selected row primary-key values (when `ScopeKind === 'records'`). */
20
+ SelectedRecordIDs?: string[];
21
+ /** Source view ID (when `ScopeKind === 'view'`). */
22
+ ViewID?: string;
23
+ /** Source list ID (when `ScopeKind === 'list'`). */
24
+ ListID?: string;
25
+ /** Filter expression (when `ScopeKind === 'filter'`). */
26
+ Filter?: string;
27
+ /**
28
+ * Driver-specific configuration resolved from the entity action's params — e.g. the `RecordProcessID`
29
+ * the {@link RecordProcessRunnerUXComponent} should run. Opaque to the framework.
30
+ */
31
+ Config?: Record<string, unknown>;
32
+ /** Acting user (server-side calls; client-side may omit). */
33
+ ContextUser?: UserInfo;
34
+ /**
35
+ * The metadata provider this work runs under (multi-provider support). When the host omits it, drivers
36
+ * fall back to the global default provider. Drivers that issue a `RunView` / GraphQL call should scope
37
+ * to this provider when present.
38
+ */
39
+ Provider?: IMetadataProvider;
40
+ /** Display label for the action (used in driver headers when present). */
41
+ ActionLabel?: string;
42
+ }
43
+ /** A driver's terminal outcome, emitted to the host. */
44
+ export interface EntityActionUXResult {
45
+ /** True when the driver applied work; false when the user backed out without applying. */
46
+ Completed: boolean;
47
+ /** When true, the host should refresh the underlying grid/list (data changed). */
48
+ RefreshData?: boolean;
49
+ /** Optional human-readable summary (e.g. "Updated 42 records"). */
50
+ Message?: string;
51
+ }
52
+ //# sourceMappingURL=runtime-ux-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-ux-context.d.ts","sourceRoot":"","sources":["../../src/lib/runtime-ux-context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEpF,yFAAyF;AACzF,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE7E;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IAClC,iCAAiC;IACjC,UAAU,EAAE,UAAU,CAAC;IACvB,sCAAsC;IACtC,SAAS,EAAE,uBAAuB,CAAC;IACnC,wEAAwE;IACxE,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,6DAA6D;IAC7D,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wDAAwD;AACxD,MAAM,WAAW,oBAAoB;IACjC,0FAA0F;IAC1F,SAAS,EAAE,OAAO,CAAC;IACnB,kFAAkF;IAClF,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=runtime-ux-context.js.map