@memberjunction/ng-entity-action-ux 5.43.0 → 5.45.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.
@@ -1 +1 @@
1
- {"version":3,"file":"field-rules-builder.component.d.ts","sourceRoot":"","sources":["../../../src/lib/field-rules-builder/field-rules-builder.component.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAyD,YAAY,EAAyB,MAAM,eAAe,CAAC;AAC3H,OAAO,EAA+C,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC3G,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAwE,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;;AAE5H,qBA+Ga,0BAA0B;IACnC,OAAO,CAAC,GAAG,CAA6B;IAEjC,KAAK,EAAE,SAAS,EAAE,CAAM;IACxB,MAAM,EAAE,MAAM,EAAE,CAAM;IACtB,cAAc,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE,CAAC,CAAM;IACxE,SAAS,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE,CAAC,CAAM;IAE1E,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,MAAM,CAAC,CAAa;IAE5B,sFAAsF;IAC7E,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAQ;IAEnD,gFAAgF;IAChF,IACI,UAAU,CAAC,KAAK,EAAE,MAAM,EAI3B;IACD,IAAI,UAAU,IAAI,MAAM,CAA6B;IAErD;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe,CAAuB;IAE9C,4EAA4E;IAC5E,IACI,KAAK,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,EAQ/C;IAED,iGAAiG;IACvF,WAAW,6BAAoC;IAEzD,2FAA2F;IACjF,WAAW,wBAA+B;IAEpD,IAAI,WAAW,IAAI,MAAM,CAAsE;IAE/F,OAAO,IAAI,IAAI;IAKf,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK/B,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAK5C,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAK1C,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAKnE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAKvD,mDAAmD;IACnD,OAAO,CAAC,IAAI;IAUZ,OAAO,CAAC,QAAQ;IAWhB,OAAO,CAAC,UAAU;yCAzGT,0BAA0B;2CAA1B,0BAA0B;CAgHtC"}
1
+ {"version":3,"file":"field-rules-builder.component.d.ts","sourceRoot":"","sources":["../../../src/lib/field-rules-builder/field-rules-builder.component.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAyD,YAAY,EAAyB,MAAM,eAAe,CAAC;AAC3H,OAAO,EAA+C,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC3G,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAwE,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;;AAG5H,qBA+Ga,0BAA0B;IACnC,OAAO,CAAC,GAAG,CAA6B;IAEjC,KAAK,EAAE,SAAS,EAAE,CAAM;IACxB,MAAM,EAAE,MAAM,EAAE,CAAM;IACtB,cAAc,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE,CAAC,CAAM;IACxE,SAAS,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE,CAAC,CAAM;IAE1E,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,MAAM,CAAC,CAAa;IAE5B,sFAAsF;IAC7E,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAQ;IAEnD,gFAAgF;IAChF,IACI,UAAU,CAAC,KAAK,EAAE,MAAM,EAI3B;IACD,IAAI,UAAU,IAAI,MAAM,CAA6B;IAErD;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe,CAAuB;IAE9C,4EAA4E;IAC5E,IACI,KAAK,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,EAQ/C;IAED,iGAAiG;IACvF,WAAW,6BAAoC;IAEzD,2FAA2F;IACjF,WAAW,wBAA+B;IAEpD,IAAI,WAAW,IAAI,MAAM,CAAsE;IAE/F,OAAO,IAAI,IAAI;IAKf,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK/B,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAK5C,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAK1C,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAKnE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAKvD,mDAAmD;IACnD,OAAO,CAAC,IAAI;IAUZ,OAAO,CAAC,QAAQ;IAWhB,OAAO,CAAC,UAAU;yCAzGT,0BAA0B;2CAA1B,0BAA0B;CAgHtC"}
@@ -10,16 +10,11 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, In
10
10
  import { EntityFieldRules, Metadata } from '@memberjunction/core';
11
11
  import { AIPromptSelectorComponent } from '../ai-prompt-selector/ai-prompt-selector.component';
12
12
  import { blankRuleDraft, draftsToRuleSet, ruleToDraft } from '../field-rules-model';
13
+ import { MJEmptyStateComponent } from '@memberjunction/ng-ui-components';
13
14
  import * as i0 from "@angular/core";
14
15
  const _forTrack0 = ($index, $item) => $item.Name;
15
16
  function FieldRulesBuilderComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
16
- i0.ɵɵelementStart(0, "div", 5);
17
- i0.ɵɵtext(1, "No rules yet. Click ");
18
- i0.ɵɵelementStart(2, "strong");
19
- i0.ɵɵtext(3, "Add rule");
20
- i0.ɵɵelementEnd();
21
- i0.ɵɵtext(4, " to start.");
22
- i0.ɵɵelementEnd();
17
+ i0.ɵɵelement(0, "mj-empty-state", 5);
23
18
  } }
24
19
  function FieldRulesBuilderComponent_For_12_For_10_Template(rf, ctx) { if (rf & 1) {
25
20
  i0.ɵɵelementStart(0, "option", 13);
@@ -290,7 +285,7 @@ export class FieldRulesBuilderComponent {
290
285
  this.validate();
291
286
  }
292
287
  static ɵfac = function FieldRulesBuilderComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FieldRulesBuilderComponent)(); };
293
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FieldRulesBuilderComponent, selectors: [["mj-field-rules-builder"]], inputs: { Provider: "Provider", EntityName: "EntityName", Value: "Value" }, outputs: { ValueChange: "ValueChange", ValidChange: "ValidChange" }, decls: 14, vars: 3, consts: [[1, "frb"], [1, "frb-head"], [1, "frb-sub"], ["type", "button", 1, "frb-add", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "frb-empty"], [1, "frb-rule"], [1, "frb-errors"], [1, "frb-rule-head"], [1, "frb-rule-num"], [1, "frb-set"], [1, "mj-input", "frb-target", 3, "change", "value"], ["value", ""], [3, "value"], [1, "mj-input", "frb-kind", 3, "change", "value"], ["value", "static"], ["value", "field"], ["value", "formula"], ["value", "lookup"], ["value", "prompt"], ["type", "button", "title", "Remove rule", 1, "frb-del", 3, "click"], [1, "fa-solid", "fa-trash"], [1, "frb-source"], ["placeholder", "Value", 1, "mj-input", 3, "value"], [1, "mj-input", 3, "value"], ["placeholder", "e.g. fields.FirstName + ' ' + fields.LastName", 1, "mj-input", 3, "value"], [1, "frb-lookup"], [3, "Value"], [1, "frb-cond"], [1, "frb-cond-label"], ["placeholder", "(always) \u2014 e.g. fields.Status === 'Active'", 1, "mj-input", 3, "input", "value"], ["placeholder", "Value", 1, "mj-input", 3, "input", "value"], [1, "mj-input", 3, "change", "value"], ["placeholder", "e.g. fields.FirstName + ' ' + fields.LastName", 1, "mj-input", 3, "input", "value"], ["placeholder", "Lookup entity", 1, "mj-input", 3, "input", "value"], ["placeholder", "Match field", 1, "mj-input", 3, "input", "value"], ["placeholder", "= this record's field", 1, "mj-input", 3, "input", "value"], ["placeholder", "Return field", 1, "mj-input", 3, "input", "value"], [3, "ValueChange", "Value"], [1, "fa-solid", "fa-circle-exclamation"]], template: function FieldRulesBuilderComponent_Template(rf, ctx) { if (rf & 1) {
288
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FieldRulesBuilderComponent, selectors: [["mj-field-rules-builder"]], inputs: { Provider: "Provider", EntityName: "EntityName", Value: "Value" }, outputs: { ValueChange: "ValueChange", ValidChange: "ValidChange" }, decls: 14, vars: 3, consts: [[1, "frb"], [1, "frb-head"], [1, "frb-sub"], ["type", "button", 1, "frb-add", 3, "click"], [1, "fa-solid", "fa-plus"], ["Size", "compact", "Icon", "", "Title", "No rules yet", "Message", "Click Add rule to start.", 1, "frb-empty"], [1, "frb-rule"], [1, "frb-errors"], [1, "frb-rule-head"], [1, "frb-rule-num"], [1, "frb-set"], [1, "mj-input", "frb-target", 3, "change", "value"], ["value", ""], [3, "value"], [1, "mj-input", "frb-kind", 3, "change", "value"], ["value", "static"], ["value", "field"], ["value", "formula"], ["value", "lookup"], ["value", "prompt"], ["type", "button", "title", "Remove rule", 1, "frb-del", 3, "click"], [1, "fa-solid", "fa-trash"], [1, "frb-source"], ["placeholder", "Value", 1, "mj-input", 3, "value"], [1, "mj-input", 3, "value"], ["placeholder", "e.g. fields.FirstName + ' ' + fields.LastName", 1, "mj-input", 3, "value"], [1, "frb-lookup"], [3, "Value"], [1, "frb-cond"], [1, "frb-cond-label"], ["placeholder", "(always) \u2014 e.g. fields.Status === 'Active'", 1, "mj-input", 3, "input", "value"], ["placeholder", "Value", 1, "mj-input", 3, "input", "value"], [1, "mj-input", 3, "change", "value"], ["placeholder", "e.g. fields.FirstName + ' ' + fields.LastName", 1, "mj-input", 3, "input", "value"], ["placeholder", "Lookup entity", 1, "mj-input", 3, "input", "value"], ["placeholder", "Match field", 1, "mj-input", 3, "input", "value"], ["placeholder", "= this record's field", 1, "mj-input", 3, "input", "value"], ["placeholder", "Return field", 1, "mj-input", 3, "input", "value"], [3, "ValueChange", "Value"], [1, "fa-solid", "fa-circle-exclamation"]], template: function FieldRulesBuilderComponent_Template(rf, ctx) { if (rf & 1) {
294
289
  i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div")(3, "h3");
295
290
  i0.ɵɵtext(4, "Field rules");
296
291
  i0.ɵɵelementEnd();
@@ -302,7 +297,7 @@ export class FieldRulesBuilderComponent {
302
297
  i0.ɵɵelement(8, "i", 4);
303
298
  i0.ɵɵtext(9, " Add rule");
304
299
  i0.ɵɵelementEnd()();
305
- i0.ɵɵconditionalCreate(10, FieldRulesBuilderComponent_Conditional_10_Template, 5, 0, "div", 5);
300
+ i0.ɵɵconditionalCreate(10, FieldRulesBuilderComponent_Conditional_10_Template, 1, 0, "mj-empty-state", 5);
306
301
  i0.ɵɵrepeaterCreate(11, FieldRulesBuilderComponent_For_12_Template, 36, 5, "div", 6, i0.ɵɵrepeaterTrackByIndex);
307
302
  i0.ɵɵconditionalCreate(13, FieldRulesBuilderComponent_Conditional_13_Template, 5, 0, "div", 7);
308
303
  i0.ɵɵelementEnd();
@@ -315,11 +310,11 @@ export class FieldRulesBuilderComponent {
315
310
  i0.ɵɵrepeater(ctx.Rules);
316
311
  i0.ɵɵadvance(2);
317
312
  i0.ɵɵconditional(ctx.Errors.length > 0 ? 13 : -1);
318
- } }, dependencies: [AIPromptSelectorComponent], styles: [".frb[_ngcontent-%COMP%] { color: var(--mj-text-primary); display: flex; flex-direction: column; gap: 12px; }\n .frb-head[_ngcontent-%COMP%] { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }\n .frb-head[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] { margin: 0; font-size: 16px; }\n .frb-sub[_ngcontent-%COMP%] { margin: 2px 0 0; font-size: 13px; color: var(--mj-text-muted); }\n .frb-add[_ngcontent-%COMP%] { display: flex; align-items: center; gap: 6px; padding: 7px 12px; border: 1px solid var(--mj-brand-primary);\n color: var(--mj-brand-primary); background: transparent; border-radius: var(--mj-radius-sm, 6px); cursor: pointer; font-weight: 600; }\n .frb-add[_ngcontent-%COMP%]:hover { background: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent); }\n .frb-empty[_ngcontent-%COMP%] { padding: 20px; text-align: center; color: var(--mj-text-muted); border: 1px dashed var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px); }\n .frb-rule[_ngcontent-%COMP%] { border: 1px solid var(--mj-border-default); border-radius: var(--mj-radius-md, 8px); padding: 12px; background: var(--mj-bg-surface-card); }\n .frb-rule-head[_ngcontent-%COMP%] { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n .frb-rule-num[_ngcontent-%COMP%] { width: 22px; height: 22px; display: inline-flex; align-items: center; justify-content: center;\n border-radius: 50%; background: var(--mj-brand-primary); color: var(--mj-text-inverse); font-size: 12px; font-weight: 700; }\n .frb-set[_ngcontent-%COMP%] { color: var(--mj-text-secondary); font-size: 14px; }\n .frb-target[_ngcontent-%COMP%], .frb-kind[_ngcontent-%COMP%] { min-width: 150px; }\n .frb-del[_ngcontent-%COMP%] { margin-left: auto; border: none; background: transparent; color: var(--mj-text-muted); cursor: pointer; padding: 4px 8px; }\n .frb-del[_ngcontent-%COMP%]:hover { color: var(--mj-status-error); }\n .frb-source[_ngcontent-%COMP%] { margin-top: 10px; }\n .frb-source[_ngcontent-%COMP%] .mj-input[_ngcontent-%COMP%] { width: 100%; }\n .frb-lookup[_ngcontent-%COMP%] { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 8px; }\n .frb-cond[_ngcontent-%COMP%] { display: flex; align-items: center; gap: 8px; margin-top: 10px; }\n .frb-cond-label[_ngcontent-%COMP%] { font-size: 13px; color: var(--mj-text-muted); white-space: nowrap; }\n .frb-cond[_ngcontent-%COMP%] .mj-input[_ngcontent-%COMP%] { flex: 1; }\n .frb-errors[_ngcontent-%COMP%] { display: flex; gap: 10px; padding: 10px 12px; background: var(--mj-status-error-bg); color: var(--mj-status-error-text);\n border-radius: var(--mj-radius-sm, 6px); font-size: 13px; }\n .frb-errors[_ngcontent-%COMP%] ul[_ngcontent-%COMP%] { margin: 0; padding-left: 18px; }"], changeDetection: 0 });
313
+ } }, dependencies: [AIPromptSelectorComponent, MJEmptyStateComponent], styles: [".frb[_ngcontent-%COMP%] { color: var(--mj-text-primary); display: flex; flex-direction: column; gap: 12px; }\n .frb-head[_ngcontent-%COMP%] { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }\n .frb-head[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] { margin: 0; font-size: 16px; }\n .frb-sub[_ngcontent-%COMP%] { margin: 2px 0 0; font-size: 13px; color: var(--mj-text-muted); }\n .frb-add[_ngcontent-%COMP%] { display: flex; align-items: center; gap: 6px; padding: 7px 12px; border: 1px solid var(--mj-brand-primary);\n color: var(--mj-brand-primary); background: transparent; border-radius: var(--mj-radius-sm, 6px); cursor: pointer; font-weight: 600; }\n .frb-add[_ngcontent-%COMP%]:hover { background: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent); }\n .frb-empty[_ngcontent-%COMP%] { padding: 20px; text-align: center; color: var(--mj-text-muted); border: 1px dashed var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px); }\n .frb-rule[_ngcontent-%COMP%] { border: 1px solid var(--mj-border-default); border-radius: var(--mj-radius-md, 8px); padding: 12px; background: var(--mj-bg-surface-card); }\n .frb-rule-head[_ngcontent-%COMP%] { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n .frb-rule-num[_ngcontent-%COMP%] { width: 22px; height: 22px; display: inline-flex; align-items: center; justify-content: center;\n border-radius: 50%; background: var(--mj-brand-primary); color: var(--mj-text-inverse); font-size: 12px; font-weight: 700; }\n .frb-set[_ngcontent-%COMP%] { color: var(--mj-text-secondary); font-size: 14px; }\n .frb-target[_ngcontent-%COMP%], .frb-kind[_ngcontent-%COMP%] { min-width: 150px; }\n .frb-del[_ngcontent-%COMP%] { margin-left: auto; border: none; background: transparent; color: var(--mj-text-muted); cursor: pointer; padding: 4px 8px; }\n .frb-del[_ngcontent-%COMP%]:hover { color: var(--mj-status-error); }\n .frb-source[_ngcontent-%COMP%] { margin-top: 10px; }\n .frb-source[_ngcontent-%COMP%] .mj-input[_ngcontent-%COMP%] { width: 100%; }\n .frb-lookup[_ngcontent-%COMP%] { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 8px; }\n .frb-cond[_ngcontent-%COMP%] { display: flex; align-items: center; gap: 8px; margin-top: 10px; }\n .frb-cond-label[_ngcontent-%COMP%] { font-size: 13px; color: var(--mj-text-muted); white-space: nowrap; }\n .frb-cond[_ngcontent-%COMP%] .mj-input[_ngcontent-%COMP%] { flex: 1; }\n .frb-errors[_ngcontent-%COMP%] { display: flex; gap: 10px; padding: 10px 12px; background: var(--mj-status-error-bg); color: var(--mj-status-error-text);\n border-radius: var(--mj-radius-sm, 6px); font-size: 13px; }\n .frb-errors[_ngcontent-%COMP%] ul[_ngcontent-%COMP%] { margin: 0; padding-left: 18px; }"], changeDetection: 0 });
319
314
  }
320
315
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FieldRulesBuilderComponent, [{
321
316
  type: Component,
322
- args: [{ selector: 'mj-field-rules-builder', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AIPromptSelectorComponent], template: `
317
+ args: [{ selector: 'mj-field-rules-builder', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AIPromptSelectorComponent, MJEmptyStateComponent], template: `
323
318
  <div class="frb">
324
319
  <div class="frb-head">
325
320
  <div>
@@ -330,7 +325,7 @@ export class FieldRulesBuilderComponent {
330
325
  </div>
331
326
 
332
327
  @if (Rules.length === 0) {
333
- <div class="frb-empty">No rules yet. Click <strong>Add rule</strong> to start.</div>
328
+ <mj-empty-state class="frb-empty" Size="compact" Icon="" Title="No rules yet" Message="Click Add rule to start." />
334
329
  }
335
330
 
336
331
  @for (rule of Rules; track $index) {
@@ -407,5 +402,5 @@ export class FieldRulesBuilderComponent {
407
402
  }], ValidChange: [{
408
403
  type: Output
409
404
  }] }); })();
410
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FieldRulesBuilderComponent, { className: "FieldRulesBuilderComponent", filePath: "lib/field-rules-builder/field-rules-builder.component.ts", lineNumber: 126 }); })();
405
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FieldRulesBuilderComponent, { className: "FieldRulesBuilderComponent", filePath: "lib/field-rules-builder/field-rules-builder.component.ts", lineNumber: 127 }); })();
411
406
  //# sourceMappingURL=field-rules-builder.component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"field-rules-builder.component.js","sourceRoot":"","sources":["../../../src/lib/field-rules-builder/field-rules-builder.component.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview A visual authoring surface for a {@link FieldRuleSet} — the rules that drive a FieldRules\n * Record Process. Each rule sets a target field from a chosen source (static / another field / formula /\n * entity lookup / AI prompt), optionally gated by a condition. Validates live against the entity's metadata\n * via `EntityFieldRules.Validate`, and emits the typed rule set (the consumer serializes it into the\n * Record Process `Configuration`).\n * @module @memberjunction/ng-entity-action-ux\n */\nimport { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, inject } from '@angular/core';\nimport { EntityFieldRules, Metadata, type EntityInfo, type IMetadataProvider } from '@memberjunction/core';\nimport type { FieldRuleSet } from '@memberjunction/global';\nimport { AIPromptSelectorComponent } from '../ai-prompt-selector/ai-prompt-selector.component';\nimport { blankRuleDraft, draftsToRuleSet, ruleToDraft, type BuilderSourceKind, type RuleDraft } from '../field-rules-model';\n\n@Component({\n selector: 'mj-field-rules-builder',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [AIPromptSelectorComponent],\n template: `\n <div class=\"frb\">\n <div class=\"frb-head\">\n <div>\n <h3>Field rules</h3>\n <p class=\"frb-sub\">Each rule sets a field on every matching {{ EntityLabel }}. Use the preview before applying.</p>\n </div>\n <button type=\"button\" class=\"frb-add\" (click)=\"AddRule()\"><i class=\"fa-solid fa-plus\"></i> Add rule</button>\n </div>\n\n @if (Rules.length === 0) {\n <div class=\"frb-empty\">No rules yet. Click <strong>Add rule</strong> to start.</div>\n }\n\n @for (rule of Rules; track $index) {\n <div class=\"frb-rule\">\n <div class=\"frb-rule-head\">\n <span class=\"frb-rule-num\">{{ $index + 1 }}</span>\n <span class=\"frb-set\">Set</span>\n <select class=\"mj-input frb-target\" [value]=\"rule.TargetField\" (change)=\"SetTarget($index, $event)\">\n <option value=\"\">— field —</option>\n @for (f of WritableFields; track f.Name) { <option [value]=\"f.Name\">{{ f.DisplayNameOrName }}</option> }\n </select>\n <span class=\"frb-set\">from</span>\n <select class=\"mj-input frb-kind\" [value]=\"rule.SourceKind\" (change)=\"SetKind($index, $event)\">\n <option value=\"static\">a fixed value</option>\n <option value=\"field\">another field</option>\n <option value=\"formula\">a formula</option>\n <option value=\"lookup\">a lookup</option>\n <option value=\"prompt\">an AI prompt</option>\n </select>\n <button type=\"button\" class=\"frb-del\" title=\"Remove rule\" (click)=\"RemoveRule($index)\"><i class=\"fa-solid fa-trash\"></i></button>\n </div>\n\n <div class=\"frb-source\">\n @switch (rule.SourceKind) {\n @case ('static') {\n <input class=\"mj-input\" placeholder=\"Value\" [value]=\"rule.StaticValue\" (input)=\"SetField($index, 'StaticValue', $event)\" />\n }\n @case ('field') {\n <select class=\"mj-input\" [value]=\"rule.SourceField\" (change)=\"SetField($index, 'SourceField', $event)\">\n <option value=\"\">— source field —</option>\n @for (f of AllFields; track f.Name) { <option [value]=\"f.Name\">{{ f.DisplayNameOrName }}</option> }\n </select>\n }\n @case ('formula') {\n <input class=\"mj-input\" placeholder=\"e.g. fields.FirstName + ' ' + fields.LastName\" [value]=\"rule.Formula\" (input)=\"SetField($index, 'Formula', $event)\" />\n }\n @case ('lookup') {\n <div class=\"frb-lookup\">\n <input class=\"mj-input\" placeholder=\"Lookup entity\" [value]=\"rule.LookupEntity\" (input)=\"SetField($index, 'LookupEntity', $event)\" />\n <input class=\"mj-input\" placeholder=\"Match field\" [value]=\"rule.LookupMatchField\" (input)=\"SetField($index, 'LookupMatchField', $event)\" />\n <input class=\"mj-input\" placeholder=\"= this record's field\" [value]=\"rule.LookupMatchValueField\" (input)=\"SetField($index, 'LookupMatchValueField', $event)\" />\n <input class=\"mj-input\" placeholder=\"Return field\" [value]=\"rule.LookupReturnField\" (input)=\"SetField($index, 'LookupReturnField', $event)\" />\n </div>\n }\n @case ('prompt') {\n <mj-ai-prompt-selector [Value]=\"rule.PromptID\" (ValueChange)=\"SetPrompt($index, $event)\"></mj-ai-prompt-selector>\n }\n }\n </div>\n\n <div class=\"frb-cond\">\n <span class=\"frb-cond-label\">Only when</span>\n <input class=\"mj-input\" placeholder=\"(always) — e.g. fields.Status === 'Active'\" [value]=\"rule.Condition\" (input)=\"SetField($index, 'Condition', $event)\" />\n </div>\n </div>\n }\n\n @if (Errors.length > 0) {\n <div class=\"frb-errors\">\n <i class=\"fa-solid fa-circle-exclamation\"></i>\n <ul>@for (e of Errors; track e) { <li>{{ e }}</li> }</ul>\n </div>\n }\n </div>\n `,\n styles: [`\n .frb { color: var(--mj-text-primary); display: flex; flex-direction: column; gap: 12px; }\n .frb-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }\n .frb-head h3 { margin: 0; font-size: 16px; }\n .frb-sub { margin: 2px 0 0; font-size: 13px; color: var(--mj-text-muted); }\n .frb-add { display: flex; align-items: center; gap: 6px; padding: 7px 12px; border: 1px solid var(--mj-brand-primary);\n color: var(--mj-brand-primary); background: transparent; border-radius: var(--mj-radius-sm, 6px); cursor: pointer; font-weight: 600; }\n .frb-add:hover { background: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent); }\n .frb-empty { padding: 20px; text-align: center; color: var(--mj-text-muted); border: 1px dashed var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px); }\n .frb-rule { border: 1px solid var(--mj-border-default); border-radius: var(--mj-radius-md, 8px); padding: 12px; background: var(--mj-bg-surface-card); }\n .frb-rule-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n .frb-rule-num { width: 22px; height: 22px; display: inline-flex; align-items: center; justify-content: center;\n border-radius: 50%; background: var(--mj-brand-primary); color: var(--mj-text-inverse); font-size: 12px; font-weight: 700; }\n .frb-set { color: var(--mj-text-secondary); font-size: 14px; }\n .frb-target, .frb-kind { min-width: 150px; }\n .frb-del { margin-left: auto; border: none; background: transparent; color: var(--mj-text-muted); cursor: pointer; padding: 4px 8px; }\n .frb-del:hover { color: var(--mj-status-error); }\n .frb-source { margin-top: 10px; }\n .frb-source .mj-input { width: 100%; }\n .frb-lookup { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 8px; }\n .frb-cond { display: flex; align-items: center; gap: 8px; margin-top: 10px; }\n .frb-cond-label { font-size: 13px; color: var(--mj-text-muted); white-space: nowrap; }\n .frb-cond .mj-input { flex: 1; }\n .frb-errors { display: flex; gap: 10px; padding: 10px 12px; background: var(--mj-status-error-bg); color: var(--mj-status-error-text);\n border-radius: var(--mj-radius-sm, 6px); font-size: 13px; }\n .frb-errors ul { margin: 0; padding-left: 18px; }\n `],\n})\nexport class FieldRulesBuilderComponent {\n private cdr = inject(ChangeDetectorRef);\n\n public Rules: RuleDraft[] = [];\n public Errors: string[] = [];\n public WritableFields: Array<{ Name: string; DisplayNameOrName: string }> = [];\n public AllFields: Array<{ Name: string; DisplayNameOrName: string }> = [];\n\n private _entityName = '';\n private entity?: EntityInfo;\n\n /** Metadata provider (multi-provider). Falls back to the global default when null. */\n @Input() Provider: IMetadataProvider | null = null;\n\n /** The entity whose fields the rules target. Loads the field lists when set. */\n @Input()\n set EntityName(value: string) {\n const prev = this._entityName;\n this._entityName = value;\n if (value && value !== prev) this.loadEntity();\n }\n get EntityName(): string { return this._entityName; }\n\n /**\n * Serialized form of our last emitted rule set. Consumers typically feed our `ValueChange` output\n * straight back in as `[Value]` (controlled-component style). Re-seeding the drafts from that echo\n * would wipe in-progress edits — most visibly a freshly-added **blank** rule, which serializes to\n * nothing (`draftsToRuleSet` drops rules with no target), so the echo arrives as `{Rules:[]}` and\n * resets the row the user just added. We ignore the echo and only re-seed on a genuinely different\n * (external) value.\n */\n private lastEmittedJSON: string | null = null;\n\n /** The initial rule set (e.g. an existing Record Process Configuration). */\n @Input()\n set Value(value: FieldRuleSet | null | undefined) {\n const incomingJSON = JSON.stringify(value?.Rules ?? []);\n if (incomingJSON === this.lastEmittedJSON) {\n return; // echo of our own emit — keep the live drafts intact\n }\n this.lastEmittedJSON = incomingJSON;\n this.Rules = value?.Rules ? value.Rules.map((r) => ruleToDraft(r)) : [];\n this.validate();\n }\n\n /** Emits the typed rule set on every edit; the consumer serializes it into the Configuration. */\n @Output() ValueChange = new EventEmitter<FieldRuleSet>();\n\n /** Emits the live validation result (true when every rule is valid against the entity). */\n @Output() ValidChange = new EventEmitter<boolean>();\n\n get EntityLabel(): string { return this.entity?.DisplayName || this.entity?.Name || 'record'; }\n\n AddRule(): void {\n this.Rules = [...this.Rules, blankRuleDraft()];\n this.emit();\n }\n\n RemoveRule(index: number): void {\n this.Rules = this.Rules.filter((_, i) => i !== index);\n this.emit();\n }\n\n SetTarget(index: number, event: Event): void {\n this.Rules[index].TargetField = (event.target as HTMLSelectElement).value;\n this.emit();\n }\n\n SetKind(index: number, event: Event): void {\n this.Rules[index].SourceKind = (event.target as HTMLSelectElement).value as BuilderSourceKind;\n this.emit();\n }\n\n SetField(index: number, field: keyof RuleDraft, event: Event): void {\n (this.Rules[index][field] as string) = (event.target as HTMLInputElement | HTMLSelectElement).value;\n this.emit();\n }\n\n SetPrompt(index: number, promptID: string | null): void {\n this.Rules[index].PromptID = promptID;\n this.emit();\n }\n\n /** Serializes the drafts, validates, and emits. */\n private emit(): void {\n const ruleSet = draftsToRuleSet(this.Rules);\n // Record what we're about to emit so the echo (consumer feeding it back via [Value]) is recognized\n // and ignored — see lastEmittedJSON. Must mirror the Value setter's comparison basis (Rules array).\n this.lastEmittedJSON = JSON.stringify(ruleSet.Rules ?? []);\n this.validate(ruleSet);\n this.ValueChange.emit(ruleSet);\n this.cdr.detectChanges();\n }\n\n private validate(ruleSet: FieldRuleSet = draftsToRuleSet(this.Rules)): void {\n if (!this._entityName) {\n this.Errors = [];\n } else {\n const result = EntityFieldRules.Validate(this._entityName, ruleSet, this.Provider ?? undefined);\n this.Errors = result.Errors;\n this.ValidChange.emit(result.Valid);\n }\n this.cdr.detectChanges();\n }\n\n private loadEntity(): void {\n this.entity = (this.Provider ?? Metadata.Provider)?.EntityByName(this._entityName);\n const map = (f: { Name: string; DisplayName?: string }) => ({ Name: f.Name, DisplayNameOrName: f.DisplayName || f.Name });\n this.AllFields = this.entity ? this.entity.Fields.map(map) : [];\n this.WritableFields = this.entity ? this.entity.Fields.filter((f) => !f.ReadOnly && !f.IsPrimaryKey).map(map) : [];\n this.validate();\n }\n}\n"]}
1
+ {"version":3,"file":"field-rules-builder.component.js","sourceRoot":"","sources":["../../../src/lib/field-rules-builder/field-rules-builder.component.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview A visual authoring surface for a {@link FieldRuleSet} — the rules that drive a FieldRules\n * Record Process. Each rule sets a target field from a chosen source (static / another field / formula /\n * entity lookup / AI prompt), optionally gated by a condition. Validates live against the entity's metadata\n * via `EntityFieldRules.Validate`, and emits the typed rule set (the consumer serializes it into the\n * Record Process `Configuration`).\n * @module @memberjunction/ng-entity-action-ux\n */\nimport { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, inject } from '@angular/core';\nimport { EntityFieldRules, Metadata, type EntityInfo, type IMetadataProvider } from '@memberjunction/core';\nimport type { FieldRuleSet } from '@memberjunction/global';\nimport { AIPromptSelectorComponent } from '../ai-prompt-selector/ai-prompt-selector.component';\nimport { blankRuleDraft, draftsToRuleSet, ruleToDraft, type BuilderSourceKind, type RuleDraft } from '../field-rules-model';\nimport { MJEmptyStateComponent } from '@memberjunction/ng-ui-components';\n\n@Component({\n selector: 'mj-field-rules-builder',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [AIPromptSelectorComponent, MJEmptyStateComponent],\n template: `\n <div class=\"frb\">\n <div class=\"frb-head\">\n <div>\n <h3>Field rules</h3>\n <p class=\"frb-sub\">Each rule sets a field on every matching {{ EntityLabel }}. Use the preview before applying.</p>\n </div>\n <button type=\"button\" class=\"frb-add\" (click)=\"AddRule()\"><i class=\"fa-solid fa-plus\"></i> Add rule</button>\n </div>\n\n @if (Rules.length === 0) {\n <mj-empty-state class=\"frb-empty\" Size=\"compact\" Icon=\"\" Title=\"No rules yet\" Message=\"Click Add rule to start.\" />\n }\n\n @for (rule of Rules; track $index) {\n <div class=\"frb-rule\">\n <div class=\"frb-rule-head\">\n <span class=\"frb-rule-num\">{{ $index + 1 }}</span>\n <span class=\"frb-set\">Set</span>\n <select class=\"mj-input frb-target\" [value]=\"rule.TargetField\" (change)=\"SetTarget($index, $event)\">\n <option value=\"\">— field —</option>\n @for (f of WritableFields; track f.Name) { <option [value]=\"f.Name\">{{ f.DisplayNameOrName }}</option> }\n </select>\n <span class=\"frb-set\">from</span>\n <select class=\"mj-input frb-kind\" [value]=\"rule.SourceKind\" (change)=\"SetKind($index, $event)\">\n <option value=\"static\">a fixed value</option>\n <option value=\"field\">another field</option>\n <option value=\"formula\">a formula</option>\n <option value=\"lookup\">a lookup</option>\n <option value=\"prompt\">an AI prompt</option>\n </select>\n <button type=\"button\" class=\"frb-del\" title=\"Remove rule\" (click)=\"RemoveRule($index)\"><i class=\"fa-solid fa-trash\"></i></button>\n </div>\n\n <div class=\"frb-source\">\n @switch (rule.SourceKind) {\n @case ('static') {\n <input class=\"mj-input\" placeholder=\"Value\" [value]=\"rule.StaticValue\" (input)=\"SetField($index, 'StaticValue', $event)\" />\n }\n @case ('field') {\n <select class=\"mj-input\" [value]=\"rule.SourceField\" (change)=\"SetField($index, 'SourceField', $event)\">\n <option value=\"\">— source field —</option>\n @for (f of AllFields; track f.Name) { <option [value]=\"f.Name\">{{ f.DisplayNameOrName }}</option> }\n </select>\n }\n @case ('formula') {\n <input class=\"mj-input\" placeholder=\"e.g. fields.FirstName + ' ' + fields.LastName\" [value]=\"rule.Formula\" (input)=\"SetField($index, 'Formula', $event)\" />\n }\n @case ('lookup') {\n <div class=\"frb-lookup\">\n <input class=\"mj-input\" placeholder=\"Lookup entity\" [value]=\"rule.LookupEntity\" (input)=\"SetField($index, 'LookupEntity', $event)\" />\n <input class=\"mj-input\" placeholder=\"Match field\" [value]=\"rule.LookupMatchField\" (input)=\"SetField($index, 'LookupMatchField', $event)\" />\n <input class=\"mj-input\" placeholder=\"= this record's field\" [value]=\"rule.LookupMatchValueField\" (input)=\"SetField($index, 'LookupMatchValueField', $event)\" />\n <input class=\"mj-input\" placeholder=\"Return field\" [value]=\"rule.LookupReturnField\" (input)=\"SetField($index, 'LookupReturnField', $event)\" />\n </div>\n }\n @case ('prompt') {\n <mj-ai-prompt-selector [Value]=\"rule.PromptID\" (ValueChange)=\"SetPrompt($index, $event)\"></mj-ai-prompt-selector>\n }\n }\n </div>\n\n <div class=\"frb-cond\">\n <span class=\"frb-cond-label\">Only when</span>\n <input class=\"mj-input\" placeholder=\"(always) — e.g. fields.Status === 'Active'\" [value]=\"rule.Condition\" (input)=\"SetField($index, 'Condition', $event)\" />\n </div>\n </div>\n }\n\n @if (Errors.length > 0) {\n <div class=\"frb-errors\">\n <i class=\"fa-solid fa-circle-exclamation\"></i>\n <ul>@for (e of Errors; track e) { <li>{{ e }}</li> }</ul>\n </div>\n }\n </div>\n `,\n styles: [`\n .frb { color: var(--mj-text-primary); display: flex; flex-direction: column; gap: 12px; }\n .frb-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }\n .frb-head h3 { margin: 0; font-size: 16px; }\n .frb-sub { margin: 2px 0 0; font-size: 13px; color: var(--mj-text-muted); }\n .frb-add { display: flex; align-items: center; gap: 6px; padding: 7px 12px; border: 1px solid var(--mj-brand-primary);\n color: var(--mj-brand-primary); background: transparent; border-radius: var(--mj-radius-sm, 6px); cursor: pointer; font-weight: 600; }\n .frb-add:hover { background: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent); }\n .frb-empty { padding: 20px; text-align: center; color: var(--mj-text-muted); border: 1px dashed var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px); }\n .frb-rule { border: 1px solid var(--mj-border-default); border-radius: var(--mj-radius-md, 8px); padding: 12px; background: var(--mj-bg-surface-card); }\n .frb-rule-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n .frb-rule-num { width: 22px; height: 22px; display: inline-flex; align-items: center; justify-content: center;\n border-radius: 50%; background: var(--mj-brand-primary); color: var(--mj-text-inverse); font-size: 12px; font-weight: 700; }\n .frb-set { color: var(--mj-text-secondary); font-size: 14px; }\n .frb-target, .frb-kind { min-width: 150px; }\n .frb-del { margin-left: auto; border: none; background: transparent; color: var(--mj-text-muted); cursor: pointer; padding: 4px 8px; }\n .frb-del:hover { color: var(--mj-status-error); }\n .frb-source { margin-top: 10px; }\n .frb-source .mj-input { width: 100%; }\n .frb-lookup { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 8px; }\n .frb-cond { display: flex; align-items: center; gap: 8px; margin-top: 10px; }\n .frb-cond-label { font-size: 13px; color: var(--mj-text-muted); white-space: nowrap; }\n .frb-cond .mj-input { flex: 1; }\n .frb-errors { display: flex; gap: 10px; padding: 10px 12px; background: var(--mj-status-error-bg); color: var(--mj-status-error-text);\n border-radius: var(--mj-radius-sm, 6px); font-size: 13px; }\n .frb-errors ul { margin: 0; padding-left: 18px; }\n `],\n})\nexport class FieldRulesBuilderComponent {\n private cdr = inject(ChangeDetectorRef);\n\n public Rules: RuleDraft[] = [];\n public Errors: string[] = [];\n public WritableFields: Array<{ Name: string; DisplayNameOrName: string }> = [];\n public AllFields: Array<{ Name: string; DisplayNameOrName: string }> = [];\n\n private _entityName = '';\n private entity?: EntityInfo;\n\n /** Metadata provider (multi-provider). Falls back to the global default when null. */\n @Input() Provider: IMetadataProvider | null = null;\n\n /** The entity whose fields the rules target. Loads the field lists when set. */\n @Input()\n set EntityName(value: string) {\n const prev = this._entityName;\n this._entityName = value;\n if (value && value !== prev) this.loadEntity();\n }\n get EntityName(): string { return this._entityName; }\n\n /**\n * Serialized form of our last emitted rule set. Consumers typically feed our `ValueChange` output\n * straight back in as `[Value]` (controlled-component style). Re-seeding the drafts from that echo\n * would wipe in-progress edits — most visibly a freshly-added **blank** rule, which serializes to\n * nothing (`draftsToRuleSet` drops rules with no target), so the echo arrives as `{Rules:[]}` and\n * resets the row the user just added. We ignore the echo and only re-seed on a genuinely different\n * (external) value.\n */\n private lastEmittedJSON: string | null = null;\n\n /** The initial rule set (e.g. an existing Record Process Configuration). */\n @Input()\n set Value(value: FieldRuleSet | null | undefined) {\n const incomingJSON = JSON.stringify(value?.Rules ?? []);\n if (incomingJSON === this.lastEmittedJSON) {\n return; // echo of our own emit — keep the live drafts intact\n }\n this.lastEmittedJSON = incomingJSON;\n this.Rules = value?.Rules ? value.Rules.map((r) => ruleToDraft(r)) : [];\n this.validate();\n }\n\n /** Emits the typed rule set on every edit; the consumer serializes it into the Configuration. */\n @Output() ValueChange = new EventEmitter<FieldRuleSet>();\n\n /** Emits the live validation result (true when every rule is valid against the entity). */\n @Output() ValidChange = new EventEmitter<boolean>();\n\n get EntityLabel(): string { return this.entity?.DisplayName || this.entity?.Name || 'record'; }\n\n AddRule(): void {\n this.Rules = [...this.Rules, blankRuleDraft()];\n this.emit();\n }\n\n RemoveRule(index: number): void {\n this.Rules = this.Rules.filter((_, i) => i !== index);\n this.emit();\n }\n\n SetTarget(index: number, event: Event): void {\n this.Rules[index].TargetField = (event.target as HTMLSelectElement).value;\n this.emit();\n }\n\n SetKind(index: number, event: Event): void {\n this.Rules[index].SourceKind = (event.target as HTMLSelectElement).value as BuilderSourceKind;\n this.emit();\n }\n\n SetField(index: number, field: keyof RuleDraft, event: Event): void {\n (this.Rules[index][field] as string) = (event.target as HTMLInputElement | HTMLSelectElement).value;\n this.emit();\n }\n\n SetPrompt(index: number, promptID: string | null): void {\n this.Rules[index].PromptID = promptID;\n this.emit();\n }\n\n /** Serializes the drafts, validates, and emits. */\n private emit(): void {\n const ruleSet = draftsToRuleSet(this.Rules);\n // Record what we're about to emit so the echo (consumer feeding it back via [Value]) is recognized\n // and ignored — see lastEmittedJSON. Must mirror the Value setter's comparison basis (Rules array).\n this.lastEmittedJSON = JSON.stringify(ruleSet.Rules ?? []);\n this.validate(ruleSet);\n this.ValueChange.emit(ruleSet);\n this.cdr.detectChanges();\n }\n\n private validate(ruleSet: FieldRuleSet = draftsToRuleSet(this.Rules)): void {\n if (!this._entityName) {\n this.Errors = [];\n } else {\n const result = EntityFieldRules.Validate(this._entityName, ruleSet, this.Provider ?? undefined);\n this.Errors = result.Errors;\n this.ValidChange.emit(result.Valid);\n }\n this.cdr.detectChanges();\n }\n\n private loadEntity(): void {\n this.entity = (this.Provider ?? Metadata.Provider)?.EntityByName(this._entityName);\n const map = (f: { Name: string; DisplayName?: string }) => ({ Name: f.Name, DisplayNameOrName: f.DisplayName || f.Name });\n this.AllFields = this.entity ? this.entity.Fields.map(map) : [];\n this.WritableFields = this.entity ? this.entity.Fields.filter((f) => !f.ReadOnly && !f.IsPrimaryKey).map(map) : [];\n this.validate();\n }\n}\n"]}
@@ -18,7 +18,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject } from '@
18
18
  import { RunView } from '@memberjunction/core';
19
19
  import { RegisterClass, SafeJSONParse } from '@memberjunction/global';
20
20
  import { RecordProcessRunNowOperation, } from '@memberjunction/core-entities';
21
- import { MJDialogComponent, MJDialogActionsComponent, MJButtonDirective, MJProgressBarComponent } from '@memberjunction/ng-ui-components';
21
+ import { MJDialogComponent, MJDialogActionsComponent, MJButtonDirective, MJProgressBarComponent, MJEmptyStateComponent } from '@memberjunction/ng-ui-components';
22
22
  import { BaseEntityActionRuntimeUX } from '../base-entity-action-runtime-ux';
23
23
  import { buildRecordProcessScope, displayValue } from '../scope';
24
24
  import * as i0 from "@angular/core";
@@ -99,25 +99,20 @@ function RecordProcessRunnerUXComponent_Case_6_Conditional_6_Template(rf, ctx) {
99
99
  i0.ɵɵtextInterpolate1("", ctx_r0.ErroredCount, " error(s)");
100
100
  } }
101
101
  function RecordProcessRunnerUXComponent_Case_6_Conditional_7_Template(rf, ctx) { if (rf & 1) {
102
- i0.ɵɵelementStart(0, "div", 16);
103
- i0.ɵɵelement(1, "i", 17);
104
- i0.ɵɵelementStart(2, "p");
105
- i0.ɵɵtext(3);
106
- i0.ɵɵelementEnd()();
102
+ i0.ɵɵelement(0, "mj-empty-state", 16);
107
103
  } if (rf & 2) {
108
104
  const ctx_r0 = i0.ɵɵnextContext(2);
109
- i0.ɵɵadvance(3);
110
- i0.ɵɵtextInterpolate1("Nothing to change \u2014 the rules didn't match any of the selected ", ctx_r0.EntityLabel, ".");
105
+ i0.ɵɵproperty("Message", "The rules did not match any of the selected " + ctx_r0.EntityLabel + ".");
111
106
  } }
112
107
  function RecordProcessRunnerUXComponent_Case_6_Conditional_8_For_2_For_5_Template(rf, ctx) { if (rf & 1) {
113
- i0.ɵɵelementStart(0, "div", 23)(1, "span", 25);
108
+ i0.ɵɵelementStart(0, "div", 22)(1, "span", 24);
114
109
  i0.ɵɵtext(2);
115
110
  i0.ɵɵelementEnd();
116
- i0.ɵɵelementStart(3, "span", 26);
111
+ i0.ɵɵelementStart(3, "span", 25);
117
112
  i0.ɵɵtext(4);
118
113
  i0.ɵɵelementEnd();
119
- i0.ɵɵelement(5, "i", 27);
120
- i0.ɵɵelementStart(6, "span", 28);
114
+ i0.ɵɵelement(5, "i", 26);
115
+ i0.ɵɵelementStart(6, "span", 27);
121
116
  i0.ɵɵtext(7);
122
117
  i0.ɵɵelementEnd()();
123
118
  } if (rf & 2) {
@@ -131,7 +126,7 @@ function RecordProcessRunnerUXComponent_Case_6_Conditional_8_For_2_For_5_Templat
131
126
  i0.ɵɵtextInterpolate(ctx_r0.Display(c_r2.NewValue));
132
127
  } }
133
128
  function RecordProcessRunnerUXComponent_Case_6_Conditional_8_For_2_Conditional_6_Template(rf, ctx) { if (rf & 1) {
134
- i0.ɵɵelementStart(0, "div", 24);
129
+ i0.ɵɵelementStart(0, "div", 23);
135
130
  i0.ɵɵtext(1);
136
131
  i0.ɵɵelementEnd();
137
132
  } if (rf & 2) {
@@ -140,12 +135,12 @@ function RecordProcessRunnerUXComponent_Case_6_Conditional_8_For_2_Conditional_6
140
135
  i0.ɵɵtextInterpolate(row_r3.ErrorMessage);
141
136
  } }
142
137
  function RecordProcessRunnerUXComponent_Case_6_Conditional_8_For_2_Template(rf, ctx) { if (rf & 1) {
143
- i0.ɵɵelementStart(0, "div", 19)(1, "div", 21);
138
+ i0.ɵɵelementStart(0, "div", 18)(1, "div", 20);
144
139
  i0.ɵɵtext(2);
145
140
  i0.ɵɵelementEnd();
146
- i0.ɵɵelementStart(3, "div", 22);
147
- i0.ɵɵrepeaterCreate(4, RecordProcessRunnerUXComponent_Case_6_Conditional_8_For_2_For_5_Template, 8, 3, "div", 23, _forTrack1);
148
- i0.ɵɵconditionalCreate(6, RecordProcessRunnerUXComponent_Case_6_Conditional_8_For_2_Conditional_6_Template, 2, 1, "div", 24);
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);
149
144
  i0.ɵɵelementEnd()();
150
145
  } if (rf & 2) {
151
146
  const row_r3 = ctx.$implicit;
@@ -159,7 +154,7 @@ function RecordProcessRunnerUXComponent_Case_6_Conditional_8_For_2_Template(rf,
159
154
  i0.ɵɵconditional(row_r3.ErrorMessage ? 6 : -1);
160
155
  } }
161
156
  function RecordProcessRunnerUXComponent_Case_6_Conditional_8_Conditional_3_Template(rf, ctx) { if (rf & 1) {
162
- i0.ɵɵelementStart(0, "p", 20);
157
+ i0.ɵɵelementStart(0, "p", 19);
163
158
  i0.ɵɵtext(1);
164
159
  i0.ɵɵelementEnd();
165
160
  } if (rf & 2) {
@@ -168,10 +163,10 @@ function RecordProcessRunnerUXComponent_Case_6_Conditional_8_Conditional_3_Templ
168
163
  i0.ɵɵtextInterpolate1("Showing the first ", ctx_r0.DiffRows.length, " changed records. The apply step covers the full scope.");
169
164
  } }
170
165
  function RecordProcessRunnerUXComponent_Case_6_Conditional_8_Template(rf, ctx) { if (rf & 1) {
171
- i0.ɵɵelementStart(0, "div", 18);
172
- i0.ɵɵrepeaterCreate(1, RecordProcessRunnerUXComponent_Case_6_Conditional_8_For_2_Template, 7, 3, "div", 19, _forTrack0);
166
+ i0.ɵɵelementStart(0, "div", 17);
167
+ i0.ɵɵrepeaterCreate(1, RecordProcessRunnerUXComponent_Case_6_Conditional_8_For_2_Template, 7, 3, "div", 18, _forTrack0);
173
168
  i0.ɵɵelementEnd();
174
- i0.ɵɵconditionalCreate(3, RecordProcessRunnerUXComponent_Case_6_Conditional_8_Conditional_3_Template, 2, 1, "p", 20);
169
+ i0.ɵɵconditionalCreate(3, RecordProcessRunnerUXComponent_Case_6_Conditional_8_Conditional_3_Template, 2, 1, "p", 19);
175
170
  } if (rf & 2) {
176
171
  const ctx_r0 = i0.ɵɵnextContext(2);
177
172
  i0.ɵɵadvance();
@@ -189,7 +184,7 @@ function RecordProcessRunnerUXComponent_Case_6_Template(rf, ctx) { if (rf & 1) {
189
184
  i0.ɵɵconditionalCreate(5, RecordProcessRunnerUXComponent_Case_6_Conditional_5_Template, 2, 1, "span", 14);
190
185
  i0.ɵɵconditionalCreate(6, RecordProcessRunnerUXComponent_Case_6_Conditional_6_Template, 2, 1, "span", 15);
191
186
  i0.ɵɵelementEnd();
192
- i0.ɵɵconditionalCreate(7, RecordProcessRunnerUXComponent_Case_6_Conditional_7_Template, 4, 1, "div", 16)(8, RecordProcessRunnerUXComponent_Case_6_Conditional_8_Template, 4, 1);
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);
193
188
  } if (rf & 2) {
194
189
  const ctx_r0 = i0.ɵɵnextContext();
195
190
  i0.ɵɵadvance(2);
@@ -205,9 +200,9 @@ function RecordProcessRunnerUXComponent_Case_6_Template(rf, ctx) { if (rf & 1) {
205
200
  } }
206
201
  function RecordProcessRunnerUXComponent_Case_8_Conditional_0_Template(rf, ctx) { if (rf & 1) {
207
202
  const _r5 = i0.ɵɵgetCurrentView();
208
- i0.ɵɵelementStart(0, "button", 30);
203
+ i0.ɵɵelementStart(0, "button", 29);
209
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()); });
210
- i0.ɵɵelement(1, "i", 31);
205
+ i0.ɵɵelement(1, "i", 30);
211
206
  i0.ɵɵtext(2);
212
207
  i0.ɵɵelementEnd();
213
208
  } if (rf & 2) {
@@ -218,7 +213,7 @@ function RecordProcessRunnerUXComponent_Case_8_Conditional_0_Template(rf, ctx) {
218
213
  function RecordProcessRunnerUXComponent_Case_8_Template(rf, ctx) { if (rf & 1) {
219
214
  const _r4 = i0.ɵɵgetCurrentView();
220
215
  i0.ɵɵconditionalCreate(0, RecordProcessRunnerUXComponent_Case_8_Conditional_0_Template, 3, 1, "button", 5);
221
- i0.ɵɵelementStart(1, "button", 29);
216
+ i0.ɵɵelementStart(1, "button", 28);
222
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()); });
223
218
  i0.ɵɵtext(2, "Cancel");
224
219
  i0.ɵɵelementEnd();
@@ -228,14 +223,14 @@ function RecordProcessRunnerUXComponent_Case_8_Template(rf, ctx) { if (rf & 1) {
228
223
  } }
229
224
  function RecordProcessRunnerUXComponent_Case_9_Template(rf, ctx) { if (rf & 1) {
230
225
  const _r6 = i0.ɵɵgetCurrentView();
231
- i0.ɵɵelementStart(0, "button", 30);
226
+ i0.ɵɵelementStart(0, "button", 29);
232
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()); });
233
228
  i0.ɵɵtext(1, "Done");
234
229
  i0.ɵɵelementEnd();
235
230
  } }
236
231
  function RecordProcessRunnerUXComponent_Case_10_Template(rf, ctx) { if (rf & 1) {
237
232
  const _r7 = i0.ɵɵgetCurrentView();
238
- i0.ɵɵelementStart(0, "button", 29);
233
+ i0.ɵɵelementStart(0, "button", 28);
239
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()); });
240
235
  i0.ɵɵtext(1, "Close");
241
236
  i0.ɵɵelementEnd();
@@ -387,7 +382,7 @@ let RecordProcessRunnerUXComponent = class RecordProcessRunnerUXComponent extend
387
382
  this.cdr.detectChanges();
388
383
  }
389
384
  static ɵfac = /*@__PURE__*/ (() => { let ɵRecordProcessRunnerUXComponent_BaseFactory; return function RecordProcessRunnerUXComponent_Factory(__ngFactoryType__) { return (ɵRecordProcessRunnerUXComponent_BaseFactory || (ɵRecordProcessRunnerUXComponent_BaseFactory = i0.ɵɵgetInheritedFactory(RecordProcessRunnerUXComponent)))(__ngFactoryType__ || RecordProcessRunnerUXComponent); }; })();
390
- 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"], [1, "rp-empty"], [1, "fa-regular", "fa-circle-check"], ["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) {
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) {
391
386
  i0.ɵɵelementStart(0, "mj-dialog", 0);
392
387
  i0.ɵɵlistener("Close", function RecordProcessRunnerUXComponent_Template_mj_dialog_Close_0_listener() { return ctx.OnCancel(); });
393
388
  i0.ɵɵelementStart(1, "div", 1);
@@ -404,7 +399,7 @@ let RecordProcessRunnerUXComponent = class RecordProcessRunnerUXComponent extend
404
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);
405
400
  i0.ɵɵadvance(6);
406
401
  i0.ɵɵconditional((tmp_5_0 = ctx.State) === "preview" ? 8 : tmp_5_0 === "done" ? 9 : tmp_5_0 === "error" ? 10 : -1);
407
- } }, dependencies: [MJDialogComponent, MJDialogActionsComponent, MJButtonDirective, MJProgressBarComponent], 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 });
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 });
408
403
  };
409
404
  RecordProcessRunnerUXComponent = RecordProcessRunnerUXComponent_1 = __decorate([
410
405
  RegisterClass(BaseEntityActionRuntimeUX, 'RecordProcessRunnerUX')
@@ -412,7 +407,7 @@ RecordProcessRunnerUXComponent = RecordProcessRunnerUXComponent_1 = __decorate([
412
407
  export { RecordProcessRunnerUXComponent };
413
408
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RecordProcessRunnerUXComponent, [{
414
409
  type: Component,
415
- args: [{ selector: 'mj-record-process-runner-ux', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [MJDialogComponent, MJDialogActionsComponent, MJButtonDirective, MJProgressBarComponent], template: `
410
+ args: [{ selector: 'mj-record-process-runner-ux', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [MJDialogComponent, MJDialogActionsComponent, MJButtonDirective, MJProgressBarComponent, MJEmptyStateComponent], template: `
416
411
  <mj-dialog [Visible]="true" [Width]="760" [Title]="Title" [Closeable]="State !== 'applying'" (Close)="OnCancel()">
417
412
  <div class="rp-runner">
418
413
  @switch (State) {
@@ -457,10 +452,10 @@ export { RecordProcessRunnerUXComponent };
457
452
  </div>
458
453
 
459
454
  @if (DiffRows.length === 0) {
460
- <div class="rp-empty">
461
- <i class="fa-regular fa-circle-check"></i>
462
- <p>Nothing to change — the rules didn't match any of the selected {{ EntityLabel }}.</p>
463
- </div>
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 + '.'" />
464
459
  } @else {
465
460
  <div class="rp-diff" role="table">
466
461
  @for (row of DiffRows; track row.RecordID) {
@@ -1 +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":"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","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 } 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],\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 <div class=\"rp-empty\">\n <i class=\"fa-regular fa-circle-check\"></i>\n <p>Nothing to change — the rules didn't match any of the selected {{ EntityLabel }}.</p>\n </div>\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"]}
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":"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","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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-entity-action-ux",
3
- "version": "5.43.0",
3
+ "version": "5.45.0",
4
4
  "description": "MemberJunction: Pluggable runtime-UX framework for entity actions — a registry-resolved component seam that lets an entity action mount a rich, interactive UI (e.g. the Record Process bulk-update runner) instead of firing blind.",
5
5
  "main": "./dist/public-api.js",
6
6
  "typings": "./dist/public-api.d.ts",
@@ -35,12 +35,12 @@
35
35
  "rxjs": "^7.8.2"
36
36
  },
37
37
  "dependencies": {
38
- "@memberjunction/core": "5.43.0",
39
- "@memberjunction/core-entities": "5.43.0",
40
- "@memberjunction/global": "5.43.0",
41
- "@memberjunction/graphql-dataprovider": "5.43.0",
42
- "@memberjunction/ng-ui-components": "5.43.0",
43
- "@memberjunction/record-set-processor-base": "5.43.0",
38
+ "@memberjunction/core": "5.45.0",
39
+ "@memberjunction/core-entities": "5.45.0",
40
+ "@memberjunction/global": "5.45.0",
41
+ "@memberjunction/graphql-dataprovider": "5.45.0",
42
+ "@memberjunction/ng-ui-components": "5.45.0",
43
+ "@memberjunction/record-set-processor-base": "5.45.0",
44
44
  "tslib": "^2.8.1"
45
45
  },
46
46
  "sideEffects": false,