@memberjunction/ng-artifacts 5.40.1 → 5.40.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/components/plugins/component-artifact-viewer.component.d.ts +11 -0
- package/dist/lib/components/plugins/component-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/plugins/component-artifact-viewer.component.js +155 -59
- package/dist/lib/components/plugins/component-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/plugins/component-permission-evaluation.d.ts +19 -0
- package/dist/lib/components/plugins/component-permission-evaluation.d.ts.map +1 -0
- package/dist/lib/components/plugins/component-permission-evaluation.js +64 -0
- package/dist/lib/components/plugins/component-permission-evaluation.js.map +1 -0
- package/package.json +18 -18
|
@@ -3,6 +3,7 @@ import { BaseArtifactViewerPluginComponent, ArtifactViewerTab } from '../base-ar
|
|
|
3
3
|
import { MJReactComponent, AngularAdapterService } from '@memberjunction/ng-react';
|
|
4
4
|
import { ComponentSpec } from '@memberjunction/interactive-component-types';
|
|
5
5
|
import { BaseEntity, CompositeKey, DataSnapshot, EntityInfo } from '@memberjunction/core';
|
|
6
|
+
import { PermissionEvaluationResult } from './component-permission-evaluation';
|
|
6
7
|
import * as i0 from "@angular/core";
|
|
7
8
|
/**
|
|
8
9
|
* Viewer component for interactive Component artifacts (React-based UI components)
|
|
@@ -61,6 +62,7 @@ export declare class ComponentArtifactViewerComponent extends BaseArtifactViewer
|
|
|
61
62
|
hasError: boolean;
|
|
62
63
|
errorMessage: string;
|
|
63
64
|
errorDetails: string;
|
|
65
|
+
permissionResult: PermissionEvaluationResult | null;
|
|
64
66
|
/**
|
|
65
67
|
* Whether this plugin has content to display in the Display tab.
|
|
66
68
|
* Returns true only if the component has code that can be rendered.
|
|
@@ -84,6 +86,15 @@ export declare class ComponentArtifactViewerComponent extends BaseArtifactViewer
|
|
|
84
86
|
* fires for the first time.
|
|
85
87
|
*/
|
|
86
88
|
private loadComponentSpec;
|
|
89
|
+
/**
|
|
90
|
+
* Evaluate whether the current user has sufficient permissions for
|
|
91
|
+
* all entities and queries referenced in the component's dataRequirements.
|
|
92
|
+
* Uses the best available spec: resolved (from registry) > cached > stripped artifact.
|
|
93
|
+
* Runs synchronously against already-loaded client-side metadata.
|
|
94
|
+
*/
|
|
95
|
+
private evaluatePermissions;
|
|
96
|
+
/** Whether the component should be blocked from rendering due to missing permissions. */
|
|
97
|
+
get isPermissionBlocked(): boolean;
|
|
87
98
|
/**
|
|
88
99
|
* Component initialization.
|
|
89
100
|
* Note: loadComponentSpec() is called in ngOnChanges which runs before ngOnInit,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-artifact-viewer.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/plugins/component-artifact-viewer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAU,YAAY,EAAE,MAAM,eAAe,CAAC;AAE5H,OAAO,EAAE,iCAAiC,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACzG,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACnF,OAAO,EAA8B,aAAa,EAAE,MAAM,6CAA6C,CAAC;AAExG,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAqB,MAAM,sBAAsB,CAAC;;
|
|
1
|
+
{"version":3,"file":"component-artifact-viewer.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/plugins/component-artifact-viewer.component.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,6 +11,7 @@ import { BuildComponentCompleteCode } from '@memberjunction/interactive-componen
|
|
|
11
11
|
import { isFormRole, getDeclaredFormEntityName } from '@memberjunction/interactive-component-types/forms';
|
|
12
12
|
import { CompositeKey, DataSnapshot, LogError, RunView } from '@memberjunction/core';
|
|
13
13
|
import { DataRequirementsViewerComponent } from './data-requirements-viewer/data-requirements-viewer.component';
|
|
14
|
+
import { evaluateComponentPermissions } from './component-permission-evaluation';
|
|
14
15
|
import * as i0 from "@angular/core";
|
|
15
16
|
import * as i1 from "@memberjunction/ng-react";
|
|
16
17
|
import * as i2 from "@angular/common";
|
|
@@ -19,7 +20,7 @@ import * as i4 from "./component-feedback-panel/component-feedback-panel.compone
|
|
|
19
20
|
const _c0 = ["reactComponent"];
|
|
20
21
|
const _forTrack0 = ($index, $item) => $item.ID;
|
|
21
22
|
function ComponentArtifactViewerComponent_Conditional_2_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
22
|
-
i0.ɵɵelementStart(0, "details",
|
|
23
|
+
i0.ɵɵelementStart(0, "details", 8)(1, "summary");
|
|
23
24
|
i0.ɵɵtext(2, "Technical Details");
|
|
24
25
|
i0.ɵɵelementEnd();
|
|
25
26
|
i0.ɵɵelementStart(3, "pre");
|
|
@@ -32,14 +33,14 @@ function ComponentArtifactViewerComponent_Conditional_2_Conditional_13_Template(
|
|
|
32
33
|
} }
|
|
33
34
|
function ComponentArtifactViewerComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
34
35
|
i0.ɵɵelementStart(0, "div", 3);
|
|
35
|
-
i0.ɵɵelement(1, "i",
|
|
36
|
+
i0.ɵɵelement(1, "i", 6);
|
|
36
37
|
i0.ɵɵelementStart(2, "h3");
|
|
37
38
|
i0.ɵɵtext(3);
|
|
38
39
|
i0.ɵɵelementEnd();
|
|
39
40
|
i0.ɵɵelementStart(4, "p");
|
|
40
41
|
i0.ɵɵtext(5, "This component could not be loaded. This usually happens when:");
|
|
41
42
|
i0.ɵɵelementEnd();
|
|
42
|
-
i0.ɵɵelementStart(6, "ul",
|
|
43
|
+
i0.ɵɵelementStart(6, "ul", 7)(7, "li");
|
|
43
44
|
i0.ɵɵtext(8, "The component registry is unavailable or unreachable");
|
|
44
45
|
i0.ɵɵelementEnd();
|
|
45
46
|
i0.ɵɵelementStart(9, "li");
|
|
@@ -48,7 +49,7 @@ function ComponentArtifactViewerComponent_Conditional_2_Template(rf, ctx) { if (
|
|
|
48
49
|
i0.ɵɵelementStart(11, "li");
|
|
49
50
|
i0.ɵɵtext(12, "There was a network error during component retrieval");
|
|
50
51
|
i0.ɵɵelementEnd()();
|
|
51
|
-
i0.ɵɵconditionalCreate(13, ComponentArtifactViewerComponent_Conditional_2_Conditional_13_Template, 5, 1, "details",
|
|
52
|
+
i0.ɵɵconditionalCreate(13, ComponentArtifactViewerComponent_Conditional_2_Conditional_13_Template, 5, 1, "details", 8);
|
|
52
53
|
i0.ɵɵelementEnd();
|
|
53
54
|
} if (rf & 2) {
|
|
54
55
|
const ctx_r0 = i0.ɵɵnextContext();
|
|
@@ -58,12 +59,12 @@ function ComponentArtifactViewerComponent_Conditional_2_Template(rf, ctx) { if (
|
|
|
58
59
|
i0.ɵɵconditional(ctx_r0.errorDetails ? 13 : -1);
|
|
59
60
|
} }
|
|
60
61
|
function ComponentArtifactViewerComponent_Conditional_3_Conditional_8_Template(rf, ctx) { if (rf & 1) {
|
|
61
|
-
i0.ɵɵelementStart(0, "span",
|
|
62
|
+
i0.ɵɵelementStart(0, "span", 14);
|
|
62
63
|
i0.ɵɵtext(1, "Mock");
|
|
63
64
|
i0.ɵɵelementEnd();
|
|
64
65
|
} }
|
|
65
66
|
function ComponentArtifactViewerComponent_Conditional_3_Conditional_10_Template(rf, ctx) { if (rf & 1) {
|
|
66
|
-
i0.ɵɵelementStart(0, "span",
|
|
67
|
+
i0.ɵɵelementStart(0, "span", 16);
|
|
67
68
|
i0.ɵɵtext(1);
|
|
68
69
|
i0.ɵɵelementEnd();
|
|
69
70
|
} if (rf & 2) {
|
|
@@ -72,8 +73,8 @@ function ComponentArtifactViewerComponent_Conditional_3_Conditional_10_Template(
|
|
|
72
73
|
i0.ɵɵtextInterpolate(ctx_r0.formEntityInfo.DisplayName || ctx_r0.formEntityInfo.Name);
|
|
73
74
|
} }
|
|
74
75
|
function ComponentArtifactViewerComponent_Conditional_3_Conditional_11_Template(rf, ctx) { if (rf & 1) {
|
|
75
|
-
i0.ɵɵelementStart(0, "span",
|
|
76
|
-
i0.ɵɵelement(1, "i",
|
|
76
|
+
i0.ɵɵelementStart(0, "span", 17);
|
|
77
|
+
i0.ɵɵelement(1, "i", 22);
|
|
77
78
|
i0.ɵɵelementEnd();
|
|
78
79
|
} if (rf & 2) {
|
|
79
80
|
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
@@ -81,17 +82,17 @@ function ComponentArtifactViewerComponent_Conditional_3_Conditional_11_Template(
|
|
|
81
82
|
} }
|
|
82
83
|
function ComponentArtifactViewerComponent_Conditional_3_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
83
84
|
const _r3 = i0.ɵɵgetCurrentView();
|
|
84
|
-
i0.ɵɵelementStart(0, "button",
|
|
85
|
+
i0.ɵɵelementStart(0, "button", 23);
|
|
85
86
|
i0.ɵɵlistener("click", function ComponentArtifactViewerComponent_Conditional_3_Conditional_13_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.onApplyClicked()); });
|
|
86
|
-
i0.ɵɵelement(1, "i",
|
|
87
|
+
i0.ɵɵelement(1, "i", 24);
|
|
87
88
|
i0.ɵɵtext(2, " Apply to my form ");
|
|
88
89
|
i0.ɵɵelementEnd();
|
|
89
90
|
} }
|
|
90
91
|
function ComponentArtifactViewerComponent_Conditional_3_Conditional_14_Conditional_2_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
91
92
|
const _r5 = i0.ɵɵgetCurrentView();
|
|
92
|
-
i0.ɵɵelementStart(0, "li",
|
|
93
|
+
i0.ɵɵelementStart(0, "li", 30);
|
|
93
94
|
i0.ɵɵlistener("click", function ComponentArtifactViewerComponent_Conditional_3_Conditional_14_Conditional_2_For_2_Template_li_click_0_listener() { const r_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.onPickerSelect(r_r6)); });
|
|
94
|
-
i0.ɵɵelement(1, "i",
|
|
95
|
+
i0.ɵɵelement(1, "i", 31);
|
|
95
96
|
i0.ɵɵelementStart(2, "span");
|
|
96
97
|
i0.ɵɵtext(3);
|
|
97
98
|
i0.ɵɵelementEnd()();
|
|
@@ -101,8 +102,8 @@ function ComponentArtifactViewerComponent_Conditional_3_Conditional_14_Condition
|
|
|
101
102
|
i0.ɵɵtextInterpolate(r_r6.Label);
|
|
102
103
|
} }
|
|
103
104
|
function ComponentArtifactViewerComponent_Conditional_3_Conditional_14_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
104
|
-
i0.ɵɵelementStart(0, "ul",
|
|
105
|
-
i0.ɵɵrepeaterCreate(1, ComponentArtifactViewerComponent_Conditional_3_Conditional_14_Conditional_2_For_2_Template, 4, 1, "li",
|
|
105
|
+
i0.ɵɵelementStart(0, "ul", 27);
|
|
106
|
+
i0.ɵɵrepeaterCreate(1, ComponentArtifactViewerComponent_Conditional_3_Conditional_14_Conditional_2_For_2_Template, 4, 1, "li", 29, _forTrack0);
|
|
106
107
|
i0.ɵɵelementEnd();
|
|
107
108
|
} if (rf & 2) {
|
|
108
109
|
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
@@ -110,23 +111,23 @@ function ComponentArtifactViewerComponent_Conditional_3_Conditional_14_Condition
|
|
|
110
111
|
i0.ɵɵrepeater(ctx_r0.recordSearchResults);
|
|
111
112
|
} }
|
|
112
113
|
function ComponentArtifactViewerComponent_Conditional_3_Conditional_14_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
113
|
-
i0.ɵɵelementStart(0, "div",
|
|
114
|
+
i0.ɵɵelementStart(0, "div", 28);
|
|
114
115
|
i0.ɵɵtext(1, "No matches.");
|
|
115
116
|
i0.ɵɵelementEnd();
|
|
116
117
|
} }
|
|
117
118
|
function ComponentArtifactViewerComponent_Conditional_3_Conditional_14_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
118
|
-
i0.ɵɵelementStart(0, "div",
|
|
119
|
+
i0.ɵɵelementStart(0, "div", 28);
|
|
119
120
|
i0.ɵɵtext(1, "Start typing to search.");
|
|
120
121
|
i0.ɵɵelementEnd();
|
|
121
122
|
} }
|
|
122
123
|
function ComponentArtifactViewerComponent_Conditional_3_Conditional_14_Template(rf, ctx) { if (rf & 1) {
|
|
123
124
|
const _r4 = i0.ɵɵgetCurrentView();
|
|
124
|
-
i0.ɵɵelementStart(0, "div",
|
|
125
|
+
i0.ɵɵelementStart(0, "div", 25);
|
|
125
126
|
i0.ɵɵlistener("click", function ComponentArtifactViewerComponent_Conditional_3_Conditional_14_Template_div_click_0_listener($event) { i0.ɵɵrestoreView(_r4); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
126
|
-
i0.ɵɵelementStart(1, "input",
|
|
127
|
+
i0.ɵɵelementStart(1, "input", 26);
|
|
127
128
|
i0.ɵɵlistener("input", function ComponentArtifactViewerComponent_Conditional_3_Conditional_14_Template_input_input_1_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.onPickerSearchInput($event.target.value)); });
|
|
128
129
|
i0.ɵɵelementEnd();
|
|
129
|
-
i0.ɵɵconditionalCreate(2, ComponentArtifactViewerComponent_Conditional_3_Conditional_14_Conditional_2_Template, 3, 0, "ul",
|
|
130
|
+
i0.ɵɵconditionalCreate(2, ComponentArtifactViewerComponent_Conditional_3_Conditional_14_Conditional_2_Template, 3, 0, "ul", 27)(3, ComponentArtifactViewerComponent_Conditional_3_Conditional_14_Conditional_3_Template, 2, 0, "div", 28)(4, ComponentArtifactViewerComponent_Conditional_3_Conditional_14_Conditional_4_Template, 2, 0, "div", 28);
|
|
130
131
|
i0.ɵɵelementEnd();
|
|
131
132
|
} if (rf & 2) {
|
|
132
133
|
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
@@ -136,8 +137,8 @@ function ComponentArtifactViewerComponent_Conditional_3_Conditional_14_Template(
|
|
|
136
137
|
i0.ɵɵconditional(ctx_r0.recordSearchResults.length > 0 ? 2 : ctx_r0.recordSearchTerm.length > 0 ? 3 : 4);
|
|
137
138
|
} }
|
|
138
139
|
function ComponentArtifactViewerComponent_Conditional_3_Conditional_15_Template(rf, ctx) { if (rf & 1) {
|
|
139
|
-
i0.ɵɵelementStart(0, "div",
|
|
140
|
-
i0.ɵɵelement(1, "mj-interactive-form",
|
|
140
|
+
i0.ɵɵelementStart(0, "div", 21);
|
|
141
|
+
i0.ɵɵelement(1, "mj-interactive-form", 32);
|
|
141
142
|
i0.ɵɵelementEnd();
|
|
142
143
|
} if (rf & 2) {
|
|
143
144
|
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
@@ -146,7 +147,7 @@ function ComponentArtifactViewerComponent_Conditional_3_Conditional_15_Template(
|
|
|
146
147
|
} }
|
|
147
148
|
function ComponentArtifactViewerComponent_Conditional_3_Conditional_16_Template(rf, ctx) { if (rf & 1) {
|
|
148
149
|
i0.ɵɵelementStart(0, "div", 3);
|
|
149
|
-
i0.ɵɵelement(1, "i",
|
|
150
|
+
i0.ɵɵelement(1, "i", 33);
|
|
150
151
|
i0.ɵɵelementStart(2, "h3");
|
|
151
152
|
i0.ɵɵtext(3, "Form preview unavailable");
|
|
152
153
|
i0.ɵɵelementEnd();
|
|
@@ -160,27 +161,27 @@ function ComponentArtifactViewerComponent_Conditional_3_Conditional_16_Template(
|
|
|
160
161
|
} }
|
|
161
162
|
function ComponentArtifactViewerComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
162
163
|
const _r2 = i0.ɵɵgetCurrentView();
|
|
163
|
-
i0.ɵɵelementStart(0, "div",
|
|
164
|
-
i0.ɵɵelement(1, "i",
|
|
165
|
-
i0.ɵɵelementStart(2, "span",
|
|
164
|
+
i0.ɵɵelementStart(0, "div", 9);
|
|
165
|
+
i0.ɵɵelement(1, "i", 10);
|
|
166
|
+
i0.ɵɵelementStart(2, "span", 11);
|
|
166
167
|
i0.ɵɵtext(3, "Previewing record:");
|
|
167
168
|
i0.ɵɵelementEnd();
|
|
168
|
-
i0.ɵɵelementStart(4, "button",
|
|
169
|
+
i0.ɵɵelementStart(4, "button", 12);
|
|
169
170
|
i0.ɵɵlistener("click", function ComponentArtifactViewerComponent_Conditional_3_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.showRecordPicker = !ctx_r0.showRecordPicker); });
|
|
170
|
-
i0.ɵɵelement(5, "i",
|
|
171
|
+
i0.ɵɵelement(5, "i", 13);
|
|
171
172
|
i0.ɵɵelementStart(6, "strong");
|
|
172
173
|
i0.ɵɵtext(7);
|
|
173
174
|
i0.ɵɵelementEnd();
|
|
174
|
-
i0.ɵɵconditionalCreate(8, ComponentArtifactViewerComponent_Conditional_3_Conditional_8_Template, 2, 0, "span",
|
|
175
|
-
i0.ɵɵelement(9, "i",
|
|
175
|
+
i0.ɵɵconditionalCreate(8, ComponentArtifactViewerComponent_Conditional_3_Conditional_8_Template, 2, 0, "span", 14);
|
|
176
|
+
i0.ɵɵelement(9, "i", 15);
|
|
176
177
|
i0.ɵɵelementEnd();
|
|
177
|
-
i0.ɵɵconditionalCreate(10, ComponentArtifactViewerComponent_Conditional_3_Conditional_10_Template, 2, 1, "span",
|
|
178
|
-
i0.ɵɵconditionalCreate(11, ComponentArtifactViewerComponent_Conditional_3_Conditional_11_Template, 2, 1, "span",
|
|
179
|
-
i0.ɵɵelement(12, "span",
|
|
180
|
-
i0.ɵɵconditionalCreate(13, ComponentArtifactViewerComponent_Conditional_3_Conditional_13_Template, 3, 0, "button",
|
|
178
|
+
i0.ɵɵconditionalCreate(10, ComponentArtifactViewerComponent_Conditional_3_Conditional_10_Template, 2, 1, "span", 16);
|
|
179
|
+
i0.ɵɵconditionalCreate(11, ComponentArtifactViewerComponent_Conditional_3_Conditional_11_Template, 2, 1, "span", 17);
|
|
180
|
+
i0.ɵɵelement(12, "span", 18);
|
|
181
|
+
i0.ɵɵconditionalCreate(13, ComponentArtifactViewerComponent_Conditional_3_Conditional_13_Template, 3, 0, "button", 19);
|
|
181
182
|
i0.ɵɵelementEnd();
|
|
182
|
-
i0.ɵɵconditionalCreate(14, ComponentArtifactViewerComponent_Conditional_3_Conditional_14_Template, 5, 2, "div",
|
|
183
|
-
i0.ɵɵconditionalCreate(15, ComponentArtifactViewerComponent_Conditional_3_Conditional_15_Template, 2, 2, "div",
|
|
183
|
+
i0.ɵɵconditionalCreate(14, ComponentArtifactViewerComponent_Conditional_3_Conditional_14_Template, 5, 2, "div", 20);
|
|
184
|
+
i0.ɵɵconditionalCreate(15, ComponentArtifactViewerComponent_Conditional_3_Conditional_15_Template, 2, 2, "div", 21)(16, ComponentArtifactViewerComponent_Conditional_3_Conditional_16_Template, 6, 1, "div", 3);
|
|
184
185
|
} if (rf & 2) {
|
|
185
186
|
const ctx_r0 = i0.ɵɵnextContext();
|
|
186
187
|
i0.ɵɵadvance(7);
|
|
@@ -198,18 +199,84 @@ function ComponentArtifactViewerComponent_Conditional_3_Template(rf, ctx) { if (
|
|
|
198
199
|
i0.ɵɵadvance();
|
|
199
200
|
i0.ɵɵconditional(ctx_r0.formRecord && ctx_r0.component ? 15 : 16);
|
|
200
201
|
} }
|
|
201
|
-
function
|
|
202
|
-
|
|
203
|
-
i0.ɵɵ
|
|
204
|
-
i0.ɵɵ
|
|
202
|
+
function ComponentArtifactViewerComponent_Conditional_4_Conditional_7_For_5_Template(rf, ctx) { if (rf & 1) {
|
|
203
|
+
i0.ɵɵelementStart(0, "li");
|
|
204
|
+
i0.ɵɵelement(1, "i", 39);
|
|
205
|
+
i0.ɵɵtext(2);
|
|
206
|
+
i0.ɵɵelementEnd();
|
|
207
|
+
} if (rf & 2) {
|
|
208
|
+
const entity_r7 = ctx.$implicit;
|
|
209
|
+
i0.ɵɵadvance(2);
|
|
210
|
+
i0.ɵɵtextInterpolate1(" ", entity_r7);
|
|
211
|
+
} }
|
|
212
|
+
function ComponentArtifactViewerComponent_Conditional_4_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
213
|
+
i0.ɵɵelementStart(0, "div", 36)(1, "span", 38);
|
|
214
|
+
i0.ɵɵtext(2, "Entities");
|
|
215
|
+
i0.ɵɵelementEnd();
|
|
216
|
+
i0.ɵɵelementStart(3, "ul");
|
|
217
|
+
i0.ɵɵrepeaterCreate(4, ComponentArtifactViewerComponent_Conditional_4_Conditional_7_For_5_Template, 3, 1, "li", null, i0.ɵɵrepeaterTrackByIdentity);
|
|
218
|
+
i0.ɵɵelementEnd()();
|
|
219
|
+
} if (rf & 2) {
|
|
220
|
+
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
221
|
+
i0.ɵɵadvance(4);
|
|
222
|
+
i0.ɵɵrepeater(ctx_r0.permissionResult.missingEntities);
|
|
223
|
+
} }
|
|
224
|
+
function ComponentArtifactViewerComponent_Conditional_4_Conditional_8_For_5_Template(rf, ctx) { if (rf & 1) {
|
|
225
|
+
i0.ɵɵelementStart(0, "li");
|
|
226
|
+
i0.ɵɵelement(1, "i", 40);
|
|
227
|
+
i0.ɵɵtext(2);
|
|
228
|
+
i0.ɵɵelementEnd();
|
|
229
|
+
} if (rf & 2) {
|
|
230
|
+
const query_r8 = ctx.$implicit;
|
|
231
|
+
i0.ɵɵadvance(2);
|
|
232
|
+
i0.ɵɵtextInterpolate1(" ", query_r8);
|
|
233
|
+
} }
|
|
234
|
+
function ComponentArtifactViewerComponent_Conditional_4_Conditional_8_Template(rf, ctx) { if (rf & 1) {
|
|
235
|
+
i0.ɵɵelementStart(0, "div", 36)(1, "span", 38);
|
|
236
|
+
i0.ɵɵtext(2, "Queries");
|
|
237
|
+
i0.ɵɵelementEnd();
|
|
238
|
+
i0.ɵɵelementStart(3, "ul");
|
|
239
|
+
i0.ɵɵrepeaterCreate(4, ComponentArtifactViewerComponent_Conditional_4_Conditional_8_For_5_Template, 3, 1, "li", null, i0.ɵɵrepeaterTrackByIdentity);
|
|
240
|
+
i0.ɵɵelementEnd()();
|
|
241
|
+
} if (rf & 2) {
|
|
242
|
+
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
243
|
+
i0.ɵɵadvance(4);
|
|
244
|
+
i0.ɵɵrepeater(ctx_r0.permissionResult.missingQueries);
|
|
245
|
+
} }
|
|
246
|
+
function ComponentArtifactViewerComponent_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
247
|
+
i0.ɵɵelementStart(0, "div", 4)(1, "div", 34);
|
|
248
|
+
i0.ɵɵelement(2, "i", 35);
|
|
249
|
+
i0.ɵɵelementEnd();
|
|
250
|
+
i0.ɵɵelementStart(3, "h3");
|
|
251
|
+
i0.ɵɵtext(4, "Insufficient Permissions");
|
|
252
|
+
i0.ɵɵelementEnd();
|
|
253
|
+
i0.ɵɵelementStart(5, "p");
|
|
254
|
+
i0.ɵɵtext(6, "This component requires data access that you don't currently have:");
|
|
255
|
+
i0.ɵɵelementEnd();
|
|
256
|
+
i0.ɵɵconditionalCreate(7, ComponentArtifactViewerComponent_Conditional_4_Conditional_7_Template, 6, 0, "div", 36);
|
|
257
|
+
i0.ɵɵconditionalCreate(8, ComponentArtifactViewerComponent_Conditional_4_Conditional_8_Template, 6, 0, "div", 36);
|
|
258
|
+
i0.ɵɵelementStart(9, "p", 37);
|
|
259
|
+
i0.ɵɵtext(10, "Contact your administrator to request access.");
|
|
260
|
+
i0.ɵɵelementEnd()();
|
|
261
|
+
} if (rf & 2) {
|
|
262
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
263
|
+
i0.ɵɵadvance(7);
|
|
264
|
+
i0.ɵɵconditional(ctx_r0.permissionResult.missingEntities.length > 0 ? 7 : -1);
|
|
265
|
+
i0.ɵɵadvance();
|
|
266
|
+
i0.ɵɵconditional(ctx_r0.permissionResult.missingQueries.length > 0 ? 8 : -1);
|
|
267
|
+
} }
|
|
268
|
+
function ComponentArtifactViewerComponent_Conditional_5_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
269
|
+
const _r9 = i0.ɵɵgetCurrentView();
|
|
270
|
+
i0.ɵɵelementStart(0, "mj-react-component", 43, 0);
|
|
271
|
+
i0.ɵɵlistener("componentEvent", function ComponentArtifactViewerComponent_Conditional_5_Conditional_0_Template_mj_react_component_componentEvent_0_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.onComponentEvent($event)); })("openEntityRecord", function ComponentArtifactViewerComponent_Conditional_5_Conditional_0_Template_mj_react_component_openEntityRecord_0_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.onOpenEntityRecord($event)); })("initialized", function ComponentArtifactViewerComponent_Conditional_5_Conditional_0_Template_mj_react_component_initialized_0_listener() { i0.ɵɵrestoreView(_r9); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.onReactComponentInitialized()); });
|
|
205
272
|
i0.ɵɵelementEnd();
|
|
206
273
|
} if (rf & 2) {
|
|
207
274
|
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
208
275
|
i0.ɵɵproperty("component", ctx_r0.component);
|
|
209
276
|
} }
|
|
210
|
-
function
|
|
211
|
-
i0.ɵɵelementStart(0, "div",
|
|
212
|
-
i0.ɵɵelement(1, "i",
|
|
277
|
+
function ComponentArtifactViewerComponent_Conditional_5_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
278
|
+
i0.ɵɵelementStart(0, "div", 42);
|
|
279
|
+
i0.ɵɵelement(1, "i", 44);
|
|
213
280
|
i0.ɵɵelementStart(2, "h3");
|
|
214
281
|
i0.ɵɵtext(3, "Component Specification");
|
|
215
282
|
i0.ɵɵelementEnd();
|
|
@@ -220,9 +287,9 @@ function ComponentArtifactViewerComponent_Conditional_4_Conditional_1_Template(r
|
|
|
220
287
|
i0.ɵɵtext(7, "View the Functional, Data, or Spec tabs for details.");
|
|
221
288
|
i0.ɵɵelementEnd()();
|
|
222
289
|
} }
|
|
223
|
-
function
|
|
290
|
+
function ComponentArtifactViewerComponent_Conditional_5_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
224
291
|
i0.ɵɵelementStart(0, "div", 3);
|
|
225
|
-
i0.ɵɵelement(1, "i",
|
|
292
|
+
i0.ɵɵelement(1, "i", 33);
|
|
226
293
|
i0.ɵɵelementStart(2, "h3");
|
|
227
294
|
i0.ɵɵtext(3, "No Component Loaded");
|
|
228
295
|
i0.ɵɵelementEnd();
|
|
@@ -230,16 +297,16 @@ function ComponentArtifactViewerComponent_Conditional_4_Conditional_2_Template(r
|
|
|
230
297
|
i0.ɵɵtext(5, "The component data is missing or invalid.");
|
|
231
298
|
i0.ɵɵelementEnd()();
|
|
232
299
|
} }
|
|
233
|
-
function
|
|
234
|
-
i0.ɵɵconditionalCreate(0,
|
|
300
|
+
function ComponentArtifactViewerComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
301
|
+
i0.ɵɵconditionalCreate(0, ComponentArtifactViewerComponent_Conditional_5_Conditional_0_Template, 2, 1, "mj-react-component", 41)(1, ComponentArtifactViewerComponent_Conditional_5_Conditional_1_Template, 8, 0, "div", 42)(2, ComponentArtifactViewerComponent_Conditional_5_Conditional_2_Template, 6, 0, "div", 3);
|
|
235
302
|
} if (rf & 2) {
|
|
236
303
|
const ctx_r0 = i0.ɵɵnextContext();
|
|
237
304
|
i0.ɵɵconditional(ctx_r0.component && (ctx_r0.component.code || ctx_r0.component.namespace) ? 0 : ctx_r0.component ? 1 : 2);
|
|
238
305
|
} }
|
|
239
|
-
function
|
|
240
|
-
const
|
|
241
|
-
i0.ɵɵelementStart(0, "mj-component-feedback-panel",
|
|
242
|
-
i0.ɵɵlistener("Closed", function
|
|
306
|
+
function ComponentArtifactViewerComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
|
|
307
|
+
const _r10 = i0.ɵɵgetCurrentView();
|
|
308
|
+
i0.ɵɵelementStart(0, "mj-component-feedback-panel", 45);
|
|
309
|
+
i0.ɵɵlistener("Closed", function ComponentArtifactViewerComponent_Conditional_6_Template_mj_component_feedback_panel_Closed_0_listener() { i0.ɵɵrestoreView(_r10); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.ShowFeedbackPanel = false); });
|
|
243
310
|
i0.ɵɵelementEnd();
|
|
244
311
|
} if (rf & 2) {
|
|
245
312
|
const ctx_r0 = i0.ɵɵnextContext();
|
|
@@ -302,6 +369,8 @@ let ComponentArtifactViewerComponent = class ComponentArtifactViewerComponent ex
|
|
|
302
369
|
hasError = false;
|
|
303
370
|
errorMessage = '';
|
|
304
371
|
errorDetails = '';
|
|
372
|
+
// Permission state
|
|
373
|
+
permissionResult = null;
|
|
305
374
|
/**
|
|
306
375
|
* Whether this plugin has content to display in the Display tab.
|
|
307
376
|
* Returns true only if the component has code that can be rendered.
|
|
@@ -343,9 +412,11 @@ let ComponentArtifactViewerComponent = class ComponentArtifactViewerComponent ex
|
|
|
343
412
|
try {
|
|
344
413
|
// Clear cached resolved spec from previous version so stale data doesn't persist
|
|
345
414
|
this._cachedResolvedSpec = null;
|
|
415
|
+
this.permissionResult = null;
|
|
346
416
|
if (this.artifactVersion?.Content) {
|
|
347
417
|
this.component = SafeJSONParse(this.artifactVersion.Content);
|
|
348
418
|
this.extractComponentParts();
|
|
419
|
+
this.evaluatePermissions();
|
|
349
420
|
// Form-aware detection. Done here (not in ngAfterViewInit) so the
|
|
350
421
|
// template's `@if (isFormArtifact)` branch decides which preview
|
|
351
422
|
// to mount on the very first render — no flash of the non-form
|
|
@@ -363,6 +434,26 @@ let ComponentArtifactViewerComponent = class ComponentArtifactViewerComponent ex
|
|
|
363
434
|
this.errorDetails = error instanceof Error ? error.message : String(error);
|
|
364
435
|
}
|
|
365
436
|
}
|
|
437
|
+
/**
|
|
438
|
+
* Evaluate whether the current user has sufficient permissions for
|
|
439
|
+
* all entities and queries referenced in the component's dataRequirements.
|
|
440
|
+
* Uses the best available spec: resolved (from registry) > cached > stripped artifact.
|
|
441
|
+
* Runs synchronously against already-loaded client-side metadata.
|
|
442
|
+
*/
|
|
443
|
+
evaluatePermissions() {
|
|
444
|
+
const spec = this.resolvedComponentSpec;
|
|
445
|
+
if (!spec)
|
|
446
|
+
return;
|
|
447
|
+
const provider = this.ProviderToUse;
|
|
448
|
+
const currentUser = provider.CurrentUser;
|
|
449
|
+
if (!currentUser)
|
|
450
|
+
return; // No user context — skip check
|
|
451
|
+
this.permissionResult = evaluateComponentPermissions(spec, currentUser, provider);
|
|
452
|
+
}
|
|
453
|
+
/** Whether the component should be blocked from rendering due to missing permissions. */
|
|
454
|
+
get isPermissionBlocked() {
|
|
455
|
+
return !!this.permissionResult && !this.permissionResult.canRun;
|
|
456
|
+
}
|
|
366
457
|
/**
|
|
367
458
|
* Component initialization.
|
|
368
459
|
* Note: loadComponentSpec() is called in ngOnChanges which runs before ngOnInit,
|
|
@@ -469,6 +560,11 @@ let ComponentArtifactViewerComponent = class ComponentArtifactViewerComponent ex
|
|
|
469
560
|
// Cache the resolved spec so it's available even after the React component is destroyed
|
|
470
561
|
this._cachedResolvedSpec = this.reactComponent.resolvedComponentSpec;
|
|
471
562
|
this.tabsChanged.emit();
|
|
563
|
+
// Re-evaluate permissions against the resolved spec — the stripped artifact
|
|
564
|
+
// spec has no dataRequirements, so the initial check in loadComponentSpec()
|
|
565
|
+
// passes trivially. The resolved spec from the registry contains the full
|
|
566
|
+
// dataRequirements with entity and query references.
|
|
567
|
+
this.evaluatePermissions();
|
|
472
568
|
}
|
|
473
569
|
}
|
|
474
570
|
onComponentEvent(event) {
|
|
@@ -713,19 +809,19 @@ let ComponentArtifactViewerComponent = class ComponentArtifactViewerComponent ex
|
|
|
713
809
|
} if (rf & 2) {
|
|
714
810
|
let _t;
|
|
715
811
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.reactComponent = _t.first);
|
|
716
|
-
} }, outputs: { tabsChanged: "tabsChanged", openEntityRecord: "openEntityRecord", applyFormRequested: "applyFormRequested" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls:
|
|
812
|
+
} }, outputs: { tabsChanged: "tabsChanged", openEntityRecord: "openEntityRecord", applyFormRequested: "applyFormRequested" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls: 7, vars: 3, consts: [["reactComponent", ""], [1, "component-artifact-viewer", 3, "ngClass"], [1, "component-preview"], [1, "error-state"], [1, "permission-warning"], [3, "ComponentSpec", "ReactContainerElement"], [1, "fa-solid", "fa-exclamation-triangle"], [2, "text-align", "left", "max-width", "500px", "color", "var(--mj-text-muted)", "line-height", "1.6"], [2, "margin-top", "20px"], [1, "form-artifact-toolstrip"], ["aria-hidden", "true", 1, "fa-solid", "fa-circle-info"], [1, "form-artifact-toolstrip-label"], ["type", "button", 1, "form-artifact-picker-chip", 3, "click"], [1, "fa-solid", "fa-magnifying-glass"], [1, "form-artifact-picker-chip-badge"], [1, "fa-solid", "fa-chevron-down"], [1, "form-artifact-toolstrip-entity"], [1, "form-artifact-toolstrip-warning", 3, "title"], [2, "flex", "1"], ["type", "button", 1, "form-artifact-apply-btn"], [1, "form-artifact-picker-panel"], [1, "form-artifact-form-host"], [1, "fa-solid", "fa-triangle-exclamation"], ["type", "button", 1, "form-artifact-apply-btn", 3, "click"], [1, "fa-solid", "fa-circle-check"], [1, "form-artifact-picker-panel", 3, "click"], ["type", "text", "placeholder", "Search records by name\u2026", 1, "form-artifact-picker-search", 3, "input", "value"], [1, "form-artifact-picker-results"], [1, "form-artifact-picker-empty"], [1, "form-artifact-picker-row"], [1, "form-artifact-picker-row", 3, "click"], [1, "fa-solid", "fa-circle-dot"], [3, "componentSpec", "record"], [1, "fa-solid", "fa-exclamation-circle"], [1, "permission-warning-icon"], [1, "fa-solid", "fa-shield-halved"], [1, "permission-warning-section"], [1, "permission-warning-hint"], [1, "permission-warning-section-label"], [1, "fa-solid", "fa-table"], [1, "fa-solid", "fa-database"], [3, "component"], [1, "no-code-state"], [3, "componentEvent", "openEntityRecord", "initialized", "component"], [1, "fa-solid", "fa-file-lines"], [3, "Closed", "ComponentSpec", "ReactContainerElement"]], template: function ComponentArtifactViewerComponent_Template(rf, ctx) { if (rf & 1) {
|
|
717
813
|
i0.ɵɵelementStart(0, "div", 1)(1, "div", 2);
|
|
718
|
-
i0.ɵɵconditionalCreate(2, ComponentArtifactViewerComponent_Conditional_2_Template, 14, 2, "div", 3)(3, ComponentArtifactViewerComponent_Conditional_3_Template, 17, 7)(4, ComponentArtifactViewerComponent_Conditional_4_Template, 3, 1);
|
|
814
|
+
i0.ɵɵconditionalCreate(2, ComponentArtifactViewerComponent_Conditional_2_Template, 14, 2, "div", 3)(3, ComponentArtifactViewerComponent_Conditional_3_Template, 17, 7)(4, ComponentArtifactViewerComponent_Conditional_4_Template, 11, 2, "div", 4)(5, ComponentArtifactViewerComponent_Conditional_5_Template, 3, 1);
|
|
719
815
|
i0.ɵɵelementEnd();
|
|
720
|
-
i0.ɵɵconditionalCreate(
|
|
816
|
+
i0.ɵɵconditionalCreate(6, ComponentArtifactViewerComponent_Conditional_6_Template, 1, 2, "mj-component-feedback-panel", 5);
|
|
721
817
|
i0.ɵɵelementEnd();
|
|
722
818
|
} if (rf & 2) {
|
|
723
819
|
i0.ɵɵproperty("ngClass", ctx.cssClass);
|
|
724
820
|
i0.ɵɵadvance(2);
|
|
725
|
-
i0.ɵɵconditional(ctx.hasError ? 2 : ctx.isFormArtifact ? 3 : 4);
|
|
726
|
-
i0.ɵɵadvance(
|
|
727
|
-
i0.ɵɵconditional(ctx.ShowFeedbackPanel && ctx.resolvedComponentSpec ?
|
|
728
|
-
} }, dependencies: [i2.NgClass, i1.MJReactComponent, i3.InteractiveFormComponent, i4.ComponentFeedbackPanelComponent], styles: [".component-artifact-viewer[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n background: var(--mj-bg-surface);\n}\n\n.component-preview[_ngcontent-%COMP%] {\n flex: 1;\n padding: 15px;\n overflow: auto;\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n\n\n.component-preview[_ngcontent-%COMP%] mj-react-component[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n\n.error-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n color: var(--mj-status-error);\n flex: 1;\n min-height: 300px;\n}\n.error-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n}\n.error-state[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n font-size: 20px;\n}\n.error-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0 0 12px 0;\n color: var(--mj-text-muted);\n}\n.error-state[_ngcontent-%COMP%] details[_ngcontent-%COMP%] {\n margin-top: 16px;\n text-align: left;\n max-width: 600px;\n}\n.error-state[_ngcontent-%COMP%] details[_ngcontent-%COMP%] summary[_ngcontent-%COMP%] {\n cursor: pointer;\n color: var(--mj-text-secondary);\n font-weight: 500;\n margin-bottom: 8px;\n}\n.error-state[_ngcontent-%COMP%] details[_ngcontent-%COMP%] summary[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n}\n.error-state[_ngcontent-%COMP%] details[_ngcontent-%COMP%] pre[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n padding: 12px;\n border-radius: 4px;\n overflow-x: auto;\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n\n\n.no-code-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n color: var(--mj-text-muted);\n flex: 1;\n}\n.no-code-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n color: var(--mj-brand-primary);\n}\n.no-code-state[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n font-size: 20px;\n color: var(--mj-text-secondary);\n}\n.no-code-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n color: var(--mj-text-muted);\n}\n\n\n\n\n\n\n\n\n\n\n.form-artifact-toolstrip[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 14px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n font-size: 12px;\n color: var(--mj-text-secondary);\n}\n.form-artifact-toolstrip-label[_ngcontent-%COMP%] {\n font-weight: 600;\n}\n.form-artifact-toolstrip-entity[_ngcontent-%COMP%] {\n margin-left: 4px;\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface));\n border-radius: 999px;\n font-size: 11px;\n font-weight: 600;\n}\n.form-artifact-toolstrip-warning[_ngcontent-%COMP%] {\n color: var(--mj-status-warning-text);\n}\n\n.form-artifact-picker-chip[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n padding: 4px 10px;\n font-size: 12px;\n color: var(--mj-text-primary);\n cursor: pointer;\n}\n.form-artifact-picker-chip[_ngcontent-%COMP%]:hover, \n.form-artifact-picker-chip[_ngcontent-%COMP%]:focus {\n border-color: var(--mj-border-focus);\n outline: none;\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n.form-artifact-picker-chip[_ngcontent-%COMP%] strong[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n font-weight: 600;\n}\n.form-artifact-picker-chip-badge[_ngcontent-%COMP%] {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning-border);\n font-size: 10px;\n font-weight: 700;\n padding: 1px 6px;\n border-radius: 999px;\n}\n\n.form-artifact-apply-btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #ffffff);\n border: 1px solid var(--mj-brand-primary);\n border-radius: var(--mj-radius-md, 8px);\n padding: 5px 12px;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n}\n.form-artifact-apply-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary-hover, var(--mj-brand-primary));\n}\n\n.form-artifact-picker-panel[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n padding: 10px 14px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.form-artifact-picker-search[_ngcontent-%COMP%] {\n width: 100%;\n padding: 7px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-bg-surface);\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n.form-artifact-picker-search[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n.form-artifact-picker-results[_ngcontent-%COMP%] {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n.form-artifact-picker-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 8px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n}\n.form-artifact-picker-row[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n.form-artifact-picker-row[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 10px;\n}\n.form-artifact-picker-empty[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 12px;\n font-style: italic;\n}\n\n.form-artifact-form-host[_ngcontent-%COMP%] {\n flex: 1;\n overflow: auto;\n background: var(--mj-bg-page);\n}"] });
|
|
821
|
+
i0.ɵɵconditional(ctx.hasError ? 2 : ctx.isFormArtifact ? 3 : ctx.isPermissionBlocked ? 4 : 5);
|
|
822
|
+
i0.ɵɵadvance(4);
|
|
823
|
+
i0.ɵɵconditional(ctx.ShowFeedbackPanel && ctx.resolvedComponentSpec ? 6 : -1);
|
|
824
|
+
} }, dependencies: [i2.NgClass, i1.MJReactComponent, i3.InteractiveFormComponent, i4.ComponentFeedbackPanelComponent], styles: [".component-artifact-viewer[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n background: var(--mj-bg-surface);\n}\n\n.component-preview[_ngcontent-%COMP%] {\n flex: 1;\n padding: 15px;\n overflow: auto;\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n\n\n.component-preview[_ngcontent-%COMP%] mj-react-component[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n\n.error-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n color: var(--mj-status-error);\n flex: 1;\n min-height: 300px;\n}\n.error-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n}\n.error-state[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n font-size: 20px;\n}\n.error-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0 0 12px 0;\n color: var(--mj-text-muted);\n}\n.error-state[_ngcontent-%COMP%] details[_ngcontent-%COMP%] {\n margin-top: 16px;\n text-align: left;\n max-width: 600px;\n}\n.error-state[_ngcontent-%COMP%] details[_ngcontent-%COMP%] summary[_ngcontent-%COMP%] {\n cursor: pointer;\n color: var(--mj-text-secondary);\n font-weight: 500;\n margin-bottom: 8px;\n}\n.error-state[_ngcontent-%COMP%] details[_ngcontent-%COMP%] summary[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n}\n.error-state[_ngcontent-%COMP%] details[_ngcontent-%COMP%] pre[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n padding: 12px;\n border-radius: 4px;\n overflow-x: auto;\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n\n\n.no-code-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n color: var(--mj-text-muted);\n flex: 1;\n}\n.no-code-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n color: var(--mj-brand-primary);\n}\n.no-code-state[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n font-size: 20px;\n color: var(--mj-text-secondary);\n}\n.no-code-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n color: var(--mj-text-muted);\n}\n\n\n\n\n\n\n\n\n\n\n.form-artifact-toolstrip[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 14px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n font-size: 12px;\n color: var(--mj-text-secondary);\n}\n.form-artifact-toolstrip-label[_ngcontent-%COMP%] {\n font-weight: 600;\n}\n.form-artifact-toolstrip-entity[_ngcontent-%COMP%] {\n margin-left: 4px;\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface));\n border-radius: 999px;\n font-size: 11px;\n font-weight: 600;\n}\n.form-artifact-toolstrip-warning[_ngcontent-%COMP%] {\n color: var(--mj-status-warning-text);\n}\n\n.form-artifact-picker-chip[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n padding: 4px 10px;\n font-size: 12px;\n color: var(--mj-text-primary);\n cursor: pointer;\n}\n.form-artifact-picker-chip[_ngcontent-%COMP%]:hover, \n.form-artifact-picker-chip[_ngcontent-%COMP%]:focus {\n border-color: var(--mj-border-focus);\n outline: none;\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n.form-artifact-picker-chip[_ngcontent-%COMP%] strong[_ngcontent-%COMP%] {\n color: var(--mj-text-primary);\n font-weight: 600;\n}\n.form-artifact-picker-chip-badge[_ngcontent-%COMP%] {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning-border);\n font-size: 10px;\n font-weight: 700;\n padding: 1px 6px;\n border-radius: 999px;\n}\n\n.form-artifact-apply-btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #ffffff);\n border: 1px solid var(--mj-brand-primary);\n border-radius: var(--mj-radius-md, 8px);\n padding: 5px 12px;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n}\n.form-artifact-apply-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary-hover, var(--mj-brand-primary));\n}\n\n.form-artifact-picker-panel[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n padding: 10px 14px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.form-artifact-picker-search[_ngcontent-%COMP%] {\n width: 100%;\n padding: 7px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-bg-surface);\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n.form-artifact-picker-search[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n.form-artifact-picker-results[_ngcontent-%COMP%] {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n.form-artifact-picker-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 8px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n}\n.form-artifact-picker-row[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n}\n.form-artifact-picker-row[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 10px;\n}\n.form-artifact-picker-empty[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n font-size: 12px;\n font-style: italic;\n}\n\n.form-artifact-form-host[_ngcontent-%COMP%] {\n flex: 1;\n overflow: auto;\n background: var(--mj-bg-page);\n}\n\n\n\n\n\n\n\n\n.permission-warning[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n flex: 1;\n min-height: 300px;\n}\n.permission-warning-icon[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n color: var(--mj-status-warning-text, #b45309);\n}\n.permission-warning[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n font-size: 20px;\n color: var(--mj-text-primary);\n}\n.permission-warning[_ngcontent-%COMP%] > p[_ngcontent-%COMP%] {\n margin: 0 0 16px 0;\n color: var(--mj-text-muted);\n}\n.permission-warning-section[_ngcontent-%COMP%] {\n text-align: left;\n max-width: 400px;\n width: 100%;\n margin-bottom: 12px;\n}\n.permission-warning-section-label[_ngcontent-%COMP%] {\n display: block;\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-muted);\n margin-bottom: 4px;\n}\n.permission-warning-section[_ngcontent-%COMP%] ul[_ngcontent-%COMP%] {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.permission-warning-section[_ngcontent-%COMP%] li[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 10px;\n font-size: 13px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-sunken, var(--mj-bg-surface));\n border-radius: 6px;\n}\n.permission-warning-section[_ngcontent-%COMP%] li[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-status-warning-text, #b45309);\n font-size: 12px;\n}\n.permission-warning-hint[_ngcontent-%COMP%] {\n margin-top: 8px;\n font-size: 12px;\n color: var(--mj-text-muted);\n}"] });
|
|
729
825
|
};
|
|
730
826
|
ComponentArtifactViewerComponent = __decorate([
|
|
731
827
|
RegisterClass(BaseArtifactViewerPluginComponent, 'ComponentArtifactViewerPlugin')
|
|
@@ -733,7 +829,7 @@ ComponentArtifactViewerComponent = __decorate([
|
|
|
733
829
|
export { ComponentArtifactViewerComponent };
|
|
734
830
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ComponentArtifactViewerComponent, [{
|
|
735
831
|
type: Component,
|
|
736
|
-
args: [{ standalone: false, selector: 'mj-component-artifact-viewer', template: "<div class=\"component-artifact-viewer\" [ngClass]=\"cssClass\">\n <!-- Component Preview (full screen) -->\n <div class=\"component-preview\">\n @if (hasError) {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <h3>{{ errorMessage }}</h3>\n <p>This component could not be loaded. This usually happens when:</p>\n <ul style=\"text-align: left; max-width: 500px; color: var(--mj-text-muted); line-height: 1.6;\">\n <li>The component registry is unavailable or unreachable</li>\n <li>The component version does not exist in the registry</li>\n <li>There was a network error during component retrieval</li>\n </ul>\n @if (errorDetails) {\n <details style=\"margin-top: 20px;\">\n <summary>Technical Details</summary>\n <pre>{{ errorDetails }}</pre>\n </details>\n }\n </div>\n } @else if (isFormArtifact) {\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n FORM-AWARE BRANCH\n Spec declares componentRole: 'form'. Render via\n <mj-interactive-form> bound to a real Top-1 record\n (or a synthetic NewRecord fallback). Picker chip\n lets the user swap records; Apply promotes the\n spec to an EntityFormOverride.\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div class=\"form-artifact-toolstrip\">\n <i class=\"fa-solid fa-circle-info\" aria-hidden=\"true\"></i>\n <span class=\"form-artifact-toolstrip-label\">Previewing record:</span>\n <button\n type=\"button\"\n class=\"form-artifact-picker-chip\"\n (click)=\"showRecordPicker = !showRecordPicker\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n <strong>{{ formRecordLabel || 'No record' }}</strong>\n @if (!formRecordIsReal) {\n <span class=\"form-artifact-picker-chip-badge\">Mock</span>\n }\n <i class=\"fa-solid fa-chevron-down\"></i>\n </button>\n @if (formEntityInfo) {\n <span class=\"form-artifact-toolstrip-entity\">{{ formEntityInfo.DisplayName || formEntityInfo.Name }}</span>\n }\n @if (formInitError) {\n <span class=\"form-artifact-toolstrip-warning\" [title]=\"formInitError\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n </span>\n }\n <span style=\"flex: 1;\"></span>\n @if (formEntityInfo) {\n <button\n type=\"button\"\n class=\"form-artifact-apply-btn\"\n (click)=\"onApplyClicked()\">\n <i class=\"fa-solid fa-circle-check\"></i>\n Apply to my form\n </button>\n }\n </div>\n\n @if (showRecordPicker && formEntityInfo) {\n <div class=\"form-artifact-picker-panel\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"text\"\n class=\"form-artifact-picker-search\"\n placeholder=\"Search records by name\u2026\"\n [value]=\"recordSearchTerm\"\n (input)=\"onPickerSearchInput($any($event.target).value)\" />\n @if (recordSearchResults.length > 0) {\n <ul class=\"form-artifact-picker-results\">\n @for (r of recordSearchResults; track r.ID) {\n <li\n class=\"form-artifact-picker-row\"\n (click)=\"onPickerSelect(r)\">\n <i class=\"fa-solid fa-circle-dot\"></i>\n <span>{{ r.Label }}</span>\n </li>\n }\n </ul>\n } @else if (recordSearchTerm.length > 0) {\n <div class=\"form-artifact-picker-empty\">No matches.</div>\n } @else {\n <div class=\"form-artifact-picker-empty\">Start typing to search.</div>\n }\n </div>\n }\n\n @if (formRecord && component) {\n <div class=\"form-artifact-form-host\">\n <mj-interactive-form\n [componentSpec]=\"component\"\n [record]=\"formRecord\">\n </mj-interactive-form>\n </div>\n } @else {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n <h3>Form preview unavailable</h3>\n <p>{{ formInitError || 'Could not bind a record to this form.' }}</p>\n </div>\n }\n } @else {\n <!-- Default (non-form) interactive component path -->\n @if (component && (component.code || component.namespace)) {\n <mj-react-component\n #reactComponent\n [component]=\"component\"\n (componentEvent)=\"onComponentEvent($event)\"\n (openEntityRecord)=\"onOpenEntityRecord($event)\"\n (initialized)=\"onReactComponentInitialized()\">\n </mj-react-component>\n } @else if (component) {\n <!-- Component spec exists but no code - show informational message -->\n <div class=\"no-code-state\">\n <i class=\"fa-solid fa-file-lines\"></i>\n <h3>Component Specification</h3>\n <p>This component has specifications but no executable code yet.</p>\n <p>View the Functional, Data, or Spec tabs for details.</p>\n </div>\n } @else {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n <h3>No Component Loaded</h3>\n <p>The component data is missing or invalid.</p>\n </div>\n }\n }\n </div>\n\n <!-- Feedback overlay panel (invoked from header button via AskUserForFeedback) -->\n @if (ShowFeedbackPanel && resolvedComponentSpec) {\n <mj-component-feedback-panel\n [ComponentSpec]=\"resolvedComponentSpec\"\n [ReactContainerElement]=\"reactComponent?.container?.nativeElement || null\"\n (Closed)=\"ShowFeedbackPanel = false\">\n </mj-component-feedback-panel>\n }\n</div>\n", styles: [".component-artifact-viewer {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n background: var(--mj-bg-surface);\n}\n\n.component-preview {\n flex: 1;\n padding: 15px;\n overflow: auto;\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n/* Ensure React component takes full height */\n.component-preview mj-react-component {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n\n.error-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n color: var(--mj-status-error);\n flex: 1;\n min-height: 300px;\n}\n.error-state i {\n font-size: 48px;\n margin-bottom: 16px;\n}\n.error-state h3 {\n margin: 0 0 8px 0;\n font-size: 20px;\n}\n.error-state p {\n margin: 0 0 12px 0;\n color: var(--mj-text-muted);\n}\n.error-state details {\n margin-top: 16px;\n text-align: left;\n max-width: 600px;\n}\n.error-state details summary {\n cursor: pointer;\n color: var(--mj-text-secondary);\n font-weight: 500;\n margin-bottom: 8px;\n}\n.error-state details summary:hover {\n color: var(--mj-text-primary);\n}\n.error-state details pre {\n background: var(--mj-bg-surface-sunken);\n padding: 12px;\n border-radius: 4px;\n overflow-x: auto;\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n/* State for component spec without executable code */\n.no-code-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n color: var(--mj-text-muted);\n flex: 1;\n}\n.no-code-state i {\n font-size: 48px;\n margin-bottom: 16px;\n color: var(--mj-brand-primary);\n}\n.no-code-state h3 {\n margin: 0 0 8px 0;\n font-size: 20px;\n color: var(--mj-text-secondary);\n}\n.no-code-state p {\n margin: 0 0 8px 0;\n color: var(--mj-text-muted);\n}\n\n\n/* ====================================================================\n FORM-AWARE BRANCH (componentRole === 'form')\n Top strip: record-picker chip + Apply button.\n Below: dropdown picker panel.\n Below: <mj-interactive-form> bound to the picked record.\n ==================================================================== */\n\n.form-artifact-toolstrip {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 14px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n font-size: 12px;\n color: var(--mj-text-secondary);\n}\n.form-artifact-toolstrip-label {\n font-weight: 600;\n}\n.form-artifact-toolstrip-entity {\n margin-left: 4px;\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface));\n border-radius: 999px;\n font-size: 11px;\n font-weight: 600;\n}\n.form-artifact-toolstrip-warning {\n color: var(--mj-status-warning-text);\n}\n\n.form-artifact-picker-chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n padding: 4px 10px;\n font-size: 12px;\n color: var(--mj-text-primary);\n cursor: pointer;\n}\n.form-artifact-picker-chip:hover,\n.form-artifact-picker-chip:focus {\n border-color: var(--mj-border-focus);\n outline: none;\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n.form-artifact-picker-chip strong {\n color: var(--mj-text-primary);\n font-weight: 600;\n}\n.form-artifact-picker-chip-badge {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning-border);\n font-size: 10px;\n font-weight: 700;\n padding: 1px 6px;\n border-radius: 999px;\n}\n\n.form-artifact-apply-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #ffffff);\n border: 1px solid var(--mj-brand-primary);\n border-radius: var(--mj-radius-md, 8px);\n padding: 5px 12px;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n}\n.form-artifact-apply-btn:hover {\n background: var(--mj-brand-primary-hover, var(--mj-brand-primary));\n}\n\n.form-artifact-picker-panel {\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n padding: 10px 14px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.form-artifact-picker-search {\n width: 100%;\n padding: 7px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-bg-surface);\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n.form-artifact-picker-search:focus {\n outline: none;\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n.form-artifact-picker-results {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n.form-artifact-picker-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 8px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n}\n.form-artifact-picker-row:hover {\n background: var(--mj-bg-surface-hover);\n}\n.form-artifact-picker-row i {\n color: var(--mj-text-muted);\n font-size: 10px;\n}\n.form-artifact-picker-empty {\n color: var(--mj-text-muted);\n font-size: 12px;\n font-style: italic;\n}\n\n.form-artifact-form-host {\n flex: 1;\n overflow: auto;\n background: var(--mj-bg-page);\n}\n"] }]
|
|
832
|
+
args: [{ standalone: false, selector: 'mj-component-artifact-viewer', template: "<div class=\"component-artifact-viewer\" [ngClass]=\"cssClass\">\n <!-- Component Preview (full screen) -->\n <div class=\"component-preview\">\n @if (hasError) {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <h3>{{ errorMessage }}</h3>\n <p>This component could not be loaded. This usually happens when:</p>\n <ul style=\"text-align: left; max-width: 500px; color: var(--mj-text-muted); line-height: 1.6;\">\n <li>The component registry is unavailable or unreachable</li>\n <li>The component version does not exist in the registry</li>\n <li>There was a network error during component retrieval</li>\n </ul>\n @if (errorDetails) {\n <details style=\"margin-top: 20px;\">\n <summary>Technical Details</summary>\n <pre>{{ errorDetails }}</pre>\n </details>\n }\n </div>\n } @else if (isFormArtifact) {\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n FORM-AWARE BRANCH\n Spec declares componentRole: 'form'. Render via\n <mj-interactive-form> bound to a real Top-1 record\n (or a synthetic NewRecord fallback). Picker chip\n lets the user swap records; Apply promotes the\n spec to an EntityFormOverride.\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div class=\"form-artifact-toolstrip\">\n <i class=\"fa-solid fa-circle-info\" aria-hidden=\"true\"></i>\n <span class=\"form-artifact-toolstrip-label\">Previewing record:</span>\n <button\n type=\"button\"\n class=\"form-artifact-picker-chip\"\n (click)=\"showRecordPicker = !showRecordPicker\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n <strong>{{ formRecordLabel || 'No record' }}</strong>\n @if (!formRecordIsReal) {\n <span class=\"form-artifact-picker-chip-badge\">Mock</span>\n }\n <i class=\"fa-solid fa-chevron-down\"></i>\n </button>\n @if (formEntityInfo) {\n <span class=\"form-artifact-toolstrip-entity\">{{ formEntityInfo.DisplayName || formEntityInfo.Name }}</span>\n }\n @if (formInitError) {\n <span class=\"form-artifact-toolstrip-warning\" [title]=\"formInitError\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n </span>\n }\n <span style=\"flex: 1;\"></span>\n @if (formEntityInfo) {\n <button\n type=\"button\"\n class=\"form-artifact-apply-btn\"\n (click)=\"onApplyClicked()\">\n <i class=\"fa-solid fa-circle-check\"></i>\n Apply to my form\n </button>\n }\n </div>\n\n @if (showRecordPicker && formEntityInfo) {\n <div class=\"form-artifact-picker-panel\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"text\"\n class=\"form-artifact-picker-search\"\n placeholder=\"Search records by name\u2026\"\n [value]=\"recordSearchTerm\"\n (input)=\"onPickerSearchInput($any($event.target).value)\" />\n @if (recordSearchResults.length > 0) {\n <ul class=\"form-artifact-picker-results\">\n @for (r of recordSearchResults; track r.ID) {\n <li\n class=\"form-artifact-picker-row\"\n (click)=\"onPickerSelect(r)\">\n <i class=\"fa-solid fa-circle-dot\"></i>\n <span>{{ r.Label }}</span>\n </li>\n }\n </ul>\n } @else if (recordSearchTerm.length > 0) {\n <div class=\"form-artifact-picker-empty\">No matches.</div>\n } @else {\n <div class=\"form-artifact-picker-empty\">Start typing to search.</div>\n }\n </div>\n }\n\n @if (formRecord && component) {\n <div class=\"form-artifact-form-host\">\n <mj-interactive-form\n [componentSpec]=\"component\"\n [record]=\"formRecord\">\n </mj-interactive-form>\n </div>\n } @else {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n <h3>Form preview unavailable</h3>\n <p>{{ formInitError || 'Could not bind a record to this form.' }}</p>\n </div>\n }\n } @else if (isPermissionBlocked) {\n <!-- Permission warning \u2014 user lacks access to one or more data sources -->\n <div class=\"permission-warning\">\n <div class=\"permission-warning-icon\">\n <i class=\"fa-solid fa-shield-halved\"></i>\n </div>\n <h3>Insufficient Permissions</h3>\n <p>This component requires data access that you don't currently have:</p>\n\n @if (permissionResult!.missingEntities.length > 0) {\n <div class=\"permission-warning-section\">\n <span class=\"permission-warning-section-label\">Entities</span>\n <ul>\n @for (entity of permissionResult!.missingEntities; track entity) {\n <li><i class=\"fa-solid fa-table\"></i> {{ entity }}</li>\n }\n </ul>\n </div>\n }\n\n @if (permissionResult!.missingQueries.length > 0) {\n <div class=\"permission-warning-section\">\n <span class=\"permission-warning-section-label\">Queries</span>\n <ul>\n @for (query of permissionResult!.missingQueries; track query) {\n <li><i class=\"fa-solid fa-database\"></i> {{ query }}</li>\n }\n </ul>\n </div>\n }\n\n <p class=\"permission-warning-hint\">Contact your administrator to request access.</p>\n </div>\n } @else {\n <!-- Default (non-form) interactive component path -->\n @if (component && (component.code || component.namespace)) {\n <mj-react-component\n #reactComponent\n [component]=\"component\"\n (componentEvent)=\"onComponentEvent($event)\"\n (openEntityRecord)=\"onOpenEntityRecord($event)\"\n (initialized)=\"onReactComponentInitialized()\">\n </mj-react-component>\n } @else if (component) {\n <!-- Component spec exists but no code - show informational message -->\n <div class=\"no-code-state\">\n <i class=\"fa-solid fa-file-lines\"></i>\n <h3>Component Specification</h3>\n <p>This component has specifications but no executable code yet.</p>\n <p>View the Functional, Data, or Spec tabs for details.</p>\n </div>\n } @else {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n <h3>No Component Loaded</h3>\n <p>The component data is missing or invalid.</p>\n </div>\n }\n }\n </div>\n\n <!-- Feedback overlay panel (invoked from header button via AskUserForFeedback) -->\n @if (ShowFeedbackPanel && resolvedComponentSpec) {\n <mj-component-feedback-panel\n [ComponentSpec]=\"resolvedComponentSpec\"\n [ReactContainerElement]=\"reactComponent?.container?.nativeElement || null\"\n (Closed)=\"ShowFeedbackPanel = false\">\n </mj-component-feedback-panel>\n }\n</div>\n", styles: [".component-artifact-viewer {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n background: var(--mj-bg-surface);\n}\n\n.component-preview {\n flex: 1;\n padding: 15px;\n overflow: auto;\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n/* Ensure React component takes full height */\n.component-preview mj-react-component {\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n\n.error-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n color: var(--mj-status-error);\n flex: 1;\n min-height: 300px;\n}\n.error-state i {\n font-size: 48px;\n margin-bottom: 16px;\n}\n.error-state h3 {\n margin: 0 0 8px 0;\n font-size: 20px;\n}\n.error-state p {\n margin: 0 0 12px 0;\n color: var(--mj-text-muted);\n}\n.error-state details {\n margin-top: 16px;\n text-align: left;\n max-width: 600px;\n}\n.error-state details summary {\n cursor: pointer;\n color: var(--mj-text-secondary);\n font-weight: 500;\n margin-bottom: 8px;\n}\n.error-state details summary:hover {\n color: var(--mj-text-primary);\n}\n.error-state details pre {\n background: var(--mj-bg-surface-sunken);\n padding: 12px;\n border-radius: 4px;\n overflow-x: auto;\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n/* State for component spec without executable code */\n.no-code-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n color: var(--mj-text-muted);\n flex: 1;\n}\n.no-code-state i {\n font-size: 48px;\n margin-bottom: 16px;\n color: var(--mj-brand-primary);\n}\n.no-code-state h3 {\n margin: 0 0 8px 0;\n font-size: 20px;\n color: var(--mj-text-secondary);\n}\n.no-code-state p {\n margin: 0 0 8px 0;\n color: var(--mj-text-muted);\n}\n\n\n/* ====================================================================\n FORM-AWARE BRANCH (componentRole === 'form')\n Top strip: record-picker chip + Apply button.\n Below: dropdown picker panel.\n Below: <mj-interactive-form> bound to the picked record.\n ==================================================================== */\n\n.form-artifact-toolstrip {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 14px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n font-size: 12px;\n color: var(--mj-text-secondary);\n}\n.form-artifact-toolstrip-label {\n font-weight: 600;\n}\n.form-artifact-toolstrip-entity {\n margin-left: 4px;\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface));\n border-radius: 999px;\n font-size: 11px;\n font-weight: 600;\n}\n.form-artifact-toolstrip-warning {\n color: var(--mj-status-warning-text);\n}\n\n.form-artifact-picker-chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n padding: 4px 10px;\n font-size: 12px;\n color: var(--mj-text-primary);\n cursor: pointer;\n}\n.form-artifact-picker-chip:hover,\n.form-artifact-picker-chip:focus {\n border-color: var(--mj-border-focus);\n outline: none;\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n.form-artifact-picker-chip strong {\n color: var(--mj-text-primary);\n font-weight: 600;\n}\n.form-artifact-picker-chip-badge {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning-border);\n font-size: 10px;\n font-weight: 700;\n padding: 1px 6px;\n border-radius: 999px;\n}\n\n.form-artifact-apply-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #ffffff);\n border: 1px solid var(--mj-brand-primary);\n border-radius: var(--mj-radius-md, 8px);\n padding: 5px 12px;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n}\n.form-artifact-apply-btn:hover {\n background: var(--mj-brand-primary-hover, var(--mj-brand-primary));\n}\n\n.form-artifact-picker-panel {\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n padding: 10px 14px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.form-artifact-picker-search {\n width: 100%;\n padding: 7px 10px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-bg-surface);\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n.form-artifact-picker-search:focus {\n outline: none;\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n.form-artifact-picker-results {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n.form-artifact-picker-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 8px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n}\n.form-artifact-picker-row:hover {\n background: var(--mj-bg-surface-hover);\n}\n.form-artifact-picker-row i {\n color: var(--mj-text-muted);\n font-size: 10px;\n}\n.form-artifact-picker-empty {\n color: var(--mj-text-muted);\n font-size: 12px;\n font-style: italic;\n}\n\n.form-artifact-form-host {\n flex: 1;\n overflow: auto;\n background: var(--mj-bg-page);\n}\n\n/* ====================================================================\n PERMISSION WARNING\n Shown before rendering when the user lacks read access to one or\n more entities/queries required by the component.\n ==================================================================== */\n\n.permission-warning {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n flex: 1;\n min-height: 300px;\n}\n.permission-warning-icon {\n font-size: 48px;\n margin-bottom: 16px;\n color: var(--mj-status-warning-text, #b45309);\n}\n.permission-warning h3 {\n margin: 0 0 8px 0;\n font-size: 20px;\n color: var(--mj-text-primary);\n}\n.permission-warning > p {\n margin: 0 0 16px 0;\n color: var(--mj-text-muted);\n}\n.permission-warning-section {\n text-align: left;\n max-width: 400px;\n width: 100%;\n margin-bottom: 12px;\n}\n.permission-warning-section-label {\n display: block;\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-muted);\n margin-bottom: 4px;\n}\n.permission-warning-section ul {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.permission-warning-section li {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 10px;\n font-size: 13px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface-sunken, var(--mj-bg-surface));\n border-radius: 6px;\n}\n.permission-warning-section li i {\n color: var(--mj-status-warning-text, #b45309);\n font-size: 12px;\n}\n.permission-warning-hint {\n margin-top: 8px;\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n"] }]
|
|
737
833
|
}], () => [{ type: i1.AngularAdapterService }], { reactComponent: [{
|
|
738
834
|
type: ViewChild,
|
|
739
835
|
args: ['reactComponent']
|
|
@@ -744,5 +840,5 @@ export { ComponentArtifactViewerComponent };
|
|
|
744
840
|
}], applyFormRequested: [{
|
|
745
841
|
type: Output
|
|
746
842
|
}] }); })();
|
|
747
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ComponentArtifactViewerComponent, { className: "ComponentArtifactViewerComponent", filePath: "src/lib/components/plugins/component-artifact-viewer.component.ts", lineNumber:
|
|
843
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ComponentArtifactViewerComponent, { className: "ComponentArtifactViewerComponent", filePath: "src/lib/components/plugins/component-artifact-viewer.component.ts", lineNumber: 26 }); })();
|
|
748
844
|
//# sourceMappingURL=component-artifact-viewer.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-artifact-viewer.component.js","sourceRoot":"","sources":["../../../../src/lib/components/plugins/component-artifact-viewer.component.ts","../../../../src/lib/components/plugins/component-artifact-viewer.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, ViewChild, AfterViewInit, OnInit, OnChanges, SimpleChanges, Output, EventEmitter } from '@angular/core';\nimport { RegisterClass, SafeJSONParse } from '@memberjunction/global';\nimport { BaseArtifactViewerPluginComponent, ArtifactViewerTab } from '../base-artifact-viewer.component';\nimport { MJReactComponent, AngularAdapterService } from '@memberjunction/ng-react';\nimport { BuildComponentCompleteCode, ComponentSpec } from '@memberjunction/interactive-component-types';\nimport { isFormRole, getDeclaredFormEntityName } from '@memberjunction/interactive-component-types/forms';\nimport { BaseEntity, CompositeKey, DataSnapshot, EntityInfo, LogError, RunView } from '@memberjunction/core';\nimport { DataRequirementsViewerComponent } from './data-requirements-viewer/data-requirements-viewer.component';\n\n/**\n * Viewer component for interactive Component artifacts (React-based UI components)\n *\n * Features:\n * - Live component preview with React rendering\n * - Dynamic tabs for component metadata (via GetAdditionalTabs)\n * - Provides tabs for: Code, Functional Requirements, Technical Design, Data Requirements\n */\n@Component({\n standalone: false,\n selector: 'mj-component-artifact-viewer',\n templateUrl: './component-artifact-viewer.component.html',\n styleUrls: ['./component-artifact-viewer.component.css']\n})\n@RegisterClass(BaseArtifactViewerPluginComponent, 'ComponentArtifactViewerPlugin')\nexport class ComponentArtifactViewerComponent extends BaseArtifactViewerPluginComponent implements OnInit, AfterViewInit, OnChanges {\n @ViewChild('reactComponent') reactComponent?: MJReactComponent;\n @Output() tabsChanged = new EventEmitter<void>();\n @Output() openEntityRecord = new EventEmitter<{entityName: string; compositeKey: CompositeKey}>();\n\n /**\n * Emitted when the user clicks \"Apply to my form\" on a form-role artifact.\n * Carries the spec (ready to hand to the agent's Create/Modify action) and\n * the entity name. The host is responsible for confirming + invoking the\n * actual server action.\n */\n @Output() applyFormRequested = new EventEmitter<{ spec: ComponentSpec; entityName: string }>();\n\n // ── Form-aware state (only populated when componentRole === 'form') ──\n\n /** True when this artifact's spec declares `componentRole: 'form'`. */\n public isFormArtifact = false;\n\n /** Entity the form targets — resolved from spec.entityName / dataRequirements. */\n public formEntityInfo: EntityInfo | null = null;\n\n /** The real or fixture record currently bound to the form preview. */\n public formRecord: BaseEntity | null = null;\n\n /** True iff `formRecord` is a real DB row (vs a fixture NewRecord()). */\n public formRecordIsReal = false;\n\n /** Label for the chip (e.g. the record's Name field). */\n public formRecordLabel = '';\n\n /** Picker UI state. */\n public showRecordPicker = false;\n public recordSearchTerm = '';\n public recordSearchResults: Array<{ ID: string; Label: string }> = [];\n public formInitError: string | null = null;\n\n // Component data\n public component: ComponentSpec | null = null;\n public componentCode: string = \"\";\n public componentName: string = '';\n\n /**\n * Cached resolved spec from the registry, preserved even after the React component\n * is destroyed (e.g., when a render error removes <mj-react-component> from the DOM).\n */\n private _cachedResolvedSpec: ComponentSpec | null = null;\n\n public get resolvedComponentSpec(): ComponentSpec | null {\n // Prefer the live React component's resolved spec (most up-to-date),\n // then fall back to our cached copy (survives DOM destruction),\n // then fall back to the stripped local spec as last resort.\n return this.reactComponent?.resolvedComponentSpec || this._cachedResolvedSpec || this.component;\n }\n\n // Feedback panel\n public ShowFeedbackPanel = false;\n\n // Error state\n public hasError = false;\n public errorMessage = '';\n public errorDetails = '';\n\n /**\n * Whether this plugin has content to display in the Display tab.\n * Returns true only if the component has code that can be rendered.\n *\n * IMPORTANT: Uses this.component (synchronously loaded from artifact JSON)\n * instead of resolvedComponentSpec (which depends on async React loading).\n * This ensures hasDisplayContent returns correct value immediately when\n * pluginLoaded fires, before React component finishes loading.\n */\n public override get hasDisplayContent(): boolean {\n // Use this.component directly - it's available synchronously after loadComponentSpec()\n return !!this.component?.namespace || !!this.component?.code\n }\n\n constructor(private adapter: AngularAdapterService) {\n super();\n }\n\n async ngOnChanges(changes: SimpleChanges): Promise<void> {\n // When artifactVersion input changes, reload the component spec\n if (changes['artifactVersion']) {\n this.loadComponentSpec();\n // Notify parent that tabs may have changed (on subsequent changes)\n if (!changes['artifactVersion'].firstChange) {\n this.tabsChanged.emit();\n }\n }\n }\n\n /**\n * Synchronously load the component spec from artifact content.\n * This is intentionally synchronous so that tabs are available immediately\n * when the parent queries GetAdditionalTabs() after pluginLoaded fires.\n *\n * The bridge purges the runtime registry + manager fetch cache itself when\n * it (re)initializes for the new spec, so we don't need to clear caches\n * from here — the bridge instance may not even exist yet when ngOnChanges\n * fires for the first time.\n */\n private loadComponentSpec(): void {\n try {\n // Clear cached resolved spec from previous version so stale data doesn't persist\n this._cachedResolvedSpec = null;\n\n if (this.artifactVersion?.Content) {\n this.component = SafeJSONParse(this.artifactVersion.Content) as ComponentSpec;\n this.extractComponentParts();\n // Form-aware detection. Done here (not in ngAfterViewInit) so the\n // template's `@if (isFormArtifact)` branch decides which preview\n // to mount on the very first render — no flash of the non-form\n // path before the form-aware UI takes over.\n this.detectAndInitFormArtifact();\n } else {\n throw new Error('Artifact content is empty');\n }\n } catch (error) {\n console.error('Failed to load component spec:', error);\n this.hasError = true;\n this.errorMessage = 'Failed to load component';\n this.errorDetails = error instanceof Error ? error.message : String(error);\n }\n }\n\n /**\n * Component initialization.\n * Note: loadComponentSpec() is called in ngOnChanges which runs before ngOnInit,\n * ensuring tabs are available when pluginLoaded fires.\n * The async adapter initialization happens here and doesn't block tab availability.\n */\n async ngOnInit(): Promise<void> {\n // Initialize Angular adapter for React components (async operation)\n try {\n await this.adapter.initialize();\n } catch (error) {\n console.error('Failed to initialize Angular adapter:', error);\n this.hasError = true;\n this.errorMessage = 'Failed to initialize component runtime';\n this.errorDetails = error instanceof Error ? error.message : String(error);\n }\n }\n\n async ngAfterViewInit(): Promise<void> {\n // Component initialization happens automatically via mj-react-component\n }\n\n /**\n * Provide additional tabs for viewing component metadata\n */\n public GetAdditionalTabs(): ArtifactViewerTab[] {\n const tabs: ArtifactViewerTab[] = [];\n\n const resolvedComponent = this.resolvedComponentSpec;\n\n if (!resolvedComponent) {\n return tabs;\n }\n\n // Functional Requirements tab\n if (resolvedComponent.functionalRequirements) {\n tabs.push({\n label: 'Functional',\n icon: 'fa-clipboard-list',\n contentType: 'markdown',\n content: resolvedComponent.functionalRequirements\n });\n }\n\n // Technical Design tab\n if (resolvedComponent.technicalDesign) {\n tabs.push({\n label: 'Technical',\n icon: 'fa-wrench',\n contentType: 'markdown',\n content: resolvedComponent.technicalDesign\n });\n }\n\n // Data Requirements tab - uses custom component for rich visualization\n if (resolvedComponent.dataRequirements) {\n tabs.push({\n label: 'Data',\n icon: 'fa-database',\n contentType: 'component',\n component: DataRequirementsViewerComponent,\n componentInputs: { dataRequirements: resolvedComponent.dataRequirements }\n });\n }\n\n // Code tab (lazy-loaded) - only show if there's actual code\n if (resolvedComponent.code && resolvedComponent.code.trim()) {\n tabs.push({\n label: 'Code',\n icon: 'fa-code',\n contentType: 'code',\n language: 'typescript',\n content: () => BuildComponentCompleteCode(resolvedComponent)\n });\n }\n\n // Spec tab - Shows fully resolved component spec in JSON format (rightmost)\n tabs.push({\n label: 'Spec',\n icon: 'fa-file-code',\n contentType: 'json',\n content: () => JSON.stringify(resolvedComponent, null, 2),\n language: 'json'\n });\n\n return tabs;\n }\n\n /**\n * Remove standard JSON tab since we provide \"Resolved JSON\" custom tab\n * The custom tab shows the fully resolved component spec instead of raw artifact JSON\n */\n public GetStandardTabRemovals(): string[] {\n return ['JSON'];\n }\n\n private extractComponentParts(): void {\n if (this.resolvedComponentSpec?.name) {\n this.componentName = this.resolvedComponentSpec.name;\n }\n if (this.resolvedComponentSpec?.code) {\n this.componentCode = BuildComponentCompleteCode(this.resolvedComponentSpec);\n }\n }\n\n /**\n * Called when MJReactComponent finishes loading the full component spec from the registry.\n * The full spec may contain Functional, Technical, and Data tabs not in the stripped spec.\n * Caches the resolved spec so it survives DOM destruction (e.g., if the component fails to\n * render and <mj-react-component> is removed by the @if/else block).\n * Emits tabsChanged so the parent panel re-evaluates allTabs and renders the new tab labels.\n */\n onReactComponentInitialized(): void {\n if (this.reactComponent?.resolvedComponentSpec &&\n this.reactComponent.resolvedComponentSpec !== this.component) {\n // Cache the resolved spec so it's available even after the React component is destroyed\n this._cachedResolvedSpec = this.reactComponent.resolvedComponentSpec;\n this.tabsChanged.emit();\n }\n }\n\n onComponentEvent(event: unknown): void {\n console.log('Component event:', event);\n\n // Handle error events from React component\n if (event && typeof event === 'object' && 'type' in event && event.type === 'error') {\n const errorEvent = event as { type: 'error'; payload: { error: string; source: string } };\n this.hasError = true;\n this.errorMessage = 'Component Failed to Load';\n this.errorDetails = errorEvent.payload.error || 'Unknown error occurred while loading the component';\n }\n }\n\n /**\n * Handle entity record open request from React component\n * Propagates the event up to parent components\n */\n onOpenEntityRecord(event: {entityName: string; key: CompositeKey}): void {\n // Transform to use 'compositeKey' name for consistency with Angular components\n this.openEntityRecord.emit({\n entityName: event.entityName,\n compositeKey: event.key\n });\n }\n\n public override GetCurrentStateSnapshot(): DataSnapshot | null {\n // First try the component's explicit or auto-captured data state.\n // MJReactComponent.getCurrentDataState() already includes the fallback\n // to intercepted RunView/RunQuery results when the React component\n // doesn't register getCurrentDataState() via callbacks.RegisterMethod.\n const dataState = this.reactComponent?.getCurrentDataState?.();\n if (dataState && typeof dataState === 'object') {\n return dataState as DataSnapshot;\n }\n\n // Fallback for components with no captured data — static mockups,\n // pure-display components, or components whose data hooks haven't fired.\n // Rather than short-circuit Analyze with \"No data available\", emit a\n // minimal snapshot so the user can still open an agent conversation about\n // the component. The artifact itself is attached to the conversation as an\n // Input junction, so the agent can reason from the spec + any static data\n // baked into the component code.\n const spec = this.resolvedComponentSpec;\n if (!spec) return null;\n\n const snap = new DataSnapshot();\n snap.title = spec.title || spec.name || this.getDisplayTitle() || undefined;\n snap.interpretation =\n `Interactive component artifact${spec.name ? ` \"${spec.name}\"` : ''}. ` +\n `No live data was captured — the component either has no data-fetching ` +\n `hooks or has not yet run its queries. The component specification is ` +\n `attached to this conversation; the agent should inspect it directly.`;\n return snap;\n }\n\n /**\n * Component artifacts support feedback when a resolved spec is available.\n */\n public override get SupportsFeedback(): boolean {\n return !!this.resolvedComponentSpec;\n }\n\n /**\n * Toggle the feedback panel open. Called from the artifact viewer header button.\n */\n public override AskUserForFeedback(): void {\n this.ShowFeedbackPanel = !this.ShowFeedbackPanel;\n }\n\n // ════════════════════════════════════════════════════════════════════\n // FORM-AWARE BRANCH\n //\n // When the artifact's spec declares `componentRole: 'form'`, the viewer\n // delegates rendering to `<mj-interactive-form>` instead of the raw\n // `<mj-react-component>` path. This gives the React form a proper\n // FormHostProps binding (bound to a real DB record by default) and lets\n // the user swap records via a search picker before applying.\n //\n // Triggered from `loadComponentSpec()` after the spec parses.\n // ════════════════════════════════════════════════════════════════════\n\n /**\n * Detect form-role and kick off entity + record resolution. Failure modes\n * (missing entity, RunView failure, entity has no rows) fall back to a\n * synthetic record from `BaseEntity.NewRecord()` so the form still mounts\n * against type-appropriate empty values.\n */\n private async detectAndInitFormArtifact(): Promise<void> {\n this.isFormArtifact = false;\n this.formEntityInfo = null;\n this.formRecord = null;\n this.formRecordIsReal = false;\n this.formRecordLabel = '';\n this.formInitError = null;\n\n const spec = this.component;\n if (!spec || !isFormRole(spec)) return;\n\n this.isFormArtifact = true;\n\n const entityName = getDeclaredFormEntityName(spec);\n if (!entityName) {\n this.formInitError = 'Form artifact has no declared entity. Showing without record context.';\n return;\n }\n\n const provider = this.ProviderToUse;\n const entity = provider?.EntityByName(entityName);\n if (!entity) {\n this.formInitError = `Entity \"${entityName}\" not registered with the active provider.`;\n return;\n }\n this.formEntityInfo = entity;\n\n // Load Top-1 record by default. If empty / fails, fall back to a fresh\n // synthetic record. Either way the form mounts — failure to find a real\n // record is informational, not fatal.\n const record = await this.loadTopOneRecord(entity);\n if (record) {\n this.formRecord = record;\n this.formRecordIsReal = true;\n this.formRecordLabel = this.computeRecordLabel(record);\n } else {\n this.formRecord = await this.buildFixtureRecord(entity);\n this.formRecordIsReal = false;\n this.formRecordLabel = 'Mock data';\n }\n }\n\n /**\n * Load the first row by NameField (then __mj_CreatedAt) so different users\n * opening the same form-role artifact see the same record bound to the\n * preview. (Retrospective fix #7 — un-ordered Top-1 was physical-order\n * and non-deterministic.)\n */\n private async loadTopOneRecord(entity: EntityInfo): Promise<BaseEntity | null> {\n try {\n const orderBy = entity.NameField?.Name\n ? `${entity.NameField.Name} ASC`\n : `__mj_CreatedAt DESC`;\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const result = await rv.RunView<BaseEntity>({\n EntityName: entity.Name,\n MaxRows: 1,\n OrderBy: orderBy,\n ResultType: 'entity_object',\n }, this.ProviderToUse.CurrentUser);\n if (result.Success && (result.Results?.length ?? 0) > 0) {\n return result.Results[0];\n }\n } catch (err) {\n LogError(`ComponentArtifactViewer: Top-1 RunView failed for ${entity.Name}: ${err instanceof Error ? err.message : String(err)}`);\n }\n return null;\n }\n\n /** Synthetic \"create-mode\" record when no real row exists or RunView failed. */\n private async buildFixtureRecord(entity: EntityInfo): Promise<BaseEntity | null> {\n try {\n const fresh = await this.ProviderToUse.GetEntityObject<BaseEntity>(entity.Name, this.ProviderToUse.CurrentUser);\n fresh.NewRecord();\n return fresh;\n } catch (err) {\n LogError(`ComponentArtifactViewer: failed to build fixture record for ${entity.Name}: ${err instanceof Error ? err.message : String(err)}`);\n return null;\n }\n }\n\n /** Pull a human-readable label from the record's NameField or first text field. */\n private computeRecordLabel(record: BaseEntity): string {\n const nameField = record.EntityInfo.NameField?.Name;\n if (nameField) {\n const v = record.Get(nameField);\n if (v != null && String(v).trim().length > 0) return String(v);\n }\n if (record.PrimaryKey?.HasValue) return record.PrimaryKey.ToConcatenatedString();\n return record.EntityInfo.Name;\n }\n\n /**\n * Search-as-you-type for the picker. Queries by name field (or any\n * indexed string field, best-effort). Limits to 8 hits for tightness.\n */\n public async onPickerSearchInput(term: string): Promise<void> {\n this.recordSearchTerm = term;\n if (!this.formEntityInfo || term.trim().length === 0) {\n this.recordSearchResults = [];\n return;\n }\n const nameField = this.formEntityInfo.NameField?.Name;\n if (!nameField) {\n this.recordSearchResults = [];\n return;\n }\n try {\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n // Best-effort LIKE filter against the entity's name field. Wrap the\n // term so we don't break SQL — RunView passes ExtraFilter as-is.\n const safe = term.replace(/'/g, \"''\");\n const result = await rv.RunView<BaseEntity>({\n EntityName: this.formEntityInfo.Name,\n ExtraFilter: `${nameField} LIKE '%${safe}%'`,\n MaxRows: 8,\n ResultType: 'entity_object',\n }, this.ProviderToUse.CurrentUser);\n if (result.Success) {\n this.recordSearchResults = (result.Results ?? []).map(r => ({\n ID: r.PrimaryKey?.ToConcatenatedString() ?? '',\n Label: this.computeRecordLabel(r),\n }));\n }\n } catch (err) {\n LogError(`ComponentArtifactViewer: picker search failed: ${err instanceof Error ? err.message : String(err)}`);\n this.recordSearchResults = [];\n }\n }\n\n /** User picked a different record from the search results. Re-bind the form. */\n public async onPickerSelect(item: { ID: string; Label: string }): Promise<void> {\n if (!this.formEntityInfo) return;\n try {\n const rec = await this.ProviderToUse.GetEntityObject<BaseEntity>(\n this.formEntityInfo.Name, this.ProviderToUse.CurrentUser,\n );\n const pk = new CompositeKey();\n pk.LoadFromURLSegment(this.formEntityInfo, item.ID);\n const loaded = await rec.InnerLoad(pk);\n if (loaded) {\n this.formRecord = rec;\n this.formRecordIsReal = true;\n this.formRecordLabel = item.Label;\n this.showRecordPicker = false;\n this.recordSearchTerm = '';\n this.recordSearchResults = [];\n }\n } catch (err) {\n LogError(`ComponentArtifactViewer: failed to load picked record ${item.ID}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n /** Bubble Apply intent up to the host (Form Builder dashboard / Sage chat). */\n public onApplyClicked(): void {\n if (!this.component || !this.formEntityInfo) return;\n this.applyFormRequested.emit({\n spec: this.component,\n entityName: this.formEntityInfo.Name,\n });\n }\n}\n","<div class=\"component-artifact-viewer\" [ngClass]=\"cssClass\">\n <!-- Component Preview (full screen) -->\n <div class=\"component-preview\">\n @if (hasError) {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <h3>{{ errorMessage }}</h3>\n <p>This component could not be loaded. This usually happens when:</p>\n <ul style=\"text-align: left; max-width: 500px; color: var(--mj-text-muted); line-height: 1.6;\">\n <li>The component registry is unavailable or unreachable</li>\n <li>The component version does not exist in the registry</li>\n <li>There was a network error during component retrieval</li>\n </ul>\n @if (errorDetails) {\n <details style=\"margin-top: 20px;\">\n <summary>Technical Details</summary>\n <pre>{{ errorDetails }}</pre>\n </details>\n }\n </div>\n } @else if (isFormArtifact) {\n <!-- ════════════════════════════════════════════════\n FORM-AWARE BRANCH\n Spec declares componentRole: 'form'. Render via\n <mj-interactive-form> bound to a real Top-1 record\n (or a synthetic NewRecord fallback). Picker chip\n lets the user swap records; Apply promotes the\n spec to an EntityFormOverride.\n ════════════════════════════════════════════════ -->\n <div class=\"form-artifact-toolstrip\">\n <i class=\"fa-solid fa-circle-info\" aria-hidden=\"true\"></i>\n <span class=\"form-artifact-toolstrip-label\">Previewing record:</span>\n <button\n type=\"button\"\n class=\"form-artifact-picker-chip\"\n (click)=\"showRecordPicker = !showRecordPicker\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n <strong>{{ formRecordLabel || 'No record' }}</strong>\n @if (!formRecordIsReal) {\n <span class=\"form-artifact-picker-chip-badge\">Mock</span>\n }\n <i class=\"fa-solid fa-chevron-down\"></i>\n </button>\n @if (formEntityInfo) {\n <span class=\"form-artifact-toolstrip-entity\">{{ formEntityInfo.DisplayName || formEntityInfo.Name }}</span>\n }\n @if (formInitError) {\n <span class=\"form-artifact-toolstrip-warning\" [title]=\"formInitError\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n </span>\n }\n <span style=\"flex: 1;\"></span>\n @if (formEntityInfo) {\n <button\n type=\"button\"\n class=\"form-artifact-apply-btn\"\n (click)=\"onApplyClicked()\">\n <i class=\"fa-solid fa-circle-check\"></i>\n Apply to my form\n </button>\n }\n </div>\n\n @if (showRecordPicker && formEntityInfo) {\n <div class=\"form-artifact-picker-panel\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"text\"\n class=\"form-artifact-picker-search\"\n placeholder=\"Search records by name…\"\n [value]=\"recordSearchTerm\"\n (input)=\"onPickerSearchInput($any($event.target).value)\" />\n @if (recordSearchResults.length > 0) {\n <ul class=\"form-artifact-picker-results\">\n @for (r of recordSearchResults; track r.ID) {\n <li\n class=\"form-artifact-picker-row\"\n (click)=\"onPickerSelect(r)\">\n <i class=\"fa-solid fa-circle-dot\"></i>\n <span>{{ r.Label }}</span>\n </li>\n }\n </ul>\n } @else if (recordSearchTerm.length > 0) {\n <div class=\"form-artifact-picker-empty\">No matches.</div>\n } @else {\n <div class=\"form-artifact-picker-empty\">Start typing to search.</div>\n }\n </div>\n }\n\n @if (formRecord && component) {\n <div class=\"form-artifact-form-host\">\n <mj-interactive-form\n [componentSpec]=\"component\"\n [record]=\"formRecord\">\n </mj-interactive-form>\n </div>\n } @else {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n <h3>Form preview unavailable</h3>\n <p>{{ formInitError || 'Could not bind a record to this form.' }}</p>\n </div>\n }\n } @else {\n <!-- Default (non-form) interactive component path -->\n @if (component && (component.code || component.namespace)) {\n <mj-react-component\n #reactComponent\n [component]=\"component\"\n (componentEvent)=\"onComponentEvent($event)\"\n (openEntityRecord)=\"onOpenEntityRecord($event)\"\n (initialized)=\"onReactComponentInitialized()\">\n </mj-react-component>\n } @else if (component) {\n <!-- Component spec exists but no code - show informational message -->\n <div class=\"no-code-state\">\n <i class=\"fa-solid fa-file-lines\"></i>\n <h3>Component Specification</h3>\n <p>This component has specifications but no executable code yet.</p>\n <p>View the Functional, Data, or Spec tabs for details.</p>\n </div>\n } @else {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n <h3>No Component Loaded</h3>\n <p>The component data is missing or invalid.</p>\n </div>\n }\n }\n </div>\n\n <!-- Feedback overlay panel (invoked from header button via AskUserForFeedback) -->\n @if (ShowFeedbackPanel && resolvedComponentSpec) {\n <mj-component-feedback-panel\n [ComponentSpec]=\"resolvedComponentSpec\"\n [ReactContainerElement]=\"reactComponent?.container?.nativeElement || null\"\n (Closed)=\"ShowFeedbackPanel = false\">\n </mj-component-feedback-panel>\n }\n</div>\n"]}
|
|
1
|
+
{"version":3,"file":"component-artifact-viewer.component.js","sourceRoot":"","sources":["../../../../src/lib/components/plugins/component-artifact-viewer.component.ts","../../../../src/lib/components/plugins/component-artifact-viewer.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, ViewChild, AfterViewInit, OnInit, OnChanges, SimpleChanges, Output, EventEmitter } from '@angular/core';\nimport { RegisterClass, SafeJSONParse } from '@memberjunction/global';\nimport { BaseArtifactViewerPluginComponent, ArtifactViewerTab } from '../base-artifact-viewer.component';\nimport { MJReactComponent, AngularAdapterService } from '@memberjunction/ng-react';\nimport { BuildComponentCompleteCode, ComponentSpec } from '@memberjunction/interactive-component-types';\nimport { isFormRole, getDeclaredFormEntityName } from '@memberjunction/interactive-component-types/forms';\nimport { BaseEntity, CompositeKey, DataSnapshot, EntityInfo, LogError, RunView } from '@memberjunction/core';\nimport { DataRequirementsViewerComponent } from './data-requirements-viewer/data-requirements-viewer.component';\nimport { evaluateComponentPermissions, PermissionEvaluationResult } from './component-permission-evaluation';\n\n/**\n * Viewer component for interactive Component artifacts (React-based UI components)\n *\n * Features:\n * - Live component preview with React rendering\n * - Dynamic tabs for component metadata (via GetAdditionalTabs)\n * - Provides tabs for: Code, Functional Requirements, Technical Design, Data Requirements\n */\n@Component({\n standalone: false,\n selector: 'mj-component-artifact-viewer',\n templateUrl: './component-artifact-viewer.component.html',\n styleUrls: ['./component-artifact-viewer.component.css']\n})\n@RegisterClass(BaseArtifactViewerPluginComponent, 'ComponentArtifactViewerPlugin')\nexport class ComponentArtifactViewerComponent extends BaseArtifactViewerPluginComponent implements OnInit, AfterViewInit, OnChanges {\n @ViewChild('reactComponent') reactComponent?: MJReactComponent;\n @Output() tabsChanged = new EventEmitter<void>();\n @Output() openEntityRecord = new EventEmitter<{entityName: string; compositeKey: CompositeKey}>();\n\n /**\n * Emitted when the user clicks \"Apply to my form\" on a form-role artifact.\n * Carries the spec (ready to hand to the agent's Create/Modify action) and\n * the entity name. The host is responsible for confirming + invoking the\n * actual server action.\n */\n @Output() applyFormRequested = new EventEmitter<{ spec: ComponentSpec; entityName: string }>();\n\n // ── Form-aware state (only populated when componentRole === 'form') ──\n\n /** True when this artifact's spec declares `componentRole: 'form'`. */\n public isFormArtifact = false;\n\n /** Entity the form targets — resolved from spec.entityName / dataRequirements. */\n public formEntityInfo: EntityInfo | null = null;\n\n /** The real or fixture record currently bound to the form preview. */\n public formRecord: BaseEntity | null = null;\n\n /** True iff `formRecord` is a real DB row (vs a fixture NewRecord()). */\n public formRecordIsReal = false;\n\n /** Label for the chip (e.g. the record's Name field). */\n public formRecordLabel = '';\n\n /** Picker UI state. */\n public showRecordPicker = false;\n public recordSearchTerm = '';\n public recordSearchResults: Array<{ ID: string; Label: string }> = [];\n public formInitError: string | null = null;\n\n // Component data\n public component: ComponentSpec | null = null;\n public componentCode: string = \"\";\n public componentName: string = '';\n\n /**\n * Cached resolved spec from the registry, preserved even after the React component\n * is destroyed (e.g., when a render error removes <mj-react-component> from the DOM).\n */\n private _cachedResolvedSpec: ComponentSpec | null = null;\n\n public get resolvedComponentSpec(): ComponentSpec | null {\n // Prefer the live React component's resolved spec (most up-to-date),\n // then fall back to our cached copy (survives DOM destruction),\n // then fall back to the stripped local spec as last resort.\n return this.reactComponent?.resolvedComponentSpec || this._cachedResolvedSpec || this.component;\n }\n\n // Feedback panel\n public ShowFeedbackPanel = false;\n\n // Error state\n public hasError = false;\n public errorMessage = '';\n public errorDetails = '';\n\n // Permission state\n public permissionResult: PermissionEvaluationResult | null = null;\n\n /**\n * Whether this plugin has content to display in the Display tab.\n * Returns true only if the component has code that can be rendered.\n *\n * IMPORTANT: Uses this.component (synchronously loaded from artifact JSON)\n * instead of resolvedComponentSpec (which depends on async React loading).\n * This ensures hasDisplayContent returns correct value immediately when\n * pluginLoaded fires, before React component finishes loading.\n */\n public override get hasDisplayContent(): boolean {\n // Use this.component directly - it's available synchronously after loadComponentSpec()\n return !!this.component?.namespace || !!this.component?.code\n }\n\n constructor(private adapter: AngularAdapterService) {\n super();\n }\n\n async ngOnChanges(changes: SimpleChanges): Promise<void> {\n // When artifactVersion input changes, reload the component spec\n if (changes['artifactVersion']) {\n this.loadComponentSpec();\n // Notify parent that tabs may have changed (on subsequent changes)\n if (!changes['artifactVersion'].firstChange) {\n this.tabsChanged.emit();\n }\n }\n }\n\n /**\n * Synchronously load the component spec from artifact content.\n * This is intentionally synchronous so that tabs are available immediately\n * when the parent queries GetAdditionalTabs() after pluginLoaded fires.\n *\n * The bridge purges the runtime registry + manager fetch cache itself when\n * it (re)initializes for the new spec, so we don't need to clear caches\n * from here — the bridge instance may not even exist yet when ngOnChanges\n * fires for the first time.\n */\n private loadComponentSpec(): void {\n try {\n // Clear cached resolved spec from previous version so stale data doesn't persist\n this._cachedResolvedSpec = null;\n this.permissionResult = null;\n\n if (this.artifactVersion?.Content) {\n this.component = SafeJSONParse(this.artifactVersion.Content) as ComponentSpec;\n this.extractComponentParts();\n this.evaluatePermissions();\n // Form-aware detection. Done here (not in ngAfterViewInit) so the\n // template's `@if (isFormArtifact)` branch decides which preview\n // to mount on the very first render — no flash of the non-form\n // path before the form-aware UI takes over.\n this.detectAndInitFormArtifact();\n } else {\n throw new Error('Artifact content is empty');\n }\n } catch (error) {\n console.error('Failed to load component spec:', error);\n this.hasError = true;\n this.errorMessage = 'Failed to load component';\n this.errorDetails = error instanceof Error ? error.message : String(error);\n }\n }\n\n /**\n * Evaluate whether the current user has sufficient permissions for\n * all entities and queries referenced in the component's dataRequirements.\n * Uses the best available spec: resolved (from registry) > cached > stripped artifact.\n * Runs synchronously against already-loaded client-side metadata.\n */\n private evaluatePermissions(): void {\n const spec = this.resolvedComponentSpec;\n if (!spec) return;\n\n const provider = this.ProviderToUse;\n const currentUser = provider.CurrentUser;\n if (!currentUser) return; // No user context — skip check\n\n this.permissionResult = evaluateComponentPermissions(spec, currentUser, provider);\n }\n\n /** Whether the component should be blocked from rendering due to missing permissions. */\n public get isPermissionBlocked(): boolean {\n return !!this.permissionResult && !this.permissionResult.canRun;\n }\n\n /**\n * Component initialization.\n * Note: loadComponentSpec() is called in ngOnChanges which runs before ngOnInit,\n * ensuring tabs are available when pluginLoaded fires.\n * The async adapter initialization happens here and doesn't block tab availability.\n */\n async ngOnInit(): Promise<void> {\n // Initialize Angular adapter for React components (async operation)\n try {\n await this.adapter.initialize();\n } catch (error) {\n console.error('Failed to initialize Angular adapter:', error);\n this.hasError = true;\n this.errorMessage = 'Failed to initialize component runtime';\n this.errorDetails = error instanceof Error ? error.message : String(error);\n }\n }\n\n async ngAfterViewInit(): Promise<void> {\n // Component initialization happens automatically via mj-react-component\n }\n\n /**\n * Provide additional tabs for viewing component metadata\n */\n public GetAdditionalTabs(): ArtifactViewerTab[] {\n const tabs: ArtifactViewerTab[] = [];\n\n const resolvedComponent = this.resolvedComponentSpec;\n\n if (!resolvedComponent) {\n return tabs;\n }\n\n // Functional Requirements tab\n if (resolvedComponent.functionalRequirements) {\n tabs.push({\n label: 'Functional',\n icon: 'fa-clipboard-list',\n contentType: 'markdown',\n content: resolvedComponent.functionalRequirements\n });\n }\n\n // Technical Design tab\n if (resolvedComponent.technicalDesign) {\n tabs.push({\n label: 'Technical',\n icon: 'fa-wrench',\n contentType: 'markdown',\n content: resolvedComponent.technicalDesign\n });\n }\n\n // Data Requirements tab - uses custom component for rich visualization\n if (resolvedComponent.dataRequirements) {\n tabs.push({\n label: 'Data',\n icon: 'fa-database',\n contentType: 'component',\n component: DataRequirementsViewerComponent,\n componentInputs: { dataRequirements: resolvedComponent.dataRequirements }\n });\n }\n\n // Code tab (lazy-loaded) - only show if there's actual code\n if (resolvedComponent.code && resolvedComponent.code.trim()) {\n tabs.push({\n label: 'Code',\n icon: 'fa-code',\n contentType: 'code',\n language: 'typescript',\n content: () => BuildComponentCompleteCode(resolvedComponent)\n });\n }\n\n // Spec tab - Shows fully resolved component spec in JSON format (rightmost)\n tabs.push({\n label: 'Spec',\n icon: 'fa-file-code',\n contentType: 'json',\n content: () => JSON.stringify(resolvedComponent, null, 2),\n language: 'json'\n });\n\n return tabs;\n }\n\n /**\n * Remove standard JSON tab since we provide \"Resolved JSON\" custom tab\n * The custom tab shows the fully resolved component spec instead of raw artifact JSON\n */\n public GetStandardTabRemovals(): string[] {\n return ['JSON'];\n }\n\n private extractComponentParts(): void {\n if (this.resolvedComponentSpec?.name) {\n this.componentName = this.resolvedComponentSpec.name;\n }\n if (this.resolvedComponentSpec?.code) {\n this.componentCode = BuildComponentCompleteCode(this.resolvedComponentSpec);\n }\n }\n\n /**\n * Called when MJReactComponent finishes loading the full component spec from the registry.\n * The full spec may contain Functional, Technical, and Data tabs not in the stripped spec.\n * Caches the resolved spec so it survives DOM destruction (e.g., if the component fails to\n * render and <mj-react-component> is removed by the @if/else block).\n * Emits tabsChanged so the parent panel re-evaluates allTabs and renders the new tab labels.\n */\n onReactComponentInitialized(): void {\n if (this.reactComponent?.resolvedComponentSpec &&\n this.reactComponent.resolvedComponentSpec !== this.component) {\n // Cache the resolved spec so it's available even after the React component is destroyed\n this._cachedResolvedSpec = this.reactComponent.resolvedComponentSpec;\n this.tabsChanged.emit();\n\n // Re-evaluate permissions against the resolved spec — the stripped artifact\n // spec has no dataRequirements, so the initial check in loadComponentSpec()\n // passes trivially. The resolved spec from the registry contains the full\n // dataRequirements with entity and query references.\n this.evaluatePermissions();\n }\n }\n\n onComponentEvent(event: unknown): void {\n console.log('Component event:', event);\n\n // Handle error events from React component\n if (event && typeof event === 'object' && 'type' in event && event.type === 'error') {\n const errorEvent = event as { type: 'error'; payload: { error: string; source: string } };\n this.hasError = true;\n this.errorMessage = 'Component Failed to Load';\n this.errorDetails = errorEvent.payload.error || 'Unknown error occurred while loading the component';\n }\n }\n\n /**\n * Handle entity record open request from React component\n * Propagates the event up to parent components\n */\n onOpenEntityRecord(event: {entityName: string; key: CompositeKey}): void {\n // Transform to use 'compositeKey' name for consistency with Angular components\n this.openEntityRecord.emit({\n entityName: event.entityName,\n compositeKey: event.key\n });\n }\n\n public override GetCurrentStateSnapshot(): DataSnapshot | null {\n // First try the component's explicit or auto-captured data state.\n // MJReactComponent.getCurrentDataState() already includes the fallback\n // to intercepted RunView/RunQuery results when the React component\n // doesn't register getCurrentDataState() via callbacks.RegisterMethod.\n const dataState = this.reactComponent?.getCurrentDataState?.();\n if (dataState && typeof dataState === 'object') {\n return dataState as DataSnapshot;\n }\n\n // Fallback for components with no captured data — static mockups,\n // pure-display components, or components whose data hooks haven't fired.\n // Rather than short-circuit Analyze with \"No data available\", emit a\n // minimal snapshot so the user can still open an agent conversation about\n // the component. The artifact itself is attached to the conversation as an\n // Input junction, so the agent can reason from the spec + any static data\n // baked into the component code.\n const spec = this.resolvedComponentSpec;\n if (!spec) return null;\n\n const snap = new DataSnapshot();\n snap.title = spec.title || spec.name || this.getDisplayTitle() || undefined;\n snap.interpretation =\n `Interactive component artifact${spec.name ? ` \"${spec.name}\"` : ''}. ` +\n `No live data was captured — the component either has no data-fetching ` +\n `hooks or has not yet run its queries. The component specification is ` +\n `attached to this conversation; the agent should inspect it directly.`;\n return snap;\n }\n\n /**\n * Component artifacts support feedback when a resolved spec is available.\n */\n public override get SupportsFeedback(): boolean {\n return !!this.resolvedComponentSpec;\n }\n\n /**\n * Toggle the feedback panel open. Called from the artifact viewer header button.\n */\n public override AskUserForFeedback(): void {\n this.ShowFeedbackPanel = !this.ShowFeedbackPanel;\n }\n\n // ════════════════════════════════════════════════════════════════════\n // FORM-AWARE BRANCH\n //\n // When the artifact's spec declares `componentRole: 'form'`, the viewer\n // delegates rendering to `<mj-interactive-form>` instead of the raw\n // `<mj-react-component>` path. This gives the React form a proper\n // FormHostProps binding (bound to a real DB record by default) and lets\n // the user swap records via a search picker before applying.\n //\n // Triggered from `loadComponentSpec()` after the spec parses.\n // ════════════════════════════════════════════════════════════════════\n\n /**\n * Detect form-role and kick off entity + record resolution. Failure modes\n * (missing entity, RunView failure, entity has no rows) fall back to a\n * synthetic record from `BaseEntity.NewRecord()` so the form still mounts\n * against type-appropriate empty values.\n */\n private async detectAndInitFormArtifact(): Promise<void> {\n this.isFormArtifact = false;\n this.formEntityInfo = null;\n this.formRecord = null;\n this.formRecordIsReal = false;\n this.formRecordLabel = '';\n this.formInitError = null;\n\n const spec = this.component;\n if (!spec || !isFormRole(spec)) return;\n\n this.isFormArtifact = true;\n\n const entityName = getDeclaredFormEntityName(spec);\n if (!entityName) {\n this.formInitError = 'Form artifact has no declared entity. Showing without record context.';\n return;\n }\n\n const provider = this.ProviderToUse;\n const entity = provider?.EntityByName(entityName);\n if (!entity) {\n this.formInitError = `Entity \"${entityName}\" not registered with the active provider.`;\n return;\n }\n this.formEntityInfo = entity;\n\n // Load Top-1 record by default. If empty / fails, fall back to a fresh\n // synthetic record. Either way the form mounts — failure to find a real\n // record is informational, not fatal.\n const record = await this.loadTopOneRecord(entity);\n if (record) {\n this.formRecord = record;\n this.formRecordIsReal = true;\n this.formRecordLabel = this.computeRecordLabel(record);\n } else {\n this.formRecord = await this.buildFixtureRecord(entity);\n this.formRecordIsReal = false;\n this.formRecordLabel = 'Mock data';\n }\n }\n\n /**\n * Load the first row by NameField (then __mj_CreatedAt) so different users\n * opening the same form-role artifact see the same record bound to the\n * preview. (Retrospective fix #7 — un-ordered Top-1 was physical-order\n * and non-deterministic.)\n */\n private async loadTopOneRecord(entity: EntityInfo): Promise<BaseEntity | null> {\n try {\n const orderBy = entity.NameField?.Name\n ? `${entity.NameField.Name} ASC`\n : `__mj_CreatedAt DESC`;\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const result = await rv.RunView<BaseEntity>({\n EntityName: entity.Name,\n MaxRows: 1,\n OrderBy: orderBy,\n ResultType: 'entity_object',\n }, this.ProviderToUse.CurrentUser);\n if (result.Success && (result.Results?.length ?? 0) > 0) {\n return result.Results[0];\n }\n } catch (err) {\n LogError(`ComponentArtifactViewer: Top-1 RunView failed for ${entity.Name}: ${err instanceof Error ? err.message : String(err)}`);\n }\n return null;\n }\n\n /** Synthetic \"create-mode\" record when no real row exists or RunView failed. */\n private async buildFixtureRecord(entity: EntityInfo): Promise<BaseEntity | null> {\n try {\n const fresh = await this.ProviderToUse.GetEntityObject<BaseEntity>(entity.Name, this.ProviderToUse.CurrentUser);\n fresh.NewRecord();\n return fresh;\n } catch (err) {\n LogError(`ComponentArtifactViewer: failed to build fixture record for ${entity.Name}: ${err instanceof Error ? err.message : String(err)}`);\n return null;\n }\n }\n\n /** Pull a human-readable label from the record's NameField or first text field. */\n private computeRecordLabel(record: BaseEntity): string {\n const nameField = record.EntityInfo.NameField?.Name;\n if (nameField) {\n const v = record.Get(nameField);\n if (v != null && String(v).trim().length > 0) return String(v);\n }\n if (record.PrimaryKey?.HasValue) return record.PrimaryKey.ToConcatenatedString();\n return record.EntityInfo.Name;\n }\n\n /**\n * Search-as-you-type for the picker. Queries by name field (or any\n * indexed string field, best-effort). Limits to 8 hits for tightness.\n */\n public async onPickerSearchInput(term: string): Promise<void> {\n this.recordSearchTerm = term;\n if (!this.formEntityInfo || term.trim().length === 0) {\n this.recordSearchResults = [];\n return;\n }\n const nameField = this.formEntityInfo.NameField?.Name;\n if (!nameField) {\n this.recordSearchResults = [];\n return;\n }\n try {\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n // Best-effort LIKE filter against the entity's name field. Wrap the\n // term so we don't break SQL — RunView passes ExtraFilter as-is.\n const safe = term.replace(/'/g, \"''\");\n const result = await rv.RunView<BaseEntity>({\n EntityName: this.formEntityInfo.Name,\n ExtraFilter: `${nameField} LIKE '%${safe}%'`,\n MaxRows: 8,\n ResultType: 'entity_object',\n }, this.ProviderToUse.CurrentUser);\n if (result.Success) {\n this.recordSearchResults = (result.Results ?? []).map(r => ({\n ID: r.PrimaryKey?.ToConcatenatedString() ?? '',\n Label: this.computeRecordLabel(r),\n }));\n }\n } catch (err) {\n LogError(`ComponentArtifactViewer: picker search failed: ${err instanceof Error ? err.message : String(err)}`);\n this.recordSearchResults = [];\n }\n }\n\n /** User picked a different record from the search results. Re-bind the form. */\n public async onPickerSelect(item: { ID: string; Label: string }): Promise<void> {\n if (!this.formEntityInfo) return;\n try {\n const rec = await this.ProviderToUse.GetEntityObject<BaseEntity>(\n this.formEntityInfo.Name, this.ProviderToUse.CurrentUser,\n );\n const pk = new CompositeKey();\n pk.LoadFromURLSegment(this.formEntityInfo, item.ID);\n const loaded = await rec.InnerLoad(pk);\n if (loaded) {\n this.formRecord = rec;\n this.formRecordIsReal = true;\n this.formRecordLabel = item.Label;\n this.showRecordPicker = false;\n this.recordSearchTerm = '';\n this.recordSearchResults = [];\n }\n } catch (err) {\n LogError(`ComponentArtifactViewer: failed to load picked record ${item.ID}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n /** Bubble Apply intent up to the host (Form Builder dashboard / Sage chat). */\n public onApplyClicked(): void {\n if (!this.component || !this.formEntityInfo) return;\n this.applyFormRequested.emit({\n spec: this.component,\n entityName: this.formEntityInfo.Name,\n });\n }\n}\n","<div class=\"component-artifact-viewer\" [ngClass]=\"cssClass\">\n <!-- Component Preview (full screen) -->\n <div class=\"component-preview\">\n @if (hasError) {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <h3>{{ errorMessage }}</h3>\n <p>This component could not be loaded. This usually happens when:</p>\n <ul style=\"text-align: left; max-width: 500px; color: var(--mj-text-muted); line-height: 1.6;\">\n <li>The component registry is unavailable or unreachable</li>\n <li>The component version does not exist in the registry</li>\n <li>There was a network error during component retrieval</li>\n </ul>\n @if (errorDetails) {\n <details style=\"margin-top: 20px;\">\n <summary>Technical Details</summary>\n <pre>{{ errorDetails }}</pre>\n </details>\n }\n </div>\n } @else if (isFormArtifact) {\n <!-- ════════════════════════════════════════════════\n FORM-AWARE BRANCH\n Spec declares componentRole: 'form'. Render via\n <mj-interactive-form> bound to a real Top-1 record\n (or a synthetic NewRecord fallback). Picker chip\n lets the user swap records; Apply promotes the\n spec to an EntityFormOverride.\n ════════════════════════════════════════════════ -->\n <div class=\"form-artifact-toolstrip\">\n <i class=\"fa-solid fa-circle-info\" aria-hidden=\"true\"></i>\n <span class=\"form-artifact-toolstrip-label\">Previewing record:</span>\n <button\n type=\"button\"\n class=\"form-artifact-picker-chip\"\n (click)=\"showRecordPicker = !showRecordPicker\">\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n <strong>{{ formRecordLabel || 'No record' }}</strong>\n @if (!formRecordIsReal) {\n <span class=\"form-artifact-picker-chip-badge\">Mock</span>\n }\n <i class=\"fa-solid fa-chevron-down\"></i>\n </button>\n @if (formEntityInfo) {\n <span class=\"form-artifact-toolstrip-entity\">{{ formEntityInfo.DisplayName || formEntityInfo.Name }}</span>\n }\n @if (formInitError) {\n <span class=\"form-artifact-toolstrip-warning\" [title]=\"formInitError\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n </span>\n }\n <span style=\"flex: 1;\"></span>\n @if (formEntityInfo) {\n <button\n type=\"button\"\n class=\"form-artifact-apply-btn\"\n (click)=\"onApplyClicked()\">\n <i class=\"fa-solid fa-circle-check\"></i>\n Apply to my form\n </button>\n }\n </div>\n\n @if (showRecordPicker && formEntityInfo) {\n <div class=\"form-artifact-picker-panel\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"text\"\n class=\"form-artifact-picker-search\"\n placeholder=\"Search records by name…\"\n [value]=\"recordSearchTerm\"\n (input)=\"onPickerSearchInput($any($event.target).value)\" />\n @if (recordSearchResults.length > 0) {\n <ul class=\"form-artifact-picker-results\">\n @for (r of recordSearchResults; track r.ID) {\n <li\n class=\"form-artifact-picker-row\"\n (click)=\"onPickerSelect(r)\">\n <i class=\"fa-solid fa-circle-dot\"></i>\n <span>{{ r.Label }}</span>\n </li>\n }\n </ul>\n } @else if (recordSearchTerm.length > 0) {\n <div class=\"form-artifact-picker-empty\">No matches.</div>\n } @else {\n <div class=\"form-artifact-picker-empty\">Start typing to search.</div>\n }\n </div>\n }\n\n @if (formRecord && component) {\n <div class=\"form-artifact-form-host\">\n <mj-interactive-form\n [componentSpec]=\"component\"\n [record]=\"formRecord\">\n </mj-interactive-form>\n </div>\n } @else {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n <h3>Form preview unavailable</h3>\n <p>{{ formInitError || 'Could not bind a record to this form.' }}</p>\n </div>\n }\n } @else if (isPermissionBlocked) {\n <!-- Permission warning — user lacks access to one or more data sources -->\n <div class=\"permission-warning\">\n <div class=\"permission-warning-icon\">\n <i class=\"fa-solid fa-shield-halved\"></i>\n </div>\n <h3>Insufficient Permissions</h3>\n <p>This component requires data access that you don't currently have:</p>\n\n @if (permissionResult!.missingEntities.length > 0) {\n <div class=\"permission-warning-section\">\n <span class=\"permission-warning-section-label\">Entities</span>\n <ul>\n @for (entity of permissionResult!.missingEntities; track entity) {\n <li><i class=\"fa-solid fa-table\"></i> {{ entity }}</li>\n }\n </ul>\n </div>\n }\n\n @if (permissionResult!.missingQueries.length > 0) {\n <div class=\"permission-warning-section\">\n <span class=\"permission-warning-section-label\">Queries</span>\n <ul>\n @for (query of permissionResult!.missingQueries; track query) {\n <li><i class=\"fa-solid fa-database\"></i> {{ query }}</li>\n }\n </ul>\n </div>\n }\n\n <p class=\"permission-warning-hint\">Contact your administrator to request access.</p>\n </div>\n } @else {\n <!-- Default (non-form) interactive component path -->\n @if (component && (component.code || component.namespace)) {\n <mj-react-component\n #reactComponent\n [component]=\"component\"\n (componentEvent)=\"onComponentEvent($event)\"\n (openEntityRecord)=\"onOpenEntityRecord($event)\"\n (initialized)=\"onReactComponentInitialized()\">\n </mj-react-component>\n } @else if (component) {\n <!-- Component spec exists but no code - show informational message -->\n <div class=\"no-code-state\">\n <i class=\"fa-solid fa-file-lines\"></i>\n <h3>Component Specification</h3>\n <p>This component has specifications but no executable code yet.</p>\n <p>View the Functional, Data, or Spec tabs for details.</p>\n </div>\n } @else {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n <h3>No Component Loaded</h3>\n <p>The component data is missing or invalid.</p>\n </div>\n }\n }\n </div>\n\n <!-- Feedback overlay panel (invoked from header button via AskUserForFeedback) -->\n @if (ShowFeedbackPanel && resolvedComponentSpec) {\n <mj-component-feedback-panel\n [ComponentSpec]=\"resolvedComponentSpec\"\n [ReactContainerElement]=\"reactComponent?.container?.nativeElement || null\"\n (Closed)=\"ShowFeedbackPanel = false\">\n </mj-component-feedback-panel>\n }\n</div>\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { IMetadataProvider, UserInfo } from '@memberjunction/core';
|
|
2
|
+
import { ComponentSpec } from '@memberjunction/interactive-component-types';
|
|
3
|
+
/**
|
|
4
|
+
* Result of evaluating a user's permissions against a component's data requirements.
|
|
5
|
+
*/
|
|
6
|
+
export interface PermissionEvaluationResult {
|
|
7
|
+
/** Whether the user can run all data operations the component requires */
|
|
8
|
+
canRun: boolean;
|
|
9
|
+
/** Entity names the user lacks read permission for */
|
|
10
|
+
missingEntities: string[];
|
|
11
|
+
/** Query names the user lacks permission to execute */
|
|
12
|
+
missingQueries: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Recursively evaluates permissions for a component and all its dependencies.
|
|
16
|
+
* Walks the full dependency tree, deduplicating results.
|
|
17
|
+
*/
|
|
18
|
+
export declare function evaluateComponentPermissions(spec: ComponentSpec, currentUser: UserInfo, provider?: IMetadataProvider): PermissionEvaluationResult;
|
|
19
|
+
//# sourceMappingURL=component-permission-evaluation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-permission-evaluation.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/plugins/component-permission-evaluation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAY,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,6CAA6C,CAAC;AAG5E;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACvC,0EAA0E;IAC1E,MAAM,EAAE,OAAO,CAAC;IAChB,sDAAsD;IACtD,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,uDAAuD;IACvD,cAAc,EAAE,MAAM,EAAE,CAAC;CAC5B;AAuDD;;;GAGG;AACH,wBAAgB,4BAA4B,CACxC,IAAI,EAAE,aAAa,EACnB,WAAW,EAAE,QAAQ,EACrB,QAAQ,CAAC,EAAE,iBAAiB,GAC7B,0BAA0B,CAe5B"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Metadata } from '@memberjunction/core';
|
|
2
|
+
import { QueryEngine } from '@memberjunction/core-entities';
|
|
3
|
+
/**
|
|
4
|
+
* Evaluates whether a user has sufficient permissions to run a component
|
|
5
|
+
* based on its declared dataRequirements. Checks both entity-level read
|
|
6
|
+
* permissions and query-level execution permissions.
|
|
7
|
+
*
|
|
8
|
+
* Runs entirely on client-side metadata — no additional server call needed.
|
|
9
|
+
*/
|
|
10
|
+
function evaluateComponentPermissionsSingle(spec, currentUser, provider) {
|
|
11
|
+
const missingEntities = [];
|
|
12
|
+
const missingQueries = [];
|
|
13
|
+
const dataReqs = spec.dataRequirements;
|
|
14
|
+
if (!dataReqs) {
|
|
15
|
+
return { canRun: true, missingEntities: [], missingQueries: [] };
|
|
16
|
+
}
|
|
17
|
+
// Check entity permissions (all current components are read-only)
|
|
18
|
+
const md = provider ?? Metadata.Provider;
|
|
19
|
+
for (const entityReq of dataReqs.entities ?? []) {
|
|
20
|
+
const entityInfo = md.EntityByName(entityReq.name);
|
|
21
|
+
if (!entityInfo)
|
|
22
|
+
continue; // Unknown entity — skip (may be stale reference)
|
|
23
|
+
const perms = entityInfo.GetUserPermisions(currentUser);
|
|
24
|
+
if (!perms?.CanRead) {
|
|
25
|
+
missingEntities.push(entityReq.name);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// Check query permissions using centralized UserCanRun logic
|
|
29
|
+
const qe = QueryEngine.Instance;
|
|
30
|
+
for (const queryReq of dataReqs.queries ?? []) {
|
|
31
|
+
if (!queryReq.name)
|
|
32
|
+
continue;
|
|
33
|
+
const queryInfo = qe.FindQueryByName(queryReq.name);
|
|
34
|
+
if (!queryInfo)
|
|
35
|
+
continue; // Unknown query — skip
|
|
36
|
+
const result = queryInfo.UserCanRun(currentUser);
|
|
37
|
+
if (!result.canRun) {
|
|
38
|
+
missingQueries.push(queryReq.name);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
canRun: missingEntities.length === 0 && missingQueries.length === 0,
|
|
43
|
+
missingEntities,
|
|
44
|
+
missingQueries,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Recursively evaluates permissions for a component and all its dependencies.
|
|
49
|
+
* Walks the full dependency tree, deduplicating results.
|
|
50
|
+
*/
|
|
51
|
+
export function evaluateComponentPermissions(spec, currentUser, provider) {
|
|
52
|
+
const result = evaluateComponentPermissionsSingle(spec, currentUser, provider);
|
|
53
|
+
for (const dep of spec.dependencies ?? []) {
|
|
54
|
+
const depResult = evaluateComponentPermissions(dep, currentUser, provider);
|
|
55
|
+
result.missingEntities.push(...depResult.missingEntities);
|
|
56
|
+
result.missingQueries.push(...depResult.missingQueries);
|
|
57
|
+
}
|
|
58
|
+
// Deduplicate
|
|
59
|
+
result.missingEntities = [...new Set(result.missingEntities)];
|
|
60
|
+
result.missingQueries = [...new Set(result.missingQueries)];
|
|
61
|
+
result.canRun = result.missingEntities.length === 0 && result.missingQueries.length === 0;
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=component-permission-evaluation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-permission-evaluation.js","sourceRoot":"","sources":["../../../../src/lib/components/plugins/component-permission-evaluation.ts"],"names":[],"mappings":"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","sourcesContent":["import { IMetadataProvider, Metadata, UserInfo } from '@memberjunction/core';\nimport { ComponentSpec } from '@memberjunction/interactive-component-types';\nimport { QueryEngine } from '@memberjunction/core-entities';\n\n/**\n * Result of evaluating a user's permissions against a component's data requirements.\n */\nexport interface PermissionEvaluationResult {\n /** Whether the user can run all data operations the component requires */\n canRun: boolean;\n /** Entity names the user lacks read permission for */\n missingEntities: string[];\n /** Query names the user lacks permission to execute */\n missingQueries: string[];\n}\n\n/**\n * Evaluates whether a user has sufficient permissions to run a component\n * based on its declared dataRequirements. Checks both entity-level read\n * permissions and query-level execution permissions.\n *\n * Runs entirely on client-side metadata — no additional server call needed.\n */\nfunction evaluateComponentPermissionsSingle(\n spec: ComponentSpec,\n currentUser: UserInfo,\n provider?: IMetadataProvider\n): PermissionEvaluationResult {\n const missingEntities: string[] = [];\n const missingQueries: string[] = [];\n const dataReqs = spec.dataRequirements;\n\n if (!dataReqs) {\n return { canRun: true, missingEntities: [], missingQueries: [] };\n }\n\n // Check entity permissions (all current components are read-only)\n const md = provider ?? Metadata.Provider;\n for (const entityReq of dataReqs.entities ?? []) {\n const entityInfo = md.EntityByName(entityReq.name);\n if (!entityInfo) continue; // Unknown entity — skip (may be stale reference)\n\n const perms = entityInfo.GetUserPermisions(currentUser);\n if (!perms?.CanRead) {\n missingEntities.push(entityReq.name);\n }\n }\n\n // Check query permissions using centralized UserCanRun logic\n const qe = QueryEngine.Instance;\n for (const queryReq of dataReqs.queries ?? []) {\n if (!queryReq.name) continue;\n\n const queryInfo = qe.FindQueryByName(queryReq.name);\n if (!queryInfo) continue; // Unknown query — skip\n\n const result = queryInfo.UserCanRun(currentUser);\n if (!result.canRun) {\n missingQueries.push(queryReq.name);\n }\n }\n\n return {\n canRun: missingEntities.length === 0 && missingQueries.length === 0,\n missingEntities,\n missingQueries,\n };\n}\n\n/**\n * Recursively evaluates permissions for a component and all its dependencies.\n * Walks the full dependency tree, deduplicating results.\n */\nexport function evaluateComponentPermissions(\n spec: ComponentSpec,\n currentUser: UserInfo,\n provider?: IMetadataProvider\n): PermissionEvaluationResult {\n const result = evaluateComponentPermissionsSingle(spec, currentUser, provider);\n\n for (const dep of spec.dependencies ?? []) {\n const depResult = evaluateComponentPermissions(dep, currentUser, provider);\n result.missingEntities.push(...depResult.missingEntities);\n result.missingQueries.push(...depResult.missingQueries);\n }\n\n // Deduplicate\n result.missingEntities = [...new Set(result.missingEntities)];\n result.missingQueries = [...new Set(result.missingQueries)];\n result.canRun = result.missingEntities.length === 0 && result.missingQueries.length === 0;\n\n return result;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-artifacts",
|
|
3
|
-
"version": "5.40.
|
|
3
|
+
"version": "5.40.2",
|
|
4
4
|
"description": "MemberJunction: Artifact viewer plugin system for rendering different artifact types (JSON, Code, Markdown, HTML, SVG, Components)",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -32,23 +32,23 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@angular/cdk": "21.1.3",
|
|
34
34
|
"@angular/forms": "21.1.3",
|
|
35
|
-
"@memberjunction/core": "5.40.
|
|
36
|
-
"@memberjunction/core-entities": "5.40.
|
|
37
|
-
"@memberjunction/global": "5.40.
|
|
38
|
-
"@memberjunction/graphql-dataprovider": "5.40.
|
|
39
|
-
"@memberjunction/interactive-component-types": "5.40.
|
|
40
|
-
"@memberjunction/ng-base-forms": "5.40.
|
|
41
|
-
"@memberjunction/ng-base-types": "5.40.
|
|
42
|
-
"@memberjunction/ng-code-editor": "5.40.
|
|
43
|
-
"@memberjunction/ng-export-service": "5.40.
|
|
44
|
-
"@memberjunction/ng-markdown": "5.40.
|
|
45
|
-
"@memberjunction/ng-notifications": "5.40.
|
|
46
|
-
"@memberjunction/ng-pagination": "5.40.
|
|
47
|
-
"@memberjunction/ng-query-viewer": "5.40.
|
|
48
|
-
"@memberjunction/ng-react": "5.40.
|
|
49
|
-
"@memberjunction/ng-shared-generic": "5.40.
|
|
50
|
-
"@memberjunction/ng-trees": "5.40.
|
|
51
|
-
"@memberjunction/ng-ui-components": "5.40.
|
|
35
|
+
"@memberjunction/core": "5.40.2",
|
|
36
|
+
"@memberjunction/core-entities": "5.40.2",
|
|
37
|
+
"@memberjunction/global": "5.40.2",
|
|
38
|
+
"@memberjunction/graphql-dataprovider": "5.40.2",
|
|
39
|
+
"@memberjunction/interactive-component-types": "5.40.2",
|
|
40
|
+
"@memberjunction/ng-base-forms": "5.40.2",
|
|
41
|
+
"@memberjunction/ng-base-types": "5.40.2",
|
|
42
|
+
"@memberjunction/ng-code-editor": "5.40.2",
|
|
43
|
+
"@memberjunction/ng-export-service": "5.40.2",
|
|
44
|
+
"@memberjunction/ng-markdown": "5.40.2",
|
|
45
|
+
"@memberjunction/ng-notifications": "5.40.2",
|
|
46
|
+
"@memberjunction/ng-pagination": "5.40.2",
|
|
47
|
+
"@memberjunction/ng-query-viewer": "5.40.2",
|
|
48
|
+
"@memberjunction/ng-react": "5.40.2",
|
|
49
|
+
"@memberjunction/ng-shared-generic": "5.40.2",
|
|
50
|
+
"@memberjunction/ng-trees": "5.40.2",
|
|
51
|
+
"@memberjunction/ng-ui-components": "5.40.2",
|
|
52
52
|
"mammoth": "^1.8.0",
|
|
53
53
|
"pdfjs-dist": "^4.10.38",
|
|
54
54
|
"rxjs": "^7.8.2",
|