@memberjunction/ng-entity-action-ux 6.2.0-edge.0 → 6.2.0-edge.2
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/dist/lib/ai-prompt-selector/ai-prompt-selector.component.js +3 -3
- package/dist/lib/ai-prompt-selector/ai-prompt-selector.component.js.map +1 -1
- package/dist/lib/field-rules-builder/field-rules-builder.component.js +5 -5
- package/dist/lib/field-rules-builder/field-rules-builder.component.js.map +1 -1
- package/dist/lib/field-rules-model.d.ts +8 -0
- package/dist/lib/field-rules-model.d.ts.map +1 -1
- package/dist/lib/field-rules-model.js +22 -6
- package/dist/lib/field-rules-model.js.map +1 -1
- package/dist/lib/prompt-grouping.d.ts +4 -0
- package/dist/lib/prompt-grouping.d.ts.map +1 -1
- package/dist/lib/prompt-grouping.js +10 -2
- package/dist/lib/prompt-grouping.js.map +1 -1
- package/dist/lib/record-process-runner/record-process-runner-ux.component.js +3 -3
- package/dist/lib/record-process-runner/record-process-runner-ux.component.js.map +1 -1
- package/dist/lib/scope.d.ts +4 -0
- package/dist/lib/scope.d.ts.map +1 -1
- package/dist/lib/scope.js +10 -2
- package/dist/lib/scope.js.map +1 -1
- package/package.json +9 -9
|
@@ -9,7 +9,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Even
|
|
|
9
9
|
import { RunView } from '@memberjunction/core';
|
|
10
10
|
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
11
11
|
import { UUIDsEqual } from '@memberjunction/global';
|
|
12
|
-
import {
|
|
12
|
+
import { FilterPromptGroups, GroupPromptsByCategory } from '../prompt-grouping';
|
|
13
13
|
import * as i0 from "@angular/core";
|
|
14
14
|
const _forTrack0 = ($index, $item) => $item.Category;
|
|
15
15
|
const _forTrack1 = ($index, $item) => $item.ID;
|
|
@@ -109,7 +109,7 @@ export class AIPromptSelectorComponent extends BaseAngularComponent {
|
|
|
109
109
|
return UUIDsEqual(prompt.ID, this.Value ?? '');
|
|
110
110
|
}
|
|
111
111
|
get FilteredGroups() {
|
|
112
|
-
return
|
|
112
|
+
return FilterPromptGroups(this.Groups, this.Filter);
|
|
113
113
|
}
|
|
114
114
|
Toggle() {
|
|
115
115
|
this.IsOpen = !this.IsOpen;
|
|
@@ -144,7 +144,7 @@ export class AIPromptSelectorComponent extends BaseAngularComponent {
|
|
|
144
144
|
ResultType: 'simple',
|
|
145
145
|
});
|
|
146
146
|
this.allPrompts = rv.Success ? (rv.Results ?? []) : [];
|
|
147
|
-
this.Groups =
|
|
147
|
+
this.Groups = GroupPromptsByCategory(this.allPrompts);
|
|
148
148
|
this.Loading = false;
|
|
149
149
|
this.cdr.detectChanges();
|
|
150
150
|
}
|
|
@@ -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 { 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"]}
|
|
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"]}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, inject } from '@angular/core';
|
|
10
10
|
import { EntityFieldRules, Metadata } from '@memberjunction/core';
|
|
11
11
|
import { AIPromptSelectorComponent } from '../ai-prompt-selector/ai-prompt-selector.component';
|
|
12
|
-
import {
|
|
12
|
+
import { BlankRuleDraft, DraftsToRuleSet, RuleToDraft } from '../field-rules-model';
|
|
13
13
|
import { MJEmptyStateComponent } from '@memberjunction/ng-ui-components';
|
|
14
14
|
import * as i0 from "@angular/core";
|
|
15
15
|
const _forTrack0 = ($index, $item) => $item.Name;
|
|
@@ -224,7 +224,7 @@ export class FieldRulesBuilderComponent {
|
|
|
224
224
|
return; // echo of our own emit — keep the live drafts intact
|
|
225
225
|
}
|
|
226
226
|
this.lastEmittedJSON = incomingJSON;
|
|
227
|
-
this.Rules = value?.Rules ? value.Rules.map((r) =>
|
|
227
|
+
this.Rules = value?.Rules ? value.Rules.map((r) => RuleToDraft(r)) : [];
|
|
228
228
|
this.validate();
|
|
229
229
|
}
|
|
230
230
|
/** Emits the typed rule set on every edit; the consumer serializes it into the Configuration. */
|
|
@@ -233,7 +233,7 @@ export class FieldRulesBuilderComponent {
|
|
|
233
233
|
ValidChange = new EventEmitter();
|
|
234
234
|
get EntityLabel() { return this.entity?.DisplayName || this.entity?.Name || 'record'; }
|
|
235
235
|
AddRule() {
|
|
236
|
-
this.Rules = [...this.Rules,
|
|
236
|
+
this.Rules = [...this.Rules, BlankRuleDraft()];
|
|
237
237
|
this.emit();
|
|
238
238
|
}
|
|
239
239
|
RemoveRule(index) {
|
|
@@ -258,7 +258,7 @@ export class FieldRulesBuilderComponent {
|
|
|
258
258
|
}
|
|
259
259
|
/** Serializes the drafts, validates, and emits. */
|
|
260
260
|
emit() {
|
|
261
|
-
const ruleSet =
|
|
261
|
+
const ruleSet = DraftsToRuleSet(this.Rules);
|
|
262
262
|
// Record what we're about to emit so the echo (consumer feeding it back via [Value]) is recognized
|
|
263
263
|
// and ignored — see lastEmittedJSON. Must mirror the Value setter's comparison basis (Rules array).
|
|
264
264
|
this.lastEmittedJSON = JSON.stringify(ruleSet.Rules ?? []);
|
|
@@ -266,7 +266,7 @@ export class FieldRulesBuilderComponent {
|
|
|
266
266
|
this.ValueChange.emit(ruleSet);
|
|
267
267
|
this.cdr.detectChanges();
|
|
268
268
|
}
|
|
269
|
-
validate(ruleSet =
|
|
269
|
+
validate(ruleSet = DraftsToRuleSet(this.Rules)) {
|
|
270
270
|
if (!this._entityName) {
|
|
271
271
|
this.Errors = [];
|
|
272
272
|
}
|
|
@@ -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';\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"]}
|
|
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"]}
|
|
@@ -22,11 +22,19 @@ export interface RuleDraft {
|
|
|
22
22
|
Condition: string;
|
|
23
23
|
}
|
|
24
24
|
/** A fresh, empty draft (static source, no target). */
|
|
25
|
+
export declare function BlankRuleDraft(): RuleDraft;
|
|
26
|
+
/** @deprecated Use {@link BlankRuleDraft}. */
|
|
25
27
|
export declare function blankRuleDraft(): RuleDraft;
|
|
26
28
|
/** Maps a draft to the engine's discriminated {@link FieldRuleValueSource}. */
|
|
29
|
+
export declare function DraftToSource(d: RuleDraft): FieldRuleValueSource;
|
|
30
|
+
/** @deprecated Use {@link DraftToSource}. */
|
|
27
31
|
export declare function draftToSource(d: RuleDraft): FieldRuleValueSource;
|
|
28
32
|
/** Builds a {@link FieldRuleSet} from drafts, dropping any with no target field. */
|
|
33
|
+
export declare function DraftsToRuleSet(drafts: RuleDraft[]): FieldRuleSet;
|
|
34
|
+
/** @deprecated Use {@link DraftsToRuleSet}. */
|
|
29
35
|
export declare function draftsToRuleSet(drafts: RuleDraft[]): FieldRuleSet;
|
|
30
36
|
/** Inflates an engine rule back into an editable draft (inverse of {@link draftToSource}). */
|
|
37
|
+
export declare function RuleToDraft(rule: FieldRule): RuleDraft;
|
|
38
|
+
/** @deprecated Use {@link RuleToDraft}. */
|
|
31
39
|
export declare function ruleToDraft(rule: FieldRule): RuleDraft;
|
|
32
40
|
//# sourceMappingURL=field-rules-model.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-rules-model.d.ts","sourceRoot":"","sources":["../../src/lib/field-rules-model.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE5F,uGAAuG;AACvG,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAErF,iEAAiE;AACjE,MAAM,WAAW,SAAS;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,iBAAiB,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,uDAAuD;AACvD,wBAAgB,cAAc,IAAI,SAAS,CAM1C;AAED,+EAA+E;AAC/E,wBAAgB,aAAa,CAAC,CAAC,EAAE,SAAS,GAAG,oBAAoB,CAsBhE;AAED,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,YAAY,CAUjE;AAED,8FAA8F;AAC9F,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CA4BtD"}
|
|
1
|
+
{"version":3,"file":"field-rules-model.d.ts","sourceRoot":"","sources":["../../src/lib/field-rules-model.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE5F,uGAAuG;AACvG,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAErF,iEAAiE;AACjE,MAAM,WAAW,SAAS;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,iBAAiB,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,uDAAuD;AACvD,wBAAgB,cAAc,IAAI,SAAS,CAM1C;AAED,8CAA8C;AAC9C,wBAAgB,cAAc,IAAI,SAAS,CAE1C;AAED,+EAA+E;AAC/E,wBAAgB,aAAa,CAAC,CAAC,EAAE,SAAS,GAAG,oBAAoB,CAsBhE;AAED,6CAA6C;AAC7C,wBAAgB,aAAa,CAAC,CAAC,EAAE,SAAS,GAAG,oBAAoB,CAEhE;AAED,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,YAAY,CAUjE;AAED,+CAA+C;AAC/C,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,YAAY,CAEjE;AAED,8FAA8F;AAC9F,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CA4BtD;AAED,2CAA2C;AAC3C,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAEtD"}
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
/** A fresh, empty draft (static source, no target). */
|
|
2
|
-
export function
|
|
2
|
+
export function BlankRuleDraft() {
|
|
3
3
|
return {
|
|
4
4
|
TargetField: '', SourceKind: 'static', StaticValue: '', SourceField: '', Formula: '',
|
|
5
5
|
LookupEntity: '', LookupMatchField: '', LookupMatchValueField: '', LookupReturnField: '',
|
|
6
6
|
PromptID: null, Condition: '',
|
|
7
7
|
};
|
|
8
8
|
}
|
|
9
|
+
/** @deprecated Use {@link BlankRuleDraft}. */
|
|
10
|
+
export function blankRuleDraft() {
|
|
11
|
+
return BlankRuleDraft();
|
|
12
|
+
}
|
|
9
13
|
/** Maps a draft to the engine's discriminated {@link FieldRuleValueSource}. */
|
|
10
|
-
export function
|
|
14
|
+
export function DraftToSource(d) {
|
|
11
15
|
switch (d.SourceKind) {
|
|
12
16
|
case 'field':
|
|
13
17
|
return { Kind: 'field', Field: d.SourceField };
|
|
@@ -30,21 +34,29 @@ export function draftToSource(d) {
|
|
|
30
34
|
return { Kind: 'static', Value: d.StaticValue };
|
|
31
35
|
}
|
|
32
36
|
}
|
|
37
|
+
/** @deprecated Use {@link DraftToSource}. */
|
|
38
|
+
export function draftToSource(d) {
|
|
39
|
+
return DraftToSource(d);
|
|
40
|
+
}
|
|
33
41
|
/** Builds a {@link FieldRuleSet} from drafts, dropping any with no target field. */
|
|
34
|
-
export function
|
|
42
|
+
export function DraftsToRuleSet(drafts) {
|
|
35
43
|
return {
|
|
36
44
|
Rules: drafts
|
|
37
45
|
.filter((d) => d.TargetField)
|
|
38
46
|
.map((d) => ({
|
|
39
47
|
TargetField: d.TargetField,
|
|
40
|
-
Source:
|
|
48
|
+
Source: DraftToSource(d),
|
|
41
49
|
...(d.Condition ? { Condition: d.Condition } : {}),
|
|
42
50
|
})),
|
|
43
51
|
};
|
|
44
52
|
}
|
|
53
|
+
/** @deprecated Use {@link DraftsToRuleSet}. */
|
|
54
|
+
export function draftsToRuleSet(drafts) {
|
|
55
|
+
return DraftsToRuleSet(drafts);
|
|
56
|
+
}
|
|
45
57
|
/** Inflates an engine rule back into an editable draft (inverse of {@link draftToSource}). */
|
|
46
|
-
export function
|
|
47
|
-
const d =
|
|
58
|
+
export function RuleToDraft(rule) {
|
|
59
|
+
const d = BlankRuleDraft();
|
|
48
60
|
d.TargetField = rule.TargetField;
|
|
49
61
|
d.Condition = rule.Condition ?? '';
|
|
50
62
|
const s = rule.Source;
|
|
@@ -73,4 +85,8 @@ export function ruleToDraft(rule) {
|
|
|
73
85
|
}
|
|
74
86
|
return d;
|
|
75
87
|
}
|
|
88
|
+
/** @deprecated Use {@link RuleToDraft}. */
|
|
89
|
+
export function ruleToDraft(rule) {
|
|
90
|
+
return RuleToDraft(rule);
|
|
91
|
+
}
|
|
76
92
|
//# sourceMappingURL=field-rules-model.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-rules-model.js","sourceRoot":"","sources":["../../src/lib/field-rules-model.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Pure (Angular-free) model + serialization for the field-rules builder: the flattened\n * {@link RuleDraft} the UI edits, and the lossless mapping to/from the engine's {@link FieldRuleSet}.\n * Kept separate from the component so it's unit-testable without a TestBed and reusable elsewhere.\n * @module @memberjunction/ng-entity-action-ux\n */\nimport type { FieldRule, FieldRuleSet, FieldRuleValueSource } from '@memberjunction/global';\n\n/** The source kinds the visual builder edits directly (entityDocument is authored as JSON for now). */\nexport type BuilderSourceKind = 'static' | 'field' | 'formula' | 'lookup' | 'prompt';\n\n/** The editable, flattened form of one rule (one screen row). */\nexport interface RuleDraft {\n TargetField: string;\n SourceKind: BuilderSourceKind;\n StaticValue: string;\n SourceField: string;\n Formula: string;\n LookupEntity: string;\n LookupMatchField: string;\n LookupMatchValueField: string;\n LookupReturnField: string;\n PromptID: string | null;\n Condition: string;\n}\n\n/** A fresh, empty draft (static source, no target). */\nexport function
|
|
1
|
+
{"version":3,"file":"field-rules-model.js","sourceRoot":"","sources":["../../src/lib/field-rules-model.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Pure (Angular-free) model + serialization for the field-rules builder: the flattened\n * {@link RuleDraft} the UI edits, and the lossless mapping to/from the engine's {@link FieldRuleSet}.\n * Kept separate from the component so it's unit-testable without a TestBed and reusable elsewhere.\n * @module @memberjunction/ng-entity-action-ux\n */\nimport type { FieldRule, FieldRuleSet, FieldRuleValueSource } from '@memberjunction/global';\n\n/** The source kinds the visual builder edits directly (entityDocument is authored as JSON for now). */\nexport type BuilderSourceKind = 'static' | 'field' | 'formula' | 'lookup' | 'prompt';\n\n/** The editable, flattened form of one rule (one screen row). */\nexport interface RuleDraft {\n TargetField: string;\n SourceKind: BuilderSourceKind;\n StaticValue: string;\n SourceField: string;\n Formula: string;\n LookupEntity: string;\n LookupMatchField: string;\n LookupMatchValueField: string;\n LookupReturnField: string;\n PromptID: string | null;\n Condition: string;\n}\n\n/** A fresh, empty draft (static source, no target). */\nexport function BlankRuleDraft(): RuleDraft {\n return {\n TargetField: '', SourceKind: 'static', StaticValue: '', SourceField: '', Formula: '',\n LookupEntity: '', LookupMatchField: '', LookupMatchValueField: '', LookupReturnField: '',\n PromptID: null, Condition: '',\n };\n}\n\n/** @deprecated Use {@link BlankRuleDraft}. */\nexport function blankRuleDraft(): RuleDraft {\n return BlankRuleDraft();\n}\n\n/** Maps a draft to the engine's discriminated {@link FieldRuleValueSource}. */\nexport function DraftToSource(d: RuleDraft): FieldRuleValueSource {\n switch (d.SourceKind) {\n case 'field':\n return { Kind: 'field', Field: d.SourceField };\n case 'formula':\n return { Kind: 'formula', Expression: d.Formula };\n case 'lookup':\n return {\n Kind: 'lookup',\n Lookup: {\n Entity: d.LookupEntity,\n MatchField: d.LookupMatchField,\n MatchValue: { Kind: 'field', Field: d.LookupMatchValueField },\n ReturnField: d.LookupReturnField,\n },\n };\n case 'prompt':\n return { Kind: 'prompt', Prompt: { PromptID: d.PromptID ?? '' } };\n case 'static':\n default:\n return { Kind: 'static', Value: d.StaticValue };\n }\n}\n\n/** @deprecated Use {@link DraftToSource}. */\nexport function draftToSource(d: RuleDraft): FieldRuleValueSource {\n return DraftToSource(d);\n}\n\n/** Builds a {@link FieldRuleSet} from drafts, dropping any with no target field. */\nexport function DraftsToRuleSet(drafts: RuleDraft[]): FieldRuleSet {\n return {\n Rules: drafts\n .filter((d) => d.TargetField)\n .map((d): FieldRule => ({\n TargetField: d.TargetField,\n Source: DraftToSource(d),\n ...(d.Condition ? { Condition: d.Condition } : {}),\n })),\n };\n}\n\n/** @deprecated Use {@link DraftsToRuleSet}. */\nexport function draftsToRuleSet(drafts: RuleDraft[]): FieldRuleSet {\n return DraftsToRuleSet(drafts);\n}\n\n/** Inflates an engine rule back into an editable draft (inverse of {@link draftToSource}). */\nexport function RuleToDraft(rule: FieldRule): RuleDraft {\n const d = BlankRuleDraft();\n d.TargetField = rule.TargetField;\n d.Condition = rule.Condition ?? '';\n const s = rule.Source;\n // entityDocument has no first-class builder UI yet — surface it as a (read-only-ish) static slot.\n d.SourceKind = s.Kind === 'entityDocument' ? 'static' : s.Kind;\n switch (s.Kind) {\n case 'static':\n d.StaticValue = s.Value == null ? '' : String(s.Value);\n break;\n case 'field':\n d.SourceField = s.Field;\n break;\n case 'formula':\n d.Formula = s.Expression;\n break;\n case 'lookup':\n d.LookupEntity = s.Lookup.Entity;\n d.LookupMatchField = s.Lookup.MatchField;\n d.LookupReturnField = s.Lookup.ReturnField;\n if (s.Lookup.MatchValue.Kind === 'field') d.LookupMatchValueField = s.Lookup.MatchValue.Field;\n break;\n case 'prompt':\n d.PromptID = s.Prompt.PromptID;\n break;\n }\n return d;\n}\n\n/** @deprecated Use {@link RuleToDraft}. */\nexport function ruleToDraft(rule: FieldRule): RuleDraft {\n return RuleToDraft(rule);\n}\n"]}
|
|
@@ -14,7 +14,11 @@ export interface PromptGroup {
|
|
|
14
14
|
Prompts: PromptOption[];
|
|
15
15
|
}
|
|
16
16
|
/** Groups prompts by their category name (blank → "Uncategorized"), preserving input order. */
|
|
17
|
+
export declare function GroupPromptsByCategory(prompts: PromptOption[]): PromptGroup[];
|
|
18
|
+
/** @deprecated Use {@link GroupPromptsByCategory}. */
|
|
17
19
|
export declare function groupPromptsByCategory(prompts: PromptOption[]): PromptGroup[];
|
|
18
20
|
/** Case-insensitive name filter that drops emptied groups. */
|
|
21
|
+
export declare function FilterPromptGroups(groups: PromptGroup[], query: string): PromptGroup[];
|
|
22
|
+
/** @deprecated Use {@link FilterPromptGroups}. */
|
|
19
23
|
export declare function filterPromptGroups(groups: PromptGroup[], query: string): PromptGroup[];
|
|
20
24
|
//# sourceMappingURL=prompt-grouping.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-grouping.d.ts","sourceRoot":"","sources":["../../src/lib/prompt-grouping.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,mDAAmD;AACnD,MAAM,WAAW,YAAY;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;CAAE;AAE7E,iFAAiF;AACjF,MAAM,WAAW,WAAW;IAAG,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,YAAY,EAAE,CAAC;CAAE;AAE3E,+FAA+F;AAC/F,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,WAAW,EAAE,CAc7E;AAED,8DAA8D;AAC9D,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,EAAE,CAMtF"}
|
|
1
|
+
{"version":3,"file":"prompt-grouping.d.ts","sourceRoot":"","sources":["../../src/lib/prompt-grouping.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,mDAAmD;AACnD,MAAM,WAAW,YAAY;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;CAAE;AAE7E,iFAAiF;AACjF,MAAM,WAAW,WAAW;IAAG,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,YAAY,EAAE,CAAC;CAAE;AAE3E,+FAA+F;AAC/F,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,WAAW,EAAE,CAc7E;AAED,sDAAsD;AACtD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,WAAW,EAAE,CAE7E;AAED,8DAA8D;AAC9D,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,EAAE,CAMtF;AAED,kDAAkD;AAClD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,EAAE,CAEtF"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @module @memberjunction/ng-entity-action-ux
|
|
4
4
|
*/
|
|
5
5
|
/** Groups prompts by their category name (blank → "Uncategorized"), preserving input order. */
|
|
6
|
-
export function
|
|
6
|
+
export function GroupPromptsByCategory(prompts) {
|
|
7
7
|
const groups = [];
|
|
8
8
|
const index = new Map();
|
|
9
9
|
for (const p of prompts) {
|
|
@@ -18,8 +18,12 @@ export function groupPromptsByCategory(prompts) {
|
|
|
18
18
|
}
|
|
19
19
|
return groups;
|
|
20
20
|
}
|
|
21
|
+
/** @deprecated Use {@link GroupPromptsByCategory}. */
|
|
22
|
+
export function groupPromptsByCategory(prompts) {
|
|
23
|
+
return GroupPromptsByCategory(prompts);
|
|
24
|
+
}
|
|
21
25
|
/** Case-insensitive name filter that drops emptied groups. */
|
|
22
|
-
export function
|
|
26
|
+
export function FilterPromptGroups(groups, query) {
|
|
23
27
|
const q = query.trim().toLowerCase();
|
|
24
28
|
if (!q)
|
|
25
29
|
return groups;
|
|
@@ -27,4 +31,8 @@ export function filterPromptGroups(groups, query) {
|
|
|
27
31
|
.map((g) => ({ Category: g.Category, Prompts: g.Prompts.filter((p) => p.Name.toLowerCase().includes(q)) }))
|
|
28
32
|
.filter((g) => g.Prompts.length > 0);
|
|
29
33
|
}
|
|
34
|
+
/** @deprecated Use {@link FilterPromptGroups}. */
|
|
35
|
+
export function filterPromptGroups(groups, query) {
|
|
36
|
+
return FilterPromptGroups(groups, query);
|
|
37
|
+
}
|
|
30
38
|
//# sourceMappingURL=prompt-grouping.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-grouping.js","sourceRoot":"","sources":["../../src/lib/prompt-grouping.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,+FAA+F;AAC/F,MAAM,UAAU,sBAAsB,CAAC,OAAuB;IAC1D,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC7C,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACtB,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ,IAAI,eAAe,CAAC;QAC/C,IAAI,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,KAAK,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;YAC5C,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,kBAAkB,CAAC,MAAqB,EAAE,KAAa;IACnE,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,IAAI,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IACtB,OAAO,MAAM;SACR,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;SAC1G,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAC7C,CAAC","sourcesContent":["/**\n * @fileoverview Pure grouping/filtering for the AI-prompt selector. Extracted for unit-testability.\n * @module @memberjunction/ng-entity-action-ux\n */\n\n/** A flat prompt option from the metadata view. */\nexport interface PromptOption { ID: string; Name: string; Category: string; }\n\n/** A category group of prompts (preserves the encounter order of categories). */\nexport interface PromptGroup { Category: string; Prompts: PromptOption[]; }\n\n/** Groups prompts by their category name (blank → \"Uncategorized\"), preserving input order. */\nexport function
|
|
1
|
+
{"version":3,"file":"prompt-grouping.js","sourceRoot":"","sources":["../../src/lib/prompt-grouping.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,+FAA+F;AAC/F,MAAM,UAAU,sBAAsB,CAAC,OAAuB;IAC1D,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC7C,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACtB,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ,IAAI,eAAe,CAAC;QAC/C,IAAI,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,KAAK,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;YAC5C,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,sBAAsB,CAAC,OAAuB;IAC1D,OAAO,sBAAsB,CAAC,OAAO,CAAC,CAAC;AAC3C,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,kBAAkB,CAAC,MAAqB,EAAE,KAAa;IACnE,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,IAAI,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IACtB,OAAO,MAAM;SACR,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;SAC1G,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,kBAAkB,CAAC,MAAqB,EAAE,KAAa;IACnE,OAAO,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAC7C,CAAC","sourcesContent":["/**\n * @fileoverview Pure grouping/filtering for the AI-prompt selector. Extracted for unit-testability.\n * @module @memberjunction/ng-entity-action-ux\n */\n\n/** A flat prompt option from the metadata view. */\nexport interface PromptOption { ID: string; Name: string; Category: string; }\n\n/** A category group of prompts (preserves the encounter order of categories). */\nexport interface PromptGroup { Category: string; Prompts: PromptOption[]; }\n\n/** Groups prompts by their category name (blank → \"Uncategorized\"), preserving input order. */\nexport function GroupPromptsByCategory(prompts: PromptOption[]): PromptGroup[] {\n const groups: PromptGroup[] = [];\n const index = new Map<string, PromptGroup>();\n for (const p of prompts) {\n const category = p.Category || 'Uncategorized';\n let group = index.get(category);\n if (!group) {\n group = { Category: category, Prompts: [] };\n index.set(category, group);\n groups.push(group);\n }\n group.Prompts.push(p);\n }\n return groups;\n}\n\n/** @deprecated Use {@link GroupPromptsByCategory}. */\nexport function groupPromptsByCategory(prompts: PromptOption[]): PromptGroup[] {\n return GroupPromptsByCategory(prompts);\n}\n\n/** Case-insensitive name filter that drops emptied groups. */\nexport function FilterPromptGroups(groups: PromptGroup[], query: string): PromptGroup[] {\n const q = query.trim().toLowerCase();\n if (!q) return groups;\n return groups\n .map((g) => ({ Category: g.Category, Prompts: g.Prompts.filter((p) => p.Name.toLowerCase().includes(q)) }))\n .filter((g) => g.Prompts.length > 0);\n}\n\n/** @deprecated Use {@link FilterPromptGroups}. */\nexport function filterPromptGroups(groups: PromptGroup[], query: string): PromptGroup[] {\n return FilterPromptGroups(groups, query);\n}\n"]}
|
|
@@ -20,7 +20,7 @@ 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';
|
|
22
22
|
import { BaseEntityActionRuntimeUX } from '../base-entity-action-runtime-ux';
|
|
23
|
-
import {
|
|
23
|
+
import { BuildRecordProcessScope, DisplayValue } from '../scope';
|
|
24
24
|
import * as i0 from "@angular/core";
|
|
25
25
|
const _forTrack0 = ($index, $item) => $item.RecordID;
|
|
26
26
|
const _forTrack1 = ($index, $item) => $item.Field;
|
|
@@ -314,7 +314,7 @@ let RecordProcessRunnerUXComponent = class RecordProcessRunnerUXComponent extend
|
|
|
314
314
|
}
|
|
315
315
|
/** Renders a value for the diff table (delegates to the pure helper). */
|
|
316
316
|
Display(value) {
|
|
317
|
-
return
|
|
317
|
+
return DisplayValue(value);
|
|
318
318
|
}
|
|
319
319
|
/**
|
|
320
320
|
* Runs the `RecordProcess.RunNow` Remote Operation and maps its typed output to a {@link RunSummary}.
|
|
@@ -370,7 +370,7 @@ let RecordProcessRunnerUXComponent = class RecordProcessRunnerUXComponent extend
|
|
|
370
370
|
}
|
|
371
371
|
/** Builds the engine scope from the host-supplied context (delegates to the pure helper). */
|
|
372
372
|
buildScope() {
|
|
373
|
-
return
|
|
373
|
+
return BuildRecordProcessScope(this.Context);
|
|
374
374
|
}
|
|
375
375
|
recordProcessID() {
|
|
376
376
|
const id = this.Context?.Config?.['RecordProcessID'];
|
|
@@ -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 { 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"]}
|
|
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/dist/lib/scope.d.ts
CHANGED
|
@@ -6,7 +6,11 @@
|
|
|
6
6
|
import type { RecordProcessScopeOverride } from '@memberjunction/core-entities';
|
|
7
7
|
import type { EntityActionUXContext } from './runtime-ux-context';
|
|
8
8
|
/** Builds the run scope from the grid/list context the host assembled. */
|
|
9
|
+
export declare function BuildRecordProcessScope(context: EntityActionUXContext): RecordProcessScopeOverride;
|
|
10
|
+
/** @deprecated Use {@link BuildRecordProcessScope}. */
|
|
9
11
|
export declare function buildRecordProcessScope(context: EntityActionUXContext): RecordProcessScopeOverride;
|
|
10
12
|
/** Renders an arbitrary value for the diff/preview table. */
|
|
13
|
+
export declare function DisplayValue(value: unknown): string;
|
|
14
|
+
/** @deprecated Use {@link DisplayValue}. */
|
|
11
15
|
export declare function displayValue(value: unknown): string;
|
|
12
16
|
//# sourceMappingURL=scope.d.ts.map
|
package/dist/lib/scope.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../src/lib/scope.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAElE,0EAA0E;AAC1E,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,qBAAqB,GAAG,0BAA0B,CAYlG;AAED,6DAA6D;AAC7D,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAKnD"}
|
|
1
|
+
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../src/lib/scope.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAElE,0EAA0E;AAC1E,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,qBAAqB,GAAG,0BAA0B,CAYlG;AAED,uDAAuD;AACvD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,qBAAqB,GAAG,0BAA0B,CAElG;AAED,6DAA6D;AAC7D,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAKnD;AAED,4CAA4C;AAC5C,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEnD"}
|
package/dist/lib/scope.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Builds the run scope from the grid/list context the host assembled. */
|
|
2
|
-
export function
|
|
2
|
+
export function BuildRecordProcessScope(context) {
|
|
3
3
|
switch (context.ScopeKind) {
|
|
4
4
|
case 'view':
|
|
5
5
|
return { Kind: 'view', ViewID: context.ViewID ?? '' };
|
|
@@ -12,8 +12,12 @@ export function buildRecordProcessScope(context) {
|
|
|
12
12
|
return { Kind: 'records', RecordIDs: context.SelectedRecordIDs ?? [] };
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
+
/** @deprecated Use {@link BuildRecordProcessScope}. */
|
|
16
|
+
export function buildRecordProcessScope(context) {
|
|
17
|
+
return BuildRecordProcessScope(context);
|
|
18
|
+
}
|
|
15
19
|
/** Renders an arbitrary value for the diff/preview table. */
|
|
16
|
-
export function
|
|
20
|
+
export function DisplayValue(value) {
|
|
17
21
|
if (value === null || value === undefined || value === '')
|
|
18
22
|
return '(empty)';
|
|
19
23
|
if (value instanceof Date)
|
|
@@ -22,4 +26,8 @@ export function displayValue(value) {
|
|
|
22
26
|
return JSON.stringify(value);
|
|
23
27
|
return String(value);
|
|
24
28
|
}
|
|
29
|
+
/** @deprecated Use {@link DisplayValue}. */
|
|
30
|
+
export function displayValue(value) {
|
|
31
|
+
return DisplayValue(value);
|
|
32
|
+
}
|
|
25
33
|
//# sourceMappingURL=scope.js.map
|
package/dist/lib/scope.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope.js","sourceRoot":"","sources":["../../src/lib/scope.ts"],"names":[],"mappings":"AAQA,0EAA0E;AAC1E,MAAM,UAAU,uBAAuB,CAAC,OAA8B;IAClE,QAAQ,OAAO,CAAC,SAAS,EAAE,CAAC;QACxB,KAAK,MAAM;YACP,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;QAC1D,KAAK,MAAM;YACP,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;QAC1D,KAAK,QAAQ;YACT,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;QACtD,KAAK,SAAS,CAAC;QACf;YACI,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,iBAAiB,IAAI,EAAE,EAAE,CAAC;IAC/E,CAAC;AACL,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,YAAY,CAAC,KAAc;IACvC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAC5E,IAAI,KAAK,YAAY,IAAI;QAAE,OAAO,KAAK,CAAC,cAAc,EAAE,CAAC;IACzD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC5D,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACzB,CAAC","sourcesContent":["/**\n * @fileoverview Pure mapping from a host-supplied {@link EntityActionUXContext} to the engine/transport\n * {@link RecordProcessScope}. Extracted so it's unit-testable without instantiating a component.\n * @module @memberjunction/ng-entity-action-ux\n */\nimport type { RecordProcessScopeOverride } from '@memberjunction/core-entities';\nimport type { EntityActionUXContext } from './runtime-ux-context';\n\n/** Builds the run scope from the grid/list context the host assembled. */\nexport function
|
|
1
|
+
{"version":3,"file":"scope.js","sourceRoot":"","sources":["../../src/lib/scope.ts"],"names":[],"mappings":"AAQA,0EAA0E;AAC1E,MAAM,UAAU,uBAAuB,CAAC,OAA8B;IAClE,QAAQ,OAAO,CAAC,SAAS,EAAE,CAAC;QACxB,KAAK,MAAM;YACP,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;QAC1D,KAAK,MAAM;YACP,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;QAC1D,KAAK,QAAQ;YACT,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;QACtD,KAAK,SAAS,CAAC;QACf;YACI,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,iBAAiB,IAAI,EAAE,EAAE,CAAC;IAC/E,CAAC;AACL,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,uBAAuB,CAAC,OAA8B;IAClE,OAAO,uBAAuB,CAAC,OAAO,CAAC,CAAC;AAC5C,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,YAAY,CAAC,KAAc;IACvC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAC5E,IAAI,KAAK,YAAY,IAAI;QAAE,OAAO,KAAK,CAAC,cAAc,EAAE,CAAC;IACzD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC5D,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACzB,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,YAAY,CAAC,KAAc;IACvC,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC","sourcesContent":["/**\n * @fileoverview Pure mapping from a host-supplied {@link EntityActionUXContext} to the engine/transport\n * {@link RecordProcessScope}. Extracted so it's unit-testable without instantiating a component.\n * @module @memberjunction/ng-entity-action-ux\n */\nimport type { RecordProcessScopeOverride } from '@memberjunction/core-entities';\nimport type { EntityActionUXContext } from './runtime-ux-context';\n\n/** Builds the run scope from the grid/list context the host assembled. */\nexport function BuildRecordProcessScope(context: EntityActionUXContext): RecordProcessScopeOverride {\n switch (context.ScopeKind) {\n case 'view':\n return { Kind: 'view', ViewID: context.ViewID ?? '' };\n case 'list':\n return { Kind: 'list', ListID: context.ListID ?? '' };\n case 'filter':\n return { Kind: 'filter', Filter: context.Filter };\n case 'records':\n default:\n return { Kind: 'records', RecordIDs: context.SelectedRecordIDs ?? [] };\n }\n}\n\n/** @deprecated Use {@link BuildRecordProcessScope}. */\nexport function buildRecordProcessScope(context: EntityActionUXContext): RecordProcessScopeOverride {\n return BuildRecordProcessScope(context);\n}\n\n/** Renders an arbitrary value for the diff/preview table. */\nexport function DisplayValue(value: unknown): string {\n if (value === null || value === undefined || value === '') return '(empty)';\n if (value instanceof Date) return value.toLocaleString();\n if (typeof value === 'object') return JSON.stringify(value);\n return String(value);\n}\n\n/** @deprecated Use {@link DisplayValue}. */\nexport function displayValue(value: unknown): string {\n return DisplayValue(value);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-entity-action-ux",
|
|
3
|
-
"version": "6.2.0-edge.
|
|
3
|
+
"version": "6.2.0-edge.2",
|
|
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",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"@angular/compiler-cli": "21.2.22",
|
|
22
22
|
"rimraf": "^5.0.0",
|
|
23
23
|
"vitest": "^4.0.18",
|
|
24
|
-
"@memberjunction/ng-test-utils": "6.2.0-edge.
|
|
24
|
+
"@memberjunction/ng-test-utils": "6.2.0-edge.2"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"@angular/common": "^21.2.22",
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
"rxjs": "^7.8.2"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@memberjunction/core": "6.2.0-edge.
|
|
34
|
-
"@memberjunction/core-entities": "6.2.0-edge.
|
|
35
|
-
"@memberjunction/global": "6.2.0-edge.
|
|
36
|
-
"@memberjunction/graphql-dataprovider": "6.2.0-edge.
|
|
37
|
-
"@memberjunction/ng-base-types": "6.2.0-edge.
|
|
38
|
-
"@memberjunction/ng-ui-components": "6.2.0-edge.
|
|
39
|
-
"@memberjunction/record-set-processor-base": "6.2.0-edge.
|
|
33
|
+
"@memberjunction/core": "6.2.0-edge.2",
|
|
34
|
+
"@memberjunction/core-entities": "6.2.0-edge.2",
|
|
35
|
+
"@memberjunction/global": "6.2.0-edge.2",
|
|
36
|
+
"@memberjunction/graphql-dataprovider": "6.2.0-edge.2",
|
|
37
|
+
"@memberjunction/ng-base-types": "6.2.0-edge.2",
|
|
38
|
+
"@memberjunction/ng-ui-components": "6.2.0-edge.2",
|
|
39
|
+
"@memberjunction/record-set-processor-base": "6.2.0-edge.2",
|
|
40
40
|
"tslib": "^2.8.1"
|
|
41
41
|
},
|
|
42
42
|
"sideEffects": false,
|