@pega/sdk 0.0.13 → 0.0.14
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/package.json +1 -1
- package/templates/angular-sdk-reference/angular.json +2 -1
- package/templates/angular-sdk-reference/package.json +1 -0
- package/templates/angular-sdk-reference/pega-constellationjs-0.26.3-local.tgz +0 -0
- package/templates/angular-sdk-reference/src/app/app.component.ts +232 -15
- package/templates/angular-sdk-reference/src/components/component-map.ts +90 -0
- package/templates/angular-sdk-reference/src/components/fields/auto-complete/auto-complete.component.ts +267 -0
- package/templates/angular-sdk-reference/src/components/fields/checkbox/checkbox.component.ts +84 -0
- package/templates/angular-sdk-reference/src/components/fields/currency/currency.component.ts +97 -0
- package/templates/angular-sdk-reference/src/components/fields/date-input/date-input.component.ts +88 -0
- package/templates/angular-sdk-reference/src/components/fields/dropdown/dropdown.component.ts +101 -0
- package/templates/angular-sdk-reference/src/components/fields/email/email.component.ts +91 -0
- package/templates/angular-sdk-reference/src/components/fields/integer/integer.component.ts +93 -0
- package/templates/angular-sdk-reference/src/components/fields/phone/phone.component.ts +91 -0
- package/templates/angular-sdk-reference/src/components/fields/radio-buttons/radio-buttons.component.ts +102 -0
- package/templates/angular-sdk-reference/src/components/fields/text-area/text-area.component.ts +98 -0
- package/templates/angular-sdk-reference/src/components/fields/text-input/text-input.component.ts +97 -0
- package/templates/angular-sdk-reference/src/components/fields/url-input/url-input.component.ts +94 -0
- package/templates/angular-sdk-reference/src/components/infra/assignment/assignment.component.ts +206 -0
- package/templates/angular-sdk-reference/src/components/infra/case-summary/case-summary.component.ts +76 -0
- package/templates/angular-sdk-reference/src/components/infra/case-view/case-view.component.ts +114 -0
- package/templates/angular-sdk-reference/src/components/infra/flow-container/flow-container.component.ts +411 -0
- package/templates/angular-sdk-reference/src/components/infra/reference/reference.component.ts +158 -0
- package/templates/angular-sdk-reference/src/components/infra/region/region.component.ts +39 -0
- package/templates/angular-sdk-reference/src/components/infra/root-container/root-container.component.ts +193 -0
- package/templates/angular-sdk-reference/src/components/infra/stages/stages.component.ts +78 -0
- package/templates/angular-sdk-reference/src/components/infra/view/view.component.ts +139 -0
- package/templates/angular-sdk-reference/src/components/infra/view-container/view-container.component.ts +191 -0
- package/templates/angular-sdk-reference/src/components/templates/default-form/default-form.component.ts +101 -0
- package/templates/angular-sdk-reference/src/components/templates/narrow-wide/narrow-wide.component.ts +37 -0
- package/templates/angular-sdk-reference/src/components/templates/one-column/one-column.component.ts +37 -0
- package/templates/angular-sdk-reference/src/components/templates/two-column/two-column.component.ts +37 -0
- package/templates/angular-sdk-reference/src/components/templates/wide-narrow/wide-narrow.component.ts +37 -0
- package/templates/angular-sdk-reference/src/helpers/event-utils.ts +18 -0
- package/templates/angular-sdk-reference/src/helpers/utils.ts +38 -0
- package/templates/angular-sdk-reference/src/helpers/versionHelpers.ts +14 -0
- package/templates/angular-sdk-reference/tsconfig.json +5 -2
- package/templates/react-sdk-reference/.ai-common/agents/embedded-flow-expert.md +11 -4
- package/templates/react-sdk-reference/.ai-common/prompts/component-manifest.json +7 -0
- package/templates/react-sdk-reference/.ai-common/prompts/generate-all-components.md +3 -1
- package/templates/react-sdk-reference/.ai-common/skills/field-components.md +75 -29
- package/templates/react-sdk-reference/.ai-common/skills/field-skills/AutoComplete.md +133 -101
- package/templates/react-sdk-reference/.ai-common/skills/field-skills/EmbeddedData.md +385 -0
- package/templates/react-sdk-reference/.ai-common/skills/field-skills/EmbeddedDataMulti.md +506 -0
- package/templates/react-sdk-reference/.ai-common/skills/field-skills/ObjectReference.md +549 -86
- package/templates/react-sdk-reference/.ai-common/skills/field-skills/UserReference.md +181 -83
- package/templates/react-sdk-reference/.ai-common/skills/template-skills/ListView.md +125 -153
- package/templates/react-sdk-reference/.ai-common/skills/template-skills/SimpleTable.md +90 -81
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Region — a layout region within a template (e.g., left/right column, region A/B).
|
|
3
|
+
* Renders its children directly via PConnectBridge.
|
|
4
|
+
*
|
|
5
|
+
* Pattern reference: angular-sdk-components RegionComponent + sdk-react Region
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
9
|
+
import { Component, Input } from '@angular/core';
|
|
10
|
+
import type { OnInit } from '@angular/core';
|
|
11
|
+
import { CommonModule } from '@angular/common';
|
|
12
|
+
import { PConnectBridgeComponent } from '@pega/sdk-angular';
|
|
13
|
+
|
|
14
|
+
@Component({
|
|
15
|
+
selector: 'pega-region',
|
|
16
|
+
standalone: true,
|
|
17
|
+
imports: [CommonModule, PConnectBridgeComponent],
|
|
18
|
+
template: `
|
|
19
|
+
<div>
|
|
20
|
+
<div *ngFor="let getter of childGetters">
|
|
21
|
+
<pconnect-bridge [getPConnect]="getter"></pconnect-bridge>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
`,
|
|
25
|
+
})
|
|
26
|
+
export class RegionComponent implements OnInit {
|
|
27
|
+
@Input() props: any;
|
|
28
|
+
|
|
29
|
+
childGetters: Array<() => typeof PConnect> = [];
|
|
30
|
+
|
|
31
|
+
ngOnInit(): void {
|
|
32
|
+
const pConn = this.props.getPConnect();
|
|
33
|
+
const children = pConn.getChildren?.() ?? [];
|
|
34
|
+
this.childGetters = children.map((child: any) => {
|
|
35
|
+
const getter = child.getPConnect ?? (() => child);
|
|
36
|
+
return getter;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RootContainer — top-level container that initializes Redux containers
|
|
3
|
+
* and renders either a View (portal mode) or ViewContainer (mashup/noPortal mode).
|
|
4
|
+
*
|
|
5
|
+
* In mashup mode (loadMashup), renderingMode === 'noPortal':
|
|
6
|
+
* - Gets children from pConn, finds a ViewContainer child
|
|
7
|
+
* - Creates a ViewContainer PConnect via PCore.createPConnect
|
|
8
|
+
* - Renders the ViewContainer which handles container init and routing
|
|
9
|
+
*
|
|
10
|
+
* Pattern reference: angular-sdk-components RootContainerComponent
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
14
|
+
import { Component, Input, NgZone, ChangeDetectorRef } from '@angular/core';
|
|
15
|
+
import type { OnInit, OnDestroy } from '@angular/core';
|
|
16
|
+
import { CommonModule } from '@angular/common';
|
|
17
|
+
import type { Unsubscribe } from '@pega/sdk-angular';
|
|
18
|
+
import { PConnectBridgeComponent } from '@pega/sdk-angular';
|
|
19
|
+
|
|
20
|
+
const options = { context: 'app' };
|
|
21
|
+
|
|
22
|
+
@Component({
|
|
23
|
+
selector: 'pega-root-container',
|
|
24
|
+
standalone: true,
|
|
25
|
+
imports: [CommonModule, PConnectBridgeComponent],
|
|
26
|
+
template: `
|
|
27
|
+
<div *ngIf="bShowRoot">
|
|
28
|
+
<!-- ViewContainer mode (mashup / noPortal) -->
|
|
29
|
+
<div *ngIf="viewContainerPConn">
|
|
30
|
+
<pconnect-bridge [getPConnect]="viewContainerGetter!"></pconnect-bridge>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<!-- Portal/View mode — render resolved pConn -->
|
|
34
|
+
<div *ngIf="!viewContainerPConn && resolvedPConn">
|
|
35
|
+
<pconnect-bridge [getPConnect]="resolvedPConnGetter!"></pconnect-bridge>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<!-- Fallback: render children -->
|
|
39
|
+
<div *ngIf="!viewContainerPConn && !resolvedPConn && children.length > 0">
|
|
40
|
+
<pconnect-bridge *ngFor="let child of children" [getPConnect]="child.getPConnect"></pconnect-bridge>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
`,
|
|
44
|
+
})
|
|
45
|
+
export class RootContainerComponent implements OnInit, OnDestroy {
|
|
46
|
+
@Input() props: any;
|
|
47
|
+
|
|
48
|
+
bShowRoot = false;
|
|
49
|
+
componentName = '';
|
|
50
|
+
viewContainerPConn: any = null;
|
|
51
|
+
viewContainerGetter: (() => typeof PConnect) | null = null;
|
|
52
|
+
resolvedPConn: any = null;
|
|
53
|
+
resolvedPConnGetter: (() => typeof PConnect) | null = null;
|
|
54
|
+
children: Array<{ getPConnect: () => typeof PConnect }> = [];
|
|
55
|
+
|
|
56
|
+
private pConn: any;
|
|
57
|
+
private observable: any;
|
|
58
|
+
private storeUnsub: Unsubscribe | undefined;
|
|
59
|
+
|
|
60
|
+
constructor(
|
|
61
|
+
private ngZone: NgZone,
|
|
62
|
+
private cdr: ChangeDetectorRef,
|
|
63
|
+
) {}
|
|
64
|
+
|
|
65
|
+
ngOnInit(): void {
|
|
66
|
+
this.pConn = this.props.getPConnect();
|
|
67
|
+
|
|
68
|
+
// Add root container items to Redux
|
|
69
|
+
const { containers } = PCore.getStore().getState();
|
|
70
|
+
const items = Object.keys(containers).filter((item) => item.includes('root'));
|
|
71
|
+
(PCore.getContainerUtils() as any).getContainerAPI().addContainerItems(items);
|
|
72
|
+
|
|
73
|
+
// Clear out hasViewContainer so ViewContainer can initialize
|
|
74
|
+
sessionStorage.setItem('hasViewContainer', 'false');
|
|
75
|
+
|
|
76
|
+
// Create observable for store-driven updates
|
|
77
|
+
// @ts-ignore
|
|
78
|
+
const { PConnectObservable } = PCore.getInitialiser().getSdkCore() as any;
|
|
79
|
+
this.observable = new PConnectObservable(this.pConn);
|
|
80
|
+
|
|
81
|
+
// Initial update
|
|
82
|
+
this.updateSelf();
|
|
83
|
+
|
|
84
|
+
// Subscribe to store changes
|
|
85
|
+
this.storeUnsub = this.observable.subscribe(() => {
|
|
86
|
+
this.updateSelf();
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
ngOnDestroy(): void {
|
|
91
|
+
this.storeUnsub?.();
|
|
92
|
+
this.observable?.destroy();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private updateSelf(): void {
|
|
96
|
+
// Get fresh props from Redux (not the initial props)
|
|
97
|
+
const myProps = this.pConn.resolveConfigProps(this.pConn.getConfigProps()) as any;
|
|
98
|
+
|
|
99
|
+
// Also get populated additional props (routingInfo comes from here)
|
|
100
|
+
const additionalProps = this.pConn.getComponentProps?.() ?? {};
|
|
101
|
+
const routingInfo = additionalProps.routingInfo ?? myProps.routingInfo;
|
|
102
|
+
const renderingMode = myProps.renderingMode ?? this.props.renderingMode;
|
|
103
|
+
const children = myProps.children ?? this.props.children;
|
|
104
|
+
const skeleton = myProps.skeleton;
|
|
105
|
+
|
|
106
|
+
const renderingModes = ['portal', 'view'];
|
|
107
|
+
const noPortalMode = 'noPortal';
|
|
108
|
+
|
|
109
|
+
if (routingInfo && renderingModes.includes(renderingMode)) {
|
|
110
|
+
this.handlePortalMode(routingInfo);
|
|
111
|
+
} else if (renderingMode === noPortalMode) {
|
|
112
|
+
this.generateViewContainerForNoPortal();
|
|
113
|
+
} else if (children && children.length > 0) {
|
|
114
|
+
// Render children directly
|
|
115
|
+
this.ngZone.run(() => {
|
|
116
|
+
this.children = children.map((child: any) => ({
|
|
117
|
+
getPConnect: child.getPConnect ?? (() => child),
|
|
118
|
+
}));
|
|
119
|
+
this.bShowRoot = true;
|
|
120
|
+
this.cdr.detectChanges();
|
|
121
|
+
});
|
|
122
|
+
} else if (skeleton !== undefined) {
|
|
123
|
+
// TODO: handle skeletons when available
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private handlePortalMode(routingInfo: any): void {
|
|
128
|
+
const { accessedOrder, items } = routingInfo;
|
|
129
|
+
if (!accessedOrder || !items) return;
|
|
130
|
+
|
|
131
|
+
const key = accessedOrder[accessedOrder.length - 1];
|
|
132
|
+
if (items[key]?.view && Object.keys(items[key].view).length > 0) {
|
|
133
|
+
const itemView = items[key].view;
|
|
134
|
+
const rootObject = PCore.createPConnect({
|
|
135
|
+
meta: itemView,
|
|
136
|
+
options: {
|
|
137
|
+
context: items[key].context,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
this.ngZone.run(() => {
|
|
142
|
+
const theNewPConn = (rootObject as any).getPConnect();
|
|
143
|
+
this.componentName = theNewPConn.getComponentName();
|
|
144
|
+
this.resolvedPConn = theNewPConn;
|
|
145
|
+
this.resolvedPConnGetter = () => theNewPConn;
|
|
146
|
+
this.bShowRoot = true;
|
|
147
|
+
this.cdr.detectChanges();
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Mashup / noPortal mode:
|
|
154
|
+
* Gets children from pConn, finds the ViewContainer child,
|
|
155
|
+
* creates a ViewContainer PConnect, and sets it for rendering.
|
|
156
|
+
*/
|
|
157
|
+
private generateViewContainerForNoPortal(): void {
|
|
158
|
+
const arChildren = this.pConn.getChildren() as any[];
|
|
159
|
+
if (!arChildren || arChildren.length === 0) return;
|
|
160
|
+
|
|
161
|
+
const localPConn = arChildren[0].getPConnect();
|
|
162
|
+
this.componentName = localPConn.getComponentName();
|
|
163
|
+
|
|
164
|
+
if (this.componentName === 'ViewContainer') {
|
|
165
|
+
const configProps = this.pConn.getConfigProps();
|
|
166
|
+
const viewContConfig = {
|
|
167
|
+
meta: {
|
|
168
|
+
type: 'ViewContainer',
|
|
169
|
+
config: configProps,
|
|
170
|
+
},
|
|
171
|
+
options,
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const createdViewContainer = PCore.createPConnect(viewContConfig) as any;
|
|
175
|
+
const vcPConn = createdViewContainer.getPConnect();
|
|
176
|
+
|
|
177
|
+
this.ngZone.run(() => {
|
|
178
|
+
this.viewContainerPConn = vcPConn;
|
|
179
|
+
this.viewContainerGetter = () => vcPConn;
|
|
180
|
+
this.bShowRoot = true;
|
|
181
|
+
this.cdr.detectChanges();
|
|
182
|
+
});
|
|
183
|
+
} else {
|
|
184
|
+
// Non-ViewContainer child — render via bridge
|
|
185
|
+
this.ngZone.run(() => {
|
|
186
|
+
this.resolvedPConn = localPConn;
|
|
187
|
+
this.resolvedPConnGetter = () => localPConn;
|
|
188
|
+
this.bShowRoot = true;
|
|
189
|
+
this.cdr.detectChanges();
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stages — renders the case stage progress bar.
|
|
3
|
+
*
|
|
4
|
+
* Pattern reference: angular-sdk-components StagesComponent + sdk-react Stages
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
8
|
+
import { Component, Input, ChangeDetectorRef, NgZone } from '@angular/core';
|
|
9
|
+
import type { OnInit, OnDestroy } from '@angular/core';
|
|
10
|
+
import { CommonModule } from '@angular/common';
|
|
11
|
+
import type { Unsubscribe } from '@pega/sdk-angular';
|
|
12
|
+
|
|
13
|
+
@Component({
|
|
14
|
+
selector: 'pega-stages',
|
|
15
|
+
standalone: true,
|
|
16
|
+
imports: [CommonModule],
|
|
17
|
+
template: `
|
|
18
|
+
<div *ngIf="stages.length > 0" style="display: flex; gap: 0.25rem; margin-bottom: 1rem;">
|
|
19
|
+
<div
|
|
20
|
+
*ngFor="let stage of stages"
|
|
21
|
+
[style.flex]="1"
|
|
22
|
+
[style.textAlign]="'center'"
|
|
23
|
+
[style.padding]="'0.5rem'"
|
|
24
|
+
[style.fontSize]="'0.75rem'"
|
|
25
|
+
[style.fontWeight]="stage.visited_status === 'active' ? '600' : '400'"
|
|
26
|
+
[style.background]="
|
|
27
|
+
stage.visited_status === 'completed' ? '#dcfce7' : stage.visited_status === 'active' ? '#dbeafe' : '#f3f4f6'
|
|
28
|
+
"
|
|
29
|
+
[style.color]="
|
|
30
|
+
stage.visited_status === 'completed' ? '#166534' : stage.visited_status === 'active' ? '#1d4ed8' : '#6b7280'
|
|
31
|
+
"
|
|
32
|
+
[style.borderRadius]="'0.25rem'"
|
|
33
|
+
>
|
|
34
|
+
{{ stage.name }}
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
`,
|
|
38
|
+
})
|
|
39
|
+
export class StagesComponent implements OnInit, OnDestroy {
|
|
40
|
+
@Input() props: any;
|
|
41
|
+
|
|
42
|
+
stages: Array<{ name: string; visited_status?: string }> = [];
|
|
43
|
+
|
|
44
|
+
private pConn: any;
|
|
45
|
+
private observable: any;
|
|
46
|
+
private storeUnsub: Unsubscribe | undefined;
|
|
47
|
+
|
|
48
|
+
constructor(
|
|
49
|
+
private ngZone: NgZone,
|
|
50
|
+
private cdr: ChangeDetectorRef,
|
|
51
|
+
) {}
|
|
52
|
+
|
|
53
|
+
ngOnInit(): void {
|
|
54
|
+
this.pConn = this.props.getPConnect();
|
|
55
|
+
// @ts-ignore
|
|
56
|
+
const { PConnectObservable } = PCore.getInitialiser().getSdkCore() as any;
|
|
57
|
+
this.observable = new PConnectObservable(this.pConn);
|
|
58
|
+
this.updateSelf();
|
|
59
|
+
|
|
60
|
+
this.storeUnsub = this.observable.subscribe(() => {
|
|
61
|
+
this.updateSelf();
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
ngOnDestroy(): void {
|
|
66
|
+
this.storeUnsub?.();
|
|
67
|
+
this.observable?.destroy();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private updateSelf(): void {
|
|
71
|
+
const configProps = this.pConn.resolveConfigProps(this.pConn.getConfigProps());
|
|
72
|
+
this.stages = configProps.stages ?? this.props.stages ?? [];
|
|
73
|
+
|
|
74
|
+
this.ngZone.run(() => {
|
|
75
|
+
this.cdr.detectChanges();
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* View — renders a named template (DefaultForm, OneColumn, etc.) or falls back
|
|
3
|
+
* to rendering children directly.
|
|
4
|
+
*
|
|
5
|
+
* Resolves template name from config, handles visibility, label display,
|
|
6
|
+
* and delegates to the appropriate template component via PConnectBridge.
|
|
7
|
+
*
|
|
8
|
+
* Pattern reference: angular-sdk-components ViewComponent + sdk-react View
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
12
|
+
import { Component, Input, ChangeDetectorRef, NgZone } from '@angular/core';
|
|
13
|
+
import type { OnInit, OnDestroy, Type } from '@angular/core';
|
|
14
|
+
import { CommonModule } from '@angular/common';
|
|
15
|
+
import type { Unsubscribe } from '@pega/sdk-angular';
|
|
16
|
+
import { PConnectBridgeComponent, getAngularComponent } from '@pega/sdk-angular';
|
|
17
|
+
|
|
18
|
+
const NO_HEADER_TEMPLATES = [
|
|
19
|
+
'SubTabs',
|
|
20
|
+
'SimpleTable',
|
|
21
|
+
'Details',
|
|
22
|
+
'DetailsTwoColumn',
|
|
23
|
+
'DetailsThreeColumn',
|
|
24
|
+
'NarrowWideDetails',
|
|
25
|
+
'WideNarrowDetails',
|
|
26
|
+
'Confirmation',
|
|
27
|
+
'DynamicTabs',
|
|
28
|
+
'DetailsSubTabs',
|
|
29
|
+
'ListView',
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
@Component({
|
|
33
|
+
selector: 'pega-view',
|
|
34
|
+
standalone: true,
|
|
35
|
+
imports: [CommonModule, PConnectBridgeComponent],
|
|
36
|
+
template: `
|
|
37
|
+
<div *ngIf="visibility !== false">
|
|
38
|
+
<!-- Label/header -->
|
|
39
|
+
<div
|
|
40
|
+
*ngIf="showLabel && label && !isNoHeaderTemplate"
|
|
41
|
+
style="font-weight: 600; font-size: 0.875rem; margin-bottom: 0.5rem;"
|
|
42
|
+
>
|
|
43
|
+
{{ label }}
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<!-- Template mode: render the named template component directly (NOT through bridge) -->
|
|
47
|
+
<ng-container *ngIf="templateComponent">
|
|
48
|
+
<ng-container *ngComponentOutlet="templateComponent; inputs: templateInputs"></ng-container>
|
|
49
|
+
</ng-container>
|
|
50
|
+
|
|
51
|
+
<!-- No template: render children directly -->
|
|
52
|
+
<div *ngIf="!templateComponent && childGetters.length > 0">
|
|
53
|
+
<div *ngFor="let getter of childGetters">
|
|
54
|
+
<pconnect-bridge [getPConnect]="getter"></pconnect-bridge>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
`,
|
|
59
|
+
})
|
|
60
|
+
export class ViewComponent implements OnInit, OnDestroy {
|
|
61
|
+
@Input() props: any;
|
|
62
|
+
|
|
63
|
+
visibility: boolean | undefined = true;
|
|
64
|
+
showLabel = false;
|
|
65
|
+
label = '';
|
|
66
|
+
templateName = '';
|
|
67
|
+
isNoHeaderTemplate = false;
|
|
68
|
+
templateComponent: Type<unknown> | null = null;
|
|
69
|
+
templateInputs: Record<string, unknown> = {};
|
|
70
|
+
childGetters: Array<() => typeof PConnect> = [];
|
|
71
|
+
|
|
72
|
+
private pConn: any;
|
|
73
|
+
private observable: any;
|
|
74
|
+
private storeUnsub: Unsubscribe | undefined;
|
|
75
|
+
|
|
76
|
+
constructor(
|
|
77
|
+
private ngZone: NgZone,
|
|
78
|
+
private cdr: ChangeDetectorRef,
|
|
79
|
+
) {}
|
|
80
|
+
|
|
81
|
+
ngOnInit(): void {
|
|
82
|
+
this.pConn = this.props.getPConnect();
|
|
83
|
+
|
|
84
|
+
// @ts-ignore
|
|
85
|
+
const { PConnectObservable } = PCore.getInitialiser().getSdkCore() as any;
|
|
86
|
+
this.observable = new PConnectObservable(this.pConn);
|
|
87
|
+
this.updateSelf();
|
|
88
|
+
|
|
89
|
+
this.storeUnsub = this.observable.subscribe(() => {
|
|
90
|
+
this.updateSelf();
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
ngOnDestroy(): void {
|
|
95
|
+
this.storeUnsub?.();
|
|
96
|
+
this.observable?.destroy();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private updateSelf(): void {
|
|
100
|
+
const configProps = this.pConn.resolveConfigProps(this.pConn.getConfigProps());
|
|
101
|
+
const inheritedProps = this.pConn.getInheritedProps?.() ?? {};
|
|
102
|
+
|
|
103
|
+
this.templateName = configProps.template ?? '';
|
|
104
|
+
this.label = inheritedProps.label || configProps.label || '';
|
|
105
|
+
this.showLabel = inheritedProps.showLabel || configProps.showLabel || false;
|
|
106
|
+
this.visibility = configProps.visibility ?? true;
|
|
107
|
+
this.isNoHeaderTemplate = NO_HEADER_TEMPLATES.includes(this.templateName);
|
|
108
|
+
|
|
109
|
+
const children = this.pConn.getChildren?.() ?? [];
|
|
110
|
+
|
|
111
|
+
if (this.templateName) {
|
|
112
|
+
// Look up the template component directly from the registry.
|
|
113
|
+
// Do NOT pass through the bridge — the bridge would call getComponentName()
|
|
114
|
+
// on the same pConn, returning "View" and causing infinite recursion.
|
|
115
|
+
this.templateComponent = getAngularComponent(this.templateName) ?? null;
|
|
116
|
+
if (this.templateComponent) {
|
|
117
|
+
this.templateInputs = {
|
|
118
|
+
props: {
|
|
119
|
+
...configProps,
|
|
120
|
+
getPConnect: () => this.pConn,
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
this.childGetters = [];
|
|
125
|
+
} else {
|
|
126
|
+
// No template — render children directly
|
|
127
|
+
this.templateComponent = null;
|
|
128
|
+
this.templateInputs = {};
|
|
129
|
+
this.childGetters = children.map((child: any) => {
|
|
130
|
+
const getter = child.getPConnect ?? (() => child);
|
|
131
|
+
return getter;
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this.ngZone.run(() => {
|
|
136
|
+
this.cdr.detectChanges();
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ViewContainer — manages view routing within a single container.
|
|
3
|
+
*
|
|
4
|
+
* Initializes containers via containerManager, subscribes to Redux store for
|
|
5
|
+
* routingInfo changes, and dynamically creates PConnect for the active view/assignment.
|
|
6
|
+
*
|
|
7
|
+
* Key flow:
|
|
8
|
+
* 1. sessionStorage('hasViewContainer') gate prevents double init
|
|
9
|
+
* 2. containerMgr.initializeContainers + addContainerItem
|
|
10
|
+
* 3. Store subscription fires updateSelf on every Redux change
|
|
11
|
+
* 4. updateSelf reads routingInfo (populated by populateAdditionalProps)
|
|
12
|
+
* 5. Creates new PConnect config for the active container item
|
|
13
|
+
* 6. Renders via PConnectBridgeComponent
|
|
14
|
+
*
|
|
15
|
+
* Pattern reference: angular-sdk-components ViewContainerComponent
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
19
|
+
import { Component, Input, ChangeDetectorRef, NgZone } from '@angular/core';
|
|
20
|
+
import type { OnInit, OnDestroy } from '@angular/core';
|
|
21
|
+
import { CommonModule } from '@angular/common';
|
|
22
|
+
import { PConnectBridgeComponent } from '@pega/sdk-angular';
|
|
23
|
+
|
|
24
|
+
@Component({
|
|
25
|
+
selector: 'pega-view-container',
|
|
26
|
+
standalone: true,
|
|
27
|
+
imports: [CommonModule, PConnectBridgeComponent],
|
|
28
|
+
template: `
|
|
29
|
+
<div [id]="buildName">
|
|
30
|
+
<!-- Resolved view via routing -->
|
|
31
|
+
<div *ngIf="isViewContainer && createdViewGetPConnect">
|
|
32
|
+
<pconnect-bridge
|
|
33
|
+
[getPConnect]="createdViewGetPConnect"
|
|
34
|
+
></pconnect-bridge>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<!-- Reference handling — delegate to bridge -->
|
|
38
|
+
<div *ngIf="!isViewContainer && viewPConnGetter">
|
|
39
|
+
<pconnect-bridge [getPConnect]="viewPConnGetter"></pconnect-bridge>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<!-- Fallback: render children directly -->
|
|
43
|
+
<div
|
|
44
|
+
*ngIf="
|
|
45
|
+
!createdViewGetPConnect && !viewPConnGetter && children.length > 0
|
|
46
|
+
"
|
|
47
|
+
>
|
|
48
|
+
<pconnect-bridge
|
|
49
|
+
*ngFor="let child of children"
|
|
50
|
+
[getPConnect]="child.getPConnect"
|
|
51
|
+
></pconnect-bridge>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
`,
|
|
55
|
+
})
|
|
56
|
+
export class ViewContainerComponent implements OnInit, OnDestroy {
|
|
57
|
+
@Input() props: any;
|
|
58
|
+
|
|
59
|
+
buildName = '';
|
|
60
|
+
isViewContainer = true;
|
|
61
|
+
createdViewGetPConnect: (() => typeof PConnect) | null = null;
|
|
62
|
+
viewPConnGetter: (() => typeof PConnect) | null = null;
|
|
63
|
+
children: Array<{ getPConnect: () => typeof PConnect }> = [];
|
|
64
|
+
|
|
65
|
+
private pConn: any;
|
|
66
|
+
private storeUnsub: (() => void) | undefined;
|
|
67
|
+
|
|
68
|
+
constructor(
|
|
69
|
+
private cdr: ChangeDetectorRef,
|
|
70
|
+
private ngZone: NgZone,
|
|
71
|
+
) {}
|
|
72
|
+
|
|
73
|
+
ngOnInit(): void {
|
|
74
|
+
this.pConn = this.props.getPConnect();
|
|
75
|
+
|
|
76
|
+
// Get children
|
|
77
|
+
this.children = (this.pConn.getChildren?.() ?? []).map((child: any) => ({
|
|
78
|
+
getPConnect: child.getPConnect ?? (() => child),
|
|
79
|
+
}));
|
|
80
|
+
|
|
81
|
+
this.buildName = this.getBuildName();
|
|
82
|
+
|
|
83
|
+
const configProps = this.pConn.resolveConfigProps(
|
|
84
|
+
this.pConn.getConfigProps(),
|
|
85
|
+
);
|
|
86
|
+
const { CONTAINER_TYPE, APP } = PCore.getConstants() as any;
|
|
87
|
+
const { name, mode, limit } = configProps;
|
|
88
|
+
|
|
89
|
+
this.pConn.isBoundToState();
|
|
90
|
+
|
|
91
|
+
// Subscribe to store FIRST — before any dispatches so we catch all updates
|
|
92
|
+
this.storeUnsub = PCore.getStore().subscribe(() => {
|
|
93
|
+
this.updateSelf();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const containerMgr = this.pConn.getContainerManager();
|
|
97
|
+
const context = this.pConn.getContextName();
|
|
98
|
+
const containerName = this.pConn.getContainerName();
|
|
99
|
+
|
|
100
|
+
// Use PCore's isContainerInitialized API instead of fragile sessionStorage gate
|
|
101
|
+
const isInitialized = (
|
|
102
|
+
PCore.getContainerUtils() as any
|
|
103
|
+
).isContainerInitialized(context, containerName);
|
|
104
|
+
|
|
105
|
+
if (!isInitialized) {
|
|
106
|
+
containerMgr.initializeContainers({
|
|
107
|
+
type:
|
|
108
|
+
mode === CONTAINER_TYPE.MULTIPLE
|
|
109
|
+
? CONTAINER_TYPE.MULTIPLE
|
|
110
|
+
: CONTAINER_TYPE.SINGLE,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (mode === CONTAINER_TYPE.MULTIPLE && limit) {
|
|
114
|
+
(PCore.getContainerUtils() as any).setContainerLimit(
|
|
115
|
+
`${APP.APP}/${name}`,
|
|
116
|
+
limit,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Add container item if not using semantic URL
|
|
122
|
+
if (!PCore.checkIfSemanticURL()) {
|
|
123
|
+
containerMgr.addContainerItem(this.pConn);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Explicit initial check — routingInfo may already be in the store
|
|
127
|
+
this.updateSelf();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
ngOnDestroy(): void {
|
|
131
|
+
this.storeUnsub?.();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private updateSelf(): void {
|
|
135
|
+
// routingInfo is populated by PConnectObservable.resolveState() which calls
|
|
136
|
+
// populateAdditionalProps — it arrives via this.props (set by the bridge),
|
|
137
|
+
// NOT from pConn.getComponentProps() which skips populateAdditionalProps.
|
|
138
|
+
const routingInfo = this.props?.routingInfo;
|
|
139
|
+
if (!routingInfo) return;
|
|
140
|
+
|
|
141
|
+
const { CREATE_DETAILS_VIEW_NAME } = PCore.getConstants() as any;
|
|
142
|
+
const { accessedOrder, items } = routingInfo;
|
|
143
|
+
|
|
144
|
+
if (!accessedOrder || !items) return;
|
|
145
|
+
|
|
146
|
+
const key = accessedOrder[accessedOrder.length - 1];
|
|
147
|
+
|
|
148
|
+
if (items[key]?.view && Object.keys(items[key].view).length > 0) {
|
|
149
|
+
const latestItem = items[key];
|
|
150
|
+
const rootView = latestItem.view;
|
|
151
|
+
const { context, name: viewName } = rootView.config;
|
|
152
|
+
const config: any = { meta: rootView };
|
|
153
|
+
config.options = {
|
|
154
|
+
context: latestItem.context,
|
|
155
|
+
pageReference: context || this.pConn.getPageReference(),
|
|
156
|
+
containerName: this.pConn.getContainerName(),
|
|
157
|
+
containerItemName: key,
|
|
158
|
+
hasForm: viewName === CREATE_DETAILS_VIEW_NAME,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const configObject = PCore.createPConnect(config);
|
|
162
|
+
const newComp = (configObject as any).getPConnect();
|
|
163
|
+
|
|
164
|
+
this.ngZone.run(() => {
|
|
165
|
+
if (newComp.getComponentName() === 'reference') {
|
|
166
|
+
// Hand off reference to the bridge for de-referencing
|
|
167
|
+
this.isViewContainer = false;
|
|
168
|
+
this.viewPConnGetter = () => newComp;
|
|
169
|
+
this.createdViewGetPConnect = null;
|
|
170
|
+
} else {
|
|
171
|
+
this.isViewContainer = true;
|
|
172
|
+
this.createdViewGetPConnect = () => newComp;
|
|
173
|
+
this.viewPConnGetter = null;
|
|
174
|
+
|
|
175
|
+
// Update children from the newly created view
|
|
176
|
+
const newChildren = newComp.getChildren?.() ?? [];
|
|
177
|
+
this.children = newChildren.map((child: any) => ({
|
|
178
|
+
getPConnect: child.getPConnect ?? (() => child),
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
181
|
+
this.cdr.detectChanges();
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private getBuildName(): string {
|
|
187
|
+
const sContext = this.pConn.getContextName?.() ?? '';
|
|
188
|
+
const sName = this.pConn.getContainerName?.() ?? '';
|
|
189
|
+
return `${sContext.toUpperCase()}/${sName.toUpperCase()}`;
|
|
190
|
+
}
|
|
191
|
+
}
|