@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.
Files changed (48) hide show
  1. package/package.json +1 -1
  2. package/templates/angular-sdk-reference/angular.json +2 -1
  3. package/templates/angular-sdk-reference/package.json +1 -0
  4. package/templates/angular-sdk-reference/pega-constellationjs-0.26.3-local.tgz +0 -0
  5. package/templates/angular-sdk-reference/src/app/app.component.ts +232 -15
  6. package/templates/angular-sdk-reference/src/components/component-map.ts +90 -0
  7. package/templates/angular-sdk-reference/src/components/fields/auto-complete/auto-complete.component.ts +267 -0
  8. package/templates/angular-sdk-reference/src/components/fields/checkbox/checkbox.component.ts +84 -0
  9. package/templates/angular-sdk-reference/src/components/fields/currency/currency.component.ts +97 -0
  10. package/templates/angular-sdk-reference/src/components/fields/date-input/date-input.component.ts +88 -0
  11. package/templates/angular-sdk-reference/src/components/fields/dropdown/dropdown.component.ts +101 -0
  12. package/templates/angular-sdk-reference/src/components/fields/email/email.component.ts +91 -0
  13. package/templates/angular-sdk-reference/src/components/fields/integer/integer.component.ts +93 -0
  14. package/templates/angular-sdk-reference/src/components/fields/phone/phone.component.ts +91 -0
  15. package/templates/angular-sdk-reference/src/components/fields/radio-buttons/radio-buttons.component.ts +102 -0
  16. package/templates/angular-sdk-reference/src/components/fields/text-area/text-area.component.ts +98 -0
  17. package/templates/angular-sdk-reference/src/components/fields/text-input/text-input.component.ts +97 -0
  18. package/templates/angular-sdk-reference/src/components/fields/url-input/url-input.component.ts +94 -0
  19. package/templates/angular-sdk-reference/src/components/infra/assignment/assignment.component.ts +206 -0
  20. package/templates/angular-sdk-reference/src/components/infra/case-summary/case-summary.component.ts +76 -0
  21. package/templates/angular-sdk-reference/src/components/infra/case-view/case-view.component.ts +114 -0
  22. package/templates/angular-sdk-reference/src/components/infra/flow-container/flow-container.component.ts +411 -0
  23. package/templates/angular-sdk-reference/src/components/infra/reference/reference.component.ts +158 -0
  24. package/templates/angular-sdk-reference/src/components/infra/region/region.component.ts +39 -0
  25. package/templates/angular-sdk-reference/src/components/infra/root-container/root-container.component.ts +193 -0
  26. package/templates/angular-sdk-reference/src/components/infra/stages/stages.component.ts +78 -0
  27. package/templates/angular-sdk-reference/src/components/infra/view/view.component.ts +139 -0
  28. package/templates/angular-sdk-reference/src/components/infra/view-container/view-container.component.ts +191 -0
  29. package/templates/angular-sdk-reference/src/components/templates/default-form/default-form.component.ts +101 -0
  30. package/templates/angular-sdk-reference/src/components/templates/narrow-wide/narrow-wide.component.ts +37 -0
  31. package/templates/angular-sdk-reference/src/components/templates/one-column/one-column.component.ts +37 -0
  32. package/templates/angular-sdk-reference/src/components/templates/two-column/two-column.component.ts +37 -0
  33. package/templates/angular-sdk-reference/src/components/templates/wide-narrow/wide-narrow.component.ts +37 -0
  34. package/templates/angular-sdk-reference/src/helpers/event-utils.ts +18 -0
  35. package/templates/angular-sdk-reference/src/helpers/utils.ts +38 -0
  36. package/templates/angular-sdk-reference/src/helpers/versionHelpers.ts +14 -0
  37. package/templates/angular-sdk-reference/tsconfig.json +5 -2
  38. package/templates/react-sdk-reference/.ai-common/agents/embedded-flow-expert.md +11 -4
  39. package/templates/react-sdk-reference/.ai-common/prompts/component-manifest.json +7 -0
  40. package/templates/react-sdk-reference/.ai-common/prompts/generate-all-components.md +3 -1
  41. package/templates/react-sdk-reference/.ai-common/skills/field-components.md +75 -29
  42. package/templates/react-sdk-reference/.ai-common/skills/field-skills/AutoComplete.md +133 -101
  43. package/templates/react-sdk-reference/.ai-common/skills/field-skills/EmbeddedData.md +385 -0
  44. package/templates/react-sdk-reference/.ai-common/skills/field-skills/EmbeddedDataMulti.md +506 -0
  45. package/templates/react-sdk-reference/.ai-common/skills/field-skills/ObjectReference.md +549 -86
  46. package/templates/react-sdk-reference/.ai-common/skills/field-skills/UserReference.md +181 -83
  47. package/templates/react-sdk-reference/.ai-common/skills/template-skills/ListView.md +125 -153
  48. 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
+ }