@pega/angular-sdk-overrides 25.1.12 → 26.1.10

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 (168) hide show
  1. package/lib/designSystemExtension/alert/alert.component.html +4 -1
  2. package/lib/designSystemExtension/alert/alert.component.scss +9 -10
  3. package/lib/designSystemExtension/alert/alert.component.ts +6 -2
  4. package/lib/designSystemExtension/field-group/field-group.component.ts +11 -3
  5. package/lib/designSystemExtension/material-case-summary/material-case-summary.component.html +51 -36
  6. package/lib/designSystemExtension/material-case-summary/material-case-summary.component.scss +12 -0
  7. package/lib/designSystemExtension/material-case-summary/material-case-summary.component.ts +11 -0
  8. package/lib/designSystemExtension/operator/operator.component.html +5 -3
  9. package/lib/designSystemExtension/operator/operator.component.scss +7 -0
  10. package/lib/designSystemExtension/operator/operator.component.ts +2 -1
  11. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.html +100 -10
  12. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.scss +141 -1
  13. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.spec.ts +1 -1
  14. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.ts +240 -72
  15. package/lib/field/auto-complete/auto-complete.component.html +46 -5
  16. package/lib/field/auto-complete/auto-complete.component.scss +92 -0
  17. package/lib/field/auto-complete/auto-complete.component.ts +350 -23
  18. package/lib/field/cancel-alert/cancel-alert.component.ts +38 -15
  19. package/lib/field/check-box/check-box.component.html +8 -6
  20. package/lib/field/check-box/check-box.component.ts +9 -2
  21. package/lib/field/currency/currency.component.html +2 -1
  22. package/lib/field/currency/currency.component.ts +10 -1
  23. package/lib/field/date/date.component.html +2 -1
  24. package/lib/field/date/date.component.ts +2 -0
  25. package/lib/field/date-time/date-time.component.html +2 -1
  26. package/lib/field/date-time/date-time.component.scss +46 -40
  27. package/lib/field/date-time/date-time.component.ts +29 -0
  28. package/lib/field/decimal/decimal.component.html +2 -1
  29. package/lib/field/decimal/decimal.component.ts +2 -0
  30. package/lib/field/dropdown/dropdown.component.html +4 -1
  31. package/lib/field/dropdown/dropdown.component.ts +18 -19
  32. package/lib/field/email/email.component.html +2 -1
  33. package/lib/field/email/email.component.ts +2 -2
  34. package/lib/field/embedded-data-multi/embedded-data-multi.component.html +3 -0
  35. package/lib/field/embedded-data-multi/embedded-data-multi.component.scss +0 -0
  36. package/lib/field/embedded-data-multi/embedded-data-multi.component.ts +173 -0
  37. package/lib/field/field.base.ts +27 -1
  38. package/lib/field/group/group.component.html +1 -1
  39. package/lib/field/group/group.component.ts +16 -7
  40. package/lib/field/integer/integer.component.html +2 -1
  41. package/lib/field/integer/integer.component.ts +4 -2
  42. package/lib/field/location/location.component.html +2 -1
  43. package/lib/field/location/location.component.ts +3 -1
  44. package/lib/field/multiselect/multiselect.component.html +1 -1
  45. package/lib/field/multiselect/multiselect.component.ts +23 -4
  46. package/lib/field/multiselect/utils.ts +5 -3
  47. package/lib/field/object-reference/object-reference.component.html +30 -15
  48. package/lib/field/object-reference/object-reference.component.ts +818 -94
  49. package/lib/field/percentage/percentage.component.html +2 -1
  50. package/lib/field/percentage/percentage.component.ts +10 -1
  51. package/lib/field/phone/phone.component.html +2 -1
  52. package/lib/field/phone/phone.component.ts +2 -1
  53. package/lib/field/radio-buttons/radio-buttons.component.html +2 -1
  54. package/lib/field/radio-buttons/radio-buttons.component.ts +10 -1
  55. package/lib/field/rich-text/rich-text.component.scss +1 -166
  56. package/lib/field/rich-text/rich-text.component.spec.ts +1 -1
  57. package/lib/field/rich-text/rich-text.component.ts +1 -1
  58. package/lib/field/selectable-card/selectable-card.component.html +1 -0
  59. package/lib/field/selectable-card/selectable-card.component.ts +31 -16
  60. package/lib/field/semantic-link/semantic-link.component.html +1 -1
  61. package/lib/field/semantic-link/semantic-link.component.scss +4 -0
  62. package/lib/field/semantic-link/semantic-link.component.ts +13 -7
  63. package/lib/field/text-area/text-area.component.html +2 -1
  64. package/lib/field/text-area/text-area.component.ts +2 -1
  65. package/lib/field/text-input/text-input.component.html +2 -1
  66. package/lib/field/text-input/text-input.component.ts +2 -1
  67. package/lib/field/time/time.component.html +2 -1
  68. package/lib/field/time/time.component.ts +2 -1
  69. package/lib/field/url/url.component.html +2 -1
  70. package/lib/field/url/url.component.ts +2 -1
  71. package/lib/field/user-reference/user-reference.component.html +4 -2
  72. package/lib/field/user-reference/user-reference.component.ts +11 -6
  73. package/lib/infra/Containers/flow-container/flow-container.component.html +2 -2
  74. package/lib/infra/Containers/flow-container/flow-container.component.ts +34 -42
  75. package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.html +12 -0
  76. package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.spec.ts +130 -0
  77. package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.ts +77 -0
  78. package/lib/infra/Containers/modal-view-container/modal-view-container.component.html +35 -8
  79. package/lib/infra/Containers/modal-view-container/modal-view-container.component.ts +185 -196
  80. package/lib/infra/Containers/view-container/view-container.component.html +1 -1
  81. package/lib/infra/Containers/view-container/view-container.component.ts +36 -43
  82. package/lib/infra/assignment/assignment.component.html +2 -1
  83. package/lib/infra/assignment/assignment.component.ts +44 -22
  84. package/lib/infra/assignment-card/assignment-card.component.html +1 -0
  85. package/lib/infra/assignment-card/assignment-card.component.ts +2 -8
  86. package/lib/infra/defer-load/defer-load.component.ts +7 -5
  87. package/lib/infra/multi-step/multi-step.component.html +25 -33
  88. package/lib/infra/multi-step/multi-step.component.scss +67 -125
  89. package/lib/infra/multi-step/multi-step.component.ts +11 -0
  90. package/lib/infra/navbar/navbar.component.ts +33 -36
  91. package/lib/infra/reference/reference.component.html +7 -1
  92. package/lib/infra/reference/reference.component.ts +63 -109
  93. package/lib/infra/region/region.component.ts +1 -3
  94. package/lib/infra/root-container/root-container.component.html +1 -0
  95. package/lib/infra/root-container/root-container.component.ts +34 -42
  96. package/lib/infra/view/view.component.ts +22 -46
  97. package/lib/template/advanced-search/advanced-search.component.ts +0 -1
  98. package/lib/template/advanced-search/search-groups/search-groups.component.ts +2 -3
  99. package/lib/template/app-shell/app-shell.component.ts +13 -13
  100. package/lib/template/banner-page/banner-page.component.ts +1 -2
  101. package/lib/template/base/details-template-base.ts +41 -14
  102. package/lib/template/base/form-template-base.ts +0 -2
  103. package/lib/template/case-summary/case-summary.component.ts +15 -7
  104. package/lib/template/case-view/case-view.component.html +14 -2
  105. package/lib/template/case-view/case-view.component.spec.ts +119 -10
  106. package/lib/template/case-view/case-view.component.ts +52 -3
  107. package/lib/template/data-reference/data-reference.component.ts +29 -22
  108. package/lib/template/data-reference/search-form/search-form.component.html +1 -1
  109. package/lib/template/data-reference/search-form/search-form.component.ts +9 -1
  110. package/lib/template/data-reference/search-form/tabsData.ts +0 -1
  111. package/lib/template/default-form/default-form.component.html +20 -4
  112. package/lib/template/default-form/default-form.component.ts +14 -5
  113. package/lib/template/default-page/default-page.component.ts +1 -2
  114. package/lib/template/details/details.component.html +23 -3
  115. package/lib/template/details/details.component.scss +9 -2
  116. package/lib/template/details/details.component.ts +3 -70
  117. package/lib/template/details-narrow-wide/details-narrow-wide.component.html +21 -2
  118. package/lib/template/details-narrow-wide/details-narrow-wide.component.scss +8 -1
  119. package/lib/template/details-narrow-wide/details-narrow-wide.component.ts +3 -39
  120. package/lib/template/details-one-column/details-one-column.component.html +23 -1
  121. package/lib/template/details-one-column/details-one-column.component.scss +14 -0
  122. package/lib/template/details-one-column/details-one-column.component.ts +3 -40
  123. package/lib/template/details-three-column/details-three-column.component.html +21 -2
  124. package/lib/template/details-three-column/details-three-column.component.scss +8 -1
  125. package/lib/template/details-three-column/details-three-column.component.ts +3 -48
  126. package/lib/template/details-two-column/details-two-column.component.html +21 -2
  127. package/lib/template/details-two-column/details-two-column.component.scss +8 -1
  128. package/lib/template/details-two-column/details-two-column.component.ts +3 -46
  129. package/lib/template/details-wide-narrow/details-wide-narrow.component.html +21 -2
  130. package/lib/template/details-wide-narrow/details-wide-narrow.component.scss +8 -1
  131. package/lib/template/details-wide-narrow/details-wide-narrow.component.ts +3 -43
  132. package/lib/template/dynamic-tabs/dynamic-tabs.component.html +1 -0
  133. package/lib/template/field-group-template/field-group-template.component.ts +19 -6
  134. package/lib/template/field-value-list/field-value-list.component.scss +17 -0
  135. package/lib/template/inline-dashboard-page/inline-dashboard-page.component.ts +2 -10
  136. package/lib/template/list-view/list-view.component.ts +47 -11
  137. package/lib/template/one-column/one-column.component.html +3 -0
  138. package/lib/template/one-column-tab/one-column-tab.component.html +1 -0
  139. package/lib/template/page/page.component.html +1 -0
  140. package/lib/template/self-service-case-view/self-service-case-view.component.html +13 -1
  141. package/lib/template/self-service-case-view/self-service-case-view.component.spec.ts +96 -11
  142. package/lib/template/self-service-case-view/self-service-case-view.component.ts +52 -3
  143. package/lib/template/simple-table/simple-table.component.ts +11 -1
  144. package/lib/template/simple-table-manual/helpers.ts +84 -16
  145. package/lib/template/simple-table-manual/simple-table-manual.component.html +30 -14
  146. package/lib/template/simple-table-manual/simple-table-manual.component.scss +8 -2
  147. package/lib/template/simple-table-manual/simple-table-manual.component.ts +142 -46
  148. package/lib/template/single-reference-readonly/single-reference-readonly.component.html +0 -0
  149. package/lib/template/single-reference-readonly/single-reference-readonly.component.scss +2 -3
  150. package/lib/template/single-reference-readonly/single-reference-readonly.component.ts +7 -8
  151. package/lib/template/wide-narrow-page/wide-narrow-page.component.ts +0 -3
  152. package/lib/template/wss-nav-bar/wss-nav-bar.component.ts +19 -21
  153. package/lib/widget/attachment/AttachmentUtils.ts +19 -11
  154. package/lib/widget/attachment/attachment.component.html +16 -6
  155. package/lib/widget/attachment/attachment.component.ts +14 -4
  156. package/lib/widget/case-history/case-history.component.ts +6 -2
  157. package/lib/widget/feed-container/feed-container.component.ts +1 -199
  158. package/lib/widget/file-utility/file-utility.component.ts +26 -30
  159. package/lib/widget/todo/todo.component.ts +7 -2
  160. package/package.json +2 -2
  161. package/lib/designSystemExtension/material-details/material-details.component.html +0 -40
  162. package/lib/designSystemExtension/material-details/material-details.component.scss +0 -116
  163. package/lib/designSystemExtension/material-details/material-details.component.spec.ts +0 -24
  164. package/lib/designSystemExtension/material-details/material-details.component.ts +0 -31
  165. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.html +0 -29
  166. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.scss +0 -34
  167. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.spec.ts +0 -22
  168. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.ts +0 -37
@@ -1,23 +1,39 @@
1
- import { Component, EventEmitter, OnInit, Output, forwardRef, inject } from '@angular/core';
1
+ import { Component, EventEmitter, OnInit, Output, ViewChild, forwardRef, inject } from '@angular/core';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import { ReactiveFormsModule } from '@angular/forms';
4
4
  import { MatOptionModule } from '@angular/material/core';
5
- import { MatAutocompleteModule } from '@angular/material/autocomplete';
5
+ import { MatAutocompleteModule, MatAutocompleteTrigger } from '@angular/material/autocomplete';
6
6
  import { MatInputModule } from '@angular/material/input';
7
7
  import { MatFormFieldModule } from '@angular/material/form-field';
8
+ import { MatButtonModule } from '@angular/material/button';
9
+ import { MatIconModule } from '@angular/material/icon';
10
+ import { MatDividerModule } from '@angular/material/divider';
8
11
  import { Observable } from 'rxjs';
9
12
  import { map, startWith } from 'rxjs/operators';
10
13
 
11
14
  import { FieldBase } from '@pega/angular-sdk-components';
15
+ import { FieldWarningDirective } from '@pega/angular-sdk-components';
12
16
  import { ComponentMapperComponent } from '@pega/angular-sdk-components';
13
17
  import { DatapageService } from '@pega/angular-sdk-components';
14
18
  import { handleEvent } from '@pega/angular-sdk-components';
15
19
  import { PConnFieldProps } from '@pega/angular-sdk-components';
16
20
 
17
- interface IOption {
21
+ interface AutoCompleteOption {
18
22
  key: string;
19
23
  value: string;
24
+ // Present only when at least one secondary column resolves to a non-empty value
25
+ secondaryComponents?: any[];
26
+ secondarySearchText?: string;
27
+ // Present only when a group-by field is configured for this (datapage-sourced) field
28
+ group?: string;
20
29
  }
30
+
31
+ // Internal, render-time-only view-model — never part of the PConnect contract
32
+ interface AutoCompleteGroup {
33
+ label: string;
34
+ options: AutoCompleteOption[];
35
+ }
36
+
21
37
  interface AutoCompleteProps extends PConnFieldProps {
22
38
  // If any, enter additional props that only exist on AutoComplete here
23
39
  deferDatasource?: boolean;
@@ -28,6 +44,12 @@ interface AutoCompleteProps extends PConnFieldProps {
28
44
  parameters?: any;
29
45
  datasource: any;
30
46
  columns: any[];
47
+ allowCreatingRecords?: boolean;
48
+ onCreateNew?: () => void;
49
+ createNewLabel?: string;
50
+ createNewRecord?: () => Promise<unknown>;
51
+ contextClass?: string;
52
+ referenceType?: string;
31
53
  }
32
54
 
33
55
  @Component({
@@ -41,6 +63,10 @@ interface AutoCompleteProps extends PConnFieldProps {
41
63
  MatInputModule,
42
64
  MatAutocompleteModule,
43
65
  MatOptionModule,
66
+ MatButtonModule,
67
+ MatIconModule,
68
+ MatDividerModule,
69
+ FieldWarningDirective,
44
70
  forwardRef(() => ComponentMapperComponent)
45
71
  ],
46
72
  providers: [DatapageService]
@@ -50,15 +76,29 @@ export class AutoCompleteComponent extends FieldBase implements OnInit {
50
76
 
51
77
  @Output() onRecordChange: EventEmitter<any> = new EventEmitter();
52
78
 
79
+ @ViewChild(MatAutocompleteTrigger) private autocompleteTrigger?: MatAutocompleteTrigger;
80
+
53
81
  configProps$: AutoCompleteProps;
54
82
 
55
- options$: any[];
83
+ options$: AutoCompleteOption[];
56
84
  listType: string;
57
- columns = [];
85
+ columns: any[] = [];
58
86
  parameters: {};
59
- filteredOptions: Observable<any[]>;
87
+ datasource: any;
88
+ filteredOptions: Observable<AutoCompleteOption[]>;
89
+ // Grouped view of filteredOptions, only rendered when hasGroupBy is true
90
+ groupedFilteredOptions$: Observable<AutoCompleteGroup[]>;
91
+ hasGroupBy = false;
60
92
  filterValue = '';
61
93
 
94
+ // "Create new" footer button state
95
+ showCreateButton = false;
96
+ createNewLabel = 'Create new';
97
+ contextClass?: string;
98
+ referenceType?: string;
99
+ private onCreateNewFn?: () => void;
100
+ private createNewRecordFn?: () => Promise<unknown>;
101
+
62
102
  // Override ngOnInit method
63
103
  override async ngOnInit(): Promise<void> {
64
104
  super.ngOnInit();
@@ -67,18 +107,36 @@ export class AutoCompleteComponent extends FieldBase implements OnInit {
67
107
  startWith(''),
68
108
  map(value => this._filter((value as string) || ''))
69
109
  );
110
+
111
+ this.groupedFilteredOptions$ = this.filteredOptions.pipe(map(options => this.buildGroups(options)));
70
112
  }
71
113
 
72
- setOptions(options: IOption[]) {
114
+ setOptions(options: AutoCompleteOption[]) {
73
115
  this.options$ = options;
74
116
  const index = this.options$?.findIndex(element => element.key === this.configProps$.value);
75
117
  this.value$ = index > -1 ? this.options$[index].value : this.configProps$.value;
76
118
  this.fieldControl.setValue(this.value$);
77
119
  }
78
120
 
79
- private _filter(value: string): string[] {
121
+ // Matches only primary text and secondary search text — group value is never used for search
122
+ private _filter(value: string): AutoCompleteOption[] {
80
123
  const filterVal = (value || this.filterValue).toLowerCase();
81
- return this.options$?.filter(option => option.value?.toLowerCase().includes(filterVal));
124
+ return this.options$?.filter(option => option.value?.toLowerCase().includes(filterVal) || option.secondarySearchText?.includes(filterVal));
125
+ }
126
+
127
+ // Buckets the already-sorted option list into contiguous groups by exact group value
128
+ buildGroups(options: AutoCompleteOption[]): AutoCompleteGroup[] {
129
+ const groups: AutoCompleteGroup[] = [];
130
+ options?.forEach(option => {
131
+ const label = option.group ?? '';
132
+ const lastGroup = groups[groups.length - 1];
133
+ if (lastGroup && lastGroup.label === label) {
134
+ lastGroup.options.push(option);
135
+ } else {
136
+ groups.push({ label, options: [option] });
137
+ }
138
+ });
139
+ return groups;
82
140
  }
83
141
 
84
142
  /**
@@ -92,24 +150,29 @@ export class AutoCompleteComponent extends FieldBase implements OnInit {
92
150
  this.updateComponentCommonProperties(this.configProps$);
93
151
 
94
152
  // Set component specific properties
95
- const { value, listType, parameters } = this.configProps$;
96
-
97
- if (value != undefined) {
98
- const index = this.options$?.findIndex(element => element.key === value);
99
- this.value$ = index > -1 ? this.options$[index].value : value;
100
- this.fieldControl.setValue(this.value$);
101
- }
153
+ const { value, listType, parameters, allowCreatingRecords, onCreateNew, createNewLabel, createNewRecord, contextClass, referenceType } =
154
+ this.configProps$;
102
155
 
103
156
  this.listType = listType;
104
157
  this.parameters = parameters;
105
158
 
159
+ this.showCreateButton = allowCreatingRecords === true;
160
+ this.createNewLabel = createNewLabel || 'Create new';
161
+ this.contextClass = contextClass;
162
+ this.referenceType = referenceType;
163
+ this.onCreateNewFn = onCreateNew;
164
+ this.createNewRecordFn = createNewRecord;
165
+
106
166
  const context = this.pConn$.getContextName();
107
167
  const { columns, datasource } = this.generateColumnsAndDataSource();
168
+ this.datasource = datasource;
108
169
 
109
170
  if (columns) {
110
171
  this.columns = this.preProcessColumns(columns);
111
172
  }
112
173
 
174
+ this.hasGroupBy = this.columns?.some(col => col.groupBy === 'true') ?? false;
175
+
113
176
  if (this.listType === 'associated') {
114
177
  const optionsList = this.utils.getOptionList(this.configProps$, this.pConn$.getDataObject('')); // 1st arg empty string until typedef marked correctly
115
178
  this.setOptions(optionsList);
@@ -119,6 +182,12 @@ export class AutoCompleteComponent extends FieldBase implements OnInit {
119
182
  const results = await this.dataPageService.getDataPageData(datasource, this.parameters, context);
120
183
  this.fillOptions(results);
121
184
  }
185
+
186
+ if (value != undefined) {
187
+ const index = this.options$?.findIndex(element => element.key === value);
188
+ this.value$ = index > -1 ? this.options$[index].value : value;
189
+ this.fieldControl.setValue(this.value$);
190
+ }
122
191
  }
123
192
 
124
193
  generateColumnsAndDataSource() {
@@ -150,22 +219,157 @@ export class AutoCompleteComponent extends FieldBase implements OnInit {
150
219
  ];
151
220
  }
152
221
 
222
+ // Secondary text and grouping are both out of scope for associated/local list options
223
+ if (this.listType !== 'associated') {
224
+ const secondaryColumns = this.getSecondaryColumnsFromMetadata();
225
+ if (secondaryColumns.length > 0) {
226
+ columns = [...(columns || []), ...secondaryColumns];
227
+ }
228
+
229
+ const groupByColumns = this.getGroupByColumnsFromMetadata();
230
+ if (groupByColumns.length > 0) {
231
+ columns = [...(columns || []), ...groupByColumns];
232
+ }
233
+ }
234
+
153
235
  return { columns, datasource };
154
236
  }
155
237
 
238
+ // Reads unresolved groupsFields metadata to derive group-by column descriptor(s); not a
239
+ // display/search column, so grouping stays independent of primary/secondary text
240
+ getGroupByColumnsFromMetadata() {
241
+ const groupsFields = (this.pConn$.getRawMetadata()?.config as any)?.groupsFields;
242
+ if (!Array.isArray(groupsFields)) {
243
+ return [];
244
+ }
245
+ return this.mapMetadataColumns(groupsFields, { display: 'false', groupBy: 'true', useForSearch: false });
246
+ }
247
+
248
+ // Reads unresolved columnsFormatter metadata to derive secondary (contextual) display columns
249
+ getSecondaryColumnsFromMetadata() {
250
+ const columnsFormatter = (this.pConn$.getRawMetadata()?.config as any)?.columnsFormatter;
251
+ if (!Array.isArray(columnsFormatter)) {
252
+ return [];
253
+ }
254
+ return this.mapMetadataColumns(columnsFormatter, { display: 'true', secondary: 'true', useForSearch: true });
255
+ }
256
+
257
+ // Shared by getSecondaryColumnsFromMetadata/getGroupByColumnsFromMetadata: value must stay an
258
+ // unresolved property reference (e.g. "@P .propName") for use as a raw-row lookup key
259
+ mapMetadataColumns(rawColumns: any[], columnFlags: object): any[] {
260
+ return rawColumns
261
+ .map(item => {
262
+ const property = item?.config?.value;
263
+ if (typeof property !== 'string' || !property) {
264
+ return undefined;
265
+ }
266
+ let value = property;
267
+ if (property.startsWith('@P ')) {
268
+ value = property.substring(3);
269
+ } else if (property.startsWith('@USER ')) {
270
+ value = property.substring(6);
271
+ }
272
+ return { value, type: item?.type, label: item?.config?.label, ...columnFlags };
273
+ })
274
+ .filter(Boolean);
275
+ }
276
+
156
277
  fillOptions(results: any) {
157
- const optionsData: any[] = [];
278
+ const optionsData: AutoCompleteOption[] = [];
158
279
  const displayColumn = this.getDisplayFieldsMetaData(this.columns);
280
+ const secondaryColumns = this.columns?.filter(col => col.display === 'true' && col.secondary === 'true') || [];
281
+ const groupByColumn = this.columns?.find(col => col.groupBy === 'true');
282
+
159
283
  results?.forEach(element => {
160
- const obj = {
284
+ const obj: AutoCompleteOption = {
161
285
  key: element[displayColumn.key] || element.pyGUID,
162
286
  value: element[displayColumn.primary]?.toString()
163
287
  };
288
+
289
+ if (secondaryColumns.length > 0) {
290
+ const secondaryComponents = this.buildSecondaryComponents(element, secondaryColumns);
291
+ if (secondaryComponents.length > 0) {
292
+ obj.secondaryComponents = secondaryComponents;
293
+ }
294
+
295
+ const secondarySearchText = this.buildSecondarySearchText(element, secondaryColumns);
296
+ if (secondarySearchText) {
297
+ obj.secondarySearchText = secondarySearchText;
298
+ }
299
+ }
300
+
301
+ if (groupByColumn) {
302
+ obj.group = this.resolveGroupValue(element[groupByColumn.value as string]);
303
+ }
304
+
164
305
  optionsData.push(obj);
165
306
  });
307
+
308
+ if (groupByColumn) {
309
+ this.sortByGroup(optionsData);
310
+ }
311
+
166
312
  this.setOptions(optionsData);
167
313
  }
168
314
 
315
+ // Null/undefined/whitespace-only source values normalize to '' — the shared blank group
316
+ resolveGroupValue(rawValue: any): string {
317
+ if (rawValue === null || rawValue === undefined) {
318
+ return '';
319
+ }
320
+ const stringValue = rawValue.toString();
321
+ return stringValue.trim() ? stringValue : '';
322
+ }
323
+
324
+ // Ascending, case-sensitive, stable sort so same-group options keep their original relative order
325
+ sortByGroup(options: AutoCompleteOption[]): void {
326
+ options.sort((a, b) => {
327
+ const groupA = a.group ?? '';
328
+ const groupB = b.group ?? '';
329
+ if (groupA < groupB) {
330
+ return -1;
331
+ }
332
+ if (groupA > groupB) {
333
+ return 1;
334
+ }
335
+ return 0;
336
+ });
337
+ }
338
+
339
+ // Rendering only — one read-only PConnect component per configured secondary field, in
340
+ // configured order, regardless of whether its value is empty (FieldValueList's own
341
+ // empty-value fallback renders the placeholder, e.g. "Label: ---"). Mirrors ScalarListComponent's
342
+ // createComponent/DISPLAY_ONLY pattern.
343
+ buildSecondaryComponents(element: any, secondaryColumns): any[] {
344
+ return secondaryColumns.map(col =>
345
+ this.pConn$.createComponent(
346
+ {
347
+ type: col.type,
348
+ config: {
349
+ value: element[col.value as string],
350
+ displayMode: 'DISPLAY_ONLY',
351
+ readOnly: true,
352
+ label: col.label
353
+ }
354
+ },
355
+ '',
356
+ 0,
357
+ {}
358
+ )
359
+ ); // 2nd, 3rd, and 4th args empty string/object/null until typedef marked correctly as optional
360
+ }
361
+
362
+ // Search only — independent of buildSecondaryComponents; never derived from rendered output.
363
+ buildSecondarySearchText(element: any, secondaryColumns): string {
364
+ return secondaryColumns
365
+ .map(col => {
366
+ const rawValue = element[col.value as string];
367
+ return rawValue === null || rawValue === undefined ? '' : rawValue.toString().trim().toLowerCase();
368
+ })
369
+ .filter(Boolean)
370
+ .join(' ');
371
+ }
372
+
169
373
  flattenParameters(params = {}) {
170
374
  const flatParams = {};
171
375
  Object.keys(params).forEach(key => {
@@ -180,12 +384,12 @@ export class AutoCompleteComponent extends FieldBase implements OnInit {
180
384
  const displayColumns = columnList.filter(col => col.display === 'true');
181
385
  const metaDataObj: any = { key: '', primary: '', secondary: [] };
182
386
  const keyCol = columnList.filter(col => col.key === 'true');
183
- metaDataObj.key = keyCol.length > 0 ? keyCol[0].value : 'auto';
387
+ metaDataObj.key = keyCol.length > 0 ? (keyCol[0].value ?? 'auto') : 'auto';
184
388
  for (let index = 0; index < displayColumns.length; index += 1) {
185
389
  if (displayColumns[index].primary === 'true') {
186
- metaDataObj.primary = displayColumns[index].value;
187
- } else {
188
- metaDataObj.secondary.push(displayColumns[index].value);
390
+ metaDataObj.primary = displayColumns[index].value ?? '';
391
+ } else if (displayColumns[index].secondary === 'true') {
392
+ metaDataObj.secondary.push(displayColumns[index].value ?? '');
189
393
  }
190
394
  }
191
395
  return metaDataObj;
@@ -211,7 +415,7 @@ export class AutoCompleteComponent extends FieldBase implements OnInit {
211
415
  let key = '';
212
416
  if (val) {
213
417
  const index = this.options$?.findIndex(element => element.value === val);
214
- key = index > -1 ? (key = this.options$[index].key) : val;
418
+ key = index > -1 ? this.options$[index].key : val;
215
419
  }
216
420
  const value = key;
217
421
  handleEvent(this.actionsApi, 'changeNblur', this.propName, value);
@@ -220,4 +424,127 @@ export class AutoCompleteComponent extends FieldBase implements OnInit {
220
424
  this.onRecordChange.emit(value);
221
425
  }
222
426
  }
427
+
428
+ // Re-fetches the options list
429
+ refreshOptionsList(): void {
430
+ if (!this.displayMode$ && this.listType !== 'associated') {
431
+ const context = this.pConn$.getContextName();
432
+ this.dataPageService
433
+ .getDataPageData(this.datasource, this.parameters, context)
434
+ .then((results: any) => this.fillOptions(results))
435
+ .catch(e => console.error(e));
436
+ }
437
+ }
438
+
439
+ // Sets values for all columns that have setProperty defined
440
+ setValuesToAdditionalFields(record: Record<string, unknown>): void {
441
+ const setPropertyList = this.columns.filter(col => col.setProperty).map(col => ({ source: col.value, target: col.setProperty, key: col.key }));
442
+
443
+ setPropertyList.forEach(prop => {
444
+ let valueToSet: string;
445
+ if (prop.key === 'true') {
446
+ valueToSet = record[prop.source]?.toString() || (record as any).pyGUID || '';
447
+ } else {
448
+ valueToSet = record[prop.source]?.toString() || '';
449
+ }
450
+
451
+ if (prop.target === 'Associated property') {
452
+ handleEvent(this.actionsApi, 'changeNblur', this.propName, valueToSet);
453
+ } else {
454
+ const target = typeof prop.target === 'string' ? prop.target : '';
455
+ const targetProp = target.startsWith('.') ? target : `.${target}`;
456
+ (this.actionsApi as any).updateFieldValue(targetProp, valueToSet, { associatedProperty: this.propName });
457
+ (this.actionsApi as any).triggerFieldChange(targetProp, valueToSet);
458
+ }
459
+ });
460
+ }
461
+
462
+ createNewButtonHandler(): void {
463
+ // Close the options panel so it doesn't remain open on top of the create-new modal
464
+ this.autocompleteTrigger?.closePanel();
465
+
466
+ if (this.onCreateNewFn) {
467
+ this.onCreateNewFn();
468
+ return;
469
+ }
470
+
471
+ if (!this.contextClass) {
472
+ return;
473
+ }
474
+
475
+ const context = this.pConn$.getContextName();
476
+ const normalizedReferenceType = typeof this.referenceType === 'string' ? this.referenceType.toLowerCase() : '';
477
+ const isDataReference = normalizedReferenceType === 'data';
478
+ const { CREATE_STAGE_DONE } = PCore.getConstants().PUB_SUB_EVENTS.CASE_EVENTS;
479
+ const DATA_OBJECT_CREATED = (PCore.getConstants().PUB_SUB_EVENTS as any).DATA_EVENTS?.DATA_OBJECT_CREATED;
480
+ const eventType = isDataReference && DATA_OBJECT_CREATED ? DATA_OBJECT_CREATED : CREATE_STAGE_DONE;
481
+ const contextClass = this.contextClass;
482
+
483
+ const createNewCallback = isDataReference
484
+ ? (data: { data?: { responseData?: Record<string, unknown> } }) => {
485
+ // Clear contexted cache before re-fetching
486
+ PCore.getDataApi().clearContextedCache(context);
487
+
488
+ const responseData = data?.data?.responseData;
489
+ if (responseData) {
490
+ this.setValuesToAdditionalFields(responseData);
491
+ const displayColumn = this.getDisplayFieldsMetaData(this.columns);
492
+ const newKey = responseData[displayColumn.key]?.toString() || (responseData as any).pyGUID;
493
+ if (this.onRecordChange && newKey) {
494
+ this.onRecordChange.emit({ id: newKey });
495
+ }
496
+ }
497
+
498
+ this.refreshOptionsList();
499
+ PCore.getPubSubUtils().unsubscribe(eventType, contextClass);
500
+ }
501
+ : (data: { caseId?: string; caseType?: string; ID?: string }) => {
502
+ // Clear contexted cache before re-fetching
503
+ PCore.getDataApi().clearContextedCache(context);
504
+
505
+ const newCaseId = data.caseId?.split(' ').pop();
506
+ if (data.caseType === contextClass) {
507
+ const selectKey = data.ID || newCaseId;
508
+
509
+ if (selectKey && this.listType !== 'associated' && this.datasource) {
510
+ this.dataPageService
511
+ .getDataPageData(this.datasource, this.parameters, context)
512
+ .then((results: any) => {
513
+ this.fillOptions(results);
514
+
515
+ const displayColumn = this.getDisplayFieldsMetaData(this.columns);
516
+ const newRecord = results?.find((el: any) => el.ID === data.ID || (el[displayColumn.key] || el.pyGUID) === selectKey);
517
+ if (newRecord) {
518
+ this.setValuesToAdditionalFields(newRecord);
519
+ } else {
520
+ handleEvent(this.actionsApi, 'changeNblur', this.propName, selectKey);
521
+ }
522
+ if (this.onRecordChange) {
523
+ this.onRecordChange.emit({ id: selectKey });
524
+ }
525
+ })
526
+ .catch(e => console.error(e));
527
+ }
528
+ PCore.getPubSubUtils().unsubscribe(eventType, contextClass);
529
+ }
530
+ };
531
+
532
+ // Build the create action if createNewRecord fn is not provided
533
+ const triggerCreate = this.createNewRecordFn
534
+ ? this.createNewRecordFn()
535
+ : isDataReference
536
+ ? this.pConn$.getActionsApi().showDataObjectCreateView(contextClass)
537
+ : this.pConn$.getActionsApi().createWork(contextClass, {
538
+ openCaseViewAfterCreate: false,
539
+ startingFields: {}
540
+ });
541
+
542
+ Promise.resolve(triggerCreate)
543
+ .then(() => {
544
+ PCore.getPubSubUtils().subscribe(eventType, createNewCallback, contextClass);
545
+ // Re-initialize the list
546
+ this.refreshOptionsList();
547
+ })
548
+ .catch(e => console.error(e));
549
+ }
223
550
  }
@@ -14,6 +14,9 @@ import { ComponentMapperComponent } from '@pega/angular-sdk-components';
14
14
  export class CancelAlertComponent implements OnChanges {
15
15
  @Input() pConn$: typeof PConnect;
16
16
  @Input() bShowAlert$: boolean;
17
+ @Input() hideDelete: boolean;
18
+ @Input() isDataObject: boolean;
19
+ @Input() skipReleaseLockRequest: any;
17
20
  @Output() onAlertState$: EventEmitter<boolean> = new EventEmitter<boolean>();
18
21
 
19
22
  itemKey: string;
@@ -61,7 +64,6 @@ export class CancelAlertComponent implements OnChanges {
61
64
  }
62
65
 
63
66
  buttonClick({ action }) {
64
- const actionsAPI = this.pConn$.getActionsApi();
65
67
  this.localizedVal = PCore.getLocaleUtils().getLocaleValue;
66
68
 
67
69
  switch (action) {
@@ -70,23 +72,44 @@ export class CancelAlertComponent implements OnChanges {
70
72
  break;
71
73
  case 'discard':
72
74
  this.psService.sendMessage(true);
73
-
74
- // eslint-disable-next-line no-case-declarations
75
- const deletePromise = actionsAPI.deleteCaseInCreateStage(this.itemKey);
76
-
77
- deletePromise
78
- .then(() => {
79
- this.psService.sendMessage(false);
80
- this.dismissAlert();
81
- PCore.getPubSubUtils().publish(PCore.getConstants().PUB_SUB_EVENTS.EVENT_CANCEL);
82
- })
83
- .catch(() => {
84
- this.psService.sendMessage(false);
85
- this.sendMessage(this.localizedVal('Delete failed.', this.localeCategory));
86
- });
75
+ this.handleDiscard();
87
76
  break;
88
77
  default:
89
78
  break;
90
79
  }
91
80
  }
81
+
82
+ // Data objects and local/bulk actions don't have a create-stage case to delete, so each needs its own engine API
83
+ handleDiscard() {
84
+ const actionsAPI = this.pConn$.getActionsApi();
85
+ // @ts-ignore - Property 'options' is private and only accessible within class 'C11nEnv'.
86
+ const isBulkAction = (this.pConn$ as any)?.options?.isBulkAction;
87
+ const isLocalAction = this.pConn$.getValue(PCore.getConstants().CASE_INFO.IS_LOCAL_ACTION);
88
+
89
+ if (!this.isDataObject && !isLocalAction && !isBulkAction) {
90
+ actionsAPI
91
+ .deleteCaseInCreateStage(this.itemKey, this.hideDelete)
92
+ .then(() => {
93
+ this.psService.sendMessage(false);
94
+ this.dismissAlert();
95
+ PCore.getPubSubUtils().publish(PCore.getConstants().PUB_SUB_EVENTS.EVENT_CANCEL);
96
+ })
97
+ .catch(() => {
98
+ this.psService.sendMessage(false);
99
+ this.sendMessage(this.localizedVal('Delete failed.', this.localeCategory));
100
+ });
101
+ } else if (isLocalAction) {
102
+ this.psService.sendMessage(false);
103
+ this.dismissAlert();
104
+ actionsAPI.cancelAssignment(this.itemKey, false);
105
+ } else if (isBulkAction) {
106
+ this.psService.sendMessage(false);
107
+ this.dismissAlert();
108
+ actionsAPI.cancelBulkAction(this.itemKey);
109
+ } else {
110
+ this.psService.sendMessage(false);
111
+ this.dismissAlert();
112
+ this.pConn$.getContainerManager().removeContainerItem({ containerItemID: this.itemKey, skipReleaseLockRequest: this.skipReleaseLockRequest });
113
+ }
114
+ }
92
115
  }
@@ -12,13 +12,13 @@
12
12
  <div [formGroup]="formGroup$" *ngIf="bVisible$">
13
13
  <div class="mat-form-field-infix" *ngIf="showLabel$">
14
14
  <span>
15
- <label class="mat-form-field-label psdk-label-readonly" [ngClass]="{ 'label-required': bRequired$ && selectionMode === 'multi' }">{{
16
- label$
17
- }}</label>
15
+ <label class="mat-form-field-label psdk-label-readonly" [ngClass]="{ 'label-required': bRequired$ && selectionMode === 'multi' }">
16
+ {{ label$ }}</label
17
+ >
18
18
  </span>
19
19
  </div>
20
20
  <div *ngIf="selectionMode === 'multi'; else single">
21
- <mat-option *ngFor="let item of listOfCheckboxes" (click)="handleChangeMultiMode($event, item)">
21
+ <div class="mat-mdc-option" *ngFor="let item of listOfCheckboxes">
22
22
  <mat-checkbox
23
23
  [labelPosition]="'after'"
24
24
  [checked]="item.selected"
@@ -28,7 +28,7 @@
28
28
  (blur)="fieldOnBlur($event)"
29
29
  >{{ item.text ?? item.value }}
30
30
  </mat-checkbox>
31
- </mat-option>
31
+ </div>
32
32
  </div>
33
33
  <ng-template #single>
34
34
  <mat-checkbox
@@ -42,7 +42,9 @@
42
42
  (blur)="fieldOnBlur($event)"
43
43
  ><span [ngClass]="{ 'label-required': bRequired$ }">{{ caption$ }}</span></mat-checkbox
44
44
  >
45
- <p *ngIf="helperText">{{ helperText }}</p>
45
+ <p *ngIf="helperText && !(fieldControl.invalid && (fieldControl.touched || fieldControl.dirty))" [appFieldWarning]="bFieldMessageVisible$">
46
+ {{ helperText }}
47
+ </p>
46
48
  </ng-template>
47
49
  <mat-error *ngIf="fieldControl.invalid && (fieldControl.touched || fieldControl.dirty)">{{ getErrorMessage() }}</mat-error>
48
50
  </div>
@@ -3,9 +3,9 @@ import { CommonModule } from '@angular/common';
3
3
  import { ReactiveFormsModule } from '@angular/forms';
4
4
  import { MatCheckboxModule } from '@angular/material/checkbox';
5
5
  import { MatFormFieldModule } from '@angular/material/form-field';
6
- import { MatOptionModule } from '@angular/material/core';
7
6
 
8
7
  import { FieldBase } from '@pega/angular-sdk-components';
8
+ import { FieldWarningDirective } from '@pega/angular-sdk-components';
9
9
  import { ComponentMapperComponent } from '@pega/angular-sdk-components';
10
10
  import { PConnFieldProps } from '@pega/angular-sdk-components';
11
11
  import { deleteInstruction, insertInstruction, updateNewInstructions } from '@pega/angular-sdk-components';
@@ -33,7 +33,14 @@ interface CheckboxProps extends Omit<PConnFieldProps, 'value'> {
33
33
  selector: 'app-check-box',
34
34
  templateUrl: './check-box.component.html',
35
35
  styleUrls: ['./check-box.component.scss'],
36
- imports: [CommonModule, ReactiveFormsModule, MatCheckboxModule, MatFormFieldModule, MatOptionModule, forwardRef(() => ComponentMapperComponent)]
36
+ imports: [
37
+ CommonModule,
38
+ ReactiveFormsModule,
39
+ MatCheckboxModule,
40
+ MatFormFieldModule,
41
+ FieldWarningDirective,
42
+ forwardRef(() => ComponentMapperComponent)
43
+ ]
37
44
  })
38
45
  export class CheckBoxComponent extends FieldBase implements OnInit, OnDestroy {
39
46
  configProps$: CheckboxProps;
@@ -4,7 +4,7 @@
4
4
  <ng-template #noDisplayMode>
5
5
  <div *ngIf="bHasForm$; else noEdit">
6
6
  <div [formGroup]="formGroup$" *ngIf="bVisible$" class="psdk-currency-field">
7
- <mat-form-field class="psdk-full-width" subscriptSizing="dynamic" [hintLabel]="helperText">
7
+ <mat-form-field class="psdk-full-width" subscriptSizing="dynamic">
8
8
  <mat-label>{{ label$ }}</mat-label>
9
9
  <div class="psdk-currency-input">
10
10
  <input
@@ -30,6 +30,7 @@
30
30
  [readonly]="bReadonly$"
31
31
  />
32
32
  </div>
33
+ <mat-hint *ngIf="helperText" [appFieldWarning]="bFieldMessageVisible$">{{ helperText }}</mat-hint>
33
34
  <mat-error *ngIf="fieldControl.invalid">{{ getErrorMessage() }}</mat-error>
34
35
  </mat-form-field>
35
36
  </div>
@@ -6,6 +6,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
6
6
  import { NgxCurrencyDirective, NgxCurrencyInputMode } from 'ngx-currency';
7
7
 
8
8
  import { FieldBase } from '@pega/angular-sdk-components';
9
+ import { FieldWarningDirective } from '@pega/angular-sdk-components';
9
10
  import { ComponentMapperComponent } from '@pega/angular-sdk-components';
10
11
  import { handleEvent } from '@pega/angular-sdk-components';
11
12
  import { getCurrencyCharacters, getCurrencyOptions } from '@pega/angular-sdk-components';
@@ -23,7 +24,15 @@ interface CurrrencyProps extends PConnFieldProps {
23
24
  selector: 'app-currency',
24
25
  templateUrl: './currency.component.html',
25
26
  styleUrls: ['./currency.component.scss'],
26
- imports: [CommonModule, ReactiveFormsModule, MatFormFieldModule, MatInputModule, NgxCurrencyDirective, forwardRef(() => ComponentMapperComponent)]
27
+ imports: [
28
+ CommonModule,
29
+ ReactiveFormsModule,
30
+ MatFormFieldModule,
31
+ MatInputModule,
32
+ NgxCurrencyDirective,
33
+ FieldWarningDirective,
34
+ forwardRef(() => ComponentMapperComponent)
35
+ ]
27
36
  })
28
37
  export class CurrencyComponent extends FieldBase {
29
38
  configProps$: CurrrencyProps;