@memberjunction/ng-entity-action-ux 5.51.0 → 6.1.0-edge.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2023 MemberJunction
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
@@ -7,9 +7,10 @@
7
7
  */
8
8
  import { EventEmitter, OnInit } from '@angular/core';
9
9
  import { type IMetadataProvider } from '@memberjunction/core';
10
+ import { BaseAngularComponent } from '@memberjunction/ng-base-types';
10
11
  import { type PromptGroup, type PromptOption } from '../prompt-grouping';
11
12
  import * as i0 from "@angular/core";
12
- export declare class AIPromptSelectorComponent implements OnInit {
13
+ export declare class AIPromptSelectorComponent extends BaseAngularComponent implements OnInit {
13
14
  private cdr;
14
15
  private host;
15
16
  /** The selected prompt's ID (two-way bindable). */
@@ -1 +1 @@
1
- {"version":3,"file":"ai-prompt-selector.component.d.ts","sourceRoot":"","sources":["../../../src/lib/ai-prompt-selector/ai-prompt-selector.component.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAKH,YAAY,EAGZ,MAAM,EAGT,MAAM,eAAe,CAAC;AACvB,OAAO,EAAW,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEvE,OAAO,EAA8C,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;;AAErH,qBAyEa,yBAA0B,YAAW,MAAM;IACpD,OAAO,CAAC,GAAG,CAA6B;IACxC,OAAO,CAAC,IAAI,CAAsB;IAElC,mDAAmD;IAC1C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC3B,WAAW,8BAAqC;IAE1D,kDAAkD;IACzC,WAAW,SAA0B;IAE9C,sFAAsF;IAC7E,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAQ;IAE5C,MAAM,UAAS;IACf,OAAO,UAAQ;IACf,MAAM,SAAM;IACZ,MAAM,EAAE,WAAW,EAAE,CAAM;IAElC,OAAO,CAAC,UAAU,CAAsB;IAElC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,iGAAiG;IACjG,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO;IAIzC,IAAI,cAAc,IAAI,WAAW,EAAE,CAElC;IAED,MAAM,IAAI,IAAI;IAMd,MAAM,CAAC,CAAC,EAAE,YAAY,GAAG,IAAI;IAO7B,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAK5B,iDAAiD;IAEjD,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;YAO1B,WAAW;yCAjEhB,yBAAyB;2CAAzB,yBAAyB;CA+ErC"}
1
+ {"version":3,"file":"ai-prompt-selector.component.d.ts","sourceRoot":"","sources":["../../../src/lib/ai-prompt-selector/ai-prompt-selector.component.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAKH,YAAY,EAGZ,MAAM,EAGT,MAAM,eAAe,CAAC;AACvB,OAAO,EAAW,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAErE,OAAO,EAA8C,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;;AAErH,qBAyEa,yBAA0B,SAAQ,oBAAqB,YAAW,MAAM;IACjF,OAAO,CAAC,GAAG,CAA6B;IACxC,OAAO,CAAC,IAAI,CAAsB;IAElC,mDAAmD;IAC1C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC3B,WAAW,8BAAqC;IAE1D,kDAAkD;IACzC,WAAW,SAA0B;IAE9C,sFAAsF;IAC7E,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAQ;IAE5C,MAAM,UAAS;IACf,OAAO,UAAQ;IACf,MAAM,SAAM;IACZ,MAAM,EAAE,WAAW,EAAE,CAAM;IAElC,OAAO,CAAC,UAAU,CAAsB;IAElC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,iGAAiG;IACjG,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO;IAIzC,IAAI,cAAc,IAAI,WAAW,EAAE,CAElC;IAED,MAAM,IAAI,IAAI;IAMd,MAAM,CAAC,CAAC,EAAE,YAAY,GAAG,IAAI;IAO7B,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAK5B,iDAAiD;IAEjD,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;YAO1B,WAAW;yCAjEhB,yBAAyB;2CAAzB,yBAAyB;CA+ErC"}
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Input, Output, inject, } from '@angular/core';
9
9
  import { RunView } from '@memberjunction/core';
10
+ import { BaseAngularComponent } from '@memberjunction/ng-base-types';
10
11
  import { UUIDsEqual } from '@memberjunction/global';
11
12
  import { filterPromptGroups, groupPromptsByCategory } from '../prompt-grouping';
12
13
  import * as i0 from "@angular/core";
@@ -82,7 +83,7 @@ function AIPromptSelectorComponent_Conditional_6_Template(rf, ctx) { if (rf & 1)
82
83
  i0.ɵɵadvance(3);
83
84
  i0.ɵɵconditional(ctx_r1.Loading ? 6 : ctx_r1.FilteredGroups.length === 0 ? 7 : 8);
84
85
  } }
85
- export class AIPromptSelectorComponent {
86
+ export class AIPromptSelectorComponent extends BaseAngularComponent {
86
87
  cdr = inject(ChangeDetectorRef);
87
88
  host = inject(ElementRef);
88
89
  /** The selected prompt's ID (two-way bindable). */
@@ -134,7 +135,7 @@ export class AIPromptSelectorComponent {
134
135
  }
135
136
  }
136
137
  async loadPrompts() {
137
- const runView = this.Provider ? RunView.FromMetadataProvider(this.Provider) : new RunView();
138
+ const runView = RunView.FromMetadataProvider(this.ProviderToUse);
138
139
  const rv = await runView.RunView({
139
140
  EntityName: 'MJ: AI Prompts',
140
141
  Fields: ['ID', 'Name', 'Category'],
@@ -147,10 +148,10 @@ export class AIPromptSelectorComponent {
147
148
  this.Loading = false;
148
149
  this.cdr.detectChanges();
149
150
  }
150
- static ɵfac = function AIPromptSelectorComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || AIPromptSelectorComponent)(); };
151
+ static ɵfac = /*@__PURE__*/ (() => { let ɵAIPromptSelectorComponent_BaseFactory; return function AIPromptSelectorComponent_Factory(__ngFactoryType__) { return (ɵAIPromptSelectorComponent_BaseFactory || (ɵAIPromptSelectorComponent_BaseFactory = i0.ɵɵgetInheritedFactory(AIPromptSelectorComponent)))(__ngFactoryType__ || AIPromptSelectorComponent); }; })();
151
152
  static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AIPromptSelectorComponent, selectors: [["mj-ai-prompt-selector"]], hostBindings: function AIPromptSelectorComponent_HostBindings(rf, ctx) { if (rf & 1) {
152
153
  i0.ɵɵlistener("click", function AIPromptSelectorComponent_click_HostBindingHandler($event) { return ctx.onDocumentClick($event); }, i0.ɵɵresolveDocument);
153
- } }, inputs: { Value: "Value", Placeholder: "Placeholder", Provider: "Provider" }, outputs: { ValueChange: "ValueChange" }, decls: 7, vars: 7, consts: [["search", ""], [1, "ps"], ["type", "button", 1, "ps-trigger", 3, "click"], [1, "ps-trigger-text"], [1, "fa-solid", "fa-wand-magic-sparkles"], [1, "fa-solid", "fa-chevron-down", "ps-caret"], ["role", "listbox", 1, "ps-panel"], [1, "ps-search"], [1, "fa-solid", "fa-magnifying-glass"], ["type", "text", "placeholder", "Search prompts\u2026", 1, "mj-input", 3, "input", "value"], [1, "ps-list"], [1, "ps-empty"], [1, "ps-group-label"], ["role", "option", 1, "ps-option", 3, "ps-option--selected"], ["role", "option", 1, "ps-option", 3, "click"], [1, "fa-solid", "fa-check"]], template: function AIPromptSelectorComponent_Template(rf, ctx) { if (rf & 1) {
154
+ } }, inputs: { Value: "Value", Placeholder: "Placeholder", Provider: "Provider" }, outputs: { ValueChange: "ValueChange" }, features: [i0.ɵɵInheritDefinitionFeature], decls: 7, vars: 7, consts: [["search", ""], [1, "ps"], ["type", "button", 1, "ps-trigger", 3, "click"], [1, "ps-trigger-text"], [1, "fa-solid", "fa-wand-magic-sparkles"], [1, "fa-solid", "fa-chevron-down", "ps-caret"], ["role", "listbox", 1, "ps-panel"], [1, "ps-search"], [1, "fa-solid", "fa-magnifying-glass"], ["type", "text", "placeholder", "Search prompts\u2026", 1, "mj-input", 3, "input", "value"], [1, "ps-list"], [1, "ps-empty"], [1, "ps-group-label"], ["role", "option", 1, "ps-option", 3, "ps-option--selected"], ["role", "option", 1, "ps-option", 3, "click"], [1, "fa-solid", "fa-check"]], template: function AIPromptSelectorComponent_Template(rf, ctx) { if (rf & 1) {
154
155
  i0.ɵɵdomElementStart(0, "div", 1)(1, "button", 2);
155
156
  i0.ɵɵdomListener("click", function AIPromptSelectorComponent_Template_button_click_1_listener() { return ctx.Toggle(); });
156
157
  i0.ɵɵdomElementStart(2, "span", 3);
@@ -227,5 +228,5 @@ export class AIPromptSelectorComponent {
227
228
  type: HostListener,
228
229
  args: ['document:click', ['$event']]
229
230
  }] }); })();
230
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AIPromptSelectorComponent, { className: "AIPromptSelectorComponent", filePath: "lib/ai-prompt-selector/ai-prompt-selector.component.ts", lineNumber: 97 }); })();
231
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AIPromptSelectorComponent, { className: "AIPromptSelectorComponent", filePath: "lib/ai-prompt-selector/ai-prompt-selector.component.ts", lineNumber: 98 }); })();
231
232
  //# sourceMappingURL=ai-prompt-selector.component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-prompt-selector.component.js","sourceRoot":"","sources":["../../../src/lib/ai-prompt-selector/ai-prompt-selector.component.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview A category-grouped, searchable AI-Prompt picker — the \"slick selector\" for choosing the\n * prompt that powers a `prompt` field-rule source. Loads `MJ: AI Prompts`, groups them by category, and\n * exposes the chosen prompt's ID via a two-way `Value`. Self-contained (no CDK overlay) so it drops into\n * any form.\n * @module @memberjunction/ng-entity-action-ux\n */\nimport {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ElementRef,\n EventEmitter,\n HostListener,\n Input,\n OnInit,\n Output,\n inject,\n} from '@angular/core';\nimport { RunView, type IMetadataProvider } from '@memberjunction/core';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { filterPromptGroups, groupPromptsByCategory, type PromptGroup, type PromptOption } from '../prompt-grouping';\n\n@Component({\n selector: 'mj-ai-prompt-selector',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"ps\" [class.ps--open]=\"IsOpen\">\n <button type=\"button\" class=\"ps-trigger\" (click)=\"Toggle()\" [attr.aria-expanded]=\"IsOpen\">\n <span class=\"ps-trigger-text\" [class.ps-placeholder]=\"!SelectedLabel\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n {{ SelectedLabel || Placeholder }}\n </span>\n <i class=\"fa-solid fa-chevron-down ps-caret\"></i>\n </button>\n\n @if (IsOpen) {\n <div class=\"ps-panel\" role=\"listbox\">\n <div class=\"ps-search\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n <input #search type=\"text\" class=\"mj-input\" placeholder=\"Search prompts…\"\n [value]=\"Filter\" (input)=\"OnFilter($event)\" />\n </div>\n <div class=\"ps-list\">\n @if (Loading) {\n <div class=\"ps-empty\">Loading prompts…</div>\n } @else if (FilteredGroups.length === 0) {\n <div class=\"ps-empty\">No prompts match “{{ Filter }}”.</div>\n } @else {\n @for (group of FilteredGroups; track group.Category) {\n <div class=\"ps-group-label\">{{ group.Category }}</div>\n @for (p of group.Prompts; track p.ID) {\n <div class=\"ps-option\" role=\"option\"\n [class.ps-option--selected]=\"IsSelected(p)\"\n (click)=\"Select(p)\">\n <span>{{ p.Name }}</span>\n @if (IsSelected(p)) { <i class=\"fa-solid fa-check\"></i> }\n </div>\n }\n }\n }\n </div>\n </div>\n }\n </div>\n `,\n styles: [`\n .ps { position: relative; width: 100%; }\n .ps-trigger { display: flex; align-items: center; justify-content: space-between; width: 100%; gap: 8px;\n padding: 8px 12px; background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 6px); color: var(--mj-text-primary); cursor: pointer; font-size: 14px; }\n .ps--open .ps-trigger { border-color: var(--mj-border-focus); }\n .ps-trigger-text { display: flex; align-items: center; gap: 8px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .ps-trigger-text i { color: var(--mj-brand-primary); }\n .ps-placeholder { color: var(--mj-text-muted); }\n .ps-caret { color: var(--mj-text-muted); font-size: 12px; transition: transform .15s ease; }\n .ps--open .ps-caret { transform: rotate(180deg); }\n .ps-panel { position: absolute; z-index: 50; top: calc(100% + 4px); left: 0; right: 0; max-height: 320px;\n display: flex; flex-direction: column; background: var(--mj-bg-surface-elevated, var(--mj-bg-surface));\n border: 1px solid var(--mj-border-default); border-radius: var(--mj-radius-md, 8px);\n box-shadow: var(--mj-shadow-md, 0 6px 20px rgba(0,0,0,.12)); overflow: hidden; }\n .ps-search { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-bottom: 1px solid var(--mj-border-subtle); }\n .ps-search i { color: var(--mj-text-muted); }\n .ps-search input { flex: 1; border: none; outline: none; background: transparent; color: var(--mj-text-primary); }\n .ps-list { overflow-y: auto; padding: 4px 0; }\n .ps-group-label { padding: 6px 12px 2px; font-size: 11px; font-weight: 700; text-transform: uppercase;\n letter-spacing: .04em; color: var(--mj-text-muted); }\n .ps-option { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 7px 12px 7px 22px;\n cursor: pointer; font-size: 14px; color: var(--mj-text-primary); }\n .ps-option:hover { background: var(--mj-bg-surface-hover); }\n .ps-option--selected { color: var(--mj-brand-primary); font-weight: 600; }\n .ps-option i { color: var(--mj-brand-primary); }\n .ps-empty { padding: 16px; text-align: center; color: var(--mj-text-muted); font-size: 13px; }\n `],\n})\nexport class AIPromptSelectorComponent implements OnInit {\n private cdr = inject(ChangeDetectorRef);\n private host = inject(ElementRef);\n\n /** The selected prompt's ID (two-way bindable). */\n @Input() Value: string | null = null;\n @Output() ValueChange = new EventEmitter<string | null>();\n\n /** Placeholder shown when nothing is selected. */\n @Input() Placeholder = 'Select an AI prompt…';\n\n /** Metadata provider (multi-provider). Falls back to the global default when null. */\n @Input() Provider: IMetadataProvider | null = null;\n\n public IsOpen = false;\n public Loading = true;\n public Filter = '';\n public Groups: PromptGroup[] = [];\n\n private allPrompts: PromptOption[] = [];\n\n async ngOnInit(): Promise<void> {\n await this.loadPrompts();\n }\n\n get SelectedLabel(): string {\n return this.allPrompts.find((p) => UUIDsEqual(p.ID, this.Value ?? ''))?.Name ?? '';\n }\n\n /** True when the given prompt is the selected one (UUID-safe across SQL Server / PostgreSQL). */\n IsSelected(prompt: PromptOption): boolean {\n return UUIDsEqual(prompt.ID, this.Value ?? '');\n }\n\n get FilteredGroups(): PromptGroup[] {\n return filterPromptGroups(this.Groups, this.Filter);\n }\n\n Toggle(): void {\n this.IsOpen = !this.IsOpen;\n if (this.IsOpen) this.Filter = '';\n this.cdr.detectChanges();\n }\n\n Select(p: PromptOption): void {\n this.Value = p.ID;\n this.ValueChange.emit(p.ID);\n this.IsOpen = false;\n this.cdr.detectChanges();\n }\n\n OnFilter(event: Event): void {\n this.Filter = (event.target as HTMLInputElement).value;\n this.cdr.detectChanges();\n }\n\n /** Close when clicking outside the component. */\n @HostListener('document:click', ['$event'])\n onDocumentClick(event: MouseEvent): void {\n if (this.IsOpen && !this.host.nativeElement.contains(event.target)) {\n this.IsOpen = false;\n this.cdr.detectChanges();\n }\n }\n\n private async loadPrompts(): Promise<void> {\n const runView = this.Provider ? RunView.FromMetadataProvider(this.Provider) : new RunView();\n const rv = await runView.RunView<PromptOption>({\n EntityName: 'MJ: AI Prompts',\n Fields: ['ID', 'Name', 'Category'],\n ExtraFilter: `Status='Active'`,\n OrderBy: 'Category, Name',\n ResultType: 'simple',\n });\n this.allPrompts = rv.Success ? (rv.Results ?? []) : [];\n this.Groups = groupPromptsByCategory(this.allPrompts);\n this.Loading = false;\n this.cdr.detectChanges();\n }\n}\n"]}
1
+ {"version":3,"file":"ai-prompt-selector.component.js","sourceRoot":"","sources":["../../../src/lib/ai-prompt-selector/ai-prompt-selector.component.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview A category-grouped, searchable AI-Prompt picker — the \"slick selector\" for choosing the\n * prompt that powers a `prompt` field-rule source. Loads `MJ: AI Prompts`, groups them by category, and\n * exposes the chosen prompt's ID via a two-way `Value`. Self-contained (no CDK overlay) so it drops into\n * any form.\n * @module @memberjunction/ng-entity-action-ux\n */\nimport {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ElementRef,\n EventEmitter,\n HostListener,\n Input,\n OnInit,\n Output,\n inject,\n} from '@angular/core';\nimport { RunView, type IMetadataProvider } from '@memberjunction/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { filterPromptGroups, groupPromptsByCategory, type PromptGroup, type PromptOption } from '../prompt-grouping';\n\n@Component({\n selector: 'mj-ai-prompt-selector',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"ps\" [class.ps--open]=\"IsOpen\">\n <button type=\"button\" class=\"ps-trigger\" (click)=\"Toggle()\" [attr.aria-expanded]=\"IsOpen\">\n <span class=\"ps-trigger-text\" [class.ps-placeholder]=\"!SelectedLabel\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n {{ SelectedLabel || Placeholder }}\n </span>\n <i class=\"fa-solid fa-chevron-down ps-caret\"></i>\n </button>\n\n @if (IsOpen) {\n <div class=\"ps-panel\" role=\"listbox\">\n <div class=\"ps-search\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n <input #search type=\"text\" class=\"mj-input\" placeholder=\"Search prompts…\"\n [value]=\"Filter\" (input)=\"OnFilter($event)\" />\n </div>\n <div class=\"ps-list\">\n @if (Loading) {\n <div class=\"ps-empty\">Loading prompts…</div>\n } @else if (FilteredGroups.length === 0) {\n <div class=\"ps-empty\">No prompts match “{{ Filter }}”.</div>\n } @else {\n @for (group of FilteredGroups; track group.Category) {\n <div class=\"ps-group-label\">{{ group.Category }}</div>\n @for (p of group.Prompts; track p.ID) {\n <div class=\"ps-option\" role=\"option\"\n [class.ps-option--selected]=\"IsSelected(p)\"\n (click)=\"Select(p)\">\n <span>{{ p.Name }}</span>\n @if (IsSelected(p)) { <i class=\"fa-solid fa-check\"></i> }\n </div>\n }\n }\n }\n </div>\n </div>\n }\n </div>\n `,\n styles: [`\n .ps { position: relative; width: 100%; }\n .ps-trigger { display: flex; align-items: center; justify-content: space-between; width: 100%; gap: 8px;\n padding: 8px 12px; background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 6px); color: var(--mj-text-primary); cursor: pointer; font-size: 14px; }\n .ps--open .ps-trigger { border-color: var(--mj-border-focus); }\n .ps-trigger-text { display: flex; align-items: center; gap: 8px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .ps-trigger-text i { color: var(--mj-brand-primary); }\n .ps-placeholder { color: var(--mj-text-muted); }\n .ps-caret { color: var(--mj-text-muted); font-size: 12px; transition: transform .15s ease; }\n .ps--open .ps-caret { transform: rotate(180deg); }\n .ps-panel { position: absolute; z-index: 50; top: calc(100% + 4px); left: 0; right: 0; max-height: 320px;\n display: flex; flex-direction: column; background: var(--mj-bg-surface-elevated, var(--mj-bg-surface));\n border: 1px solid var(--mj-border-default); border-radius: var(--mj-radius-md, 8px);\n box-shadow: var(--mj-shadow-md, 0 6px 20px rgba(0,0,0,.12)); overflow: hidden; }\n .ps-search { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-bottom: 1px solid var(--mj-border-subtle); }\n .ps-search i { color: var(--mj-text-muted); }\n .ps-search input { flex: 1; border: none; outline: none; background: transparent; color: var(--mj-text-primary); }\n .ps-list { overflow-y: auto; padding: 4px 0; }\n .ps-group-label { padding: 6px 12px 2px; font-size: 11px; font-weight: 700; text-transform: uppercase;\n letter-spacing: .04em; color: var(--mj-text-muted); }\n .ps-option { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 7px 12px 7px 22px;\n cursor: pointer; font-size: 14px; color: var(--mj-text-primary); }\n .ps-option:hover { background: var(--mj-bg-surface-hover); }\n .ps-option--selected { color: var(--mj-brand-primary); font-weight: 600; }\n .ps-option i { color: var(--mj-brand-primary); }\n .ps-empty { padding: 16px; text-align: center; color: var(--mj-text-muted); font-size: 13px; }\n `],\n})\nexport class AIPromptSelectorComponent extends BaseAngularComponent implements OnInit {\n private cdr = inject(ChangeDetectorRef);\n private host = inject(ElementRef);\n\n /** The selected prompt's ID (two-way bindable). */\n @Input() Value: string | null = null;\n @Output() ValueChange = new EventEmitter<string | null>();\n\n /** Placeholder shown when nothing is selected. */\n @Input() Placeholder = 'Select an AI prompt…';\n\n /** Metadata provider (multi-provider). Falls back to the global default when null. */\n @Input() Provider: IMetadataProvider | null = null;\n\n public IsOpen = false;\n public Loading = true;\n public Filter = '';\n public Groups: PromptGroup[] = [];\n\n private allPrompts: PromptOption[] = [];\n\n async ngOnInit(): Promise<void> {\n await this.loadPrompts();\n }\n\n get SelectedLabel(): string {\n return this.allPrompts.find((p) => UUIDsEqual(p.ID, this.Value ?? ''))?.Name ?? '';\n }\n\n /** True when the given prompt is the selected one (UUID-safe across SQL Server / PostgreSQL). */\n IsSelected(prompt: PromptOption): boolean {\n return UUIDsEqual(prompt.ID, this.Value ?? '');\n }\n\n get FilteredGroups(): PromptGroup[] {\n return filterPromptGroups(this.Groups, this.Filter);\n }\n\n Toggle(): void {\n this.IsOpen = !this.IsOpen;\n if (this.IsOpen) this.Filter = '';\n this.cdr.detectChanges();\n }\n\n Select(p: PromptOption): void {\n this.Value = p.ID;\n this.ValueChange.emit(p.ID);\n this.IsOpen = false;\n this.cdr.detectChanges();\n }\n\n OnFilter(event: Event): void {\n this.Filter = (event.target as HTMLInputElement).value;\n this.cdr.detectChanges();\n }\n\n /** Close when clicking outside the component. */\n @HostListener('document:click', ['$event'])\n onDocumentClick(event: MouseEvent): void {\n if (this.IsOpen && !this.host.nativeElement.contains(event.target)) {\n this.IsOpen = false;\n this.cdr.detectChanges();\n }\n }\n\n private async loadPrompts(): Promise<void> {\n const runView = RunView.FromMetadataProvider(this.ProviderToUse);\n const rv = await runView.RunView<PromptOption>({\n EntityName: 'MJ: AI Prompts',\n Fields: ['ID', 'Name', 'Category'],\n ExtraFilter: `Status='Active'`,\n OrderBy: 'Category, Name',\n ResultType: 'simple',\n });\n this.allPrompts = rv.Success ? (rv.Results ?? []) : [];\n this.Groups = groupPromptsByCategory(this.allPrompts);\n this.Loading = false;\n this.cdr.detectChanges();\n }\n}\n"]}
@@ -15,7 +15,7 @@ var RecordProcessRunnerUXComponent_1;
15
15
  * @module @memberjunction/ng-entity-action-ux
16
16
  */
17
17
  import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject } from '@angular/core';
18
- import { RunView } from '@memberjunction/core';
18
+ import { Metadata, RunView } from '@memberjunction/core';
19
19
  import { RegisterClass, SafeJSONParse } from '@memberjunction/global';
20
20
  import { RecordProcessRunNowOperation, } from '@memberjunction/core-entities';
21
21
  import { MJDialogComponent, MJDialogActionsComponent, MJButtonDirective, MJProgressBarComponent, MJEmptyStateComponent } from '@memberjunction/ng-ui-components';
@@ -339,7 +339,7 @@ let RecordProcessRunnerUXComponent = class RecordProcessRunnerUXComponent extend
339
339
  }
340
340
  /** Reads `MJ: Process Run Details` for the dry-run and builds the per-record diff rows. */
341
341
  async loadDiff(processRunID) {
342
- const rv = await (this.Context?.Provider ? RunView.FromMetadataProvider(this.Context.Provider) : new RunView()).RunView({
342
+ const rv = await RunView.FromMetadataProvider(this.Context?.Provider ?? Metadata.Provider).RunView({
343
343
  EntityName: 'MJ: Process Run Details',
344
344
  ExtraFilter: `ProcessRunID='${processRunID}'`,
345
345
  Fields: ['RecordID', 'Status', 'ResultPayload', 'ErrorMessage'],
@@ -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":";;;;;;;AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC9F,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EACH,4BAA4B,GAI/B,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACjK,OAAO,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;;;;;IA6DzC,8BAAuB;IACnB,qCAAmD;IACnD,yBAAG;IAAA,mFAAyD;IAChE,AADgE,iBAAI,EAC9D;;;IAGN,8BAAuB;IACnB,qCAAmD;IACnD,yBAAG;IAAA,YAA+D;IACtE,AADsE,iBAAI,EACpE;;;IADC,eAA+D;IAA/D,2GAA+D;;;IAItE,8BAAsB;IAClB,uBAAgD;IAE5C,AADJ,2BAAK,aACO;IAAA,yCAAyB;IAAA,iBAAS;IAC1C,yBAAG;IAAA,YAAkB;IAE7B,AADI,AADyB,iBAAI,EACvB,EACJ;;;IAFK,eAAkB;IAAlB,yCAAkB;;;IAUjB,6BAAmB;IAAA,YAAyD;IAAA,iBAAI;;;IAA7D,cAAyD;IAAzD,8HAAyD;;;IALxF,8BAAqB;IACjB,uBAAwC;IAEpC,AADJ,2BAAK,aACO;IAAA,YAAiB;IAAA,iBAAS;IAClC,sGAA8B;IAItC,AADI,iBAAM,EACJ;;;IALU,eAAiB;IAAjB,wCAAiB;IACzB,cAEC;IAFD,+FAEC;;;IAQgB,gCAAqC;IAAA,YAA4B;IAAA,iBAAO;;;IAAnC,cAA4B;IAA5B,4DAA4B;;;IACjE,gCAAqC;IAAA,YAA2B;IAAA,iBAAO;;;IAAlC,cAA2B;IAA3B,2DAA2B;;;IAIrF,qCAGqF;;;IAAjF,mGAA8E;;;IAS1D,AADJ,+BAAuB,eACI;IAAA,YAAa;IAAA,iBAAO;IAC3C,gCAAqB;IAAA,YAAyB;IAAA,iBAAO;IACrD,wBAAqD;IACrD,gCAAqB;IAAA,YAAyB;IAClD,AADkD,iBAAO,EACnD;;;;IAJqB,eAAa;IAAb,gCAAa;IACf,eAAyB;IAAzB,mDAAyB;IAEzB,eAAyB;IAAzB,mDAAyB;;;IAIlD,+BAAwC;IAAA,YAAsB;IAAA,iBAAM;;;IAA5B,cAAsB;IAAtB,yCAAsB;;;IAXtE,AADJ,+BAAuC,cACa;IAAA,YAAkB;IAAA,iBAAM;IACxE,+BAA6B;IACzB,6HAOC;IACD,4HAAwB;IAIhC,AADI,iBAAM,EACJ;;;IAduB,cAAsB;IAAtB,uCAAsB;IAAC,cAAkB;IAAlB,qCAAkB;IAE9D,eAOC;IAPD,6BAOC;IACD,eAEC;IAFD,8CAEC;;;IAMb,6BAAwB;IAAA,YAA8F;IAAA,iBAAI;;;IAAlG,cAA8F;IAA9F,8HAA8F;;;IArB1H,+BAAkC;IAC9B,uHAiBC;IACL,iBAAM;IACN,oHAAiB;;;IAnBb,cAiBC;IAjBD,8BAiBC;IAEL,eAEC;IAFD,2CAEC;;;IAlCD,AADJ,+BAAwB,eACmB;IAAA,YAA6F;IAAA,iBAAO;IAC3I,gCAAsB;IAAA,YAAsC;IAAA,iBAAO;IACnE,yGAAoB;IACpB,yGAAoB;IACxB,iBAAM;IAOJ,AALF,mHAA6B,uEAKpB;;;IAXkC,eAA6F;IAA7F,kLAA6F;IAC9G,eAAsC;IAAtC,sEAAsC;IAC5D,cAA+F;IAA/F,8CAA+F;IAC/F,cAA8F;IAA9F,8CAA8F;IAGlG,cA6BC;IA7BD,sDA6BC;;;;IASG,kCAAqD;IAAlB,0MAAS,cAAO,KAAC;IAChD,wBAAiC;IAAC,YACtC;IAAA,iBAAS;;;IAD6B,eACtC;IADsC,wEACtC;;;;IAHJ,0GAAkB;IAKlB,kCAAsC;IAArB,2LAAS,iBAAU,KAAC;IAAC,sBAAM;IAAA,iBAAS;;;IALrD,4CAIC;;;;IAGY,kCAAsD;IAAnB,2LAAS,eAAQ,KAAC;IAAC,oBAAI;IAAA,iBAAS;;;;IAClE,kCAAsC;IAArB,4LAAS,iBAAU,KAAC;IAAC,qBAAK;IAAA,iBAAS;;AAoCnF,IAAM,8BAA8B,GAApC,MAAM,8BAA+B,SAAQ,yBAAyB;;IACjE,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAExC,mFAAmF;IAC3E,MAAM,CAAU,gBAAgB,GAAG,GAAG,CAAC;IAExC,KAAK,GAAgB,SAAS,CAAC;IAC/B,YAAY,GAAG,EAAE,CAAC;IAClB,QAAQ,GAAoB,EAAE,CAAC;IAC/B,aAAa,CAAc;IAC3B,aAAa,CAAc;IAC3B,kBAAkB,GAAG,CAAC,CAAC;IACvB,gBAAgB,GAAG,CAAC,CAAC;IACrB,SAAS,GAAG,KAAK,CAAC;IAEzB,IAAI,KAAK,KAAa,OAAO,IAAI,CAAC,OAAO,EAAE,WAAW,IAAI,oBAAoB,CAAC,CAAC,CAAC;IACjF,IAAI,WAAW,KAAa,OAAO,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,WAAW,IAAI,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,IAAI,SAAS,CAAC,CAAC,CAAC;IAC1H,IAAI,UAAU,KAAc,OAAO,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC,CAAC,CAAC;IACjE,IAAI,YAAY,KAAa,OAAO,IAAI,CAAC,aAAa,EAAE,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;IACvE,IAAI,YAAY,KAAa,OAAO,IAAI,CAAC,aAAa,EAAE,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;IACvE,IAAI,WAAW;QACX,MAAM,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC;QAC7B,OAAO,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,SAAS,IAAI,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;IACvE,CAAC;IAED,kFAAkF;IAClF,KAAK,CAAC,KAAK;QACP,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QAC/C,IAAI,CAAC,eAAe,EAAE,CAAC;YACnB,IAAI,CAAC,OAAO,CAAC,gDAAgD,CAAC,CAAC;YAC/D,OAAO;QACX,CAAC;QACD,IAAI,CAAC;YACD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;YACxD,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;YAC7B,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;gBACnB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,IAAI,oCAAoC,CAAC,CAAC;gBAC3E,OAAO;YACX,CAAC;YACD,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;gBACvB,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YAC9C,CAAC;YACD,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAC7B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,IAAI,CAAC,OAAO,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7D,CAAC;IACL,CAAC;IAED,iDAAiD;IACjD,KAAK,CAAC,KAAK;QACP,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QAC/C,IAAI,CAAC,eAAe;YAAE,OAAO;QAC7B,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC;QACxB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QACzB,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;YACxD,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC;YAC5B,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAClB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,IAAI,mCAAmC,CAAC,CAAC;gBACzE,OAAO;YACX,CAAC;YACD,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC;YACpB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QAC7B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,IAAI,CAAC,OAAO,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7D,CAAC;IACL,CAAC;IAED,oEAAoE;IACpE,MAAM;QACF,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAC3F,CAAC;IAED,uEAAuE;IACvE,QAAQ;QACJ,IAAI,IAAI,CAAC,KAAK,KAAK,UAAU;YAAE,OAAO,CAAC,gCAAgC;QACvE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IAC1B,CAAC;IAED,yEAAyE;IACzE,OAAO,CAAC,KAAc;QAClB,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,KAAK,CAAC,eAAuB,EAAE,MAAe;QACxD,MAAM,KAAK,GAA6B,EAAE,eAAe,EAAE,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,CAAC;QAC9F,MAAM,MAAM,GACR,MAAM,IAAI,4BAA4B,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,IAAI,SAAS,EAAE,CAAC,CAAC;QAC/G,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;QACxB,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,IAAI,CAAC,EAAE,MAAM,KAAK,WAAW,CAAC;QAC9D,OAAO;YACH,OAAO,EAAE,SAAS;YAClB,YAAY,EAAE,CAAC,EAAE,YAAY;YAC7B,SAAS,EAAE,CAAC,EAAE,SAAS,IAAI,CAAC;YAC5B,SAAS,EAAE,CAAC,EAAE,OAAO,IAAI,CAAC;YAC1B,OAAO,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC;YACtB,OAAO,EAAE,CAAC,EAAE,OAAO,IAAI,CAAC;YACxB,MAAM,EAAE,CAAC,EAAE,MAAM;YACjB,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,CAAC,EAAE,YAAY,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;SAC9I,CAAC;IACN,CAAC;IAED,2FAA2F;IACnF,KAAK,CAAC,QAAQ,CAAC,YAAoB;QACvC,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC;YACpH,UAAU,EAAE,yBAAyB;YACrC,WAAW,EAAE,iBAAiB,YAAY,GAAG;YAC7C,MAAM,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,eAAe,EAAE,cAAc,CAAC;YAC/D,OAAO,EAAE,UAAU;YACnB,OAAO,EAAE,gCAA8B,CAAC,gBAAgB;YACxD,UAAU,EAAE,QAAQ;SACvB,CAAC,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,OAAO;YAAE,OAAO;QAExB,MAAM,IAAI,GAAoB,EAAE,CAAC;QACjC,IAAI,cAAc,GAAG,CAAC,CAAC;QACvB,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,KAAK,MAAM,MAAM,IAAI,EAAE,CAAC,OAAsB,EAAE,CAAC;YAC7C,MAAM,OAAO,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa,CAAc,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACpG,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YAC3F,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACrB,cAAc,EAAE,CAAC;gBACjB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;YACnC,CAAC;YACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;gBAC5C,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;YACzH,CAAC;QACL,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,kBAAkB,GAAG,cAAc,CAAC;QACzC,IAAI,CAAC,gBAAgB,GAAG,YAAY,CAAC;QACrC,IAAI,CAAC,SAAS,GAAG,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,IAAI,CAAC,CAAC,GAAG,gCAA8B,CAAC,gBAAgB,CAAC;IAC5G,CAAC;IAED,6FAA6F;IACrF,UAAU;QACd,OAAO,uBAAuB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IAEO,eAAe;QACnB,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,iBAAiB,CAAC,CAAC;QACrD,OAAO,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACpE,CAAC;IAEO,OAAO,CAAC,OAAe;QAC3B,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC;QAC5B,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC;QACrB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAC7B,CAAC;qSA1JQ,8BAA8B,yBAA9B,8BAA8B;6DAA9B,8BAA8B;YA5HnC,oCAAkH;YAArB,8GAAS,cAAU,IAAC;YAC7G,8BAAuB;YAkCf,AAXA,AATA,AANA,AANA,yFAAmB,mEAMC,mEAMH,mEASD,yDAWG;YAwC3B,iBAAM;YAEN,yCAAmB;YAWX,AADA,AARA,+EAAmB,sEAQH,wEACC;YAG7B,AADI,iBAAoB,EACZ;;;;YA3F8C,AAAhB,AAAd,AAAjB,8BAAgB,cAAc,oBAAgB,uCAAmC;YAEpF,eAwEC;YAxED,2CAAA,SAAS,mBAAT,UAAU,mBAAV,OAAO,mBAAP,MAAM,mBAAN,SAAS,UAwER;YAID,eAWC;YAXD,2CAAA,SAAS,mBAAT,MAAM,mBAAN,OAAO,WAWN;4BA3FH,iBAAiB,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,qBAAqB;;AA8H9G,8BAA8B;IAnI1C,aAAa,CAAC,yBAAyB,EAAE,uBAAuB,CAAC;GAmIrD,8BAA8B,CA2J1C;;iFA3JY,8BAA8B;cAlI1C,SAAS;2BACI,6BAA6B,cAC3B,IAAI,mBACC,uBAAuB,CAAC,MAAM,WACtC,CAAC,iBAAiB,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,qBAAqB,CAAC,YAC9G;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA6FT;;kFAgCQ,8BAA8B","sourcesContent":["/**\n * @fileoverview The flagship runtime-UX driver: runs a `MJ: Record Processes` (typically a FieldRules\n * bulk-update) as a safe, two-step flow — a server dry-run computes the exact per-record diff, the user\n * reviews it, and only on confirm does the real write happen. Registered as `RecordProcessRunnerUX`; an\n * entity action points its `RuntimeUXDriverClass` here and the host mounts it. All server work goes through\n * the typed {@link GraphQLRecordProcessClient}; the rich diff is read with `RunView` over\n * `MJ: Process Run Details`.\n * @module @memberjunction/ng-entity-action-ux\n */\nimport { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject } from '@angular/core';\nimport { RunView } from '@memberjunction/core';\nimport { RegisterClass, SafeJSONParse } from '@memberjunction/global';\nimport {\n RecordProcessRunNowOperation,\n type RecordProcessRunNowInput,\n type RecordProcessRunNowOutput,\n type RecordProcessScopeOverride,\n} from '@memberjunction/core-entities';\nimport type { RemoteOpResult } from '@memberjunction/core';\nimport { MJDialogComponent, MJDialogActionsComponent, MJButtonDirective, MJProgressBarComponent, MJEmptyStateComponent } from '@memberjunction/ng-ui-components';\nimport { BaseEntityActionRuntimeUX } from '../base-entity-action-runtime-ux';\nimport { buildRecordProcessScope, displayValue } from '../scope';\n\n/** The run summary the runner renders, mapped from the operation's typed output. */\ninterface RunSummary {\n Success: boolean;\n ProcessRunID?: string;\n Processed: number;\n Succeeded: number;\n Errored: number;\n Skipped: number;\n Status?: string;\n ErrorMessage?: string;\n}\n\n/** The driver's flow state. */\ntype RunnerState = 'loading' | 'preview' | 'applying' | 'done' | 'error';\n\n/** One computed field change (mirrors the engine's `FieldChange` for the bits the UI shows). */\ninterface DiffChange {\n Field: string;\n OldValue: unknown;\n NewValue: unknown;\n Applied: boolean;\n Changed: boolean;\n Error?: string;\n}\n\n/** The JSON payload persisted on each `MJ: Process Run Details.ResultPayload` for a FieldRules run. */\ninterface DiffPayload {\n DryRun: boolean;\n Changes: DiffChange[];\n ChangedFields: string[];\n}\n\n/** A per-record row of the preview table. */\ninterface RecordDiffRow {\n RecordID: string;\n Status: string;\n Changes: DiffChange[];\n ErrorMessage?: string;\n}\n\n/** Raw `MJ: Process Run Details` projection used to build the diff. */\ninterface RawDetail {\n RecordID: string;\n Status: string;\n ResultPayload?: string;\n ErrorMessage?: string;\n}\n\n@RegisterClass(BaseEntityActionRuntimeUX, 'RecordProcessRunnerUX')\n@Component({\n selector: 'mj-record-process-runner-ux',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [MJDialogComponent, MJDialogActionsComponent, MJButtonDirective, MJProgressBarComponent, MJEmptyStateComponent],\n template: `\n <mj-dialog [Visible]=\"true\" [Width]=\"760\" [Title]=\"Title\" [Closeable]=\"State !== 'applying'\" (Close)=\"OnCancel()\">\n <div class=\"rp-runner\">\n @switch (State) {\n @case ('loading') {\n <div class=\"rp-status\">\n <mj-progress-bar Type=\"infinite\"></mj-progress-bar>\n <p>Computing a safe preview — no changes have been made yet…</p>\n </div>\n }\n @case ('applying') {\n <div class=\"rp-status\">\n <mj-progress-bar Type=\"infinite\"></mj-progress-bar>\n <p>Applying changes to {{ ChangedRecordCount }} {{ EntityLabel }}…</p>\n </div>\n }\n @case ('error') {\n <div class=\"rp-error\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <div>\n <strong>Couldn't run this process</strong>\n <p>{{ ErrorMessage }}</p>\n </div>\n </div>\n }\n @case ('done') {\n <div class=\"rp-done\">\n <i class=\"fa-solid fa-circle-check\"></i>\n <div>\n <strong>{{ DoneMessage }}</strong>\n @if (AppliedResult?.Errored) {\n <p class=\"rp-warn\">{{ AppliedResult?.Errored }} record(s) reported an error.</p>\n }\n </div>\n </div>\n }\n @case ('preview') {\n <div class=\"rp-summary\">\n <span class=\"rp-pill rp-pill--primary\">{{ ChangedRecordCount }} of {{ PreviewResult?.Processed ?? 0 }} {{ EntityLabel }} will change</span>\n <span class=\"rp-pill\">{{ TotalChangeCount }} field update(s)</span>\n @if (SkippedCount) { <span class=\"rp-pill rp-pill--muted\">{{ SkippedCount }} unchanged</span> }\n @if (ErroredCount) { <span class=\"rp-pill rp-pill--error\">{{ ErroredCount }} error(s)</span> }\n </div>\n\n @if (DiffRows.length === 0) {\n <mj-empty-state class=\"rp-empty\" Size=\"compact\" Variant=\"success\"\n Icon=\"fa-regular fa-circle-check\"\n Title=\"Nothing to change\"\n [Message]=\"'The rules did not match any of the selected ' + EntityLabel + '.'\" />\n } @else {\n <div class=\"rp-diff\" role=\"table\">\n @for (row of DiffRows; track row.RecordID) {\n <div class=\"rp-diff-record\" role=\"row\">\n <div class=\"rp-diff-key\" [title]=\"row.RecordID\">{{ row.RecordID }}</div>\n <div class=\"rp-diff-changes\">\n @for (c of row.Changes; track c.Field) {\n <div class=\"rp-change\">\n <span class=\"rp-field\">{{ c.Field }}</span>\n <span class=\"rp-old\">{{ Display(c.OldValue) }}</span>\n <i class=\"fa-solid fa-arrow-right-long rp-arrow\"></i>\n <span class=\"rp-new\">{{ Display(c.NewValue) }}</span>\n </div>\n }\n @if (row.ErrorMessage) {\n <div class=\"rp-change rp-change--error\">{{ row.ErrorMessage }}</div>\n }\n </div>\n </div>\n }\n </div>\n @if (Truncated) {\n <p class=\"rp-truncated\">Showing the first {{ DiffRows.length }} changed records. The apply step covers the full scope.</p>\n }\n }\n }\n }\n </div>\n\n <mj-dialog-actions>\n @switch (State) {\n @case ('preview') {\n @if (HasChanges) {\n <button mjButton variant=\"primary\" (click)=\"Apply()\">\n <i class=\"fa-solid fa-check\"></i> Apply {{ TotalChangeCount }} change(s)\n </button>\n }\n <button mjButton (click)=\"OnCancel()\">Cancel</button>\n }\n @case ('done') { <button mjButton variant=\"primary\" (click)=\"Finish()\">Done</button> }\n @case ('error') { <button mjButton (click)=\"OnCancel()\">Close</button> }\n }\n </mj-dialog-actions>\n </mj-dialog>\n `,\n styles: [`\n .rp-runner { min-height: 120px; color: var(--mj-text-primary); }\n .rp-status { display: flex; flex-direction: column; align-items: center; gap: 16px; padding: 32px 12px; color: var(--mj-text-secondary); }\n .rp-status mj-progress-bar { width: 60%; }\n .rp-error, .rp-done { display: flex; gap: 14px; align-items: flex-start; padding: 16px; border-radius: var(--mj-radius-md, 8px); }\n .rp-error { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n .rp-done { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n .rp-error i, .rp-done i { font-size: 22px; margin-top: 2px; }\n .rp-error p, .rp-done p { margin: 4px 0 0; }\n .rp-warn { color: var(--mj-status-warning-text); }\n .rp-summary { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }\n .rp-pill { padding: 4px 12px; border-radius: 999px; font-size: 13px; font-weight: 600; background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n .rp-pill--primary { background: color-mix(in srgb, var(--mj-brand-primary) 14%, var(--mj-bg-surface)); color: var(--mj-brand-primary); }\n .rp-pill--error { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n .rp-pill--muted { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n .rp-empty { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 28px; color: var(--mj-text-muted); }\n .rp-empty i { font-size: 26px; color: var(--mj-status-success); }\n .rp-diff { max-height: 420px; overflow-y: auto; border: 1px solid var(--mj-border-default); border-radius: var(--mj-radius-md, 8px); }\n .rp-diff-record { display: grid; grid-template-columns: 180px 1fr; gap: 12px; padding: 10px 14px; border-bottom: 1px solid var(--mj-border-subtle); }\n .rp-diff-record:last-child { border-bottom: none; }\n .rp-diff-key { font-family: var(--mj-font-mono, monospace); font-size: 12px; color: var(--mj-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .rp-change { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 2px 0; font-size: 13px; }\n .rp-change--error { color: var(--mj-status-error-text); }\n .rp-field { font-weight: 600; min-width: 120px; }\n .rp-old { color: var(--mj-text-muted); text-decoration: line-through; }\n .rp-new { color: var(--mj-status-success-text); font-weight: 600; }\n .rp-arrow { color: var(--mj-text-disabled); font-size: 11px; }\n .rp-truncated { margin-top: 10px; font-size: 12px; color: var(--mj-text-muted); }\n button i { margin-right: 6px; }\n `],\n})\nexport class RecordProcessRunnerUXComponent extends BaseEntityActionRuntimeUX {\n private cdr = inject(ChangeDetectorRef);\n\n /** Cap on preview rows we render (the apply step always covers the full scope). */\n private static readonly MAX_PREVIEW_ROWS = 500;\n\n public State: RunnerState = 'loading';\n public ErrorMessage = '';\n public DiffRows: RecordDiffRow[] = [];\n public PreviewResult?: RunSummary;\n public AppliedResult?: RunSummary;\n public ChangedRecordCount = 0;\n public TotalChangeCount = 0;\n public Truncated = false;\n\n get Title(): string { return this.Context?.ActionLabel || 'Run Record Process'; }\n get EntityLabel(): string { return this.Context?.EntityInfo?.DisplayName || this.Context?.EntityInfo?.Name || 'records'; }\n get HasChanges(): boolean { return this.ChangedRecordCount > 0; }\n get SkippedCount(): number { return this.PreviewResult?.Skipped ?? 0; }\n get ErroredCount(): number { return this.PreviewResult?.Errored ?? 0; }\n get DoneMessage(): string {\n const r = this.AppliedResult;\n return r ? `Updated ${r.Succeeded} ${this.EntityLabel}.` : 'Done.';\n }\n\n /** Step 1: run a dry-run on the server and load the resulting per-record diff. */\n async Start(): Promise<void> {\n const recordProcessID = this.recordProcessID();\n if (!recordProcessID) {\n this.toError('This action has no RecordProcessID configured.');\n return;\n }\n try {\n const preview = await this.runOp(recordProcessID, true);\n this.PreviewResult = preview;\n if (!preview.Success) {\n this.toError(preview.ErrorMessage || 'The preview could not be computed.');\n return;\n }\n if (preview.ProcessRunID) {\n await this.loadDiff(preview.ProcessRunID);\n }\n this.State = 'preview';\n this.cdr.detectChanges();\n } catch (e) {\n this.toError(e instanceof Error ? e.message : String(e));\n }\n }\n\n /** Step 2: the user confirmed — run for real. */\n async Apply(): Promise<void> {\n const recordProcessID = this.recordProcessID();\n if (!recordProcessID) return;\n this.State = 'applying';\n this.cdr.detectChanges();\n try {\n const result = await this.runOp(recordProcessID, false);\n this.AppliedResult = result;\n if (!result.Success) {\n this.toError(result.ErrorMessage || 'The changes could not be applied.');\n return;\n }\n this.State = 'done';\n this.cdr.detectChanges();\n } catch (e) {\n this.toError(e instanceof Error ? e.message : String(e));\n }\n }\n\n /** Closes after a successful apply, telling the host to refresh. */\n Finish(): void {\n this.Completed.emit({ Completed: true, RefreshData: true, Message: this.DoneMessage });\n }\n\n /** Dismiss without applying (also used for the error/close button). */\n OnCancel(): void {\n if (this.State === 'applying') return; // don't allow dismiss mid-write\n this.Cancelled.emit();\n }\n\n /** Renders a value for the diff table (delegates to the pure helper). */\n Display(value: unknown): string {\n return displayValue(value);\n }\n\n /**\n * Runs the `RecordProcess.RunNow` Remote Operation and maps its typed output to a {@link RunSummary}.\n * `Execute` routes through the host's provider — marshalled over the generic `ExecuteRemoteOperation`\n * transport on the client, in-process on the server — so there is no bespoke resolver/client.\n */\n private async runOp(recordProcessID: string, dryRun: boolean): Promise<RunSummary> {\n const input: RecordProcessRunNowInput = { recordProcessID, scope: this.buildScope(), dryRun };\n const result: RemoteOpResult<RecordProcessRunNowOutput> =\n await new RecordProcessRunNowOperation().Execute(input, { provider: this.Context?.Provider ?? undefined });\n const o = result.Output;\n const completed = result.Success && o?.status === 'Completed';\n return {\n Success: completed,\n ProcessRunID: o?.processRunID,\n Processed: o?.processed ?? 0,\n Succeeded: o?.success ?? 0,\n Errored: o?.error ?? 0,\n Skipped: o?.skipped ?? 0,\n Status: o?.status,\n ErrorMessage: result.ErrorMessage ?? o?.errorMessage ?? (o && o.status !== 'Completed' ? `Run ended with status '${o.status}'` : undefined),\n };\n }\n\n /** Reads `MJ: Process Run Details` for the dry-run and builds the per-record diff rows. */\n private async loadDiff(processRunID: string): Promise<void> {\n const rv = await (this.Context?.Provider ? RunView.FromMetadataProvider(this.Context.Provider) : new RunView()).RunView({\n EntityName: 'MJ: Process Run Details',\n ExtraFilter: `ProcessRunID='${processRunID}'`,\n Fields: ['RecordID', 'Status', 'ResultPayload', 'ErrorMessage'],\n OrderBy: 'RecordID',\n MaxRows: RecordProcessRunnerUXComponent.MAX_PREVIEW_ROWS,\n ResultType: 'simple',\n });\n if (!rv.Success) return;\n\n const rows: RecordDiffRow[] = [];\n let changedRecords = 0;\n let totalChanges = 0;\n for (const detail of rv.Results as RawDetail[]) {\n const payload = detail.ResultPayload ? SafeJSONParse<DiffPayload>(detail.ResultPayload) : undefined;\n const changes = (payload?.Changes ?? []).filter((c) => c.Applied && c.Changed && !c.Error);\n if (changes.length > 0) {\n changedRecords++;\n totalChanges += changes.length;\n }\n if (changes.length > 0 || detail.ErrorMessage) {\n rows.push({ RecordID: detail.RecordID, Status: detail.Status, Changes: changes, ErrorMessage: detail.ErrorMessage });\n }\n }\n this.DiffRows = rows;\n this.ChangedRecordCount = changedRecords;\n this.TotalChangeCount = totalChanges;\n this.Truncated = (this.PreviewResult?.Processed ?? 0) > RecordProcessRunnerUXComponent.MAX_PREVIEW_ROWS;\n }\n\n /** Builds the engine scope from the host-supplied context (delegates to the pure helper). */\n private buildScope(): RecordProcessScopeOverride {\n return buildRecordProcessScope(this.Context);\n }\n\n private recordProcessID(): string | undefined {\n const id = this.Context?.Config?.['RecordProcessID'];\n return typeof id === 'string' && id.length > 0 ? id : undefined;\n }\n\n private toError(message: string): void {\n this.ErrorMessage = message;\n this.State = 'error';\n this.cdr.detectChanges();\n }\n}\n"]}
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 { Metadata, 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 RunView.FromMetadataProvider(this.Context?.Provider ?? Metadata.Provider).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,18 +1,12 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-entity-action-ux",
3
- "version": "5.51.0",
3
+ "version": "6.1.0-edge.1",
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",
7
7
  "files": [
8
8
  "/dist"
9
9
  ],
10
- "scripts": {
11
- "build": "npm run clean && ngc",
12
- "clean": "rimraf dist",
13
- "test": "vitest run",
14
- "test:watch": "vitest"
15
- },
16
10
  "keywords": [
17
11
  "MemberJunction",
18
12
  "Angular",
@@ -26,26 +20,36 @@
26
20
  "@angular/compiler": "21.1.3",
27
21
  "@angular/compiler-cli": "21.1.3",
28
22
  "rimraf": "^5.0.0",
29
- "vitest": "^4.0.18"
23
+ "vitest": "^4.0.18",
24
+ "@memberjunction/ng-test-utils": "6.1.0-edge.1"
30
25
  },
31
26
  "peerDependencies": {
32
- "@angular/common": "21.1.3",
33
- "@angular/core": "21.1.3",
34
- "@angular/forms": "21.1.3",
27
+ "@angular/common": "^21.1.3",
28
+ "@angular/core": "^21.1.3",
29
+ "@angular/forms": "^21.1.3",
35
30
  "rxjs": "^7.8.2"
36
31
  },
37
32
  "dependencies": {
38
- "@memberjunction/core": "5.51.0",
39
- "@memberjunction/core-entities": "5.51.0",
40
- "@memberjunction/global": "5.51.0",
41
- "@memberjunction/graphql-dataprovider": "5.51.0",
42
- "@memberjunction/ng-ui-components": "5.51.0",
43
- "@memberjunction/record-set-processor-base": "5.51.0",
33
+ "@memberjunction/core": "6.1.0-edge.1",
34
+ "@memberjunction/core-entities": "6.1.0-edge.1",
35
+ "@memberjunction/global": "6.1.0-edge.1",
36
+ "@memberjunction/graphql-dataprovider": "6.1.0-edge.1",
37
+ "@memberjunction/ng-base-types": "6.1.0-edge.1",
38
+ "@memberjunction/ng-ui-components": "6.1.0-edge.1",
39
+ "@memberjunction/record-set-processor-base": "6.1.0-edge.1",
44
40
  "tslib": "^2.8.1"
45
41
  },
46
42
  "sideEffects": false,
47
43
  "repository": {
48
44
  "type": "git",
49
45
  "url": "https://github.com/MemberJunction/MJ"
46
+ },
47
+ "mjUILayer": "widgets",
48
+ "scripts": {
49
+ "build": "npm run clean && ngc",
50
+ "clean": "rimraf dist",
51
+ "test": "vitest run",
52
+ "test:types": "tsc --noEmit -p tsconfig.spec.json",
53
+ "test:watch": "vitest"
50
54
  }
51
- }
55
+ }