@pega/angular-sdk-overrides 0.26.2 → 0.26.4

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 (68) 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.ts +179 -184
  17. package/lib/infra/assignment/assignment.component.html +2 -1
  18. package/lib/infra/assignment/assignment.component.ts +30 -13
  19. package/lib/infra/defer-load/defer-load.component.ts +6 -3
  20. package/lib/infra/multi-step/multi-step.component.html +25 -33
  21. package/lib/infra/multi-step/multi-step.component.scss +48 -0
  22. package/lib/infra/multi-step/multi-step.component.ts +11 -0
  23. package/lib/infra/view/view.component.ts +17 -39
  24. package/lib/template/base/details-template-base.ts +35 -54
  25. package/lib/template/case-summary/case-summary.component.ts +15 -1
  26. package/lib/template/case-view/case-view.component.html +13 -1
  27. package/lib/template/case-view/case-view.component.spec.ts +119 -10
  28. package/lib/template/case-view/case-view.component.ts +52 -3
  29. package/lib/template/data-reference/data-reference.component.ts +27 -18
  30. package/lib/template/data-reference/search-form/search-form.component.html +1 -1
  31. package/lib/template/data-reference/search-form/search-form.component.ts +2 -0
  32. package/lib/template/details/details.component.html +23 -3
  33. package/lib/template/details/details.component.scss +9 -2
  34. package/lib/template/details/details.component.ts +3 -32
  35. package/lib/template/details-narrow-wide/details-narrow-wide.component.html +21 -2
  36. package/lib/template/details-narrow-wide/details-narrow-wide.component.scss +8 -1
  37. package/lib/template/details-narrow-wide/details-narrow-wide.component.ts +3 -37
  38. package/lib/template/details-one-column/details-one-column.component.html +23 -1
  39. package/lib/template/details-one-column/details-one-column.component.scss +14 -0
  40. package/lib/template/details-one-column/details-one-column.component.ts +3 -38
  41. package/lib/template/details-three-column/details-three-column.component.html +21 -2
  42. package/lib/template/details-three-column/details-three-column.component.scss +8 -1
  43. package/lib/template/details-three-column/details-three-column.component.ts +3 -46
  44. package/lib/template/details-two-column/details-two-column.component.html +21 -2
  45. package/lib/template/details-two-column/details-two-column.component.scss +8 -1
  46. package/lib/template/details-two-column/details-two-column.component.ts +3 -44
  47. package/lib/template/details-wide-narrow/details-wide-narrow.component.html +21 -2
  48. package/lib/template/details-wide-narrow/details-wide-narrow.component.scss +8 -1
  49. package/lib/template/details-wide-narrow/details-wide-narrow.component.ts +3 -41
  50. package/lib/template/field-value-list/field-value-list.component.scss +17 -0
  51. package/lib/template/list-view/list-view.component.ts +39 -0
  52. package/lib/template/self-service-case-view/self-service-case-view.component.html +13 -1
  53. package/lib/template/self-service-case-view/self-service-case-view.component.spec.ts +96 -11
  54. package/lib/template/self-service-case-view/self-service-case-view.component.ts +52 -3
  55. package/lib/template/simple-table-manual/helpers.ts +27 -6
  56. package/lib/template/simple-table-manual/simple-table-manual.component.html +1 -3
  57. package/lib/template/simple-table-manual/simple-table-manual.component.scss +4 -0
  58. package/lib/template/simple-table-manual/simple-table-manual.component.ts +51 -20
  59. package/lib/template/single-reference-readonly/single-reference-readonly.component.scss +0 -4
  60. package/package.json +1 -1
  61. package/lib/designSystemExtension/material-details/material-details.component.html +0 -40
  62. package/lib/designSystemExtension/material-details/material-details.component.scss +0 -116
  63. package/lib/designSystemExtension/material-details/material-details.component.spec.ts +0 -24
  64. package/lib/designSystemExtension/material-details/material-details.component.ts +0 -31
  65. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.html +0 -50
  66. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.scss +0 -34
  67. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.spec.ts +0 -22
  68. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.ts +0 -61
@@ -1,4 +1,5 @@
1
1
  import { Component, forwardRef } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
2
3
  import { ComponentMapperComponent } from '@pega/angular-sdk-components';
3
4
  import { DetailsTemplateBase } from '@pega/angular-sdk-components';
4
5
 
@@ -6,45 +7,6 @@ import { DetailsTemplateBase } from '@pega/angular-sdk-components';
6
7
  selector: 'app-details-wide-narrow',
7
8
  templateUrl: './details-wide-narrow.component.html',
8
9
  styleUrls: ['./details-wide-narrow.component.scss'],
9
- imports: [forwardRef(() => ComponentMapperComponent)]
10
+ imports: [CommonModule, forwardRef(() => ComponentMapperComponent)]
10
11
  })
11
- export class DetailsWideNarrowComponent extends DetailsTemplateBase {
12
- override pConn$: typeof PConnect;
13
-
14
- highlightedDataArr: any[] = [];
15
- showHighlightedData: boolean;
16
- arFields$: any[] = [];
17
- arFields2$: any[] = [];
18
- propsToUse: any = {};
19
-
20
- override updateSelf() {
21
- const rawMetaData: any = this.pConn$.resolveConfigProps(this.pConn$.getRawMetadata()?.config);
22
- this.showHighlightedData = rawMetaData?.showHighlightedData;
23
-
24
- if (this.showHighlightedData) {
25
- const highlightedData = rawMetaData?.highlightedData;
26
- this.highlightedDataArr = highlightedData.map(field => {
27
- field.config.displayMode = 'STACKED_LARGE_VAL';
28
-
29
- if (field.config.value === '@P .pyStatusWork') {
30
- field.type = 'TextInput';
31
- field.config.displayAsStatus = true;
32
- }
33
-
34
- return field;
35
- });
36
- }
37
-
38
- this.pConn$.setInheritedProp('displayMode', 'DISPLAY_ONLY');
39
- this.pConn$.setInheritedProp('readOnly', true);
40
-
41
- const kids = this.pConn$.getChildren() as any[];
42
- for (const kid of kids) {
43
- if (kids.indexOf(kid) == 0) {
44
- this.arFields$ = this.processDetailFields(kid);
45
- } else {
46
- this.arFields2$ = this.processDetailFields(kid);
47
- }
48
- }
49
- }
50
- }
12
+ export class DetailsWideNarrowComponent extends DetailsTemplateBase {}
@@ -21,3 +21,20 @@
21
21
  .psdk-val-labels-left {
22
22
  white-space: break-spaces;
23
23
  }
24
+
25
+ .psdk-container-stacked-large-val {
26
+ margin: 8px 0;
27
+ }
28
+
29
+ .psdk-container-stacked-large-val .psdk-grid-label {
30
+ color: rgba(0, 0, 0, 0.6);
31
+ font-size: 0.875rem;
32
+ font-weight: 400;
33
+ line-height: 1.5;
34
+ }
35
+
36
+ .psdk-val-stacked {
37
+ color: rgba(0, 0, 0, 0.87);
38
+ font-size: 1.25rem;
39
+ line-height: 1.5;
40
+ }
@@ -18,6 +18,7 @@ import { DragDropModule, CdkDragDrop, moveItemInArray, CdkDropList, CdkDrag } fr
18
18
  import { ProgressSpinnerService } from '@pega/angular-sdk-components';
19
19
  import { Utils } from '@pega/angular-sdk-components';
20
20
  import { ComponentMapperComponent } from '@pega/angular-sdk-components';
21
+ import { AngularPConnectData, AngularPConnectService } from '@pega/angular-sdk-components';
21
22
  import { getCurrencyOptions } from '@pega/angular-sdk-components';
22
23
  import { getLocale, getSeconds } from '@pega/angular-sdk-components';
23
24
  import { formatters } from '@pega/angular-sdk-components';
@@ -162,18 +163,28 @@ export class ListViewComponent implements OnInit, OnDestroy {
162
163
  paging: any;
163
164
  fieldDefs: any;
164
165
  checkBoxValue: string;
166
+ // Last value seen on the bound property, used to detect external changes.
167
+ lastSelectedPropValue: any;
165
168
  label?: string = '';
166
169
  uniqueId = crypto.randomUUID();
167
170
  displayAs: any;
168
171
  showRecords: any;
169
172
  identifier: string;
170
173
  promotedFiltersId: string;
174
+
175
+ // Used with AngularPConnect
176
+ angularPConnectData: AngularPConnectData = {};
177
+
171
178
  constructor(
179
+ private angularPConnect: AngularPConnectService,
172
180
  private psService: ProgressSpinnerService,
173
181
  public utils: Utils
174
182
  ) {}
175
183
 
176
184
  ngOnInit(): void {
185
+ // First thing in initialization is registering and subscribing to the AngularPConnect service
186
+ this.angularPConnectData = this.angularPConnect.registerAndSubscribeComponent(this, this.onStateChange);
187
+
177
188
  this.configProps$ = this.pConn$.getConfigProps() as ListViewProps;
178
189
  /** By default, pyGUID is used for Data classes and pyID is for Work classes as row-id/key */
179
190
  const defRowID = this.configProps$?.referenceType === 'Case' ? 'pyID' : 'pyGUID';
@@ -197,6 +208,7 @@ export class ListViewComponent implements OnInit, OnDestroy {
197
208
  this.selectionMode = this.configProps$.selectionMode;
198
209
 
199
210
  this.checkBoxValue = this.configProps$.value;
211
+ this.lastSelectedPropValue = this.configProps$.value;
200
212
 
201
213
  this.arFilterMainButtons$.push({ actionID: 'submit', jsAction: 'submit', name: 'Submit' });
202
214
  this.arFilterSecondaryButtons$.push({ actionID: 'cancel', jsAction: 'cancel', name: 'Cancel' });
@@ -266,6 +278,29 @@ export class ListViewComponent implements OnInit, OnDestroy {
266
278
  this.clearSelectionsAndUpdateTable(this.pConn$, this.uniqueId, this.configProps$?.viewName);
267
279
  }
268
280
 
281
+ onStateChange() {
282
+ // Should always check the bridge to see if the component should
283
+ // update itself (re-render)
284
+ const bUpdateSelf = this.angularPConnect.shouldComponentUpdate(this);
285
+
286
+ // ONLY call updateSelf when the component should update
287
+ if (bUpdateSelf) {
288
+ this.updateSelf();
289
+ }
290
+ }
291
+
292
+ updateSelf(): void {
293
+ // The selected row is driven by the bound property, which another field on the screen may change.
294
+ const latestConfigProps = this.pConn$.getConfigProps() as ListViewProps;
295
+ const latestValue = latestConfigProps?.value;
296
+
297
+ // Only re-sync on an actual property change, so a row the user just picked is not reverted.
298
+ if (this.selectionMode === SELECTION_MODE.SINGLE && latestValue !== this.lastSelectedPropValue) {
299
+ this.lastSelectedPropValue = latestValue;
300
+ this.checkBoxValue = latestValue;
301
+ }
302
+ }
303
+
269
304
  clearSelectionsAndUpdateTable(getPConnect: any, uniqueId: string, viewName): void {
270
305
  const clearSelectionsAndRefreshList = ({ viewName: name, clearSelections }: any) => {
271
306
  if (name === viewName) {
@@ -487,6 +522,10 @@ export class ListViewComponent implements OnInit, OnDestroy {
487
522
  // }
488
523
 
489
524
  ngOnDestroy() {
525
+ if (this.angularPConnectData.unsubscribeFn) {
526
+ this.angularPConnectData.unsubscribeFn();
527
+ }
528
+
490
529
  PCore.getPubSubUtils().unsubscribe(
491
530
  PCore.getConstants().PUB_SUB_EVENTS.EVENT_DASHBOARD_FILTER_CHANGE,
492
531
  `dashboard-component-${'id'}`,
@@ -2,7 +2,9 @@
2
2
  <div class="psdk-self-service-case-view-header">
3
3
  <div class="heading">{{ this.heading$ }}</div>
4
4
  <div *ngIf="showCaseActions" class="psdk-case-view-buttons">
5
- <button mat-raised-button color="secondary" [matMenuTriggerFor]="actionMenu">{{ localizedVal('Actions...', localeCategory) }}</button>
5
+ <button mat-raised-button color="secondary" [matMenuTriggerFor]="actionMenu" [disabled]="bActionsMenuDisabled$">
6
+ {{ localizedVal('Actions...', localeCategory) }}
7
+ </button>
6
8
  <mat-menu #actionMenu="matMenu" overlapTrigger="false">
7
9
  <ng-container *ngFor="let action of arAvailableActions$">
8
10
  <button mat-menu-item (click)="_menuActionClick(action)">
@@ -14,6 +16,16 @@
14
16
  {{ process.name }}
15
17
  </button>
16
18
  </ng-container>
19
+ <ng-container *ngFor="let action of arDataObjectActions$">
20
+ <button mat-menu-item (click)="_menuDataObjectActionClick(action)">
21
+ {{ localizedVal(action.name, '', localeKey) }}
22
+ </button>
23
+ </ng-container>
24
+ <ng-container *ngFor="let action of arCreateCaseActions$">
25
+ <button mat-menu-item (click)="_menuCreateCaseActionClick(action)">
26
+ {{ localizedVal(action.name, '', localeKey) }}
27
+ </button>
28
+ </ng-container>
17
29
  </mat-menu>
18
30
  </div>
19
31
  </div>
@@ -1,24 +1,109 @@
1
- import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2
-
3
1
  import { SelfServiceCaseViewComponent } from './self-service-case-view.component';
4
2
 
5
3
  describe('SelfServiceCaseViewComponent', () => {
6
4
  let component: SelfServiceCaseViewComponent;
7
- let fixture: ComponentFixture<SelfServiceCaseViewComponent>;
5
+ let actionsApi: { openDataObjectAction: jasmine.Spy; createWork: jasmine.Spy };
6
+ let dataInfoActions: any;
7
+ let configProps: any;
8
+ let dataRecord: any;
8
9
 
9
- beforeEach(waitForAsync(() => {
10
- TestBed.configureTestingModule({
11
- declarations: [SelfServiceCaseViewComponent]
12
- }).compileComponents();
13
- }));
10
+ const utilityChild = {
11
+ getPConnect: () => ({ getRawMetadata: () => ({ type: 'region', name: 'Utilities', children: [] }) })
12
+ };
14
13
 
15
14
  beforeEach(() => {
16
- fixture = TestBed.createComponent(SelfServiceCaseViewComponent);
17
- component = fixture.componentInstance;
18
- fixture.detectChanges();
15
+ (globalThis as any).PCore = {
16
+ getLocaleUtils: () => ({ getLocaleValue: (value: string) => value }),
17
+ getCaseUtils: () => ({ isObjectCaseType: () => false })
18
+ };
19
+
20
+ actionsApi = {
21
+ openDataObjectAction: jasmine.createSpy('openDataObjectAction'),
22
+ createWork: jasmine.createSpy('createWork')
23
+ };
24
+ dataInfoActions = undefined;
25
+ dataRecord = { PlanID: 'P-1' };
26
+ // Summary region is switched off so the test does not depend on case summary metadata.
27
+ configProps = { icon: 'case', header: 'Header', subheader: 'ID-1', showSummaryRegion: false, showCaseActions: true };
28
+
29
+ const utils = {
30
+ getImageSrc: () => '',
31
+ getSDKStaticContentUrl: () => '',
32
+ getBooleanValue: (value: any) => value === true || value === 'true'
33
+ } as any;
34
+
35
+ component = new SelfServiceCaseViewComponent({} as any, utils);
36
+ component.pConn$ = {
37
+ resolveConfigProps: () => configProps,
38
+ getConfigProps: () => configProps,
39
+ getCaseLocaleReference: () => 'MY-CLASS!CASE!MY-CASE',
40
+ getChildren: () => [utilityChild, utilityChild, utilityChild, utilityChild, utilityChild],
41
+ getLocalizationService: () => ({ getLocalizedText: (value: string) => value }),
42
+ getDataObject: () => ({ caseInfo: { ID: 'C-1', availableActions: [], availableProcesses: [] } }),
43
+ getValue: (prop: string, context?: string) => {
44
+ if (prop === '.actions' && context === 'dataInfo') return dataInfoActions;
45
+ if (prop === '.content' && context === 'dataInfo') return dataRecord;
46
+ if (prop === '.classID' && context === 'dataInfo.content') return 'My-Data-Class';
47
+ if (context === '') return dataRecord[prop.slice(1)];
48
+ return undefined;
49
+ },
50
+ getActionsApi: () => actionsApi
51
+ } as any;
52
+ component.localizedVal = (value: string) => value;
19
53
  });
20
54
 
21
55
  it('should create', () => {
22
56
  expect(component).toBeTruthy();
23
57
  });
58
+
59
+ it('lists data object actions published under dataInfo', () => {
60
+ dataInfoActions = {
61
+ availableActions: [{ ID: 'Edit', name: 'Edit' }],
62
+ availableCreateCaseActions: [{ ID: 'My-Work-Class', name: 'Request Plan Change' }]
63
+ };
64
+
65
+ component.fullUpdate();
66
+
67
+ expect(component.arDataObjectActions$.length).toBe(1);
68
+ expect(component.arCreateCaseActions$.length).toBe(1);
69
+ expect(component.bActionsMenuDisabled$).toBeFalse();
70
+ });
71
+
72
+ it('falls back to empty arrays and disables the menu when nothing is available', () => {
73
+ component.fullUpdate();
74
+
75
+ expect(component.arDataObjectActions$).toEqual([]);
76
+ expect(component.arCreateCaseActions$).toEqual([]);
77
+ expect(component.bActionsMenuDisabled$).toBeTrue();
78
+ });
79
+
80
+ it('keeps honouring the showCaseActions configuration flag', () => {
81
+ configProps = { ...configProps, showCaseActions: false };
82
+
83
+ component.fullUpdate();
84
+
85
+ expect(component.showCaseActions).toBeFalse();
86
+ });
87
+
88
+ it('preserves the case locale reference used for action labels', () => {
89
+ component.fullUpdate();
90
+
91
+ expect(component.localeKey).toBe('MY-CLASS!CASE!MY-CASE');
92
+ });
93
+
94
+ it('opens a data object action with the record class and content', () => {
95
+ component._menuDataObjectActionClick({ ID: 'updateCVV', name: 'Update CVV' });
96
+
97
+ expect(actionsApi.openDataObjectAction).toHaveBeenCalledWith('My-Data-Class', dataRecord, 'updateCVV', 'Update CVV');
98
+ });
99
+
100
+ it('starts a new case with starting fields nested under the target reference field', () => {
101
+ component._menuCreateCaseActionClick({
102
+ ID: 'My-Work-Class',
103
+ name: 'Request Plan Change',
104
+ targetDataReferenceField: { field: 'PlanRef', inputs: [{ linkedField: 'PlanID' }] }
105
+ });
106
+
107
+ expect(actionsApi.createWork).toHaveBeenCalledWith('My-Work-Class', { startingFields: { PlanRef: { PlanID: 'P-1' } } });
108
+ });
24
109
  });
@@ -9,6 +9,7 @@ import { AngularPConnectData, AngularPConnectService } from '@pega/angular-sdk-c
9
9
  import { Utils } from '@pega/angular-sdk-components';
10
10
  import { ComponentMapperComponent } from '@pega/angular-sdk-components';
11
11
  import { prepareCaseSummaryData } from '@pega/angular-sdk-components';
12
+ import { CreateCaseAction, DataObjectAction, DataObjectActions } from '@pega/angular-sdk-components';
12
13
 
13
14
  interface SelfServiceCaseViewProps {
14
15
  // If any, enter additional props that only exist on this component
@@ -45,6 +46,9 @@ export class SelfServiceCaseViewComponent implements OnInit, OnDestroy {
45
46
 
46
47
  arAvailableActions$: any[] = [];
47
48
  arAvailabeProcesses$: any[] = [];
49
+ arDataObjectActions$: DataObjectAction[] = [];
50
+ arCreateCaseActions$: CreateCaseAction[] = [];
51
+ bActionsMenuDisabled$ = true;
48
52
 
49
53
  caseSummaryPConn$: any;
50
54
  currentCaseID = '';
@@ -102,10 +106,31 @@ export class SelfServiceCaseViewComponent implements OnInit, OnDestroy {
102
106
  sessionStorage.setItem('okToInitFlowContainer', 'true');
103
107
  } else {
104
108
  this.updateHeaderAndSummary();
109
+ this.updateCaseActions();
105
110
  }
106
111
  }
107
112
  }
108
113
 
114
+ // React re-reads every action source on each render. Here fullUpdate is gated on a case ID
115
+ // change and so runs once, while actions (and a data object's `dataInfo.actions`) arrive
116
+ // later, so they are all refreshed on every store update instead.
117
+ updateCaseActions() {
118
+ const caseInfo = this.pConn$.getDataObject()?.caseInfo ?? {};
119
+ this.arAvailableActions$ = caseInfo.availableActions ?? [];
120
+ this.arAvailabeProcesses$ = caseInfo.availableProcesses ?? [];
121
+ this.editAction = this.arAvailableActions$.find(action => action.ID === 'pyUpdateCaseDetails');
122
+
123
+ const dataInfoActions: DataObjectActions = this.pConn$.getValue('.actions', 'dataInfo') ?? {};
124
+ this.arDataObjectActions$ = dataInfoActions.availableActions ?? [];
125
+ this.arCreateCaseActions$ = dataInfoActions.availableCreateCaseActions ?? [];
126
+
127
+ this.bActionsMenuDisabled$ =
128
+ this.arAvailableActions$.length === 0 &&
129
+ this.arAvailabeProcesses$.length === 0 &&
130
+ this.arDataObjectActions$.length === 0 &&
131
+ this.arCreateCaseActions$.length === 0;
132
+ }
133
+
109
134
  hasCaseIDChanged(): boolean {
110
135
  if (this.currentCaseID !== this.pConn$.getDataObject().caseInfo.ID) {
111
136
  this.currentCaseID = this.pConn$.getDataObject().caseInfo.ID;
@@ -144,9 +169,8 @@ export class SelfServiceCaseViewComponent implements OnInit, OnDestroy {
144
169
 
145
170
  const caseInfo = this.pConn$.getDataObject().caseInfo;
146
171
  this.currentCaseID = caseInfo.ID;
147
- this.arAvailableActions$ = caseInfo?.availableActions ? caseInfo.availableActions : [];
148
- this.editAction = this.arAvailableActions$.find(action => action.ID === 'pyUpdateCaseDetails');
149
- this.arAvailabeProcesses$ = caseInfo?.availableProcesses ? caseInfo.availableProcesses : [];
172
+
173
+ this.updateCaseActions();
150
174
 
151
175
  const { showCaseLifecycle = true, showSummaryRegion = true, showUtilitiesRegion = true, showCaseActions = true, caseClass } = this.configProps$;
152
176
  this.showCaseLifecycle = this.utils.getBooleanValue(showCaseLifecycle);
@@ -204,4 +228,29 @@ export class SelfServiceCaseViewComponent implements OnInit, OnDestroy {
204
228
 
205
229
  openProcessAction(data.ID, { ...data });
206
230
  }
231
+
232
+ _menuDataObjectActionClick(action: DataObjectAction) {
233
+ const actionsAPI = this.pConn$.getActionsApi();
234
+ const openDataObjectAction = actionsAPI.openDataObjectAction.bind(actionsAPI);
235
+ const classID = this.pConn$.getValue('.classID', 'dataInfo.content');
236
+ const dataRecord = this.pConn$.getValue('.content', 'dataInfo');
237
+
238
+ openDataObjectAction(classID, dataRecord, action.ID, this.localizedVal(action.name, '', this.localeKey));
239
+ }
240
+
241
+ _menuCreateCaseActionClick(action: CreateCaseAction) {
242
+ const actionsAPI = this.pConn$.getActionsApi();
243
+ const createWork = actionsAPI.createWork.bind(actionsAPI);
244
+ const { field, inputs } = action.targetDataReferenceField ?? {};
245
+ const startingFields: Record<string, any> = {};
246
+
247
+ // The engine expects the linked values nested under the target reference field.
248
+ if (field) {
249
+ inputs?.forEach(input => {
250
+ startingFields[field] = { ...startingFields[field], [input.linkedField]: this.pConn$.getValue(`.${input.linkedField}`, '') };
251
+ });
252
+ }
253
+
254
+ createWork(action.ID, { startingFields });
255
+ }
207
256
  }
@@ -148,12 +148,33 @@ const SUPPORTED_FIELD_TYPES = [
148
148
  'RichText'
149
149
  ];
150
150
 
151
+ // PCore.getNameSpaceUtils() exists at runtime but is missing from the pcore typedefs.
152
+ export const getQualifiedPrimaryFieldsName = (): string =>
153
+ (PCore as any).getNameSpaceUtils?.()?.getDefaultQualifiedName(PRIMARY_FIELDS) ?? PRIMARY_FIELDS;
154
+
155
+ export const isPrimaryFieldsValue = (value): boolean => value === PRIMARY_FIELDS || value === getQualifiedPrimaryFieldsName();
156
+
157
+ /**
158
+ * Strips annotation prefixes (ex: "@P .FirstName") and the leading dot from a raw config value.
159
+ */
160
+ export const getPropertyNameFromConfigValue = (configValue): string => {
161
+ let name = configValue ?? '';
162
+ if (name.startsWith('@')) {
163
+ name = name.substring(name.indexOf(' ') + 1);
164
+ }
165
+ if (name.startsWith('.')) {
166
+ name = name.substring(1);
167
+ }
168
+ return name;
169
+ };
170
+
151
171
  export const getConfigFields = (rawFields, contextClass, primaryFieldsViewIndex) => {
152
172
  let primaryFields: any = [];
153
173
  let configFields: any = [];
174
+ const safeRawFields = rawFields || [];
154
175
 
155
176
  if (primaryFieldsViewIndex > -1) {
156
- let primaryFieldVMD: any = PCore.getMetadataUtils().resolveView(PRIMARY_FIELDS);
177
+ let primaryFieldVMD: any = PCore.getMetadataUtils().resolveView(getQualifiedPrimaryFieldsName());
157
178
  if (Array.isArray(primaryFieldVMD)) {
158
179
  primaryFieldVMD = primaryFieldVMD.find(primaryFieldView => primaryFieldView.classID === contextClass);
159
180
  primaryFields = primaryFieldVMD?.children?.[0]?.children || [];
@@ -166,7 +187,7 @@ export const getConfigFields = (rawFields, contextClass, primaryFieldsViewIndex)
166
187
  }
167
188
  }
168
189
 
169
- configFields = [...rawFields.slice(0, primaryFieldsViewIndex), ...primaryFields, ...rawFields.slice(primaryFieldsViewIndex + 1)];
190
+ configFields = [...safeRawFields.slice(0, primaryFieldsViewIndex), ...primaryFields, ...safeRawFields.slice(primaryFieldsViewIndex + 1)];
170
191
  // filter duplicate fields after combining raw fields and primary fields
171
192
  return configFields.filter((field, index) => configFields.findIndex(_field => field.config?.value === _field.config?.value) === index);
172
193
  };
@@ -200,7 +221,7 @@ export const updateFieldLabels = (fields, configFields, primaryFieldsViewIndex,
200
221
  const { columnsRawConfig = [] } = options;
201
222
  fields.forEach((field, idx) => {
202
223
  const rawColumnConfig = columnsRawConfig[idx]?.config;
203
- if (field.config.value === PRIMARY_FIELDS) {
224
+ if (isPrimaryFieldsValue(field.config.value)) {
204
225
  labelsOfFields.push('');
205
226
  } else if (isFLProperty(rawColumnConfig?.label ?? rawColumnConfig?.caption)) {
206
227
  labelsOfFields.push(getFieldLabel(rawColumnConfig) || field.config.label || field.config.caption);
@@ -217,7 +238,7 @@ export const updateFieldLabels = (fields, configFields, primaryFieldsViewIndex,
217
238
  let label = configFields[i].config?.label;
218
239
  if (isFLProperty(label)) {
219
240
  label = getFieldLabel(configFields[i].config);
220
- } else if (label.startsWith('@')) {
241
+ } else if (label?.startsWith('@')) {
221
242
  label = label.substring(3);
222
243
  }
223
244
  if (pConnect) {
@@ -247,7 +268,7 @@ export const buildFieldsForTable = (configFields, pConnect, showActionColumn, op
247
268
  label: fieldsLabels[index],
248
269
  fillAvailableSpace: !!field.config.fillAvailableSpace,
249
270
  id: `${index}`,
250
- name: field.config.value.substr(4),
271
+ name: getPropertyNameFromConfigValue(field.config.value),
251
272
  cellRenderer: TABLE_CELL,
252
273
  sort: false,
253
274
  noContextMenu: true,
@@ -256,7 +277,7 @@ export const buildFieldsForTable = (configFields, pConnect, showActionColumn, op
256
277
  ...field
257
278
  },
258
279
  // BUG-615253: Workaround for autosize in table with lazy loading components
259
- width: getFieldWidth(field, fields[index].config.label)
280
+ width: getFieldWidth(field, fieldsLabels[index])
260
281
  };
261
282
  });
262
283
 
@@ -113,9 +113,7 @@
113
113
  </tr>
114
114
  </table>
115
115
  </div>
116
- <button *ngIf="showAddRowButton" mat-button color="primary" style="font-size: 16px" (click)="addRecord()">
117
- + {{ localizedVal('Add', localeCategory) }}
118
- </button>
116
+ <button *ngIf="showAddRowButton" mat-button color="primary" style="font-size: 16px" (click)="addRecord()">+ {{ addButtonLabel$ }}</button>
119
117
  </ng-container>
120
118
 
121
119
  <!-- pop overs for filters-->
@@ -165,6 +165,10 @@ tr.mat-mdc-header-row {
165
165
  text-align: center;
166
166
  border: 1px solid var(--mat-sys-outline-variant);
167
167
  border-top: none;
168
+
169
+ td.mat-cell {
170
+ text-align: center;
171
+ }
168
172
  }
169
173
 
170
174
  .psdk-utility-card-action-svg-icon {