@memberjunction/ng-explorer-core 5.38.0 → 5.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app-routing.module.d.ts.map +1 -1
- package/dist/app-routing.module.js +13 -13
- package/dist/app-routing.module.js.map +1 -1
- package/dist/generated/lazy-feature-config.d.ts +1 -1
- package/dist/generated/lazy-feature-config.d.ts.map +1 -1
- package/dist/generated/lazy-feature-config.js +3 -2
- package/dist/generated/lazy-feature-config.js.map +1 -1
- package/dist/lib/guards/app-lock-guard.service.d.ts +26 -0
- package/dist/lib/guards/app-lock-guard.service.d.ts.map +1 -0
- package/dist/lib/guards/app-lock-guard.service.js +55 -0
- package/dist/lib/guards/app-lock-guard.service.js.map +1 -0
- package/dist/lib/resource-wrappers/chat-conversations-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/chat-conversations-resource.component.js +40 -27
- package/dist/lib/resource-wrappers/chat-conversations-resource.component.js.map +1 -1
- package/dist/lib/resource-wrappers/view-resource.component.d.ts +6 -5
- package/dist/lib/resource-wrappers/view-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/view-resource.component.js +19 -24
- package/dist/lib/resource-wrappers/view-resource.component.js.map +1 -1
- package/dist/lib/shell/components/tabs/tab-container.component.d.ts.map +1 -1
- package/dist/lib/shell/components/tabs/tab-container.component.js +9 -0
- package/dist/lib/shell/components/tabs/tab-container.component.js.map +1 -1
- package/dist/lib/shell/shell.component.d.ts +24 -6
- package/dist/lib/shell/shell.component.d.ts.map +1 -1
- package/dist/lib/shell/shell.component.js +360 -191
- package/dist/lib/shell/shell.component.js.map +1 -1
- package/dist/lib/single-record/single-record.component.d.ts +31 -75
- package/dist/lib/single-record/single-record.component.d.ts.map +1 -1
- package/dist/lib/single-record/single-record.component.js +60 -471
- package/dist/lib/single-record/single-record.component.js.map +1 -1
- package/dist/lib/single-search-result/single-search-result.component.d.ts +3 -8
- package/dist/lib/single-search-result/single-search-result.component.d.ts.map +1 -1
- package/dist/lib/single-search-result/single-search-result.component.js +19 -68
- package/dist/lib/single-search-result/single-search-result.component.js.map +1 -1
- package/dist/public-api.d.ts +1 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +1 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +46 -46
- package/dist/lib/__tests__/form-resolver.service.test.d.ts +0 -2
- package/dist/lib/__tests__/form-resolver.service.test.d.ts.map +0 -1
- package/dist/lib/__tests__/form-resolver.service.test.js +0 -258
- package/dist/lib/__tests__/form-resolver.service.test.js.map +0 -1
- package/dist/lib/services/form-resolver.service.d.ts +0 -139
- package/dist/lib/services/form-resolver.service.d.ts.map +0 -1
- package/dist/lib/services/form-resolver.service.js +0 -235
- package/dist/lib/services/form-resolver.service.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-explorer-core",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.40.0",
|
|
4
4
|
"description": "MemberJunction Explorer: Core Angular Components",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -32,51 +32,51 @@
|
|
|
32
32
|
"@angular/cdk": "21.1.3",
|
|
33
33
|
"@angular/platform-browser": "21.1.3",
|
|
34
34
|
"@auth0/auth0-angular": "^2.6.0",
|
|
35
|
-
"@memberjunction/ai-core-plus": "5.
|
|
36
|
-
"@memberjunction/ai-engine-base": "5.
|
|
37
|
-
"@memberjunction/communication-types": "5.
|
|
38
|
-
"@memberjunction/core": "5.
|
|
39
|
-
"@memberjunction/core-entities": "5.
|
|
40
|
-
"@memberjunction/entity-communications-client": "5.
|
|
41
|
-
"@memberjunction/export-engine": "5.
|
|
42
|
-
"@memberjunction/global": "5.
|
|
43
|
-
"@memberjunction/graphql-dataprovider": "5.
|
|
44
|
-
"@memberjunction/interactive-component-types": "5.
|
|
45
|
-
"@memberjunction/lists-base": "5.
|
|
46
|
-
"@memberjunction/ng-ai-test-harness": "5.
|
|
47
|
-
"@memberjunction/ng-artifacts": "5.
|
|
48
|
-
"@memberjunction/ng-auth-services": "5.
|
|
49
|
-
"@memberjunction/ng-base-application": "5.
|
|
50
|
-
"@memberjunction/ng-base-forms": "5.
|
|
51
|
-
"@memberjunction/ng-base-types": "5.
|
|
52
|
-
"@memberjunction/ng-container-directives": "5.
|
|
53
|
-
"@memberjunction/ng-conversations": "5.
|
|
54
|
-
"@memberjunction/ng-dashboard-viewer": "5.
|
|
55
|
-
"@memberjunction/ng-dashboards": "5.
|
|
56
|
-
"@memberjunction/ng-entity-form-dialog": "5.
|
|
57
|
-
"@memberjunction/ng-entity-permissions": "5.
|
|
58
|
-
"@memberjunction/ng-entity-viewer": "5.
|
|
59
|
-
"@memberjunction/ng-explorer-settings": "5.
|
|
60
|
-
"@memberjunction/ng-export-service": "5.
|
|
61
|
-
"@memberjunction/ng-feedback": "5.
|
|
62
|
-
"@memberjunction/ng-file-storage": "5.
|
|
63
|
-
"@memberjunction/ng-generic-dialog": "5.
|
|
64
|
-
"@memberjunction/ng-list-detail-grid": "5.
|
|
65
|
-
"@memberjunction/ng-list-management": "5.
|
|
66
|
-
"@memberjunction/ng-notifications": "5.
|
|
67
|
-
"@memberjunction/ng-pagination": "5.
|
|
68
|
-
"@memberjunction/ng-query-viewer": "5.
|
|
69
|
-
"@memberjunction/ng-record-changes": "5.
|
|
70
|
-
"@memberjunction/ng-record-selector": "5.
|
|
71
|
-
"@memberjunction/ng-record-tags": "5.
|
|
72
|
-
"@memberjunction/ng-resource-permissions": "5.
|
|
73
|
-
"@memberjunction/ng-search": "5.
|
|
74
|
-
"@memberjunction/ng-shared": "5.
|
|
75
|
-
"@memberjunction/ng-shared-generic": "5.
|
|
76
|
-
"@memberjunction/ng-ui-components": "5.
|
|
77
|
-
"@memberjunction/ng-user-avatar": "5.
|
|
78
|
-
"@memberjunction/ng-word-cloud": "5.
|
|
79
|
-
"@memberjunction/templates-base-types": "5.
|
|
35
|
+
"@memberjunction/ai-core-plus": "5.40.0",
|
|
36
|
+
"@memberjunction/ai-engine-base": "5.40.0",
|
|
37
|
+
"@memberjunction/communication-types": "5.40.0",
|
|
38
|
+
"@memberjunction/core": "5.40.0",
|
|
39
|
+
"@memberjunction/core-entities": "5.40.0",
|
|
40
|
+
"@memberjunction/entity-communications-client": "5.40.0",
|
|
41
|
+
"@memberjunction/export-engine": "5.40.0",
|
|
42
|
+
"@memberjunction/global": "5.40.0",
|
|
43
|
+
"@memberjunction/graphql-dataprovider": "5.40.0",
|
|
44
|
+
"@memberjunction/interactive-component-types": "5.40.0",
|
|
45
|
+
"@memberjunction/lists-base": "5.40.0",
|
|
46
|
+
"@memberjunction/ng-ai-test-harness": "5.40.0",
|
|
47
|
+
"@memberjunction/ng-artifacts": "5.40.0",
|
|
48
|
+
"@memberjunction/ng-auth-services": "5.40.0",
|
|
49
|
+
"@memberjunction/ng-base-application": "5.40.0",
|
|
50
|
+
"@memberjunction/ng-base-forms": "5.40.0",
|
|
51
|
+
"@memberjunction/ng-base-types": "5.40.0",
|
|
52
|
+
"@memberjunction/ng-container-directives": "5.40.0",
|
|
53
|
+
"@memberjunction/ng-conversations": "5.40.0",
|
|
54
|
+
"@memberjunction/ng-dashboard-viewer": "5.40.0",
|
|
55
|
+
"@memberjunction/ng-dashboards": "5.40.0",
|
|
56
|
+
"@memberjunction/ng-entity-form-dialog": "5.40.0",
|
|
57
|
+
"@memberjunction/ng-entity-permissions": "5.40.0",
|
|
58
|
+
"@memberjunction/ng-entity-viewer": "5.40.0",
|
|
59
|
+
"@memberjunction/ng-explorer-settings": "5.40.0",
|
|
60
|
+
"@memberjunction/ng-export-service": "5.40.0",
|
|
61
|
+
"@memberjunction/ng-feedback": "5.40.0",
|
|
62
|
+
"@memberjunction/ng-file-storage": "5.40.0",
|
|
63
|
+
"@memberjunction/ng-generic-dialog": "5.40.0",
|
|
64
|
+
"@memberjunction/ng-list-detail-grid": "5.40.0",
|
|
65
|
+
"@memberjunction/ng-list-management": "5.40.0",
|
|
66
|
+
"@memberjunction/ng-notifications": "5.40.0",
|
|
67
|
+
"@memberjunction/ng-pagination": "5.40.0",
|
|
68
|
+
"@memberjunction/ng-query-viewer": "5.40.0",
|
|
69
|
+
"@memberjunction/ng-record-changes": "5.40.0",
|
|
70
|
+
"@memberjunction/ng-record-selector": "5.40.0",
|
|
71
|
+
"@memberjunction/ng-record-tags": "5.40.0",
|
|
72
|
+
"@memberjunction/ng-resource-permissions": "5.40.0",
|
|
73
|
+
"@memberjunction/ng-search": "5.40.0",
|
|
74
|
+
"@memberjunction/ng-shared": "5.40.0",
|
|
75
|
+
"@memberjunction/ng-shared-generic": "5.40.0",
|
|
76
|
+
"@memberjunction/ng-ui-components": "5.40.0",
|
|
77
|
+
"@memberjunction/ng-user-avatar": "5.40.0",
|
|
78
|
+
"@memberjunction/ng-word-cloud": "5.40.0",
|
|
79
|
+
"@memberjunction/templates-base-types": "5.40.0",
|
|
80
80
|
"golden-layout": "^2.6.0",
|
|
81
81
|
"rxjs": "^7.8.2",
|
|
82
82
|
"tslib": "^2.8.1"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"form-resolver.service.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/form-resolver.service.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for FormResolverService.
|
|
3
|
-
*
|
|
4
|
-
* The service builds a SQL filter + ORDER BY against `EntityFormOverride`,
|
|
5
|
-
* runs it via RunView, and falls back to ClassFactory when no override matches.
|
|
6
|
-
* These tests capture the RunView call args (to validate filter shape) and
|
|
7
|
-
* stub responses to verify each resolution branch.
|
|
8
|
-
*/
|
|
9
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
10
|
-
// ----- Module mocks -------------------------------------------------------
|
|
11
|
-
// Mock UserInfoEngine — the resolver's variant preferences now persist via
|
|
12
|
-
// the user-settings table (cross-device, cross-browser) instead of
|
|
13
|
-
// localStorage. In-memory map mirrors the engine's cache behavior.
|
|
14
|
-
const settingsBacking = {};
|
|
15
|
-
vi.mock('@memberjunction/core-entities', () => ({
|
|
16
|
-
UserInfoEngine: {
|
|
17
|
-
Instance: {
|
|
18
|
-
GetSetting: (key) => settingsBacking[key],
|
|
19
|
-
SetSettingDebounced: (key, value) => {
|
|
20
|
-
settingsBacking[key] = value;
|
|
21
|
-
},
|
|
22
|
-
DeleteSetting: async (key) => {
|
|
23
|
-
delete settingsBacking[key];
|
|
24
|
-
return true;
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
}));
|
|
29
|
-
vi.mock('@angular/core', () => ({
|
|
30
|
-
Injectable: () => (target) => target,
|
|
31
|
-
}));
|
|
32
|
-
// RunView mock — we capture every call's params so tests can assert on them.
|
|
33
|
-
const runViewCalls = [];
|
|
34
|
-
let runViewResponse = { Success: true, Results: [] };
|
|
35
|
-
vi.mock('@memberjunction/core', () => ({
|
|
36
|
-
LogError: vi.fn(),
|
|
37
|
-
RunView: class {
|
|
38
|
-
static FromMetadataProvider(_provider) {
|
|
39
|
-
return new (class {
|
|
40
|
-
async RunView(params, user) {
|
|
41
|
-
runViewCalls.push({ params, user });
|
|
42
|
-
return runViewResponse;
|
|
43
|
-
}
|
|
44
|
-
})();
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
}));
|
|
48
|
-
// ClassFactory mock — controls what the fallback branch returns.
|
|
49
|
-
let classFactoryReg = null;
|
|
50
|
-
vi.mock('@memberjunction/global', () => ({
|
|
51
|
-
MJGlobal: {
|
|
52
|
-
Instance: {
|
|
53
|
-
ClassFactory: {
|
|
54
|
-
GetRegistration: vi.fn(() => classFactoryReg),
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
// The post-refactor resolver uses UUIDsEqual to match a user-selected
|
|
59
|
-
// variant ID; stub it with a case-insensitive string compare.
|
|
60
|
-
UUIDsEqual: (a, b) => (a ?? '').toLowerCase() === (b ?? '').toLowerCase(),
|
|
61
|
-
}));
|
|
62
|
-
vi.mock('@memberjunction/ng-base-forms', () => ({
|
|
63
|
-
BaseFormComponent: class FakeBaseFormComponent {
|
|
64
|
-
},
|
|
65
|
-
}));
|
|
66
|
-
const entity = { ID: 'entity-1', Name: 'Customer' };
|
|
67
|
-
const user = (overrides) => ({ ID: 'user-1', UserRoles: [], ...overrides });
|
|
68
|
-
const provider = {};
|
|
69
|
-
const overrideRow = (overrides) => ({
|
|
70
|
-
ID: 'row-1', EntityID: 'entity-1', ComponentID: 'comp-1',
|
|
71
|
-
Scope: 'Global', UserID: null, RoleID: null,
|
|
72
|
-
Priority: 0, Status: 'Active', ...overrides,
|
|
73
|
-
});
|
|
74
|
-
let service;
|
|
75
|
-
beforeEach(async () => {
|
|
76
|
-
runViewCalls.length = 0;
|
|
77
|
-
runViewResponse = { Success: true, Results: [] };
|
|
78
|
-
classFactoryReg = null;
|
|
79
|
-
for (const k of Object.keys(settingsBacking))
|
|
80
|
-
delete settingsBacking[k];
|
|
81
|
-
vi.clearAllMocks();
|
|
82
|
-
const mod = await import('../services/form-resolver.service');
|
|
83
|
-
service = new mod.FormResolverService();
|
|
84
|
-
});
|
|
85
|
-
// ----- Tests --------------------------------------------------------------
|
|
86
|
-
describe('FormResolverService.ResolveFormForEntity', () => {
|
|
87
|
-
it('returns kind=interactive when an override matches', async () => {
|
|
88
|
-
runViewResponse = { Success: true, Results: [overrideRow({})] };
|
|
89
|
-
const result = await service.ResolveFormForEntity(entity, user({}), provider);
|
|
90
|
-
expect(result.kind).toBe('interactive');
|
|
91
|
-
if (result.kind === 'interactive') {
|
|
92
|
-
expect(result.override.ComponentID).toBe('comp-1');
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
it('passes only "MJ: Entity Form Overrides" to RunView', async () => {
|
|
96
|
-
await service.ResolveFormForEntity(entity, user({}), provider);
|
|
97
|
-
expect(runViewCalls[0].params.EntityName).toBe('MJ: Entity Form Overrides');
|
|
98
|
-
});
|
|
99
|
-
it('filters by the entity ID and does NOT filter by Status (returns full variant list)', async () => {
|
|
100
|
-
// Post-refactor: the resolver lists ALL applicable variants (any
|
|
101
|
-
// Status) so the variant switcher can show Pending / Inactive
|
|
102
|
-
// alongside Active. The Active pick happens in-memory via pickActive.
|
|
103
|
-
await service.ResolveFormForEntity(entity, user({}), provider);
|
|
104
|
-
const filter = runViewCalls[0].params.ExtraFilter;
|
|
105
|
-
expect(filter).toContain(`EntityID='entity-1'`);
|
|
106
|
-
expect(filter).not.toContain(`Status='Active'`);
|
|
107
|
-
});
|
|
108
|
-
it('orders by User > Role > Global scope, then Priority DESC, then CreatedAt DESC', async () => {
|
|
109
|
-
await service.ResolveFormForEntity(entity, user({}), provider);
|
|
110
|
-
const orderBy = runViewCalls[0].params.OrderBy;
|
|
111
|
-
expect(orderBy).toContain(`WHEN 'User' THEN 1`);
|
|
112
|
-
expect(orderBy).toContain(`WHEN 'Role' THEN 2`);
|
|
113
|
-
expect(orderBy).toContain('Priority DESC');
|
|
114
|
-
expect(orderBy).toContain('__mj_CreatedAt DESC');
|
|
115
|
-
});
|
|
116
|
-
it('includes UserID match in the filter', async () => {
|
|
117
|
-
await service.ResolveFormForEntity(entity, user({ ID: 'me' }), provider);
|
|
118
|
-
const filter = runViewCalls[0].params.ExtraFilter;
|
|
119
|
-
expect(filter).toContain(`Scope='User' AND UserID='me'`);
|
|
120
|
-
});
|
|
121
|
-
it('includes role-membership clause for each role the user has', async () => {
|
|
122
|
-
const u = user({ UserRoles: [{ RoleID: 'r1' }, { RoleID: 'r2' }] });
|
|
123
|
-
await service.ResolveFormForEntity(entity, u, provider);
|
|
124
|
-
const filter = runViewCalls[0].params.ExtraFilter;
|
|
125
|
-
expect(filter).toContain(`Scope='Role' AND RoleID IN ('r1','r2')`);
|
|
126
|
-
});
|
|
127
|
-
it('emits an always-false role clause when the user has no roles', async () => {
|
|
128
|
-
await service.ResolveFormForEntity(entity, user({ UserRoles: [] }), provider);
|
|
129
|
-
const filter = runViewCalls[0].params.ExtraFilter;
|
|
130
|
-
// No roles = the user can never match a Role-scoped row. We encode that
|
|
131
|
-
// as (1=0) rather than producing `IN ()` which is invalid SQL.
|
|
132
|
-
expect(filter).toContain('(1=0)');
|
|
133
|
-
expect(filter).not.toContain('IN ()');
|
|
134
|
-
});
|
|
135
|
-
it('does NOT cap the query at MaxRows=1 — needs the full variant list', async () => {
|
|
136
|
-
// Post-refactor: the resolver returns variants for the picker, so it
|
|
137
|
-
// can't limit to one row. The pickActive() in-memory still selects
|
|
138
|
-
// the top match for the default-rendered form.
|
|
139
|
-
await service.ResolveFormForEntity(entity, user({}), provider);
|
|
140
|
-
expect(runViewCalls[0].params.MaxRows).toBeUndefined();
|
|
141
|
-
});
|
|
142
|
-
it('returns variants list alongside the resolved override', async () => {
|
|
143
|
-
const variants = [
|
|
144
|
-
overrideRow({ ID: 'v-active', Status: 'Active', Scope: 'User', UserID: 'user-1' }),
|
|
145
|
-
overrideRow({ ID: 'v-pending', Status: 'Pending', Scope: 'User', UserID: 'user-1' }),
|
|
146
|
-
overrideRow({ ID: 'v-inactive', Status: 'Inactive', Scope: 'Global' }),
|
|
147
|
-
];
|
|
148
|
-
runViewResponse = { Success: true, Results: variants };
|
|
149
|
-
const result = await service.ResolveFormForEntity(entity, user({}), provider);
|
|
150
|
-
expect(result.variants?.length).toBe(3);
|
|
151
|
-
expect(result.variants?.map(v => v.ID).sort()).toEqual(['v-active', 'v-inactive', 'v-pending']);
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
describe('FormResolverService.ListVariantsForEntity', () => {
|
|
155
|
-
it('returns the full applicable list (any status)', async () => {
|
|
156
|
-
runViewResponse = {
|
|
157
|
-
Success: true,
|
|
158
|
-
Results: [
|
|
159
|
-
overrideRow({ ID: 'v1', Status: 'Active' }),
|
|
160
|
-
overrideRow({ ID: 'v2', Status: 'Pending' }),
|
|
161
|
-
],
|
|
162
|
-
};
|
|
163
|
-
const variants = await service.ListVariantsForEntity(entity, user({}), provider);
|
|
164
|
-
expect(variants.length).toBe(2);
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
describe('FormResolverService session-local variant selection', () => {
|
|
168
|
-
beforeEach(() => {
|
|
169
|
-
// The resolver writes to localStorage; reset before each test.
|
|
170
|
-
if (typeof window !== 'undefined' && window.localStorage) {
|
|
171
|
-
window.localStorage.clear();
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
it('honours a saved variant ID over the default Active pick', async () => {
|
|
175
|
-
runViewResponse = {
|
|
176
|
-
Success: true,
|
|
177
|
-
Results: [
|
|
178
|
-
overrideRow({ ID: 'v-default-active', Status: 'Active', Scope: 'Global', Priority: 0 }),
|
|
179
|
-
overrideRow({ ID: 'v-user-selected', Status: 'Active', Scope: 'User', UserID: 'user-1', Priority: 0 }),
|
|
180
|
-
],
|
|
181
|
-
};
|
|
182
|
-
// Save the user's choice
|
|
183
|
-
service.SetSelectedVariant(entity.Name, 'v-user-selected');
|
|
184
|
-
const result = await service.ResolveFormForEntity(entity, user({}), provider);
|
|
185
|
-
expect(result.kind).toBe('interactive');
|
|
186
|
-
if (result.kind === 'interactive') {
|
|
187
|
-
expect(result.override.ID).toBe('v-user-selected');
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
it('clears a saved variant ID that no longer matches an Active row', async () => {
|
|
191
|
-
runViewResponse = {
|
|
192
|
-
Success: true,
|
|
193
|
-
Results: [overrideRow({ ID: 'v-active', Status: 'Active' })],
|
|
194
|
-
};
|
|
195
|
-
service.SetSelectedVariant(entity.Name, 'v-stale');
|
|
196
|
-
const result = await service.ResolveFormForEntity(entity, user({}), provider);
|
|
197
|
-
expect(result.kind).toBe('interactive');
|
|
198
|
-
if (result.kind === 'interactive') {
|
|
199
|
-
expect(result.override.ID).toBe('v-active');
|
|
200
|
-
}
|
|
201
|
-
// Stale selection should have been cleared
|
|
202
|
-
expect(service.GetSelectedVariant(entity.Name)).toBeNull();
|
|
203
|
-
});
|
|
204
|
-
it('ClearSelectedVariant wipes the stored choice', () => {
|
|
205
|
-
service.SetSelectedVariant(entity.Name, 'v1');
|
|
206
|
-
expect(service.GetSelectedVariant(entity.Name)).toBe('v1');
|
|
207
|
-
service.ClearSelectedVariant(entity.Name);
|
|
208
|
-
expect(service.GetSelectedVariant(entity.Name)).toBeNull();
|
|
209
|
-
});
|
|
210
|
-
it('SetExplicitDefault stores the sentinel so pickActive returns null even when Active overrides exist', async () => {
|
|
211
|
-
// Hardcoded — matches FormResolverService.EXPLICIT_DEFAULT_SENTINEL.
|
|
212
|
-
const SENTINEL = '__codegen-default__';
|
|
213
|
-
service.SetExplicitDefault(entity.Name);
|
|
214
|
-
expect(service.GetSelectedVariant(entity.Name)).toBe(SENTINEL);
|
|
215
|
-
runViewResponse = { Success: true, Results: [overrideRow({ ID: 'v-active', Scope: 'Global', Status: 'Active' })] };
|
|
216
|
-
const result = await service.ResolveFormForEntity(entity, user({}), provider);
|
|
217
|
-
// Even with an Active override in the variants list, the sentinel
|
|
218
|
-
// forces a fall-through to the CodeGen / Angular fallback.
|
|
219
|
-
expect(result.kind).not.toBe('interactive');
|
|
220
|
-
});
|
|
221
|
-
it('is case-insensitive on the entity name', () => {
|
|
222
|
-
service.SetSelectedVariant('Customer', 'v1');
|
|
223
|
-
expect(service.GetSelectedVariant('customer')).toBe('v1');
|
|
224
|
-
expect(service.GetSelectedVariant('CUSTOMER')).toBe('v1');
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
describe('FormResolverService.ResolveFormForEntity — fallback behaviour', () => {
|
|
228
|
-
it('uses ResultType=simple to skip BaseEntity hydration on the hot path', async () => {
|
|
229
|
-
await service.ResolveFormForEntity(entity, user({}), provider);
|
|
230
|
-
expect(runViewCalls[0].params.ResultType).toBe('simple');
|
|
231
|
-
});
|
|
232
|
-
it('falls back to ClassFactory when no override matches', async () => {
|
|
233
|
-
runViewResponse = { Success: true, Results: [] };
|
|
234
|
-
classFactoryReg = { SubClass: class FakeForm {
|
|
235
|
-
} };
|
|
236
|
-
const result = await service.ResolveFormForEntity(entity, user({}), provider);
|
|
237
|
-
expect(result.kind).toBe('class');
|
|
238
|
-
if (result.kind === 'class') {
|
|
239
|
-
expect(result.subClass).toBe(classFactoryReg.SubClass);
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
it('returns kind=none when neither override nor ClassFactory has a form', async () => {
|
|
243
|
-
runViewResponse = { Success: true, Results: [] };
|
|
244
|
-
classFactoryReg = null;
|
|
245
|
-
const result = await service.ResolveFormForEntity(entity, user({}), provider);
|
|
246
|
-
expect(result.kind).toBe('none');
|
|
247
|
-
});
|
|
248
|
-
it('falls through when the RunView fails (treats lookup failure as "no override")', async () => {
|
|
249
|
-
runViewResponse = { Success: false, ErrorMessage: 'db down' };
|
|
250
|
-
classFactoryReg = { SubClass: class FakeForm {
|
|
251
|
-
} };
|
|
252
|
-
const result = await service.ResolveFormForEntity(entity, user({}), provider);
|
|
253
|
-
// Lookup failure should NOT block the user from seeing the class-based form
|
|
254
|
-
// — better to show *something* than to fail closed on every entity load.
|
|
255
|
-
expect(result.kind).toBe('class');
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
//# sourceMappingURL=form-resolver.service.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"form-resolver.service.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/form-resolver.service.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Tests for FormResolverService.\n *\n * The service builds a SQL filter + ORDER BY against `EntityFormOverride`,\n * runs it via RunView, and falls back to ClassFactory when no override matches.\n * These tests capture the RunView call args (to validate filter shape) and\n * stub responses to verify each resolution branch.\n */\nimport { describe, it, expect, vi, beforeEach } from 'vitest';\n\n// ----- Module mocks -------------------------------------------------------\n\n// Mock UserInfoEngine — the resolver's variant preferences now persist via\n// the user-settings table (cross-device, cross-browser) instead of\n// localStorage. In-memory map mirrors the engine's cache behavior.\nconst settingsBacking: Record<string, string> = {};\nvi.mock('@memberjunction/core-entities', () => ({\n UserInfoEngine: {\n Instance: {\n GetSetting: (key: string) => settingsBacking[key],\n SetSettingDebounced: (key: string, value: string) => {\n settingsBacking[key] = value;\n },\n DeleteSetting: async (key: string) => {\n delete settingsBacking[key];\n return true;\n },\n },\n },\n}));\n\nvi.mock('@angular/core', () => ({\n Injectable: () => (target: Function) => target,\n}));\n\n// RunView mock — we capture every call's params so tests can assert on them.\nconst runViewCalls: Array<{ params: any; user: any }> = [];\nlet runViewResponse: { Success: boolean; Results?: any[]; ErrorMessage?: string } = { Success: true, Results: [] };\n\nvi.mock('@memberjunction/core', () => ({\n LogError: vi.fn(),\n RunView: class {\n static FromMetadataProvider(_provider: any) {\n return new (class {\n async RunView(params: any, user: any) {\n runViewCalls.push({ params, user });\n return runViewResponse;\n }\n })();\n }\n },\n}));\n\n// ClassFactory mock — controls what the fallback branch returns.\nlet classFactoryReg: { SubClass: any } | null = null;\nvi.mock('@memberjunction/global', () => ({\n MJGlobal: {\n Instance: {\n ClassFactory: {\n GetRegistration: vi.fn(() => classFactoryReg),\n },\n },\n },\n // The post-refactor resolver uses UUIDsEqual to match a user-selected\n // variant ID; stub it with a case-insensitive string compare.\n UUIDsEqual: (a: string, b: string) => (a ?? '').toLowerCase() === (b ?? '').toLowerCase(),\n}));\n\nvi.mock('@memberjunction/ng-base-forms', () => ({\n BaseFormComponent: class FakeBaseFormComponent {},\n}));\n\n// ----- Test setup ---------------------------------------------------------\n\nimport type { FormResolverService } from '../services/form-resolver.service';\n\nconst entity = { ID: 'entity-1', Name: 'Customer' } as any;\nconst user = (overrides: Partial<{ ID: string; UserRoles: Array<{ RoleID: string }> }>) =>\n ({ ID: 'user-1', UserRoles: [], ...overrides } as any);\nconst provider = {} as any;\n\nconst overrideRow = (overrides: Partial<{\n ID: string; EntityID: string; ComponentID: string;\n Scope: 'User' | 'Role' | 'Global';\n UserID: string | null; RoleID: string | null;\n Priority: number; Status: 'Active' | 'Inactive' | 'Pending';\n}>) => ({\n ID: 'row-1', EntityID: 'entity-1', ComponentID: 'comp-1',\n Scope: 'Global' as const, UserID: null, RoleID: null,\n Priority: 0, Status: 'Active' as const, ...overrides,\n});\n\nlet service: FormResolverService;\n\nbeforeEach(async () => {\n runViewCalls.length = 0;\n runViewResponse = { Success: true, Results: [] };\n classFactoryReg = null;\n for (const k of Object.keys(settingsBacking)) delete settingsBacking[k];\n vi.clearAllMocks();\n const mod = await import('../services/form-resolver.service');\n service = new mod.FormResolverService();\n});\n\n// ----- Tests --------------------------------------------------------------\n\ndescribe('FormResolverService.ResolveFormForEntity', () => {\n\n it('returns kind=interactive when an override matches', async () => {\n runViewResponse = { Success: true, Results: [overrideRow({})] };\n\n const result = await service.ResolveFormForEntity(entity, user({}), provider);\n\n expect(result.kind).toBe('interactive');\n if (result.kind === 'interactive') {\n expect(result.override.ComponentID).toBe('comp-1');\n }\n });\n\n it('passes only \"MJ: Entity Form Overrides\" to RunView', async () => {\n await service.ResolveFormForEntity(entity, user({}), provider);\n expect(runViewCalls[0].params.EntityName).toBe('MJ: Entity Form Overrides');\n });\n\n it('filters by the entity ID and does NOT filter by Status (returns full variant list)', async () => {\n // Post-refactor: the resolver lists ALL applicable variants (any\n // Status) so the variant switcher can show Pending / Inactive\n // alongside Active. The Active pick happens in-memory via pickActive.\n await service.ResolveFormForEntity(entity, user({}), provider);\n const filter = runViewCalls[0].params.ExtraFilter;\n expect(filter).toContain(`EntityID='entity-1'`);\n expect(filter).not.toContain(`Status='Active'`);\n });\n\n it('orders by User > Role > Global scope, then Priority DESC, then CreatedAt DESC', async () => {\n await service.ResolveFormForEntity(entity, user({}), provider);\n const orderBy = runViewCalls[0].params.OrderBy;\n expect(orderBy).toContain(`WHEN 'User' THEN 1`);\n expect(orderBy).toContain(`WHEN 'Role' THEN 2`);\n expect(orderBy).toContain('Priority DESC');\n expect(orderBy).toContain('__mj_CreatedAt DESC');\n });\n\n it('includes UserID match in the filter', async () => {\n await service.ResolveFormForEntity(entity, user({ ID: 'me' }), provider);\n const filter = runViewCalls[0].params.ExtraFilter;\n expect(filter).toContain(`Scope='User' AND UserID='me'`);\n });\n\n it('includes role-membership clause for each role the user has', async () => {\n const u = user({ UserRoles: [{ RoleID: 'r1' }, { RoleID: 'r2' }] });\n await service.ResolveFormForEntity(entity, u, provider);\n const filter = runViewCalls[0].params.ExtraFilter;\n expect(filter).toContain(`Scope='Role' AND RoleID IN ('r1','r2')`);\n });\n\n it('emits an always-false role clause when the user has no roles', async () => {\n await service.ResolveFormForEntity(entity, user({ UserRoles: [] }), provider);\n const filter = runViewCalls[0].params.ExtraFilter;\n // No roles = the user can never match a Role-scoped row. We encode that\n // as (1=0) rather than producing `IN ()` which is invalid SQL.\n expect(filter).toContain('(1=0)');\n expect(filter).not.toContain('IN ()');\n });\n\n it('does NOT cap the query at MaxRows=1 — needs the full variant list', async () => {\n // Post-refactor: the resolver returns variants for the picker, so it\n // can't limit to one row. The pickActive() in-memory still selects\n // the top match for the default-rendered form.\n await service.ResolveFormForEntity(entity, user({}), provider);\n expect(runViewCalls[0].params.MaxRows).toBeUndefined();\n });\n\n it('returns variants list alongside the resolved override', async () => {\n const variants = [\n overrideRow({ ID: 'v-active', Status: 'Active', Scope: 'User', UserID: 'user-1' }),\n overrideRow({ ID: 'v-pending', Status: 'Pending', Scope: 'User', UserID: 'user-1' }),\n overrideRow({ ID: 'v-inactive', Status: 'Inactive', Scope: 'Global' }),\n ];\n runViewResponse = { Success: true, Results: variants };\n const result = await service.ResolveFormForEntity(entity, user({}), provider);\n expect(result.variants?.length).toBe(3);\n expect(result.variants?.map(v => v.ID).sort()).toEqual(['v-active', 'v-inactive', 'v-pending']);\n });\n});\n\ndescribe('FormResolverService.ListVariantsForEntity', () => {\n\n it('returns the full applicable list (any status)', async () => {\n runViewResponse = {\n Success: true,\n Results: [\n overrideRow({ ID: 'v1', Status: 'Active' }),\n overrideRow({ ID: 'v2', Status: 'Pending' }),\n ],\n };\n const variants = await service.ListVariantsForEntity(entity, user({}), provider);\n expect(variants.length).toBe(2);\n });\n});\n\ndescribe('FormResolverService session-local variant selection', () => {\n\n beforeEach(() => {\n // The resolver writes to localStorage; reset before each test.\n if (typeof window !== 'undefined' && window.localStorage) {\n window.localStorage.clear();\n }\n });\n\n it('honours a saved variant ID over the default Active pick', async () => {\n runViewResponse = {\n Success: true,\n Results: [\n overrideRow({ ID: 'v-default-active', Status: 'Active', Scope: 'Global', Priority: 0 }),\n overrideRow({ ID: 'v-user-selected', Status: 'Active', Scope: 'User', UserID: 'user-1', Priority: 0 }),\n ],\n };\n // Save the user's choice\n service.SetSelectedVariant(entity.Name, 'v-user-selected');\n const result = await service.ResolveFormForEntity(entity, user({}), provider);\n expect(result.kind).toBe('interactive');\n if (result.kind === 'interactive') {\n expect(result.override.ID).toBe('v-user-selected');\n }\n });\n\n it('clears a saved variant ID that no longer matches an Active row', async () => {\n runViewResponse = {\n Success: true,\n Results: [ overrideRow({ ID: 'v-active', Status: 'Active' }) ],\n };\n service.SetSelectedVariant(entity.Name, 'v-stale');\n const result = await service.ResolveFormForEntity(entity, user({}), provider);\n expect(result.kind).toBe('interactive');\n if (result.kind === 'interactive') {\n expect(result.override.ID).toBe('v-active');\n }\n // Stale selection should have been cleared\n expect(service.GetSelectedVariant(entity.Name)).toBeNull();\n });\n\n it('ClearSelectedVariant wipes the stored choice', () => {\n service.SetSelectedVariant(entity.Name, 'v1');\n expect(service.GetSelectedVariant(entity.Name)).toBe('v1');\n service.ClearSelectedVariant(entity.Name);\n expect(service.GetSelectedVariant(entity.Name)).toBeNull();\n });\n\n it('SetExplicitDefault stores the sentinel so pickActive returns null even when Active overrides exist', async () => {\n // Hardcoded — matches FormResolverService.EXPLICIT_DEFAULT_SENTINEL.\n const SENTINEL = '__codegen-default__';\n service.SetExplicitDefault(entity.Name);\n expect(service.GetSelectedVariant(entity.Name)).toBe(SENTINEL);\n runViewResponse = { Success: true, Results: [overrideRow({ ID: 'v-active', Scope: 'Global', Status: 'Active' })] };\n const result = await service.ResolveFormForEntity(entity, user({}), provider);\n // Even with an Active override in the variants list, the sentinel\n // forces a fall-through to the CodeGen / Angular fallback.\n expect(result.kind).not.toBe('interactive');\n });\n\n it('is case-insensitive on the entity name', () => {\n service.SetSelectedVariant('Customer', 'v1');\n expect(service.GetSelectedVariant('customer')).toBe('v1');\n expect(service.GetSelectedVariant('CUSTOMER')).toBe('v1');\n });\n});\n\ndescribe('FormResolverService.ResolveFormForEntity — fallback behaviour', () => {\n\n it('uses ResultType=simple to skip BaseEntity hydration on the hot path', async () => {\n await service.ResolveFormForEntity(entity, user({}), provider);\n expect(runViewCalls[0].params.ResultType).toBe('simple');\n });\n\n it('falls back to ClassFactory when no override matches', async () => {\n runViewResponse = { Success: true, Results: [] };\n classFactoryReg = { SubClass: class FakeForm {} };\n\n const result = await service.ResolveFormForEntity(entity, user({}), provider);\n\n expect(result.kind).toBe('class');\n if (result.kind === 'class') {\n expect(result.subClass).toBe(classFactoryReg.SubClass);\n }\n });\n\n it('returns kind=none when neither override nor ClassFactory has a form', async () => {\n runViewResponse = { Success: true, Results: [] };\n classFactoryReg = null;\n\n const result = await service.ResolveFormForEntity(entity, user({}), provider);\n\n expect(result.kind).toBe('none');\n });\n\n it('falls through when the RunView fails (treats lookup failure as \"no override\")', async () => {\n runViewResponse = { Success: false, ErrorMessage: 'db down' };\n classFactoryReg = { SubClass: class FakeForm {} };\n\n const result = await service.ResolveFormForEntity(entity, user({}), provider);\n\n // Lookup failure should NOT block the user from seeing the class-based form\n // — better to show *something* than to fail closed on every entity load.\n expect(result.kind).toBe('class');\n });\n});\n"]}
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import { Type } from '@angular/core';
|
|
2
|
-
import { IMetadataProvider, UserInfo, EntityInfo } from '@memberjunction/core';
|
|
3
|
-
import { BaseFormComponent } from '@memberjunction/ng-base-forms';
|
|
4
|
-
import * as i0 from "@angular/core";
|
|
5
|
-
/**
|
|
6
|
-
* Slim row shape for an `EntityFormOverride` lookup. Resolution doesn't need
|
|
7
|
-
* the full BaseEntity object — `simple` ResultType is faster and avoids a
|
|
8
|
-
* compile-time dependency on the generated entity class. The row is only ever
|
|
9
|
-
* read by the resolver; mutation goes through the generated entity in other
|
|
10
|
-
* code paths (Studio, AI authoring).
|
|
11
|
-
*/
|
|
12
|
-
export interface EntityFormOverrideRow {
|
|
13
|
-
ID: string;
|
|
14
|
-
EntityID: string;
|
|
15
|
-
ComponentID: string;
|
|
16
|
-
Scope: 'User' | 'Role' | 'Global';
|
|
17
|
-
UserID: string | null;
|
|
18
|
-
RoleID: string | null;
|
|
19
|
-
Priority: number;
|
|
20
|
-
Status: 'Active' | 'Inactive' | 'Pending';
|
|
21
|
-
Name?: string;
|
|
22
|
-
Description?: string | null;
|
|
23
|
-
}
|
|
24
|
-
/** Resolved form kind for a given (entity, user, roles) tuple. */
|
|
25
|
-
export type FormResolution = {
|
|
26
|
-
kind: 'interactive';
|
|
27
|
-
override: EntityFormOverrideRow;
|
|
28
|
-
variants: EntityFormOverrideRow[];
|
|
29
|
-
} | {
|
|
30
|
-
kind: 'class';
|
|
31
|
-
subClass: Type<BaseFormComponent>;
|
|
32
|
-
variants: EntityFormOverrideRow[];
|
|
33
|
-
} | {
|
|
34
|
-
kind: 'none';
|
|
35
|
-
variants: EntityFormOverrideRow[];
|
|
36
|
-
};
|
|
37
|
-
/**
|
|
38
|
-
* Picks the form to render for an entity record and exposes the full list of
|
|
39
|
-
* applicable variants so the toolbar's variant switcher can offer alternates.
|
|
40
|
-
*
|
|
41
|
-
* Tier order for the **default** pick:
|
|
42
|
-
* 1. `EntityFormOverride` row matching the caller's User/Role/Global scope,
|
|
43
|
-
* Status='Active', ordered by scope tier (User > Role > Global), then
|
|
44
|
-
* Priority DESC, then `__mj_CreatedAt DESC`. First row wins.
|
|
45
|
-
* 2. Existing `ClassFactory.GetRegistration(BaseFormComponent, entityName)` —
|
|
46
|
-
* the @RegisterClass + CodeGen-generated path used today.
|
|
47
|
-
* 3. None — caller surfaces the "no form registered" error.
|
|
48
|
-
*
|
|
49
|
-
* **Session selection.** If the user previously chose a non-default variant
|
|
50
|
-
* via the variant switcher and that choice is still applicable + Active, that
|
|
51
|
-
* choice wins over the default. Choice is keyed by entity name in
|
|
52
|
-
* `localStorage`. Honoring this is part of the variant-switcher contract;
|
|
53
|
-
* without it, switching variants would only last for the lifetime of the
|
|
54
|
-
* record-form view.
|
|
55
|
-
*
|
|
56
|
-
* **Performance.** Backed by `InteractiveFormsEngine` (in MJCoreEntities)
|
|
57
|
-
* — overrides are cached in memory with BaseEntity event-driven
|
|
58
|
-
* invalidation, so resolution is an O(N) JS filter against a small
|
|
59
|
-
* set instead of a per-LoadForm RunView round-trip. Cold latency
|
|
60
|
-
* dropped from ~50ms to <1ms after the first load. The engine is
|
|
61
|
-
* lazy-Config'd here on first resolution; users who never open a
|
|
62
|
-
* record pay nothing.
|
|
63
|
-
*/
|
|
64
|
-
export declare class FormResolverService {
|
|
65
|
-
ResolveFormForEntity(entity: EntityInfo, user: UserInfo, provider: IMetadataProvider): Promise<FormResolution>;
|
|
66
|
-
/**
|
|
67
|
-
* Public list-API for the variant switcher UI. Returns all variants
|
|
68
|
-
* applicable to (entity, user). Includes Active, Pending, and Inactive
|
|
69
|
-
* rows so the picker can offer "switch back to v1.0.0" alongside the
|
|
70
|
-
* current active variant.
|
|
71
|
-
*/
|
|
72
|
-
ListVariantsForEntity(entity: EntityInfo, user: UserInfo, provider: IMetadataProvider): Promise<EntityFormOverrideRow[]>;
|
|
73
|
-
/**
|
|
74
|
-
* Sentinel stored in localStorage when the user **explicitly picks the
|
|
75
|
-
* "Default form"** option from the variant picker. Distinct from a
|
|
76
|
-
* missing localStorage key (= "no preference, use auto-pick rules")
|
|
77
|
-
* and from an override UUID. Without this sentinel, picking "Default"
|
|
78
|
-
* cleared the preference and `pickActive` re-applied the auto-pick —
|
|
79
|
-
* which always selects the first Active override, making the
|
|
80
|
-
* CodeGen/Angular fallback unreachable from the UI.
|
|
81
|
-
*
|
|
82
|
-
* Format: a leading `__` makes it visually distinct from a UUID and
|
|
83
|
-
* impossible to collide with one (UUIDs don't contain underscores).
|
|
84
|
-
*/
|
|
85
|
-
static readonly EXPLICIT_DEFAULT_SENTINEL = "__codegen-default__";
|
|
86
|
-
/**
|
|
87
|
-
* Build the per-entity setting key. Lowercased so case variants of
|
|
88
|
-
* the entity name collapse onto a single record in `MJ: User Settings`.
|
|
89
|
-
*/
|
|
90
|
-
private settingKey;
|
|
91
|
-
/**
|
|
92
|
-
* Persist a specific override choice. Pass the override UUID. Writes
|
|
93
|
-
* via `UserInfoEngine.SetSettingDebounced` so rapid successive picks
|
|
94
|
-
* don't hammer the DB. Use {@link SetExplicitDefault} to record "user
|
|
95
|
-
* wants the CodeGen Angular fallback" and {@link ClearSelectedVariant}
|
|
96
|
-
* to wipe the preference entirely (revert to auto-pick).
|
|
97
|
-
*/
|
|
98
|
-
SetSelectedVariant(entityName: string, overrideID: string): void;
|
|
99
|
-
/**
|
|
100
|
-
* Record that the user explicitly picked the "Default form" row in
|
|
101
|
-
* the picker. `pickActive` reads this sentinel and returns null even
|
|
102
|
-
* when Active overrides exist for the entity, so the form-loading
|
|
103
|
-
* path falls back to CodeGen's `@RegisterClass` lookup.
|
|
104
|
-
*/
|
|
105
|
-
SetExplicitDefault(entityName: string): void;
|
|
106
|
-
/**
|
|
107
|
-
* Wipe the user's stored preference for this entity. Next load
|
|
108
|
-
* applies the auto-pick rules (first Active override in tier order).
|
|
109
|
-
* Called internally when a saved override ID is no longer valid.
|
|
110
|
-
* Fire-and-forget — the resolver doesn't need to await the delete.
|
|
111
|
-
*/
|
|
112
|
-
ClearSelectedVariant(entityName: string): void;
|
|
113
|
-
/**
|
|
114
|
-
* Read the user's previously-saved variant choice for the entity.
|
|
115
|
-
* Synchronous because `UserInfoEngine` keeps the user-settings table
|
|
116
|
-
* in memory after bootstrap. Returns the stored UUID, the explicit-
|
|
117
|
-
* default sentinel, or null when no preference exists.
|
|
118
|
-
*/
|
|
119
|
-
GetSelectedVariant(entityName: string): string | null;
|
|
120
|
-
private listApplicableVariants;
|
|
121
|
-
/**
|
|
122
|
-
* Pick the override that should actually render given the variant list
|
|
123
|
-
* and the user's stored selection (if any).
|
|
124
|
-
*
|
|
125
|
-
* Selection rules:
|
|
126
|
-
* - If the user explicitly picked the "Default form" row in the
|
|
127
|
-
* variant picker (sentinel in localStorage) → return null so the
|
|
128
|
-
* form-loading path falls back to CodeGen's `@RegisterClass` lookup.
|
|
129
|
-
* This is what makes the Angular fallback reachable from the UI.
|
|
130
|
-
* - Else if the user has a saved variant ID AND that variant is in
|
|
131
|
-
* the applicable list AND it's Active → use it.
|
|
132
|
-
* - Else → first Active row in tier+priority order (auto-pick).
|
|
133
|
-
* - Else → null (fall back to CodeGen/@RegisterClass path).
|
|
134
|
-
*/
|
|
135
|
-
private pickActive;
|
|
136
|
-
static ɵfac: i0.ɵɵFactoryDeclaration<FormResolverService, never>;
|
|
137
|
-
static ɵprov: i0.ɵɵInjectableDeclaration<FormResolverService>;
|
|
138
|
-
}
|
|
139
|
-
//# sourceMappingURL=form-resolver.service.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"form-resolver.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/form-resolver.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,IAAI,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,UAAU,EAAY,MAAM,sBAAsB,CAAC;AAGzF,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;;AAGlE;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,kEAAkE;AAClE,MAAM,MAAM,cAAc,GACpB;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,EAAE,qBAAqB,CAAC;IAAC,QAAQ,EAAE,qBAAqB,EAAE,CAAA;CAAE,GAC3F;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAAC,QAAQ,EAAE,qBAAqB,EAAE,CAAA;CAAE,GACvF;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,qBAAqB,EAAE,CAAA;CAAE,CAAC;AAiB1D;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBACa,mBAAmB;IAEtB,oBAAoB,CACtB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,iBAAiB,GAC5B,OAAO,CAAC,cAAc,CAAC;IAc1B;;;;;OAKG;IACG,qBAAqB,CACvB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,iBAAiB,GAC5B,OAAO,CAAC,qBAAqB,EAAE,CAAC;IAInC;;;;;;;;;;;OAWG;IACH,gBAAuB,yBAAyB,yBAAyB;IAEzE;;;OAGG;IACH,OAAO,CAAC,UAAU;IAIlB;;;;;;OAMG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAIhE;;;;;OAKG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAO5C;;;;;OAKG;IACH,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAK9C;;;;;OAKG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;YAMvC,sBAAsB;IAwEpC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,UAAU;yCA7LT,mBAAmB;6CAAnB,mBAAmB;CA6M/B"}
|