@memberjunction/ng-entity-action-ux 0.0.1 → 5.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +175 -43
- package/dist/lib/ai-prompt-selector/ai-prompt-selector.component.d.ts +41 -0
- package/dist/lib/ai-prompt-selector/ai-prompt-selector.component.d.ts.map +1 -0
- package/dist/lib/ai-prompt-selector/ai-prompt-selector.component.js +231 -0
- package/dist/lib/ai-prompt-selector/ai-prompt-selector.component.js.map +1 -0
- package/dist/lib/base-entity-action-runtime-ux.d.ts +31 -0
- package/dist/lib/base-entity-action-runtime-ux.d.ts.map +1 -0
- package/dist/lib/base-entity-action-runtime-ux.js +34 -0
- package/dist/lib/base-entity-action-runtime-ux.js.map +1 -0
- package/dist/lib/entity-action-ux-host.component.d.ts +32 -0
- package/dist/lib/entity-action-ux-host.component.d.ts.map +1 -0
- package/dist/lib/entity-action-ux-host.component.js +89 -0
- package/dist/lib/entity-action-ux-host.component.js.map +1 -0
- package/dist/lib/field-rules-builder/field-rules-builder.component.d.ts +62 -0
- package/dist/lib/field-rules-builder/field-rules-builder.component.d.ts.map +1 -0
- package/dist/lib/field-rules-builder/field-rules-builder.component.js +411 -0
- package/dist/lib/field-rules-builder/field-rules-builder.component.js.map +1 -0
- package/dist/lib/field-rules-model.d.ts +32 -0
- package/dist/lib/field-rules-model.d.ts.map +1 -0
- package/dist/lib/field-rules-model.js +76 -0
- package/dist/lib/field-rules-model.js.map +1 -0
- package/dist/lib/prompt-grouping.d.ts +20 -0
- package/dist/lib/prompt-grouping.d.ts.map +1 -0
- package/dist/lib/prompt-grouping.js +30 -0
- package/dist/lib/prompt-grouping.js.map +1 -0
- package/dist/lib/record-process-runner/record-process-runner-ux.component.d.ts +76 -0
- package/dist/lib/record-process-runner/record-process-runner-ux.component.d.ts.map +1 -0
- package/dist/lib/record-process-runner/record-process-runner-ux.component.js +511 -0
- package/dist/lib/record-process-runner/record-process-runner-ux.component.js.map +1 -0
- package/dist/lib/runtime-ux-context.d.ts +52 -0
- package/dist/lib/runtime-ux-context.d.ts.map +1 -0
- package/dist/lib/runtime-ux-context.js +2 -0
- package/dist/lib/runtime-ux-context.js.map +1 -0
- package/dist/lib/scope.d.ts +12 -0
- package/dist/lib/scope.d.ts.map +1 -0
- package/dist/lib/scope.js +25 -0
- package/dist/lib/scope.js.map +1 -0
- package/dist/public-api.d.ts +15 -0
- package/dist/public-api.d.ts.map +1 -0
- package/dist/public-api.js +27 -0
- package/dist/public-api.js.map +1 -0
- package/package.json +47 -6
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The host that turns a registered driver key into a live, context-fed component. A grid /
|
|
3
|
+
* list / form drops `<mj-entity-action-ux-host>` into its template, binds the entity action's
|
|
4
|
+
* `RuntimeUXDriverClass` + an {@link EntityActionUXContext}, and forwards the driver's outcome. The host
|
|
5
|
+
* owns the ClassFactory resolution + dynamic mount so callers stay declarative.
|
|
6
|
+
* @module @memberjunction/ng-entity-action-ux
|
|
7
|
+
*/
|
|
8
|
+
import { AfterViewInit, EventEmitter, OnDestroy } from '@angular/core';
|
|
9
|
+
import type { EntityActionUXContext, EntityActionUXResult } from './runtime-ux-context';
|
|
10
|
+
import * as i0 from "@angular/core";
|
|
11
|
+
export declare class EntityActionUXHostComponent implements AfterViewInit, OnDestroy {
|
|
12
|
+
private cdr;
|
|
13
|
+
/** Registration key of the driver to mount (the entity action's `RuntimeUXDriverClass`). */
|
|
14
|
+
DriverClass: string;
|
|
15
|
+
/** The work context handed to the driver. */
|
|
16
|
+
Context: EntityActionUXContext;
|
|
17
|
+
/** Forwarded from the mounted driver when it finishes. */
|
|
18
|
+
Completed: EventEmitter<EntityActionUXResult>;
|
|
19
|
+
/** Forwarded from the mounted driver when the user dismisses it. */
|
|
20
|
+
Cancelled: EventEmitter<void>;
|
|
21
|
+
/** Emitted when the named driver can't be resolved (misconfiguration), so the host can fall back. */
|
|
22
|
+
DriverNotFound: EventEmitter<string>;
|
|
23
|
+
private container;
|
|
24
|
+
private subs;
|
|
25
|
+
ngAfterViewInit(): void;
|
|
26
|
+
/** Resolves the driver class via the ClassFactory and mounts it with the context. */
|
|
27
|
+
private mount;
|
|
28
|
+
ngOnDestroy(): void;
|
|
29
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<EntityActionUXHostComponent, never>;
|
|
30
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<EntityActionUXHostComponent, "mj-entity-action-ux-host", never, { "DriverClass": { "alias": "DriverClass"; "required": false; }; "Context": { "alias": "Context"; "required": false; }; }, { "Completed": "Completed"; "Cancelled": "Cancelled"; "DriverNotFound": "DriverNotFound"; }, never, never, true, never>;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=entity-action-ux-host.component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-action-ux-host.component.d.ts","sourceRoot":"","sources":["../../src/lib/entity-action-ux-host.component.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACH,aAAa,EAIb,YAAY,EAEZ,SAAS,EAMZ,MAAM,eAAe,CAAC;AAIvB,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;;AAExF,qBAMa,2BAA4B,YAAW,aAAa,EAAE,SAAS;IACxE,OAAO,CAAC,GAAG,CAA6B;IAExC,4FAA4F;IACnF,WAAW,EAAG,MAAM,CAAC;IAE9B,6CAA6C;IACpC,OAAO,EAAG,qBAAqB,CAAC;IAEzC,0DAA0D;IAChD,SAAS,qCAA4C;IAE/D,oEAAoE;IAC1D,SAAS,qBAA4B;IAE/C,qGAAqG;IAC3F,cAAc,uBAA8B;IAEF,OAAO,CAAC,SAAS,CAAoB;IAEzF,OAAO,CAAC,IAAI,CAAsB;IAElC,eAAe,IAAI,IAAI;IAKvB,qFAAqF;IACrF,OAAO,CAAC,KAAK;IAsBb,WAAW,IAAI,IAAI;yCAlDV,2BAA2B;2CAA3B,2BAA2B;CAsDvC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The host that turns a registered driver key into a live, context-fed component. A grid /
|
|
3
|
+
* list / form drops `<mj-entity-action-ux-host>` into its template, binds the entity action's
|
|
4
|
+
* `RuntimeUXDriverClass` + an {@link EntityActionUXContext}, and forwards the driver's outcome. The host
|
|
5
|
+
* owns the ClassFactory resolution + dynamic mount so callers stay declarative.
|
|
6
|
+
* @module @memberjunction/ng-entity-action-ux
|
|
7
|
+
*/
|
|
8
|
+
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, ViewChild, ViewContainerRef, inject, } from '@angular/core';
|
|
9
|
+
import { MJGlobal } from '@memberjunction/global';
|
|
10
|
+
import { BaseEntityActionRuntimeUX } from './base-entity-action-runtime-ux';
|
|
11
|
+
import * as i0 from "@angular/core";
|
|
12
|
+
const _c0 = ["container"];
|
|
13
|
+
function EntityActionUXHostComponent_ng_template_0_Template(rf, ctx) { }
|
|
14
|
+
export class EntityActionUXHostComponent {
|
|
15
|
+
cdr = inject(ChangeDetectorRef);
|
|
16
|
+
/** Registration key of the driver to mount (the entity action's `RuntimeUXDriverClass`). */
|
|
17
|
+
DriverClass;
|
|
18
|
+
/** The work context handed to the driver. */
|
|
19
|
+
Context;
|
|
20
|
+
/** Forwarded from the mounted driver when it finishes. */
|
|
21
|
+
Completed = new EventEmitter();
|
|
22
|
+
/** Forwarded from the mounted driver when the user dismisses it. */
|
|
23
|
+
Cancelled = new EventEmitter();
|
|
24
|
+
/** Emitted when the named driver can't be resolved (misconfiguration), so the host can fall back. */
|
|
25
|
+
DriverNotFound = new EventEmitter();
|
|
26
|
+
container;
|
|
27
|
+
subs = [];
|
|
28
|
+
ngAfterViewInit() {
|
|
29
|
+
// Defer one microtask so the view is settled before we create the dynamic component.
|
|
30
|
+
Promise.resolve().then(() => this.mount());
|
|
31
|
+
}
|
|
32
|
+
/** Resolves the driver class via the ClassFactory and mounts it with the context. */
|
|
33
|
+
mount() {
|
|
34
|
+
if (!this.DriverClass) {
|
|
35
|
+
this.DriverNotFound.emit('(empty)');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const registration = MJGlobal.Instance.ClassFactory.GetRegistration(BaseEntityActionRuntimeUX, this.DriverClass);
|
|
39
|
+
if (!registration) {
|
|
40
|
+
this.DriverNotFound.emit(this.DriverClass);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
this.container.clear();
|
|
44
|
+
const ref = this.container.createComponent(registration.SubClass);
|
|
45
|
+
const driver = ref.instance;
|
|
46
|
+
driver.Context = this.Context;
|
|
47
|
+
this.subs.push(driver.Completed.subscribe((r) => this.Completed.emit(r)));
|
|
48
|
+
this.subs.push(driver.Cancelled.subscribe(() => this.Cancelled.emit()));
|
|
49
|
+
void driver.Start();
|
|
50
|
+
this.cdr.detectChanges();
|
|
51
|
+
}
|
|
52
|
+
ngOnDestroy() {
|
|
53
|
+
this.subs.forEach((s) => s.unsubscribe());
|
|
54
|
+
this.container?.clear();
|
|
55
|
+
}
|
|
56
|
+
static ɵfac = function EntityActionUXHostComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || EntityActionUXHostComponent)(); };
|
|
57
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: EntityActionUXHostComponent, selectors: [["mj-entity-action-ux-host"]], viewQuery: function EntityActionUXHostComponent_Query(rf, ctx) { if (rf & 1) {
|
|
58
|
+
i0.ɵɵviewQuery(_c0, 5, ViewContainerRef);
|
|
59
|
+
} if (rf & 2) {
|
|
60
|
+
let _t;
|
|
61
|
+
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.container = _t.first);
|
|
62
|
+
} }, inputs: { DriverClass: "DriverClass", Context: "Context" }, outputs: { Completed: "Completed", Cancelled: "Cancelled", DriverNotFound: "DriverNotFound" }, decls: 2, vars: 0, consts: [["container", ""]], template: function EntityActionUXHostComponent_Template(rf, ctx) { if (rf & 1) {
|
|
63
|
+
i0.ɵɵdomTemplate(0, EntityActionUXHostComponent_ng_template_0_Template, 0, 0, "ng-template", null, 0, i0.ɵɵtemplateRefExtractor);
|
|
64
|
+
} }, encapsulation: 2, changeDetection: 0 });
|
|
65
|
+
}
|
|
66
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(EntityActionUXHostComponent, [{
|
|
67
|
+
type: Component,
|
|
68
|
+
args: [{
|
|
69
|
+
selector: 'mj-entity-action-ux-host',
|
|
70
|
+
standalone: true,
|
|
71
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
72
|
+
template: `<ng-template #container></ng-template>`,
|
|
73
|
+
}]
|
|
74
|
+
}], null, { DriverClass: [{
|
|
75
|
+
type: Input
|
|
76
|
+
}], Context: [{
|
|
77
|
+
type: Input
|
|
78
|
+
}], Completed: [{
|
|
79
|
+
type: Output
|
|
80
|
+
}], Cancelled: [{
|
|
81
|
+
type: Output
|
|
82
|
+
}], DriverNotFound: [{
|
|
83
|
+
type: Output
|
|
84
|
+
}], container: [{
|
|
85
|
+
type: ViewChild,
|
|
86
|
+
args: ['container', { read: ViewContainerRef }]
|
|
87
|
+
}] }); })();
|
|
88
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EntityActionUXHostComponent, { className: "EntityActionUXHostComponent", filePath: "lib/entity-action-ux-host.component.ts", lineNumber: 33 }); })();
|
|
89
|
+
//# sourceMappingURL=entity-action-ux-host.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-action-ux-host.component.js","sourceRoot":"","sources":["../../src/lib/entity-action-ux-host.component.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAEH,uBAAuB,EACvB,iBAAiB,EACjB,SAAS,EACT,YAAY,EACZ,KAAK,EAEL,MAAM,EAEN,SAAS,EACT,gBAAgB,EAChB,MAAM,GACT,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAElD,OAAO,EAAE,yBAAyB,EAAE,MAAM,iCAAiC,CAAC;;;;AAS5E,MAAM,OAAO,2BAA2B;IAC5B,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAExC,4FAA4F;IACnF,WAAW,CAAU;IAE9B,6CAA6C;IACpC,OAAO,CAAyB;IAEzC,0DAA0D;IAChD,SAAS,GAAG,IAAI,YAAY,EAAwB,CAAC;IAE/D,oEAAoE;IAC1D,SAAS,GAAG,IAAI,YAAY,EAAQ,CAAC;IAE/C,qGAAqG;IAC3F,cAAc,GAAG,IAAI,YAAY,EAAU,CAAC;IAEM,SAAS,CAAoB;IAEjF,IAAI,GAAmB,EAAE,CAAC;IAElC,eAAe;QACX,qFAAqF;QACrF,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,qFAAqF;IAC7E,KAAK;QACT,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACpB,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACpC,OAAO;QACX,CAAC;QACD,MAAM,YAAY,GAAG,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,eAAe,CAAC,yBAAyB,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACjH,IAAI,CAAC,YAAY,EAAE,CAAC;YAChB,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAC3C,OAAO;QACX,CAAC;QAED,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,YAAY,CAAC,QAA2C,CAAC,CAAC;QACrG,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC;QAC5B,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1E,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAExE,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;IAC7B,CAAC;IAED,WAAW;QACP,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAC1C,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC;IAC5B,CAAC;qHArDQ,2BAA2B;6DAA3B,2BAA2B;mCAkBJ,gBAAgB;;;;;YApBrC,gIAAwB;;;iFAE1B,2BAA2B;cANvC,SAAS;eAAC;gBACP,QAAQ,EAAE,0BAA0B;gBACpC,UAAU,EAAE,IAAI;gBAChB,eAAe,EAAE,uBAAuB,CAAC,MAAM;gBAC/C,QAAQ,EAAE,wCAAwC;aACrD;;kBAKI,KAAK;;kBAGL,KAAK;;kBAGL,MAAM;;kBAGN,MAAM;;kBAGN,MAAM;;kBAEN,SAAS;mBAAC,WAAW,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE;;kFAlBzC,2BAA2B","sourcesContent":["/**\n * @fileoverview The host that turns a registered driver key into a live, context-fed component. A grid /\n * list / form drops `<mj-entity-action-ux-host>` into its template, binds the entity action's\n * `RuntimeUXDriverClass` + an {@link EntityActionUXContext}, and forwards the driver's outcome. The host\n * owns the ClassFactory resolution + dynamic mount so callers stay declarative.\n * @module @memberjunction/ng-entity-action-ux\n */\nimport {\n AfterViewInit,\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n EventEmitter,\n Input,\n OnDestroy,\n Output,\n Type,\n ViewChild,\n ViewContainerRef,\n inject,\n} from '@angular/core';\nimport { MJGlobal } from '@memberjunction/global';\nimport { Subscription } from 'rxjs';\nimport { BaseEntityActionRuntimeUX } from './base-entity-action-runtime-ux';\nimport type { EntityActionUXContext, EntityActionUXResult } from './runtime-ux-context';\n\n@Component({\n selector: 'mj-entity-action-ux-host',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `<ng-template #container></ng-template>`,\n})\nexport class EntityActionUXHostComponent implements AfterViewInit, OnDestroy {\n private cdr = inject(ChangeDetectorRef);\n\n /** Registration key of the driver to mount (the entity action's `RuntimeUXDriverClass`). */\n @Input() DriverClass!: string;\n\n /** The work context handed to the driver. */\n @Input() Context!: EntityActionUXContext;\n\n /** Forwarded from the mounted driver when it finishes. */\n @Output() Completed = new EventEmitter<EntityActionUXResult>();\n\n /** Forwarded from the mounted driver when the user dismisses it. */\n @Output() Cancelled = new EventEmitter<void>();\n\n /** Emitted when the named driver can't be resolved (misconfiguration), so the host can fall back. */\n @Output() DriverNotFound = new EventEmitter<string>();\n\n @ViewChild('container', { read: ViewContainerRef }) private container!: ViewContainerRef;\n\n private subs: Subscription[] = [];\n\n ngAfterViewInit(): void {\n // Defer one microtask so the view is settled before we create the dynamic component.\n Promise.resolve().then(() => this.mount());\n }\n\n /** Resolves the driver class via the ClassFactory and mounts it with the context. */\n private mount(): void {\n if (!this.DriverClass) {\n this.DriverNotFound.emit('(empty)');\n return;\n }\n const registration = MJGlobal.Instance.ClassFactory.GetRegistration(BaseEntityActionRuntimeUX, this.DriverClass);\n if (!registration) {\n this.DriverNotFound.emit(this.DriverClass);\n return;\n }\n\n this.container.clear();\n const ref = this.container.createComponent(registration.SubClass as Type<BaseEntityActionRuntimeUX>);\n const driver = ref.instance;\n driver.Context = this.Context;\n this.subs.push(driver.Completed.subscribe((r) => this.Completed.emit(r)));\n this.subs.push(driver.Cancelled.subscribe(() => this.Cancelled.emit()));\n\n void driver.Start();\n this.cdr.detectChanges();\n }\n\n ngOnDestroy(): void {\n this.subs.forEach((s) => s.unsubscribe());\n this.container?.clear();\n }\n}\n"]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview A visual authoring surface for a {@link FieldRuleSet} — the rules that drive a FieldRules
|
|
3
|
+
* Record Process. Each rule sets a target field from a chosen source (static / another field / formula /
|
|
4
|
+
* entity lookup / AI prompt), optionally gated by a condition. Validates live against the entity's metadata
|
|
5
|
+
* via `EntityFieldRules.Validate`, and emits the typed rule set (the consumer serializes it into the
|
|
6
|
+
* Record Process `Configuration`).
|
|
7
|
+
* @module @memberjunction/ng-entity-action-ux
|
|
8
|
+
*/
|
|
9
|
+
import { EventEmitter } from '@angular/core';
|
|
10
|
+
import { type IMetadataProvider } from '@memberjunction/core';
|
|
11
|
+
import type { FieldRuleSet } from '@memberjunction/global';
|
|
12
|
+
import { type RuleDraft } from '../field-rules-model';
|
|
13
|
+
import * as i0 from "@angular/core";
|
|
14
|
+
export declare class FieldRulesBuilderComponent {
|
|
15
|
+
private cdr;
|
|
16
|
+
Rules: RuleDraft[];
|
|
17
|
+
Errors: string[];
|
|
18
|
+
WritableFields: Array<{
|
|
19
|
+
Name: string;
|
|
20
|
+
DisplayNameOrName: string;
|
|
21
|
+
}>;
|
|
22
|
+
AllFields: Array<{
|
|
23
|
+
Name: string;
|
|
24
|
+
DisplayNameOrName: string;
|
|
25
|
+
}>;
|
|
26
|
+
private _entityName;
|
|
27
|
+
private entity?;
|
|
28
|
+
/** Metadata provider (multi-provider). Falls back to the global default when null. */
|
|
29
|
+
Provider: IMetadataProvider | null;
|
|
30
|
+
/** The entity whose fields the rules target. Loads the field lists when set. */
|
|
31
|
+
set EntityName(value: string);
|
|
32
|
+
get EntityName(): string;
|
|
33
|
+
/**
|
|
34
|
+
* Serialized form of our last emitted rule set. Consumers typically feed our `ValueChange` output
|
|
35
|
+
* straight back in as `[Value]` (controlled-component style). Re-seeding the drafts from that echo
|
|
36
|
+
* would wipe in-progress edits — most visibly a freshly-added **blank** rule, which serializes to
|
|
37
|
+
* nothing (`draftsToRuleSet` drops rules with no target), so the echo arrives as `{Rules:[]}` and
|
|
38
|
+
* resets the row the user just added. We ignore the echo and only re-seed on a genuinely different
|
|
39
|
+
* (external) value.
|
|
40
|
+
*/
|
|
41
|
+
private lastEmittedJSON;
|
|
42
|
+
/** The initial rule set (e.g. an existing Record Process Configuration). */
|
|
43
|
+
set Value(value: FieldRuleSet | null | undefined);
|
|
44
|
+
/** Emits the typed rule set on every edit; the consumer serializes it into the Configuration. */
|
|
45
|
+
ValueChange: EventEmitter<FieldRuleSet>;
|
|
46
|
+
/** Emits the live validation result (true when every rule is valid against the entity). */
|
|
47
|
+
ValidChange: EventEmitter<boolean>;
|
|
48
|
+
get EntityLabel(): string;
|
|
49
|
+
AddRule(): void;
|
|
50
|
+
RemoveRule(index: number): void;
|
|
51
|
+
SetTarget(index: number, event: Event): void;
|
|
52
|
+
SetKind(index: number, event: Event): void;
|
|
53
|
+
SetField(index: number, field: keyof RuleDraft, event: Event): void;
|
|
54
|
+
SetPrompt(index: number, promptID: string | null): void;
|
|
55
|
+
/** Serializes the drafts, validates, and emits. */
|
|
56
|
+
private emit;
|
|
57
|
+
private validate;
|
|
58
|
+
private loadEntity;
|
|
59
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FieldRulesBuilderComponent, never>;
|
|
60
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FieldRulesBuilderComponent, "mj-field-rules-builder", never, { "Provider": { "alias": "Provider"; "required": false; }; "EntityName": { "alias": "EntityName"; "required": false; }; "Value": { "alias": "Value"; "required": false; }; }, { "ValueChange": "ValueChange"; "ValidChange": "ValidChange"; }, never, never, true, never>;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=field-rules-builder.component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-rules-builder.component.d.ts","sourceRoot":"","sources":["../../../src/lib/field-rules-builder/field-rules-builder.component.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAyD,YAAY,EAAyB,MAAM,eAAe,CAAC;AAC3H,OAAO,EAA+C,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC3G,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAwE,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;;AAE5H,qBA+Ga,0BAA0B;IACnC,OAAO,CAAC,GAAG,CAA6B;IAEjC,KAAK,EAAE,SAAS,EAAE,CAAM;IACxB,MAAM,EAAE,MAAM,EAAE,CAAM;IACtB,cAAc,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE,CAAC,CAAM;IACxE,SAAS,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE,CAAC,CAAM;IAE1E,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,MAAM,CAAC,CAAa;IAE5B,sFAAsF;IAC7E,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAQ;IAEnD,gFAAgF;IAChF,IACI,UAAU,CAAC,KAAK,EAAE,MAAM,EAI3B;IACD,IAAI,UAAU,IAAI,MAAM,CAA6B;IAErD;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe,CAAuB;IAE9C,4EAA4E;IAC5E,IACI,KAAK,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,EAQ/C;IAED,iGAAiG;IACvF,WAAW,6BAAoC;IAEzD,2FAA2F;IACjF,WAAW,wBAA+B;IAEpD,IAAI,WAAW,IAAI,MAAM,CAAsE;IAE/F,OAAO,IAAI,IAAI;IAKf,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK/B,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAK5C,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAK1C,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAKnE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAKvD,mDAAmD;IACnD,OAAO,CAAC,IAAI;IAUZ,OAAO,CAAC,QAAQ;IAWhB,OAAO,CAAC,UAAU;yCAzGT,0BAA0B;2CAA1B,0BAA0B;CAgHtC"}
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview A visual authoring surface for a {@link FieldRuleSet} — the rules that drive a FieldRules
|
|
3
|
+
* Record Process. Each rule sets a target field from a chosen source (static / another field / formula /
|
|
4
|
+
* entity lookup / AI prompt), optionally gated by a condition. Validates live against the entity's metadata
|
|
5
|
+
* via `EntityFieldRules.Validate`, and emits the typed rule set (the consumer serializes it into the
|
|
6
|
+
* Record Process `Configuration`).
|
|
7
|
+
* @module @memberjunction/ng-entity-action-ux
|
|
8
|
+
*/
|
|
9
|
+
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, inject } from '@angular/core';
|
|
10
|
+
import { EntityFieldRules, Metadata } from '@memberjunction/core';
|
|
11
|
+
import { AIPromptSelectorComponent } from '../ai-prompt-selector/ai-prompt-selector.component';
|
|
12
|
+
import { blankRuleDraft, draftsToRuleSet, ruleToDraft } from '../field-rules-model';
|
|
13
|
+
import * as i0 from "@angular/core";
|
|
14
|
+
const _forTrack0 = ($index, $item) => $item.Name;
|
|
15
|
+
function FieldRulesBuilderComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
|
|
16
|
+
i0.ɵɵelementStart(0, "div", 5);
|
|
17
|
+
i0.ɵɵtext(1, "No rules yet. Click ");
|
|
18
|
+
i0.ɵɵelementStart(2, "strong");
|
|
19
|
+
i0.ɵɵtext(3, "Add rule");
|
|
20
|
+
i0.ɵɵelementEnd();
|
|
21
|
+
i0.ɵɵtext(4, " to start.");
|
|
22
|
+
i0.ɵɵelementEnd();
|
|
23
|
+
} }
|
|
24
|
+
function FieldRulesBuilderComponent_For_12_For_10_Template(rf, ctx) { if (rf & 1) {
|
|
25
|
+
i0.ɵɵelementStart(0, "option", 13);
|
|
26
|
+
i0.ɵɵtext(1);
|
|
27
|
+
i0.ɵɵelementEnd();
|
|
28
|
+
} if (rf & 2) {
|
|
29
|
+
const f_r4 = ctx.$implicit;
|
|
30
|
+
i0.ɵɵproperty("value", f_r4.Name);
|
|
31
|
+
i0.ɵɵadvance();
|
|
32
|
+
i0.ɵɵtextInterpolate(f_r4.DisplayNameOrName);
|
|
33
|
+
} }
|
|
34
|
+
function FieldRulesBuilderComponent_For_12_Case_27_Template(rf, ctx) { if (rf & 1) {
|
|
35
|
+
const _r5 = i0.ɵɵgetCurrentView();
|
|
36
|
+
i0.ɵɵelementStart(0, "input", 31);
|
|
37
|
+
i0.ɵɵlistener("input", function FieldRulesBuilderComponent_For_12_Case_27_Template_input_input_0_listener($event) { i0.ɵɵrestoreView(_r5); const $index_r2 = i0.ɵɵnextContext().$index; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.SetField($index_r2, "StaticValue", $event)); });
|
|
38
|
+
i0.ɵɵelementEnd();
|
|
39
|
+
} if (rf & 2) {
|
|
40
|
+
const rule_r6 = i0.ɵɵnextContext().$implicit;
|
|
41
|
+
i0.ɵɵproperty("value", rule_r6.StaticValue);
|
|
42
|
+
} }
|
|
43
|
+
function FieldRulesBuilderComponent_For_12_Case_28_For_4_Template(rf, ctx) { if (rf & 1) {
|
|
44
|
+
i0.ɵɵelementStart(0, "option", 13);
|
|
45
|
+
i0.ɵɵtext(1);
|
|
46
|
+
i0.ɵɵelementEnd();
|
|
47
|
+
} if (rf & 2) {
|
|
48
|
+
const f_r8 = ctx.$implicit;
|
|
49
|
+
i0.ɵɵproperty("value", f_r8.Name);
|
|
50
|
+
i0.ɵɵadvance();
|
|
51
|
+
i0.ɵɵtextInterpolate(f_r8.DisplayNameOrName);
|
|
52
|
+
} }
|
|
53
|
+
function FieldRulesBuilderComponent_For_12_Case_28_Template(rf, ctx) { if (rf & 1) {
|
|
54
|
+
const _r7 = i0.ɵɵgetCurrentView();
|
|
55
|
+
i0.ɵɵelementStart(0, "select", 32);
|
|
56
|
+
i0.ɵɵlistener("change", function FieldRulesBuilderComponent_For_12_Case_28_Template_select_change_0_listener($event) { i0.ɵɵrestoreView(_r7); const $index_r2 = i0.ɵɵnextContext().$index; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.SetField($index_r2, "SourceField", $event)); });
|
|
57
|
+
i0.ɵɵelementStart(1, "option", 12);
|
|
58
|
+
i0.ɵɵtext(2, "\u2014 source field \u2014");
|
|
59
|
+
i0.ɵɵelementEnd();
|
|
60
|
+
i0.ɵɵrepeaterCreate(3, FieldRulesBuilderComponent_For_12_Case_28_For_4_Template, 2, 2, "option", 13, _forTrack0);
|
|
61
|
+
i0.ɵɵelementEnd();
|
|
62
|
+
} if (rf & 2) {
|
|
63
|
+
const rule_r6 = i0.ɵɵnextContext().$implicit;
|
|
64
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
65
|
+
i0.ɵɵproperty("value", rule_r6.SourceField);
|
|
66
|
+
i0.ɵɵadvance(3);
|
|
67
|
+
i0.ɵɵrepeater(ctx_r2.AllFields);
|
|
68
|
+
} }
|
|
69
|
+
function FieldRulesBuilderComponent_For_12_Case_29_Template(rf, ctx) { if (rf & 1) {
|
|
70
|
+
const _r9 = i0.ɵɵgetCurrentView();
|
|
71
|
+
i0.ɵɵelementStart(0, "input", 33);
|
|
72
|
+
i0.ɵɵlistener("input", function FieldRulesBuilderComponent_For_12_Case_29_Template_input_input_0_listener($event) { i0.ɵɵrestoreView(_r9); const $index_r2 = i0.ɵɵnextContext().$index; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.SetField($index_r2, "Formula", $event)); });
|
|
73
|
+
i0.ɵɵelementEnd();
|
|
74
|
+
} if (rf & 2) {
|
|
75
|
+
const rule_r6 = i0.ɵɵnextContext().$implicit;
|
|
76
|
+
i0.ɵɵproperty("value", rule_r6.Formula);
|
|
77
|
+
} }
|
|
78
|
+
function FieldRulesBuilderComponent_For_12_Case_30_Template(rf, ctx) { if (rf & 1) {
|
|
79
|
+
const _r10 = i0.ɵɵgetCurrentView();
|
|
80
|
+
i0.ɵɵelementStart(0, "div", 26)(1, "input", 34);
|
|
81
|
+
i0.ɵɵlistener("input", function FieldRulesBuilderComponent_For_12_Case_30_Template_input_input_1_listener($event) { i0.ɵɵrestoreView(_r10); const $index_r2 = i0.ɵɵnextContext().$index; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.SetField($index_r2, "LookupEntity", $event)); });
|
|
82
|
+
i0.ɵɵelementEnd();
|
|
83
|
+
i0.ɵɵelementStart(2, "input", 35);
|
|
84
|
+
i0.ɵɵlistener("input", function FieldRulesBuilderComponent_For_12_Case_30_Template_input_input_2_listener($event) { i0.ɵɵrestoreView(_r10); const $index_r2 = i0.ɵɵnextContext().$index; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.SetField($index_r2, "LookupMatchField", $event)); });
|
|
85
|
+
i0.ɵɵelementEnd();
|
|
86
|
+
i0.ɵɵelementStart(3, "input", 36);
|
|
87
|
+
i0.ɵɵlistener("input", function FieldRulesBuilderComponent_For_12_Case_30_Template_input_input_3_listener($event) { i0.ɵɵrestoreView(_r10); const $index_r2 = i0.ɵɵnextContext().$index; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.SetField($index_r2, "LookupMatchValueField", $event)); });
|
|
88
|
+
i0.ɵɵelementEnd();
|
|
89
|
+
i0.ɵɵelementStart(4, "input", 37);
|
|
90
|
+
i0.ɵɵlistener("input", function FieldRulesBuilderComponent_For_12_Case_30_Template_input_input_4_listener($event) { i0.ɵɵrestoreView(_r10); const $index_r2 = i0.ɵɵnextContext().$index; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.SetField($index_r2, "LookupReturnField", $event)); });
|
|
91
|
+
i0.ɵɵelementEnd()();
|
|
92
|
+
} if (rf & 2) {
|
|
93
|
+
const rule_r6 = i0.ɵɵnextContext().$implicit;
|
|
94
|
+
i0.ɵɵadvance();
|
|
95
|
+
i0.ɵɵproperty("value", rule_r6.LookupEntity);
|
|
96
|
+
i0.ɵɵadvance();
|
|
97
|
+
i0.ɵɵproperty("value", rule_r6.LookupMatchField);
|
|
98
|
+
i0.ɵɵadvance();
|
|
99
|
+
i0.ɵɵproperty("value", rule_r6.LookupMatchValueField);
|
|
100
|
+
i0.ɵɵadvance();
|
|
101
|
+
i0.ɵɵproperty("value", rule_r6.LookupReturnField);
|
|
102
|
+
} }
|
|
103
|
+
function FieldRulesBuilderComponent_For_12_Case_31_Template(rf, ctx) { if (rf & 1) {
|
|
104
|
+
const _r11 = i0.ɵɵgetCurrentView();
|
|
105
|
+
i0.ɵɵelementStart(0, "mj-ai-prompt-selector", 38);
|
|
106
|
+
i0.ɵɵlistener("ValueChange", function FieldRulesBuilderComponent_For_12_Case_31_Template_mj_ai_prompt_selector_ValueChange_0_listener($event) { i0.ɵɵrestoreView(_r11); const $index_r2 = i0.ɵɵnextContext().$index; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.SetPrompt($index_r2, $event)); });
|
|
107
|
+
i0.ɵɵelementEnd();
|
|
108
|
+
} if (rf & 2) {
|
|
109
|
+
const rule_r6 = i0.ɵɵnextContext().$implicit;
|
|
110
|
+
i0.ɵɵproperty("Value", rule_r6.PromptID);
|
|
111
|
+
} }
|
|
112
|
+
function FieldRulesBuilderComponent_For_12_Template(rf, ctx) { if (rf & 1) {
|
|
113
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
114
|
+
i0.ɵɵelementStart(0, "div", 6)(1, "div", 8)(2, "span", 9);
|
|
115
|
+
i0.ɵɵtext(3);
|
|
116
|
+
i0.ɵɵelementEnd();
|
|
117
|
+
i0.ɵɵelementStart(4, "span", 10);
|
|
118
|
+
i0.ɵɵtext(5, "Set");
|
|
119
|
+
i0.ɵɵelementEnd();
|
|
120
|
+
i0.ɵɵelementStart(6, "select", 11);
|
|
121
|
+
i0.ɵɵlistener("change", function FieldRulesBuilderComponent_For_12_Template_select_change_6_listener($event) { const $index_r2 = i0.ɵɵrestoreView(_r1).$index; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.SetTarget($index_r2, $event)); });
|
|
122
|
+
i0.ɵɵelementStart(7, "option", 12);
|
|
123
|
+
i0.ɵɵtext(8, "\u2014 field \u2014");
|
|
124
|
+
i0.ɵɵelementEnd();
|
|
125
|
+
i0.ɵɵrepeaterCreate(9, FieldRulesBuilderComponent_For_12_For_10_Template, 2, 2, "option", 13, _forTrack0);
|
|
126
|
+
i0.ɵɵelementEnd();
|
|
127
|
+
i0.ɵɵelementStart(11, "span", 10);
|
|
128
|
+
i0.ɵɵtext(12, "from");
|
|
129
|
+
i0.ɵɵelementEnd();
|
|
130
|
+
i0.ɵɵelementStart(13, "select", 14);
|
|
131
|
+
i0.ɵɵlistener("change", function FieldRulesBuilderComponent_For_12_Template_select_change_13_listener($event) { const $index_r2 = i0.ɵɵrestoreView(_r1).$index; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.SetKind($index_r2, $event)); });
|
|
132
|
+
i0.ɵɵelementStart(14, "option", 15);
|
|
133
|
+
i0.ɵɵtext(15, "a fixed value");
|
|
134
|
+
i0.ɵɵelementEnd();
|
|
135
|
+
i0.ɵɵelementStart(16, "option", 16);
|
|
136
|
+
i0.ɵɵtext(17, "another field");
|
|
137
|
+
i0.ɵɵelementEnd();
|
|
138
|
+
i0.ɵɵelementStart(18, "option", 17);
|
|
139
|
+
i0.ɵɵtext(19, "a formula");
|
|
140
|
+
i0.ɵɵelementEnd();
|
|
141
|
+
i0.ɵɵelementStart(20, "option", 18);
|
|
142
|
+
i0.ɵɵtext(21, "a lookup");
|
|
143
|
+
i0.ɵɵelementEnd();
|
|
144
|
+
i0.ɵɵelementStart(22, "option", 19);
|
|
145
|
+
i0.ɵɵtext(23, "an AI prompt");
|
|
146
|
+
i0.ɵɵelementEnd()();
|
|
147
|
+
i0.ɵɵelementStart(24, "button", 20);
|
|
148
|
+
i0.ɵɵlistener("click", function FieldRulesBuilderComponent_For_12_Template_button_click_24_listener() { const $index_r2 = i0.ɵɵrestoreView(_r1).$index; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.RemoveRule($index_r2)); });
|
|
149
|
+
i0.ɵɵelement(25, "i", 21);
|
|
150
|
+
i0.ɵɵelementEnd()();
|
|
151
|
+
i0.ɵɵelementStart(26, "div", 22);
|
|
152
|
+
i0.ɵɵconditionalCreate(27, FieldRulesBuilderComponent_For_12_Case_27_Template, 1, 1, "input", 23)(28, FieldRulesBuilderComponent_For_12_Case_28_Template, 5, 1, "select", 24)(29, FieldRulesBuilderComponent_For_12_Case_29_Template, 1, 1, "input", 25)(30, FieldRulesBuilderComponent_For_12_Case_30_Template, 5, 4, "div", 26)(31, FieldRulesBuilderComponent_For_12_Case_31_Template, 1, 1, "mj-ai-prompt-selector", 27);
|
|
153
|
+
i0.ɵɵelementEnd();
|
|
154
|
+
i0.ɵɵelementStart(32, "div", 28)(33, "span", 29);
|
|
155
|
+
i0.ɵɵtext(34, "Only when");
|
|
156
|
+
i0.ɵɵelementEnd();
|
|
157
|
+
i0.ɵɵelementStart(35, "input", 30);
|
|
158
|
+
i0.ɵɵlistener("input", function FieldRulesBuilderComponent_For_12_Template_input_input_35_listener($event) { const $index_r2 = i0.ɵɵrestoreView(_r1).$index; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.SetField($index_r2, "Condition", $event)); });
|
|
159
|
+
i0.ɵɵelementEnd()()();
|
|
160
|
+
} if (rf & 2) {
|
|
161
|
+
let tmp_14_0;
|
|
162
|
+
const rule_r6 = ctx.$implicit;
|
|
163
|
+
const $index_r2 = ctx.$index;
|
|
164
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
165
|
+
i0.ɵɵadvance(3);
|
|
166
|
+
i0.ɵɵtextInterpolate($index_r2 + 1);
|
|
167
|
+
i0.ɵɵadvance(3);
|
|
168
|
+
i0.ɵɵproperty("value", rule_r6.TargetField);
|
|
169
|
+
i0.ɵɵadvance(3);
|
|
170
|
+
i0.ɵɵrepeater(ctx_r2.WritableFields);
|
|
171
|
+
i0.ɵɵadvance(4);
|
|
172
|
+
i0.ɵɵproperty("value", rule_r6.SourceKind);
|
|
173
|
+
i0.ɵɵadvance(14);
|
|
174
|
+
i0.ɵɵconditional((tmp_14_0 = rule_r6.SourceKind) === "static" ? 27 : tmp_14_0 === "field" ? 28 : tmp_14_0 === "formula" ? 29 : tmp_14_0 === "lookup" ? 30 : tmp_14_0 === "prompt" ? 31 : -1);
|
|
175
|
+
i0.ɵɵadvance(8);
|
|
176
|
+
i0.ɵɵproperty("value", rule_r6.Condition);
|
|
177
|
+
} }
|
|
178
|
+
function FieldRulesBuilderComponent_Conditional_13_For_4_Template(rf, ctx) { if (rf & 1) {
|
|
179
|
+
i0.ɵɵelementStart(0, "li");
|
|
180
|
+
i0.ɵɵtext(1);
|
|
181
|
+
i0.ɵɵelementEnd();
|
|
182
|
+
} if (rf & 2) {
|
|
183
|
+
const e_r12 = ctx.$implicit;
|
|
184
|
+
i0.ɵɵadvance();
|
|
185
|
+
i0.ɵɵtextInterpolate(e_r12);
|
|
186
|
+
} }
|
|
187
|
+
function FieldRulesBuilderComponent_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
188
|
+
i0.ɵɵelementStart(0, "div", 7);
|
|
189
|
+
i0.ɵɵelement(1, "i", 39);
|
|
190
|
+
i0.ɵɵelementStart(2, "ul");
|
|
191
|
+
i0.ɵɵrepeaterCreate(3, FieldRulesBuilderComponent_Conditional_13_For_4_Template, 2, 1, "li", null, i0.ɵɵrepeaterTrackByIdentity);
|
|
192
|
+
i0.ɵɵelementEnd()();
|
|
193
|
+
} if (rf & 2) {
|
|
194
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
195
|
+
i0.ɵɵadvance(3);
|
|
196
|
+
i0.ɵɵrepeater(ctx_r2.Errors);
|
|
197
|
+
} }
|
|
198
|
+
export class FieldRulesBuilderComponent {
|
|
199
|
+
cdr = inject(ChangeDetectorRef);
|
|
200
|
+
Rules = [];
|
|
201
|
+
Errors = [];
|
|
202
|
+
WritableFields = [];
|
|
203
|
+
AllFields = [];
|
|
204
|
+
_entityName = '';
|
|
205
|
+
entity;
|
|
206
|
+
/** Metadata provider (multi-provider). Falls back to the global default when null. */
|
|
207
|
+
Provider = null;
|
|
208
|
+
/** The entity whose fields the rules target. Loads the field lists when set. */
|
|
209
|
+
set EntityName(value) {
|
|
210
|
+
const prev = this._entityName;
|
|
211
|
+
this._entityName = value;
|
|
212
|
+
if (value && value !== prev)
|
|
213
|
+
this.loadEntity();
|
|
214
|
+
}
|
|
215
|
+
get EntityName() { return this._entityName; }
|
|
216
|
+
/**
|
|
217
|
+
* Serialized form of our last emitted rule set. Consumers typically feed our `ValueChange` output
|
|
218
|
+
* straight back in as `[Value]` (controlled-component style). Re-seeding the drafts from that echo
|
|
219
|
+
* would wipe in-progress edits — most visibly a freshly-added **blank** rule, which serializes to
|
|
220
|
+
* nothing (`draftsToRuleSet` drops rules with no target), so the echo arrives as `{Rules:[]}` and
|
|
221
|
+
* resets the row the user just added. We ignore the echo and only re-seed on a genuinely different
|
|
222
|
+
* (external) value.
|
|
223
|
+
*/
|
|
224
|
+
lastEmittedJSON = null;
|
|
225
|
+
/** The initial rule set (e.g. an existing Record Process Configuration). */
|
|
226
|
+
set Value(value) {
|
|
227
|
+
const incomingJSON = JSON.stringify(value?.Rules ?? []);
|
|
228
|
+
if (incomingJSON === this.lastEmittedJSON) {
|
|
229
|
+
return; // echo of our own emit — keep the live drafts intact
|
|
230
|
+
}
|
|
231
|
+
this.lastEmittedJSON = incomingJSON;
|
|
232
|
+
this.Rules = value?.Rules ? value.Rules.map((r) => ruleToDraft(r)) : [];
|
|
233
|
+
this.validate();
|
|
234
|
+
}
|
|
235
|
+
/** Emits the typed rule set on every edit; the consumer serializes it into the Configuration. */
|
|
236
|
+
ValueChange = new EventEmitter();
|
|
237
|
+
/** Emits the live validation result (true when every rule is valid against the entity). */
|
|
238
|
+
ValidChange = new EventEmitter();
|
|
239
|
+
get EntityLabel() { return this.entity?.DisplayName || this.entity?.Name || 'record'; }
|
|
240
|
+
AddRule() {
|
|
241
|
+
this.Rules = [...this.Rules, blankRuleDraft()];
|
|
242
|
+
this.emit();
|
|
243
|
+
}
|
|
244
|
+
RemoveRule(index) {
|
|
245
|
+
this.Rules = this.Rules.filter((_, i) => i !== index);
|
|
246
|
+
this.emit();
|
|
247
|
+
}
|
|
248
|
+
SetTarget(index, event) {
|
|
249
|
+
this.Rules[index].TargetField = event.target.value;
|
|
250
|
+
this.emit();
|
|
251
|
+
}
|
|
252
|
+
SetKind(index, event) {
|
|
253
|
+
this.Rules[index].SourceKind = event.target.value;
|
|
254
|
+
this.emit();
|
|
255
|
+
}
|
|
256
|
+
SetField(index, field, event) {
|
|
257
|
+
this.Rules[index][field] = event.target.value;
|
|
258
|
+
this.emit();
|
|
259
|
+
}
|
|
260
|
+
SetPrompt(index, promptID) {
|
|
261
|
+
this.Rules[index].PromptID = promptID;
|
|
262
|
+
this.emit();
|
|
263
|
+
}
|
|
264
|
+
/** Serializes the drafts, validates, and emits. */
|
|
265
|
+
emit() {
|
|
266
|
+
const ruleSet = draftsToRuleSet(this.Rules);
|
|
267
|
+
// Record what we're about to emit so the echo (consumer feeding it back via [Value]) is recognized
|
|
268
|
+
// and ignored — see lastEmittedJSON. Must mirror the Value setter's comparison basis (Rules array).
|
|
269
|
+
this.lastEmittedJSON = JSON.stringify(ruleSet.Rules ?? []);
|
|
270
|
+
this.validate(ruleSet);
|
|
271
|
+
this.ValueChange.emit(ruleSet);
|
|
272
|
+
this.cdr.detectChanges();
|
|
273
|
+
}
|
|
274
|
+
validate(ruleSet = draftsToRuleSet(this.Rules)) {
|
|
275
|
+
if (!this._entityName) {
|
|
276
|
+
this.Errors = [];
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
const result = EntityFieldRules.Validate(this._entityName, ruleSet, this.Provider ?? undefined);
|
|
280
|
+
this.Errors = result.Errors;
|
|
281
|
+
this.ValidChange.emit(result.Valid);
|
|
282
|
+
}
|
|
283
|
+
this.cdr.detectChanges();
|
|
284
|
+
}
|
|
285
|
+
loadEntity() {
|
|
286
|
+
this.entity = (this.Provider ?? Metadata.Provider)?.EntityByName(this._entityName);
|
|
287
|
+
const map = (f) => ({ Name: f.Name, DisplayNameOrName: f.DisplayName || f.Name });
|
|
288
|
+
this.AllFields = this.entity ? this.entity.Fields.map(map) : [];
|
|
289
|
+
this.WritableFields = this.entity ? this.entity.Fields.filter((f) => !f.ReadOnly && !f.IsPrimaryKey).map(map) : [];
|
|
290
|
+
this.validate();
|
|
291
|
+
}
|
|
292
|
+
static ɵfac = function FieldRulesBuilderComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FieldRulesBuilderComponent)(); };
|
|
293
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FieldRulesBuilderComponent, selectors: [["mj-field-rules-builder"]], inputs: { Provider: "Provider", EntityName: "EntityName", Value: "Value" }, outputs: { ValueChange: "ValueChange", ValidChange: "ValidChange" }, decls: 14, vars: 3, consts: [[1, "frb"], [1, "frb-head"], [1, "frb-sub"], ["type", "button", 1, "frb-add", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "frb-empty"], [1, "frb-rule"], [1, "frb-errors"], [1, "frb-rule-head"], [1, "frb-rule-num"], [1, "frb-set"], [1, "mj-input", "frb-target", 3, "change", "value"], ["value", ""], [3, "value"], [1, "mj-input", "frb-kind", 3, "change", "value"], ["value", "static"], ["value", "field"], ["value", "formula"], ["value", "lookup"], ["value", "prompt"], ["type", "button", "title", "Remove rule", 1, "frb-del", 3, "click"], [1, "fa-solid", "fa-trash"], [1, "frb-source"], ["placeholder", "Value", 1, "mj-input", 3, "value"], [1, "mj-input", 3, "value"], ["placeholder", "e.g. fields.FirstName + ' ' + fields.LastName", 1, "mj-input", 3, "value"], [1, "frb-lookup"], [3, "Value"], [1, "frb-cond"], [1, "frb-cond-label"], ["placeholder", "(always) \u2014 e.g. fields.Status === 'Active'", 1, "mj-input", 3, "input", "value"], ["placeholder", "Value", 1, "mj-input", 3, "input", "value"], [1, "mj-input", 3, "change", "value"], ["placeholder", "e.g. fields.FirstName + ' ' + fields.LastName", 1, "mj-input", 3, "input", "value"], ["placeholder", "Lookup entity", 1, "mj-input", 3, "input", "value"], ["placeholder", "Match field", 1, "mj-input", 3, "input", "value"], ["placeholder", "= this record's field", 1, "mj-input", 3, "input", "value"], ["placeholder", "Return field", 1, "mj-input", 3, "input", "value"], [3, "ValueChange", "Value"], [1, "fa-solid", "fa-circle-exclamation"]], template: function FieldRulesBuilderComponent_Template(rf, ctx) { if (rf & 1) {
|
|
294
|
+
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div")(3, "h3");
|
|
295
|
+
i0.ɵɵtext(4, "Field rules");
|
|
296
|
+
i0.ɵɵelementEnd();
|
|
297
|
+
i0.ɵɵelementStart(5, "p", 2);
|
|
298
|
+
i0.ɵɵtext(6);
|
|
299
|
+
i0.ɵɵelementEnd()();
|
|
300
|
+
i0.ɵɵelementStart(7, "button", 3);
|
|
301
|
+
i0.ɵɵlistener("click", function FieldRulesBuilderComponent_Template_button_click_7_listener() { return ctx.AddRule(); });
|
|
302
|
+
i0.ɵɵelement(8, "i", 4);
|
|
303
|
+
i0.ɵɵtext(9, " Add rule");
|
|
304
|
+
i0.ɵɵelementEnd()();
|
|
305
|
+
i0.ɵɵconditionalCreate(10, FieldRulesBuilderComponent_Conditional_10_Template, 5, 0, "div", 5);
|
|
306
|
+
i0.ɵɵrepeaterCreate(11, FieldRulesBuilderComponent_For_12_Template, 36, 5, "div", 6, i0.ɵɵrepeaterTrackByIndex);
|
|
307
|
+
i0.ɵɵconditionalCreate(13, FieldRulesBuilderComponent_Conditional_13_Template, 5, 0, "div", 7);
|
|
308
|
+
i0.ɵɵelementEnd();
|
|
309
|
+
} if (rf & 2) {
|
|
310
|
+
i0.ɵɵadvance(6);
|
|
311
|
+
i0.ɵɵtextInterpolate1("Each rule sets a field on every matching ", ctx.EntityLabel, ". Use the preview before applying.");
|
|
312
|
+
i0.ɵɵadvance(4);
|
|
313
|
+
i0.ɵɵconditional(ctx.Rules.length === 0 ? 10 : -1);
|
|
314
|
+
i0.ɵɵadvance();
|
|
315
|
+
i0.ɵɵrepeater(ctx.Rules);
|
|
316
|
+
i0.ɵɵadvance(2);
|
|
317
|
+
i0.ɵɵconditional(ctx.Errors.length > 0 ? 13 : -1);
|
|
318
|
+
} }, dependencies: [AIPromptSelectorComponent], styles: [".frb[_ngcontent-%COMP%] { color: var(--mj-text-primary); display: flex; flex-direction: column; gap: 12px; }\n .frb-head[_ngcontent-%COMP%] { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }\n .frb-head[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] { margin: 0; font-size: 16px; }\n .frb-sub[_ngcontent-%COMP%] { margin: 2px 0 0; font-size: 13px; color: var(--mj-text-muted); }\n .frb-add[_ngcontent-%COMP%] { display: flex; align-items: center; gap: 6px; padding: 7px 12px; border: 1px solid var(--mj-brand-primary);\n color: var(--mj-brand-primary); background: transparent; border-radius: var(--mj-radius-sm, 6px); cursor: pointer; font-weight: 600; }\n .frb-add[_ngcontent-%COMP%]:hover { background: color-mix(in srgb, var(--mj-brand-primary) 8%, transparent); }\n .frb-empty[_ngcontent-%COMP%] { padding: 20px; text-align: center; color: var(--mj-text-muted); border: 1px dashed var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px); }\n .frb-rule[_ngcontent-%COMP%] { border: 1px solid var(--mj-border-default); border-radius: var(--mj-radius-md, 8px); padding: 12px; background: var(--mj-bg-surface-card); }\n .frb-rule-head[_ngcontent-%COMP%] { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\n .frb-rule-num[_ngcontent-%COMP%] { width: 22px; height: 22px; display: inline-flex; align-items: center; justify-content: center;\n border-radius: 50%; background: var(--mj-brand-primary); color: var(--mj-text-inverse); font-size: 12px; font-weight: 700; }\n .frb-set[_ngcontent-%COMP%] { color: var(--mj-text-secondary); font-size: 14px; }\n .frb-target[_ngcontent-%COMP%], .frb-kind[_ngcontent-%COMP%] { min-width: 150px; }\n .frb-del[_ngcontent-%COMP%] { margin-left: auto; border: none; background: transparent; color: var(--mj-text-muted); cursor: pointer; padding: 4px 8px; }\n .frb-del[_ngcontent-%COMP%]:hover { color: var(--mj-status-error); }\n .frb-source[_ngcontent-%COMP%] { margin-top: 10px; }\n .frb-source[_ngcontent-%COMP%] .mj-input[_ngcontent-%COMP%] { width: 100%; }\n .frb-lookup[_ngcontent-%COMP%] { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 8px; }\n .frb-cond[_ngcontent-%COMP%] { display: flex; align-items: center; gap: 8px; margin-top: 10px; }\n .frb-cond-label[_ngcontent-%COMP%] { font-size: 13px; color: var(--mj-text-muted); white-space: nowrap; }\n .frb-cond[_ngcontent-%COMP%] .mj-input[_ngcontent-%COMP%] { flex: 1; }\n .frb-errors[_ngcontent-%COMP%] { display: flex; gap: 10px; padding: 10px 12px; background: var(--mj-status-error-bg); color: var(--mj-status-error-text);\n border-radius: var(--mj-radius-sm, 6px); font-size: 13px; }\n .frb-errors[_ngcontent-%COMP%] ul[_ngcontent-%COMP%] { margin: 0; padding-left: 18px; }"], changeDetection: 0 });
|
|
319
|
+
}
|
|
320
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FieldRulesBuilderComponent, [{
|
|
321
|
+
type: Component,
|
|
322
|
+
args: [{ selector: 'mj-field-rules-builder', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [AIPromptSelectorComponent], template: `
|
|
323
|
+
<div class="frb">
|
|
324
|
+
<div class="frb-head">
|
|
325
|
+
<div>
|
|
326
|
+
<h3>Field rules</h3>
|
|
327
|
+
<p class="frb-sub">Each rule sets a field on every matching {{ EntityLabel }}. Use the preview before applying.</p>
|
|
328
|
+
</div>
|
|
329
|
+
<button type="button" class="frb-add" (click)="AddRule()"><i class="fa-solid fa-plus"></i> Add rule</button>
|
|
330
|
+
</div>
|
|
331
|
+
|
|
332
|
+
@if (Rules.length === 0) {
|
|
333
|
+
<div class="frb-empty">No rules yet. Click <strong>Add rule</strong> to start.</div>
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
@for (rule of Rules; track $index) {
|
|
337
|
+
<div class="frb-rule">
|
|
338
|
+
<div class="frb-rule-head">
|
|
339
|
+
<span class="frb-rule-num">{{ $index + 1 }}</span>
|
|
340
|
+
<span class="frb-set">Set</span>
|
|
341
|
+
<select class="mj-input frb-target" [value]="rule.TargetField" (change)="SetTarget($index, $event)">
|
|
342
|
+
<option value="">— field —</option>
|
|
343
|
+
@for (f of WritableFields; track f.Name) { <option [value]="f.Name">{{ f.DisplayNameOrName }}</option> }
|
|
344
|
+
</select>
|
|
345
|
+
<span class="frb-set">from</span>
|
|
346
|
+
<select class="mj-input frb-kind" [value]="rule.SourceKind" (change)="SetKind($index, $event)">
|
|
347
|
+
<option value="static">a fixed value</option>
|
|
348
|
+
<option value="field">another field</option>
|
|
349
|
+
<option value="formula">a formula</option>
|
|
350
|
+
<option value="lookup">a lookup</option>
|
|
351
|
+
<option value="prompt">an AI prompt</option>
|
|
352
|
+
</select>
|
|
353
|
+
<button type="button" class="frb-del" title="Remove rule" (click)="RemoveRule($index)"><i class="fa-solid fa-trash"></i></button>
|
|
354
|
+
</div>
|
|
355
|
+
|
|
356
|
+
<div class="frb-source">
|
|
357
|
+
@switch (rule.SourceKind) {
|
|
358
|
+
@case ('static') {
|
|
359
|
+
<input class="mj-input" placeholder="Value" [value]="rule.StaticValue" (input)="SetField($index, 'StaticValue', $event)" />
|
|
360
|
+
}
|
|
361
|
+
@case ('field') {
|
|
362
|
+
<select class="mj-input" [value]="rule.SourceField" (change)="SetField($index, 'SourceField', $event)">
|
|
363
|
+
<option value="">— source field —</option>
|
|
364
|
+
@for (f of AllFields; track f.Name) { <option [value]="f.Name">{{ f.DisplayNameOrName }}</option> }
|
|
365
|
+
</select>
|
|
366
|
+
}
|
|
367
|
+
@case ('formula') {
|
|
368
|
+
<input class="mj-input" placeholder="e.g. fields.FirstName + ' ' + fields.LastName" [value]="rule.Formula" (input)="SetField($index, 'Formula', $event)" />
|
|
369
|
+
}
|
|
370
|
+
@case ('lookup') {
|
|
371
|
+
<div class="frb-lookup">
|
|
372
|
+
<input class="mj-input" placeholder="Lookup entity" [value]="rule.LookupEntity" (input)="SetField($index, 'LookupEntity', $event)" />
|
|
373
|
+
<input class="mj-input" placeholder="Match field" [value]="rule.LookupMatchField" (input)="SetField($index, 'LookupMatchField', $event)" />
|
|
374
|
+
<input class="mj-input" placeholder="= this record's field" [value]="rule.LookupMatchValueField" (input)="SetField($index, 'LookupMatchValueField', $event)" />
|
|
375
|
+
<input class="mj-input" placeholder="Return field" [value]="rule.LookupReturnField" (input)="SetField($index, 'LookupReturnField', $event)" />
|
|
376
|
+
</div>
|
|
377
|
+
}
|
|
378
|
+
@case ('prompt') {
|
|
379
|
+
<mj-ai-prompt-selector [Value]="rule.PromptID" (ValueChange)="SetPrompt($index, $event)"></mj-ai-prompt-selector>
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
</div>
|
|
383
|
+
|
|
384
|
+
<div class="frb-cond">
|
|
385
|
+
<span class="frb-cond-label">Only when</span>
|
|
386
|
+
<input class="mj-input" placeholder="(always) — e.g. fields.Status === 'Active'" [value]="rule.Condition" (input)="SetField($index, 'Condition', $event)" />
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
@if (Errors.length > 0) {
|
|
392
|
+
<div class="frb-errors">
|
|
393
|
+
<i class="fa-solid fa-circle-exclamation"></i>
|
|
394
|
+
<ul>@for (e of Errors; track e) { <li>{{ e }}</li> }</ul>
|
|
395
|
+
</div>
|
|
396
|
+
}
|
|
397
|
+
</div>
|
|
398
|
+
`, 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 "] }]
|
|
399
|
+
}], null, { Provider: [{
|
|
400
|
+
type: Input
|
|
401
|
+
}], EntityName: [{
|
|
402
|
+
type: Input
|
|
403
|
+
}], Value: [{
|
|
404
|
+
type: Input
|
|
405
|
+
}], ValueChange: [{
|
|
406
|
+
type: Output
|
|
407
|
+
}], ValidChange: [{
|
|
408
|
+
type: Output
|
|
409
|
+
}] }); })();
|
|
410
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FieldRulesBuilderComponent, { className: "FieldRulesBuilderComponent", filePath: "lib/field-rules-builder/field-rules-builder.component.ts", lineNumber: 126 }); })();
|
|
411
|
+
//# sourceMappingURL=field-rules-builder.component.js.map
|