@pega/angular-sdk-overrides 0.26.2 → 0.26.3

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 (69) hide show
  1. package/lib/designSystemExtension/field-group/field-group.component.ts +11 -3
  2. package/lib/designSystemExtension/material-case-summary/material-case-summary.component.html +12 -3
  3. package/lib/field/auto-complete/auto-complete.component.html +44 -4
  4. package/lib/field/auto-complete/auto-complete.component.scss +92 -0
  5. package/lib/field/auto-complete/auto-complete.component.ts +341 -16
  6. package/lib/field/cancel-alert/cancel-alert.component.ts +38 -15
  7. package/lib/field/group/group.component.html +1 -1
  8. package/lib/field/group/group.component.ts +1 -3
  9. package/lib/field/semantic-link/semantic-link.component.html +1 -1
  10. package/lib/field/semantic-link/semantic-link.component.scss +4 -0
  11. package/lib/infra/Containers/flow-container/flow-container.component.html +1 -1
  12. package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.html +12 -0
  13. package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.spec.ts +130 -0
  14. package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.ts +77 -0
  15. package/lib/infra/Containers/modal-view-container/modal-view-container.component.html +35 -8
  16. package/lib/infra/Containers/modal-view-container/modal-view-container.component.spec.ts +24 -11
  17. package/lib/infra/Containers/modal-view-container/modal-view-container.component.ts +179 -184
  18. package/lib/infra/assignment/assignment.component.html +2 -1
  19. package/lib/infra/assignment/assignment.component.ts +30 -13
  20. package/lib/infra/defer-load/defer-load.component.ts +6 -3
  21. package/lib/infra/multi-step/multi-step.component.html +25 -33
  22. package/lib/infra/multi-step/multi-step.component.scss +48 -0
  23. package/lib/infra/multi-step/multi-step.component.ts +11 -0
  24. package/lib/infra/view/view.component.ts +17 -39
  25. package/lib/template/base/details-template-base.ts +35 -54
  26. package/lib/template/case-summary/case-summary.component.ts +15 -1
  27. package/lib/template/case-view/case-view.component.html +13 -1
  28. package/lib/template/case-view/case-view.component.spec.ts +119 -10
  29. package/lib/template/case-view/case-view.component.ts +52 -3
  30. package/lib/template/data-reference/data-reference.component.ts +27 -18
  31. package/lib/template/data-reference/search-form/search-form.component.html +1 -1
  32. package/lib/template/data-reference/search-form/search-form.component.ts +2 -0
  33. package/lib/template/details/details.component.html +23 -3
  34. package/lib/template/details/details.component.scss +9 -2
  35. package/lib/template/details/details.component.ts +3 -32
  36. package/lib/template/details-narrow-wide/details-narrow-wide.component.html +21 -2
  37. package/lib/template/details-narrow-wide/details-narrow-wide.component.scss +8 -1
  38. package/lib/template/details-narrow-wide/details-narrow-wide.component.ts +3 -37
  39. package/lib/template/details-one-column/details-one-column.component.html +23 -1
  40. package/lib/template/details-one-column/details-one-column.component.scss +14 -0
  41. package/lib/template/details-one-column/details-one-column.component.ts +3 -38
  42. package/lib/template/details-three-column/details-three-column.component.html +21 -2
  43. package/lib/template/details-three-column/details-three-column.component.scss +8 -1
  44. package/lib/template/details-three-column/details-three-column.component.ts +3 -46
  45. package/lib/template/details-two-column/details-two-column.component.html +21 -2
  46. package/lib/template/details-two-column/details-two-column.component.scss +8 -1
  47. package/lib/template/details-two-column/details-two-column.component.ts +3 -44
  48. package/lib/template/details-wide-narrow/details-wide-narrow.component.html +21 -2
  49. package/lib/template/details-wide-narrow/details-wide-narrow.component.scss +8 -1
  50. package/lib/template/details-wide-narrow/details-wide-narrow.component.ts +3 -41
  51. package/lib/template/field-value-list/field-value-list.component.scss +17 -0
  52. package/lib/template/list-view/list-view.component.ts +39 -0
  53. package/lib/template/self-service-case-view/self-service-case-view.component.html +13 -1
  54. package/lib/template/self-service-case-view/self-service-case-view.component.spec.ts +96 -11
  55. package/lib/template/self-service-case-view/self-service-case-view.component.ts +52 -3
  56. package/lib/template/simple-table-manual/helpers.ts +27 -6
  57. package/lib/template/simple-table-manual/simple-table-manual.component.html +1 -3
  58. package/lib/template/simple-table-manual/simple-table-manual.component.scss +4 -0
  59. package/lib/template/simple-table-manual/simple-table-manual.component.ts +51 -20
  60. package/lib/template/single-reference-readonly/single-reference-readonly.component.scss +0 -4
  61. package/package.json +1 -1
  62. package/lib/designSystemExtension/material-details/material-details.component.html +0 -40
  63. package/lib/designSystemExtension/material-details/material-details.component.scss +0 -116
  64. package/lib/designSystemExtension/material-details/material-details.component.spec.ts +0 -24
  65. package/lib/designSystemExtension/material-details/material-details.component.ts +0 -31
  66. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.html +0 -50
  67. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.scss +0 -34
  68. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.spec.ts +0 -22
  69. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.ts +0 -61
@@ -1,17 +1,32 @@
1
1
  import { ChangeDetectorRef, Component, OnInit, Input, Output, EventEmitter, forwardRef, OnDestroy } from '@angular/core';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import { FormBuilder, FormGroup } from '@angular/forms';
4
- import isEqual from 'fast-deep-equal';
5
4
  import { AngularPConnectData, AngularPConnectService } from '@pega/angular-sdk-components';
6
5
  import { ProgressSpinnerService } from '@pega/angular-sdk-components';
6
+ import { BannerService } from 'packages/angular-sdk-components/src/lib/_services/banner.service';
7
7
  import { ComponentMapperComponent } from '@pega/angular-sdk-components';
8
- import { getBanners } from '@pega/angular-sdk-components';
9
8
 
10
9
  /**
11
10
  * WARNING: This file is part of the infrastructure component responsible for working with Redux and managing the creation and update of Redux containers and PConnect.
12
11
  * You may override Material components within this component if needed, but do not modify any container-related logic. Changing this logic can lead to unexpected behavior.
13
12
  */
14
13
 
14
+ // One entry per open modal — supports stacking (e.g. "Create new" opened from within another modal)
15
+ interface ModalEntry {
16
+ key: string;
17
+ title: string;
18
+ createdViewPConn$: any;
19
+ arChildren$: any[];
20
+ isMultiRecordData: boolean;
21
+ isDataObjectModal: boolean;
22
+ dataRecordKeys: string;
23
+ dataObjectActionID: string;
24
+ dataObjectAction: string;
25
+ dataObjectClassId: string;
26
+ context: string;
27
+ updateToken: number;
28
+ }
29
+
15
30
  @Component({
16
31
  selector: 'app-modal-view-container',
17
32
  templateUrl: './modal-view-container.component.html',
@@ -27,41 +42,33 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy {
27
42
  // Used with AngularPConnect
28
43
  angularPConnectData: AngularPConnectData = {};
29
44
 
30
- arChildren$: any[];
31
45
  stateProps$: object;
32
- banners: any;
33
- templateName$: string;
34
- buildName$: string;
35
- context$: string;
36
- title$ = '';
37
- bShowModal$ = false;
38
- itemKey$: string;
39
46
  formGroup$: FormGroup;
40
- oCaseInfo: object = {};
41
-
42
- // for causing a change on assignment
43
- updateToken$ = 0;
44
47
 
45
48
  routingInfoRef: any = {};
46
49
 
47
- // created object is now a View with a Template
48
- // Use its PConnect to render the CaseView; DON'T replace this.pConn$
49
- createdViewPConn$: any;
50
+ // Stack of open modals — supports one modal opening another (e.g. "Create new" from within a modal).
51
+ // Keyed the same as PCore's routingInfo.items so open/update/close can be derived from accessedOrder.
52
+ modalStack: ModalEntry[] = [];
53
+ private modalCollection: Record<string, object> = {};
50
54
 
51
55
  bSubscribed = false;
52
56
  cancelPConn$?: typeof PConnect;
57
+ cancelHideDelete$: boolean;
58
+ cancelIsDataObject$: boolean;
59
+ cancelSkipReleaseLockRequest$: any;
53
60
  bShowCancelAlert$ = false;
54
61
  bAlertState: boolean;
55
62
  localizedVal: Function;
56
63
  localeCategory = 'Data Object';
57
- isMultiRecord = false;
58
64
  actionsDialog = false;
59
65
 
60
66
  constructor(
61
67
  private angularPConnect: AngularPConnectService,
62
68
  private cdRef: ChangeDetectorRef,
63
69
  private psService: ProgressSpinnerService,
64
- private fb: FormBuilder
70
+ private fb: FormBuilder,
71
+ public bannerService: BannerService
65
72
  ) {
66
73
  // create the formGroup
67
74
  this.formGroup$ = fb.group({ hideRequired: false });
@@ -71,22 +78,12 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy {
71
78
  // First thing in initialization is registering and subscribing to the AngularPConnect service
72
79
  this.angularPConnectData = this.angularPConnect.registerAndSubscribeComponent(this, this.onStateChange);
73
80
 
74
- const baseContext = this.pConn$.getContextName();
75
- const acName = this.pConn$.getContainerName();
76
-
77
- // for now, in general this should be overridden by updateSelf(), and not be blank
78
- if (this.itemKey$ === '') {
79
- this.itemKey$ = baseContext.concat('/').concat(acName);
80
- }
81
-
82
81
  const containerMgr = this.pConn$.getContainerManager();
83
82
 
84
83
  containerMgr.initializeContainers({
85
84
  type: 'multiple'
86
85
  });
87
86
 
88
- // const { CONTAINER_TYPE, PUB_SUB_EVENTS } = PCore.getConstants();
89
-
90
87
  this.angularPConnect.shouldComponentUpdate(this);
91
88
  this.localizedVal = PCore.getLocaleUtils().getLocaleValue;
92
89
  }
@@ -114,10 +111,6 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy {
114
111
  // ONLY call updateSelf when the component should update
115
112
  if (bUpdateSelf) {
116
113
  this.updateSelf();
117
- } else if (this.bShowModal$) {
118
- // right now onlu get one updated when initial diaplay. So, once modal is up
119
- // let fall through and do a check with "compareCaseInfoIsDifferent" until fixed
120
- // this.updateSelf();
121
114
  }
122
115
  }
123
116
 
@@ -134,141 +127,102 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy {
134
127
  } catch (ex) {
135
128
  console.log(ex);
136
129
  }
137
- // const configProps = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps());
138
130
  this.stateProps$ = this.pConn$.getStateProps();
139
- this.banners = this.getBanners();
140
131
 
141
- if (!loadingInfo) {
142
- // turn off spinner
143
- // this.psService.sendMessage(false);
132
+ if (!routingInfo || loadingInfo) {
133
+ return;
144
134
  }
145
135
 
146
- if (routingInfo && !loadingInfo /* && this.bUpdate */) {
147
- const currentOrder = routingInfo.accessedOrder;
136
+ const { accessedOrder, type } = routingInfo;
148
137
 
149
- if (undefined == currentOrder) {
150
- return;
151
- }
138
+ if (undefined == accessedOrder) {
139
+ return;
140
+ }
152
141
 
153
- const currentItems = routingInfo.items;
154
-
155
- const { key, latestItem } = this.getKeyAndLatestItem(routingInfo);
156
-
157
- if (currentOrder.length > 0) {
158
- if (currentItems[key] && currentItems[key].view && Object.keys(currentItems[key].view).length > 0) {
159
- const currentItem = currentItems[key];
160
- const rootView = currentItem.view;
161
- const { context } = rootView.config;
162
- const config: any = { meta: rootView };
163
- config.options = {
164
- context: currentItem.context,
165
- hasForm: true,
166
- pageReference: context || this.pConn$.getPageReference()
167
- };
168
-
169
- if (!this.bSubscribed) {
170
- this.bSubscribed = true;
171
- const { PUB_SUB_EVENTS } = PCore.getConstants();
172
- PCore.getPubSubUtils().subscribe(
173
- PUB_SUB_EVENTS.EVENT_SHOW_CANCEL_ALERT,
174
- payload => {
175
- this.showAlert(payload);
176
- },
177
- PUB_SUB_EVENTS.EVENT_SHOW_CANCEL_ALERT
178
- );
179
- }
180
-
181
- // let configObject = PCore.createPConnect(config);
182
-
183
- // THIS is where the ViewContainer creates a View
184
- // The config has meta.config.type = "view"
185
- this.createView(routingInfo, currentItem, latestItem, key);
142
+ const { MULTIPLE } = PCore.getConstants().CONTAINER_TYPE;
143
+ const { key, latestItem } = this.getKeyAndLatestItem(routingInfo);
144
+
145
+ if (latestItem && type === MULTIPLE && (this.isOpenModalAction(accessedOrder) || this.isUpdateModalAction(accessedOrder))) {
146
+ const currentItem = routingInfo.items[key];
147
+ if (currentItem?.view && Object.keys(currentItem.view).length > 0) {
148
+ if (!this.bSubscribed) {
149
+ this.bSubscribed = true;
150
+ const { PUB_SUB_EVENTS } = PCore.getConstants();
151
+ PCore.getPubSubUtils().subscribe(
152
+ PUB_SUB_EVENTS.EVENT_SHOW_CANCEL_ALERT,
153
+ payload => {
154
+ this.showAlert(payload);
155
+ },
156
+ PUB_SUB_EVENTS.EVENT_SHOW_CANCEL_ALERT
157
+ );
186
158
  }
187
- } else {
188
- this.hideModal();
189
- }
190
- }
191
- }
192
159
 
193
- createView(routingInfo, currentItem, latestItem, key) {
194
- const configObject = this.getConfigObject(currentItem, null, false);
195
- const newComp = configObject?.getPConnect();
196
- // const newCompName = newComp.getComponentName();
197
- const caseInfo = newComp && newComp.getDataObject() && newComp.getDataObject().caseInfo ? newComp.getDataObject().caseInfo : null;
198
- // The metadata for pyDetails changed such that the "template": "CaseView"
199
- // is no longer a child of the created View but is in the created View's
200
- // config. So, we DON'T want to replace this.pConn$ since the created
201
- // component is a View (and not a ViewContainer). We now look for the
202
- // "template" type directly in the created component (newComp) and NOT
203
- // as a child of the newly created component.
204
- // console.log(`---> ModalViewContainer created new ${newCompName}`);
205
-
206
- // Use the newly created component (View) info but DO NOT replace
207
- // this ModalViewContainer's pConn$, etc.
208
- // Note that we're now using the newly created View's PConnect in the
209
- // ViewContainer HTML template to guide what's rendered similar to what
210
- // the Nebula/Constellation return of React.Fragment does
211
-
212
- // right now need to check caseInfo for changes, to trigger redraw, not getting
213
- // changes from angularPconnect except for first draw
214
- if (newComp && caseInfo && this.compareCaseInfoIsDifferent(caseInfo)) {
215
- this.psService.sendMessage(false);
216
-
217
- this.createdViewPConn$ = newComp;
218
- const newConfigProps = newComp.getConfigProps();
219
- this.templateName$ = 'template' in newConfigProps ? (newConfigProps.template as string) : '';
220
-
221
- const { actionName } = latestItem;
222
- const theNewCaseInfo = newComp.getCaseInfo();
223
- // const caseName = theNewCaseInfo.getName();
224
- const ID = theNewCaseInfo.getBusinessID() || theNewCaseInfo.getID();
225
-
226
- const caseTypeName = theNewCaseInfo.getCaseTypeName();
227
- const isDataObject = routingInfo.items[latestItem.context].resourceType === PCore.getConstants().RESOURCE_TYPES.DATA;
228
- const dataObjectAction = routingInfo.items[latestItem.context].resourceStatus;
229
- this.isMultiRecord = routingInfo.items[latestItem.context].isMultiRecordData;
230
- this.context$ = latestItem.context;
231
- this.title$ =
232
- isDataObject || this.isMultiRecord
233
- ? this.getModalHeading(dataObjectAction)
234
- : this.determineModalHeaderByAction(actionName, caseTypeName, ID, this.createdViewPConn$?.getCaseLocaleReference());
235
-
236
- const bIsRefComponent = this.checkIfRefComponent(newComp);
237
-
238
- if (bIsRefComponent) {
239
- this.arChildren$ = [newComp.getComponent()];
240
- } else {
241
- // update children with new view's children
242
- this.arChildren$ = newComp.getChildren();
160
+ this.upsertModal(routingInfo, latestItem, key, accessedOrder);
243
161
  }
244
-
245
- this.bShowModal$ = true;
246
-
247
- // for when non modal
248
- this.modalVisibleChange.emit(this.bShowModal$);
249
-
250
- // save off itemKey to be used for finishAssignment, etc.
251
- this.itemKey$ = key;
252
-
253
- // cause a change for assignment
254
- this.updateToken$ = new Date().getTime();
255
- this.cdRef.markForCheck();
162
+ } else if (this.isCloseModalAction(accessedOrder)) {
163
+ this.handleModalClose(accessedOrder);
256
164
  }
257
165
  }
258
166
 
259
- hideModal() {
260
- if (this.bShowModal$) {
261
- // other code in Nebula/Constellation not needed currently, but if so later,
262
- // should put here
167
+ // Builds/refreshes one modal-stack entry and pushes or updates it in-place
168
+ upsertModal(routingInfo, latestItem, key, accessedOrder) {
169
+ const entry = this.buildModalEntry(routingInfo, latestItem, key);
170
+
171
+ if (this.isUpdateModalAction(accessedOrder)) {
172
+ this.modalStack = this.modalStack.map(modal => (modal.key === key ? entry : modal));
173
+ } else if (this.isOpenModalAction(accessedOrder)) {
174
+ this.handleModalOpen(key);
175
+ this.modalStack = [...this.modalStack, entry];
263
176
  }
264
177
 
265
- this.bShowModal$ = false;
178
+ this.psService.sendMessage(false);
179
+ this.modalVisibleChange.emit(this.modalStack.length > 0);
180
+ this.cdRef.markForCheck();
181
+ }
266
182
 
267
- // for when non modal
268
- this.modalVisibleChange.emit(this.bShowModal$);
183
+ buildModalEntry(routingInfo, latestItem, key): ModalEntry {
184
+ const configObject = this.getConfigObject(latestItem, null, false);
185
+ // latestItem is only reached once its view metadata is populated (see updateSelf), so the
186
+ // created component is always present here — same assumption the old single-modal code made.
187
+ const newComp = configObject!.getPConnect();
188
+
189
+ const { actionName } = latestItem;
190
+ const theCaseInfo = newComp.getCaseInfo();
191
+ const ID = theCaseInfo.getBusinessID() || theCaseInfo.getID();
192
+ const caseTypeName = theCaseInfo.getCaseTypeName();
193
+
194
+ const isDataObject = routingInfo.items[latestItem.context].resourceType === PCore.getConstants().RESOURCE_TYPES.DATA;
195
+ const dataObjectAction = routingInfo.items[latestItem.context].resourceStatus;
196
+ const isMultiRecordData = routingInfo.items[latestItem.context].isMultiRecordData;
197
+
198
+ const title = this.getHeadingValue(
199
+ latestItem,
200
+ isMultiRecordData,
201
+ isDataObject,
202
+ actionName,
203
+ dataObjectAction,
204
+ caseTypeName,
205
+ ID,
206
+ newComp?.getCaseLocaleReference()
207
+ );
269
208
 
270
- this.oCaseInfo = {};
271
- this.cdRef.markForCheck();
209
+ const bIsRefComponent = this.checkIfRefComponent(newComp);
210
+ const arChildren$ = bIsRefComponent ? [newComp.getComponent()] : newComp.getChildren();
211
+
212
+ return {
213
+ key,
214
+ title,
215
+ createdViewPConn$: newComp,
216
+ arChildren$,
217
+ isMultiRecordData,
218
+ isDataObjectModal: isDataObject && !isMultiRecordData,
219
+ dataRecordKeys: latestItem.key || '',
220
+ dataObjectActionID: routingInfo.items[latestItem.context].actionID || '',
221
+ dataObjectAction: dataObjectAction || '',
222
+ dataObjectClassId: newComp.getValue('.classID') || '',
223
+ context: latestItem.context,
224
+ updateToken: new Date().getTime()
225
+ };
272
226
  }
273
227
 
274
228
  getConfigObject(item, pConnect, isReverseCoexistence = false) {
@@ -315,13 +269,17 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy {
315
269
  this.bAlertState = bData;
316
270
  this.bShowCancelAlert$ = false;
317
271
  if (this.bAlertState) {
318
- this.hideModal();
272
+ // Discard confirmed — matches react-sdk: drop the whole stack rather than just the top entry
273
+ this.modalCollection = {};
274
+ this.modalStack = [];
275
+ this.modalVisibleChange.emit(false);
276
+ this.cdRef.markForCheck();
319
277
  }
320
278
  }
321
279
 
322
280
  showAlert(payload) {
323
281
  const { latestItem } = this.getKeyAndLatestItem(this.routingInfoRef.current);
324
- const { isModalAction } = payload;
282
+ const { isModalAction, hideDelete, isDataObject, skipReleaseLockRequest } = payload;
325
283
 
326
284
  /*
327
285
  If we are in create stage full page mode, created a new case and trying to click on cancel button
@@ -330,6 +288,9 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy {
330
288
  if (latestItem && isModalAction && !this.actionsDialog) {
331
289
  const configObject = this.getConfigObject(latestItem, this.pConn$);
332
290
  this.cancelPConn$ = configObject?.getPConnect();
291
+ this.cancelHideDelete$ = hideDelete;
292
+ this.cancelIsDataObject$ = isDataObject;
293
+ this.cancelSkipReleaseLockRequest$ = skipReleaseLockRequest;
333
294
  this.bShowCancelAlert$ = true;
334
295
  this.cdRef.markForCheck();
335
296
  }
@@ -353,37 +314,62 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy {
353
314
  return {};
354
315
  }
355
316
 
356
- compareCaseInfoIsDifferent(oCurrentCaseInfo: object): boolean {
357
- let bRet = false;
358
-
359
- // fast-deep-equal version
360
- if (isEqual !== undefined) {
361
- bRet = !isEqual(this.oCaseInfo, oCurrentCaseInfo);
362
- } else {
363
- const sCurrnentCaseInfo = JSON.stringify(oCurrentCaseInfo);
364
- const sOldCaseInfo = JSON.stringify(this.oCaseInfo);
365
- // stringify compare version
366
- if (sCurrnentCaseInfo != sOldCaseInfo) {
367
- bRet = true;
368
- }
369
- }
317
+ // Open/update/close are derived purely from how the count of known modals compares to
318
+ // routingInfo.accessedOrder — mirrors react-sdk's modal-stack fix so that closing one modal
319
+ // (removing one entry from accessedOrder) can never be mistaken for closing all of them.
320
+ isOpenModalAction(accessedOrder: string[]): boolean {
321
+ return Object.keys(this.modalCollection).length < accessedOrder.length;
322
+ }
370
323
 
371
- // if different, save off new case info
372
- if (bRet) {
373
- this.oCaseInfo = JSON.parse(JSON.stringify(oCurrentCaseInfo));
374
- }
324
+ isUpdateModalAction(accessedOrder: string[]): boolean {
325
+ return Object.keys(this.modalCollection).length === accessedOrder.length;
326
+ }
327
+
328
+ isCloseModalAction(accessedOrder: string[]): boolean {
329
+ return Object.keys(this.modalCollection).length > accessedOrder.length;
330
+ }
331
+
332
+ handleModalOpen(key: string) {
333
+ this.modalCollection = { ...this.modalCollection, [key]: {} };
334
+ }
375
335
 
376
- return bRet;
336
+ handleModalClose(accessedOrder: string[]) {
337
+ const closedModalKey = Object.keys(this.modalCollection).find(modalKey => !accessedOrder.includes(modalKey));
338
+
339
+ if (closedModalKey) {
340
+ const updatedModalCollection = { ...this.modalCollection };
341
+ delete updatedModalCollection[closedModalKey];
342
+ this.modalCollection = updatedModalCollection;
343
+
344
+ this.modalStack = this.modalStack.filter(modal => modal.key !== closedModalKey);
345
+ this.modalVisibleChange.emit(this.modalStack.length > 0);
346
+ this.bannerService.clearBanners(closedModalKey);
347
+ this.cdRef.markForCheck();
348
+ }
377
349
  }
378
350
 
379
- getBanners() {
380
- return getBanners({ target: this.itemKey$, ...this.stateProps$ });
351
+ getModalHeading(dataObjectAction, actionName) {
352
+ switch (dataObjectAction) {
353
+ case PCore.getConstants().RESOURCE_STATUS.CREATE:
354
+ return this.localizedVal('Add Record', this.localeCategory);
355
+ case PCore.getConstants().RESOURCE_STATUS.OPEN_FLOW_ACTION:
356
+ return this.localizedVal(actionName, this.localeCategory);
357
+ default:
358
+ return this.localizedVal('Edit Record', this.localeCategory);
359
+ }
381
360
  }
382
361
 
383
- getModalHeading(dataObjectAction) {
384
- return dataObjectAction === PCore.getConstants().RESOURCE_STATUS.CREATE
385
- ? this.localizedVal('Add Record', this.localeCategory)
386
- : this.localizedVal('Edit Record', this.localeCategory);
362
+ getHeadingValue(latestItem, isMultiRecordData, isDataObject, actionName, dataObjectAction, caseTypeName, ID, caseLocaleRef) {
363
+ if (isMultiRecordData) {
364
+ return latestItem.heading;
365
+ }
366
+ if (isDataObject) {
367
+ if (actionName) {
368
+ return this.localizedVal(actionName, this.localeCategory);
369
+ }
370
+ return this.getModalHeading(dataObjectAction, actionName);
371
+ }
372
+ return this.determineModalHeaderByAction(actionName, caseTypeName, ID, caseLocaleRef);
387
373
  }
388
374
 
389
375
  determineModalHeaderByAction(actionName, caseTypeName, ID, caseLocaleRef) {
@@ -393,14 +379,23 @@ export class ModalViewContainerComponent implements OnInit, OnDestroy {
393
379
  return `${this.localizedVal('Create', this.localeCategory)} ${this.localizedVal(caseTypeName, undefined, caseLocaleRef)} (${ID})`;
394
380
  }
395
381
 
396
- closeActionsDialog = () => {
382
+ // Closes one modal by key (bound per-instance in the template), or the topmost when omitted
383
+ closeActionsDialog = (modalKey?: string) => {
397
384
  this.actionsDialog = true;
398
- this.bShowModal$ = false;
399
385
 
400
- // for when non modal
401
- this.modalVisibleChange.emit(this.bShowModal$);
386
+ this.modalStack = modalKey ? this.modalStack.filter(modal => modal.key !== modalKey) : this.modalStack.slice(0, -1);
402
387
 
403
- this.oCaseInfo = {};
388
+ this.modalVisibleChange.emit(this.modalStack.length > 0);
389
+ this.bannerService.clearBanners(modalKey ?? '');
404
390
  this.cdRef.markForCheck();
405
391
  };
392
+
393
+ // Binds closeActionsDialog to a specific modal's key so closing one stacked modal never affects the others
394
+ closeModalFor(modalKey: string) {
395
+ return () => this.closeActionsDialog(modalKey);
396
+ }
397
+
398
+ trackByModalKey(index: number, modal: ModalEntry) {
399
+ return modal.key;
400
+ }
406
401
  }
@@ -1,5 +1,5 @@
1
1
  <div>
2
- <div><component-mapper name="AlertBanner" [props]="{ banners: bannerService.banners }" [parent]="this"></component-mapper></div>
2
+ <div><component-mapper name="AlertBanner" [props]="{ banners: bannerService.getBanners(itemKey$) }" [parent]="this"></component-mapper></div>
3
3
  <div *ngIf="bHasNavigation$" class="psdk-stepper">
4
4
  <component-mapper
5
5
  name="MultiStep"
@@ -10,6 +10,7 @@
10
10
  arMainButtons$,
11
11
  arSecondaryButtons$,
12
12
  bIsVertical$,
13
+ stepIndicator$,
13
14
  arCurrentStepIndicies$,
14
15
  arNavigationSteps$
15
16
  }"
@@ -27,6 +27,8 @@ interface AssignmentProps {
27
27
  template: string;
28
28
  }
29
29
 
30
+ type StepIndicator = 'horizontal' | 'vertical' | 'vertical-start';
31
+
30
32
  @Component({
31
33
  selector: 'app-assignment',
32
34
  templateUrl: './assignment.component.html',
@@ -60,6 +62,7 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges {
60
62
 
61
63
  bHasNavigation$ = false;
62
64
  bIsVertical$ = false;
65
+ stepIndicator$: StepIndicator = 'horizontal';
63
66
  prevNavigationSteps: any[] = [];
64
67
  arCurrentStepIndicies$: number[] = [];
65
68
  arNavigationSteps$: any[] = [];
@@ -108,6 +111,8 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges {
108
111
  if (this.angularPConnectData.unsubscribeFn) {
109
112
  this.angularPConnectData.unsubscribeFn();
110
113
  }
114
+
115
+ this.bannerService.clearBanners(this.itemKey$);
111
116
  }
112
117
 
113
118
  // Callback passed when subscribing to store change
@@ -240,14 +245,22 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges {
240
245
  }
241
246
 
242
247
  createButtonsForMultiStepForm(oCaseInfo) {
243
- this.bHasNavigation$ = true;
244
-
245
- if ((oCaseInfo.navigation.template && oCaseInfo.navigation.template.toLowerCase() === 'standard') || oCaseInfo?.navigation?.steps?.length === 1) {
246
- this.bHasNavigation$ = false;
247
- } else if (oCaseInfo.navigation.template && oCaseInfo.navigation.template.toLowerCase() === 'vertical') {
248
- this.bIsVertical$ = true;
249
- } else {
250
- this.bIsVertical$ = false;
248
+ const navigationTemplate = oCaseInfo.navigation.template?.toLowerCase();
249
+ const stepCount = oCaseInfo.navigation.steps?.length ?? 0;
250
+ const shouldHideNavigation = navigationTemplate === 'standard' || stepCount <= 1;
251
+
252
+ this.bHasNavigation$ = !shouldHideNavigation;
253
+ this.bIsVertical$ = false;
254
+ this.stepIndicator$ = 'horizontal';
255
+
256
+ if (!shouldHideNavigation) {
257
+ if (navigationTemplate === 'vertical') {
258
+ this.bIsVertical$ = true;
259
+ this.stepIndicator$ = 'vertical';
260
+ } else if (navigationTemplate === 'vertical-left') {
261
+ this.bIsVertical$ = true;
262
+ this.stepIndicator$ = 'vertical-start';
263
+ }
251
264
  }
252
265
 
253
266
  // iterate through steps to find current one(s)
@@ -304,7 +317,7 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges {
304
317
 
305
318
  buttonClick(sAction, sButtonType) {
306
319
  this.snackBarRef?.dismiss();
307
- this.bannerService.clearBanners();
320
+ this.bannerService.clearBanners(this.itemKey$);
308
321
  PCore.getPubSubUtils().publish('clearBannerMessages');
309
322
  // right now, done on an individual basis, setting bReInit to true
310
323
  // upon the next flow container state change, will cause the flow container
@@ -496,10 +509,14 @@ export class AssignmentComponent implements OnInit, OnDestroy, OnChanges {
496
509
  refreshProps.forEach(prop => {
497
510
  PCore.getRefreshManager().registerForRefresh(
498
511
  'PROP_CHANGE',
499
- this.pConn$.getActionsApi().refreshCaseView.bind(this.pConn$.getActionsApi(), caseKey, '', pageReference, {
500
- ...refreshOptions,
501
- refreshFor: prop[0]
502
- }),
512
+ // The registered prop is an authored pattern with an empty list index (ex: ".Addons().Type"),
513
+ // which the server rejects. The refresh manager hands back the concrete path that changed.
514
+ (matchedPath?: string) => {
515
+ this.pConn$.getActionsApi().refreshCaseView(caseKey, '', pageReference, {
516
+ ...refreshOptions,
517
+ refreshFor: matchedPath || prop[0]
518
+ });
519
+ },
503
520
  `${pageReference}.${prop[1]}`,
504
521
  `${context}/${pageReference}`,
505
522
  context
@@ -1,4 +1,4 @@
1
- import { Component, OnInit, Input, forwardRef, OnDestroy, OnChanges, SimpleChanges } from '@angular/core';
1
+ import { Component, OnInit, Input, forwardRef, OnDestroy, OnChanges, SimpleChanges, ChangeDetectorRef } from '@angular/core';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import { publicConstants } from '@pega/pcore-pconnect-typedefs/constants';
4
4
  import { ComponentMapperComponent } from '@pega/angular-sdk-components';
@@ -35,7 +35,10 @@ export class DeferLoadComponent implements OnInit, OnDestroy, OnChanges {
35
35
  PAGE: any;
36
36
  DATA: any;
37
37
  lastUpdateCaseTime;
38
- constructor(private angularPConnect: AngularPConnectService) {
38
+ constructor(
39
+ private angularPConnect: AngularPConnectService,
40
+ private cdRef: ChangeDetectorRef
41
+ ) {
39
42
  this.constants = PCore.getConstants();
40
43
  }
41
44
 
@@ -132,7 +135,7 @@ export class DeferLoadComponent implements OnInit, OnDestroy, OnChanges {
132
135
  PCore.getDeferLoadManager().stop(this.deferLoadId, this.pConn$.getContextName());
133
136
  }
134
137
  }
135
- // this.cdRef.detectChanges();
138
+ this.cdRef.detectChanges();
136
139
  }
137
140
 
138
141
  loadActiveTab() {