@pega/angular-sdk-overrides 23.1.10 → 24.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 (85) hide show
  1. package/lib/designSystemExtension/material-case-summary/material-case-summary.component.html +7 -4
  2. package/lib/designSystemExtension/material-case-summary/material-case-summary.component.ts +3 -1
  3. package/lib/designSystemExtension/material-vertical-tabs/material-vertical-tabs.component.html +1 -1
  4. package/lib/designSystemExtension/operator/operator.component.ts +10 -5
  5. package/lib/field/auto-complete/auto-complete.component.ts +1 -1
  6. package/lib/field/cancel-alert/cancel-alert.component.ts +0 -2
  7. package/lib/field/check-box/check-box.component.html +16 -15
  8. package/lib/field/check-box/check-box.component.scss +14 -1
  9. package/lib/field/check-box/check-box.component.ts +126 -44
  10. package/lib/field/currency/currency.component.html +15 -6
  11. package/lib/field/currency/currency.component.ts +36 -18
  12. package/lib/field/date-time/date-time.component.html +5 -5
  13. package/lib/field/date-time/date-time.component.ts +15 -37
  14. package/lib/field/decimal/decimal.component.html +14 -4
  15. package/lib/field/decimal/decimal.component.ts +42 -5
  16. package/lib/field/dropdown/dropdown.component.ts +0 -3
  17. package/lib/field/group/group.component.html +1 -1
  18. package/lib/field/group/group.component.ts +4 -0
  19. package/lib/field/multiselect/multiselect.component.html +33 -0
  20. package/lib/field/multiselect/multiselect.component.scss +7 -0
  21. package/lib/field/multiselect/multiselect.component.spec.ts +21 -0
  22. package/lib/field/multiselect/multiselect.component.ts +359 -0
  23. package/lib/field/multiselect/utils.ts +209 -0
  24. package/lib/field/percentage/percentage.component.html +15 -4
  25. package/lib/field/percentage/percentage.component.ts +29 -5
  26. package/lib/field/radio-buttons/radio-buttons.component.ts +0 -3
  27. package/lib/field/rich-text/config-ext.json +10 -0
  28. package/lib/field/rich-text/rich-text.component.html +1 -1
  29. package/lib/field/scalar-list/scalar-list.component.ts +2 -1
  30. package/lib/field/text-area/text-area.component.ts +0 -2
  31. package/lib/field/time/time.component.html +1 -0
  32. package/lib/field/time/time.component.ts +2 -0
  33. package/lib/field/url/url.component.html +1 -0
  34. package/lib/field/url/url.component.ts +2 -0
  35. package/lib/field/user-reference/user-reference.component.html +50 -45
  36. package/lib/field/user-reference/user-reference.component.ts +33 -15
  37. package/lib/infra/Containers/base-components/flow-container-base.component.ts +22 -0
  38. package/lib/infra/Containers/base-components/helper.ts +89 -0
  39. package/lib/infra/Containers/flow-container/flow-container.component.html +8 -3
  40. package/lib/infra/Containers/flow-container/flow-container.component.ts +30 -29
  41. package/lib/infra/Containers/modal-view-container/modal-view-container.component.ts +40 -8
  42. package/lib/infra/Containers/view-container/view-container.component.ts +0 -1
  43. package/lib/infra/assignment/assignment.component.ts +38 -39
  44. package/lib/infra/dashboard-filter/dashboard-filter.component.ts +0 -1
  45. package/lib/infra/defer-load/defer-load.component.ts +5 -8
  46. package/lib/infra/multi-step/multi-step.component.html +1 -1
  47. package/lib/infra/multi-step/multi-step.component.scss +1 -0
  48. package/lib/infra/navbar/navbar.component.html +4 -4
  49. package/lib/infra/navbar/navbar.component.ts +6 -3
  50. package/lib/infra/view/view.component.ts +1 -1
  51. package/lib/template/banner-page/config-ext.json +9 -0
  52. package/lib/template/case-summary/case-summary.component.ts +37 -3
  53. package/lib/template/case-view/case-view.component.html +3 -3
  54. package/lib/template/case-view/case-view.component.scss +2 -0
  55. package/lib/template/case-view/case-view.component.ts +0 -6
  56. package/lib/template/data-reference/data-reference.component.ts +1 -3
  57. package/lib/template/dynamic-tabs/dynamic-tabs.component.ts +0 -1
  58. package/lib/template/field-group-template/field-group-template.component.ts +4 -12
  59. package/lib/template/field-value-list/field-value-list.component.html +7 -2
  60. package/lib/template/field-value-list/field-value-list.component.ts +1 -0
  61. package/lib/template/inline-dashboard-page/config-ext.json +9 -0
  62. package/lib/template/list-view/list-view.component.html +6 -6
  63. package/lib/template/list-view/list-view.component.ts +36 -28
  64. package/lib/template/list-view/listViewHelpers.ts +0 -1
  65. package/lib/template/repeating-structures/repeating-structures.component.ts +1 -2
  66. package/lib/template/simple-table/simple-table.component.ts +0 -2
  67. package/lib/template/simple-table-manual/helpers.ts +1 -1
  68. package/lib/template/simple-table-manual/simple-table-manual.component.html +1 -1
  69. package/lib/template/simple-table-manual/simple-table-manual.component.ts +49 -19
  70. package/lib/template/simple-table-select/simple-table-select.component.ts +2 -4
  71. package/lib/template/wss-nav-bar/wss-nav-bar.component.html +1 -1
  72. package/lib/template/wss-nav-bar/wss-nav-bar.component.ts +2 -1
  73. package/lib/widget/attachment/attachment.component.html +50 -26
  74. package/lib/widget/attachment/attachment.component.scss +118 -0
  75. package/lib/widget/attachment/attachment.component.ts +256 -501
  76. package/lib/widget/case-history/case-history.component.ts +1 -2
  77. package/lib/widget/feed-container/feed-container.component.ts +0 -4
  78. package/lib/widget/file-utility/file-utility.component.html +2 -2
  79. package/lib/widget/file-utility/file-utility.component.ts +13 -17
  80. package/lib/widget/list-utility/list-utility.component.html +1 -1
  81. package/lib/widget/quick-create/config-ext.json +9 -0
  82. package/lib/widget/quick-create/quick-create.component.ts +1 -1
  83. package/lib/widget/todo/todo.component.html +6 -5
  84. package/lib/widget/todo/todo.component.ts +7 -6
  85. package/package.json +1 -1
@@ -1,7 +1,9 @@
1
- import { Component, OnInit, Input, NgZone, forwardRef, OnDestroy } from '@angular/core';
1
+ import { Component, OnInit, Input, NgZone, forwardRef, OnDestroy, ViewChild, ElementRef } from '@angular/core';
2
2
  import { FormGroup } from '@angular/forms';
3
3
  import { CommonModule } from '@angular/common';
4
4
  import { MatButtonModule } from '@angular/material/button';
5
+ import { MatMenuModule } from '@angular/material/menu';
6
+ import { MatIconModule } from '@angular/material/icon';
5
7
  import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
6
8
  import download from 'downloadjs';
7
9
  import { AngularPConnectData, AngularPConnectService } from '@pega/angular-sdk-components';
@@ -21,7 +23,7 @@ interface AttachmentProps extends Omit<PConnFieldProps, 'value'> {
21
23
  templateUrl: './attachment.component.html',
22
24
  styleUrls: ['./attachment.component.scss'],
23
25
  standalone: true,
24
- imports: [CommonModule, MatProgressSpinnerModule, MatButtonModule, forwardRef(() => ComponentMapperComponent)]
26
+ imports: [CommonModule, MatProgressSpinnerModule, MatMenuModule, MatIconModule, MatButtonModule, forwardRef(() => ComponentMapperComponent)]
25
27
  })
26
28
  export class AttachmentComponent implements OnInit, OnDestroy {
27
29
  @Input() pConn$: typeof PConnect;
@@ -29,6 +31,7 @@ export class AttachmentComponent implements OnInit, OnDestroy {
29
31
 
30
32
  // For interaction with AngularPConnect
31
33
  angularPConnectData: AngularPConnectData = {};
34
+ @ViewChild('uploader', { static: false }) fileInput: ElementRef;
32
35
 
33
36
  label$ = '';
34
37
  value$: any;
@@ -37,16 +40,8 @@ export class AttachmentComponent implements OnInit, OnDestroy {
37
40
  bDisabled$ = false;
38
41
  bVisible$ = true;
39
42
  bLoading$ = false;
40
- arFiles$: any[] = [];
41
- arFileList$: any[] = [];
42
- removeFileFromList$: any;
43
- arMenuList$: any[] = [];
44
43
  bShowSelector$ = true;
45
- bShowJustDelete$ = false;
46
- att_valueRef: any;
47
44
  att_categoryName: string;
48
- att_id: string;
49
- myFiles: any;
50
45
  fileTemp: any = {};
51
46
  caseID: any;
52
47
  allowMultiple$ = false;
@@ -55,7 +50,17 @@ export class AttachmentComponent implements OnInit, OnDestroy {
55
50
  validateMessage: string | undefined = '';
56
51
  valueRef: string;
57
52
  imagePath$: string;
58
-
53
+ localizedVal = PCore.getLocaleUtils().getLocaleValue;
54
+ localeCategory = 'CosmosFields';
55
+ uploadMultipleFilesLabel = this.localizedVal('file_upload_text_multiple', this.localeCategory);
56
+ uploadSingleFileLabel = this.localizedVal('file_upload_text_one', this.localeCategory);
57
+ filesWithError: any = [];
58
+ files: any = [];
59
+ categoryName: string;
60
+ displayMode: string | undefined;
61
+ srcImg: any;
62
+ deleteIcon: string;
63
+ tempFilesToBeUploaded: any[];
59
64
  constructor(
60
65
  private angularPConnect: AngularPConnectService,
61
66
  private utils: Utils,
@@ -65,13 +70,36 @@ export class AttachmentComponent implements OnInit, OnDestroy {
65
70
  ngOnInit(): void {
66
71
  // // First thing in initialization is registering and subscribing to the AngularPConnect service
67
72
  this.angularPConnectData = this.angularPConnect.registerAndSubscribeComponent(this, this.onStateChange);
68
-
69
- this.removeFileFromList$ = { onClick: this._removeFileFromList.bind(this) };
70
-
71
73
  this.caseID = PCore.getStoreValue('.pyID', 'caseInfo.content', this.pConn$.getContextName());
72
-
73
- // let configProps: any = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps());
74
+ this.srcImg = this.utils.getImageSrc('document-doc', this.utils.getSDKStaticContentUrl());
75
+ this.deleteIcon = this.utils.getImageSrc('trash', this.utils.getSDKStaticContentUrl());
74
76
  this.checkAndUpdate();
77
+ this.getAttachments();
78
+ }
79
+
80
+ getAttachments() {
81
+ let tempUploadedFiles = this.getCurrentAttachmentsList(this.getAttachmentKey(this.valueRef), this.pConn$.getContextName());
82
+ tempUploadedFiles = tempUploadedFiles.filter(f => f.label === this.valueRef && f.delete !== true);
83
+ this.files?.map(f => {
84
+ return f.responseProps?.pzInsKey && !f.responseProps.pzInsKey.includes('temp')
85
+ ? {
86
+ ...f,
87
+ props: {
88
+ ...f.props,
89
+ onDelete: () => this.deleteFile(f)
90
+ }
91
+ }
92
+ : { ...f };
93
+ });
94
+ this.files = [...this.files, ...tempUploadedFiles];
95
+ PCore.getPubSubUtils().subscribe(
96
+ PCore.getConstants().PUB_SUB_EVENTS.CASE_EVENTS.ASSIGNMENT_SUBMISSION,
97
+ this.resetAttachmentStoredState.bind(this),
98
+ this.caseID
99
+ );
100
+ return () => {
101
+ PCore.getPubSubUtils().unsubscribe(PCore.getConstants().PUB_SUB_EVENTS.CASE_EVENTS.ASSIGNMENT_SUBMISSION, this.caseID);
102
+ };
75
103
  }
76
104
 
77
105
  checkAndUpdate() {
@@ -85,77 +113,15 @@ export class AttachmentComponent implements OnInit, OnDestroy {
85
113
  }
86
114
  }
87
115
 
88
- ngOnDestroy(): void {
89
- if (this.angularPConnectData.unsubscribeFn) {
90
- this.angularPConnectData.unsubscribeFn();
91
- }
92
-
93
- PCore.getPubSubUtils().unsubscribe(PCore.getConstants().PUB_SUB_EVENTS.CASE_EVENTS.ASSIGNMENT_SUBMISSION, this.caseID);
94
- }
95
-
96
116
  // Callback passed when subscribing to store change
97
117
  onStateChange() {
98
118
  this.checkAndUpdate();
99
119
  }
100
120
 
101
- updateAttachmentsInfo() {
102
- // @ts-ignore - Property 'attachmentsInfo' does not exist on type 'C11nEnv'
103
- if (!this.pConn$.attachmentsInfo) {
104
- // @ts-ignore - Property 'attachmentsInfo' does not exist on type 'C11nEnv'
105
- this.pConn$.attachmentsInfo = {
106
- type: 'File',
107
- attachmentFieldName: this.att_valueRef,
108
- category: this.att_categoryName
109
- };
110
- }
111
- }
112
-
113
- processFile(file, i) {
114
- file.props.type = file.responseProps.pyMimeFileExtension;
115
- file.props.mimeType = file.responseProps.pyMimeFileExtension;
116
- file.props.ID = file.responseProps.pzInsKey;
117
-
118
- const arMenuList = [
119
- {
120
- icon: 'download',
121
- text: this.pConn$.getLocalizedValue('Download', '', ''),
122
- onClick: () => this._downloadFileFromList(this.value$.pxResults[i])
123
- },
124
- {
125
- icon: 'trash',
126
- text: this.pConn$.getLocalizedValue('Delete', '', ''),
127
- onClick: () => this._removeFileFromList(this.arFileList$[i])
128
- }
129
- ];
130
-
131
- const arFilesAttachmentIDs: any = [];
132
- this.arFileList$.forEach(arFile => {
133
- arFilesAttachmentIDs.push(arFile.id);
134
- });
135
-
136
- if (!arFilesAttachmentIDs.includes(file.props.ID)) {
137
- this.arFileList$.push(
138
- this.getNewListUtilityItemProps({
139
- att: file.props,
140
- downloadFile: null,
141
- cancelFile: null,
142
- deleteFile: null,
143
- removeFile: null
144
- })
145
- );
146
- }
147
-
148
- this.arFileList$[i].actions = arMenuList;
149
- this.arFileList$[i].noDeleteIcon = true;
150
-
151
- this.bShowSelector$ = false;
152
- }
153
-
154
121
  updateSelf() {
155
122
  const configProps: AttachmentProps = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps()) as AttachmentProps;
156
123
  const stateProps: any = this.pConn$.getStateProps();
157
-
158
- const { value, label, extensions } = configProps;
124
+ const { value, label, extensions, displayMode } = configProps;
159
125
 
160
126
  if (configProps.required != null) {
161
127
  this.bRequired$ = this.utils.getBooleanValue(configProps.required);
@@ -177,114 +143,62 @@ export class AttachmentComponent implements OnInit, OnDestroy {
177
143
  this.allowMultiple$ = this.utils.getBooleanValue(configProps.allowMultiple);
178
144
  }
179
145
 
180
- this.bShowJustDelete$ = true;
181
146
  this.label$ = label;
182
147
  this.value$ = value;
183
148
  this.status = stateProps.status;
149
+
184
150
  this.validateMessage = this.angularPConnectData.validateMessage;
185
151
  this.extensions$ = extensions;
186
152
  this.valueRef = (this.pConn$.getStateProps() as any).value;
187
153
  this.valueRef = this.valueRef.startsWith('.') ? this.valueRef.substring(1) : this.valueRef;
188
-
154
+ this.displayMode = displayMode;
189
155
  /* this is a temporary fix because required is supposed to be passed as a boolean and NOT as a string */
190
156
  let { required, disabled } = configProps;
191
157
  [required, disabled] = [required, disabled].map(prop => prop === true || (typeof prop === 'string' && prop === 'true'));
192
158
 
193
- this.att_categoryName = '';
159
+ this.categoryName = '';
194
160
  if (value && value.pyCategoryName) {
195
- this.att_categoryName = value.pyCategoryName;
161
+ this.categoryName = value.pyCategoryName;
196
162
  }
197
163
 
198
- this.att_valueRef = (this.pConn$.getStateProps() as any).value;
199
- this.att_valueRef = this.att_valueRef.indexOf('.') === 0 ? this.att_valueRef.substring(1) : this.att_valueRef;
164
+ if (value?.pxResults && +value.pyCount > 0) {
165
+ this.files = value.pxResults.map(f => this.buildFilePropsFromResponse(f));
166
+ }
200
167
 
201
168
  this.updateAttachments();
202
169
  }
203
170
 
204
- updateAttachments() {
205
- const attachmentsFromServer = this.value$ && this.value$.pxResults && +this.value$.pyCount > 0;
206
-
207
- if (attachmentsFromServer) {
208
- this.updateAttachmentsFromServer();
209
- } else {
210
- // Get the attachments from the Redux
211
- this.myFiles = this.getCurrentAttachmentsList(this.getAttachmentKey(this.att_valueRef), this.pConn$.getContextName());
212
-
213
- if (this.myFiles?.length && this.arFiles$.length === 0) {
214
- this.arFileList$ = this.myFiles.map(att => {
215
- return this.getNewListUtilityItemProps({
216
- att,
217
- downloadFile: null,
218
- cancelFile: null,
219
- deleteFile: null,
220
- removeFile: null
221
- });
222
- });
171
+ buildFilePropsFromResponse(respObj) {
172
+ return {
173
+ props: {
174
+ meta: `${respObj.pyCategoryName}, ${respObj.pxCreateOperator}`,
175
+ name: respObj.pyAttachName,
176
+ icon: this.utils.getIconFromFileType(respObj.pyMimeFileExtension)
177
+ },
178
+ responseProps: {
179
+ ...respObj
223
180
  }
224
-
225
- const myFilesAttachmentIDs: any = [];
226
- this.myFiles.forEach(myFile => {
227
- myFilesAttachmentIDs.push(myFile.ID);
228
- });
229
-
230
- this.arFiles$.forEach(arFile => {
231
- if (!myFilesAttachmentIDs.includes(arFile.ID)) {
232
- this.myFiles = [...this.myFiles, arFile];
233
- }
234
- });
235
- }
236
-
237
- this.bShowJustDelete$ = true;
238
- this.bShowSelector$ = !(this.arFileList$?.length > 0) || this.allowMultiple$;
239
-
240
- if (this.arFileList$.length > 0) {
241
- this.CheckForInvalidAttachment();
242
- }
243
- PCore.getPubSubUtils().subscribe(
244
- PCore.getConstants().PUB_SUB_EVENTS.CASE_EVENTS.ASSIGNMENT_SUBMISSION,
245
- this.resetAttachmentStoredState.bind(this),
246
- this.caseID
247
- );
181
+ };
248
182
  }
249
183
 
250
- updateAttachmentsFromServer() {
251
- this.value$.pxResults.forEach((attachment, i) => {
252
- const file: any = this.buildFilePropsFromResponse(attachment);
253
- if (file.responseProps) {
254
- this.updateAttachmentsInfo();
255
- if (file.responseProps.pzInsKey && !file.responseProps.pzInsKey.includes('temp')) {
256
- this.processFile(file, i);
257
- }
258
- if (file) {
259
- const currentAttachmentList = this.getCurrentAttachmentsList(this.getAttachmentKey(this.att_valueRef), this.pConn$.getContextName());
260
- const index = currentAttachmentList.findIndex(element => element.props.ID === file.props.ID);
261
- let tempFiles: any = [];
262
- if (index < 0) {
263
- tempFiles = [file];
264
- }
265
-
266
- PCore.getStateUtils().updateState(
267
- this.pConn$.getContextName(),
268
- this.getAttachmentKey(this.att_valueRef),
269
- [...currentAttachmentList, ...tempFiles],
270
- {
271
- pageReference: 'context_data',
272
- isArrayDeepMerge: false
273
- }
274
- );
275
- }
276
- }
277
- });
184
+ updateAttachments() {
185
+ if (this.files.length > 0 && this.displayMode !== 'DISPLAY_ONLY') {
186
+ const currentAttachmentList = this.getCurrentAttachmentsList(this.getAttachmentKey(this.valueRef), this.pConn$.getContextName());
187
+ // block duplicate files to redux store when added 1 after another to prevent multiple duplicates being added to the case on submit
188
+ const tempFiles = this.files.filter(f => currentAttachmentList.findIndex(fr => fr.ID === f.ID) === -1 && !f.inProgress && f.responseProps);
189
+ const updatedAttList = [...currentAttachmentList, ...tempFiles];
190
+ this.updateAttachmentState(this.pConn$, this.getAttachmentKey(this.valueRef), updatedAttList);
191
+ }
278
192
  }
279
193
 
280
194
  resetAttachmentStoredState() {
281
- PCore.getStateUtils().updateState(this.pConn$.getContextName(), this.getAttachmentKey(this.att_valueRef), undefined, {
195
+ PCore.getStateUtils().updateState(this.pConn$?.getContextName(), this.getAttachmentKey(this.valueRef), undefined, {
282
196
  pageReference: 'context_data',
283
197
  isArrayDeepMerge: false
284
198
  });
285
199
  }
286
200
 
287
- _downloadFileFromList(fileObj: any) {
201
+ downloadFile(fileObj: any) {
288
202
  PCore.getAttachmentUtils()
289
203
  // @ts-ignore - 3rd parameter "responseEncoding" should be optional
290
204
  .downloadAttachment(fileObj.pzInsKey, this.pConn$.getContextName())
@@ -304,146 +218,198 @@ export class AttachmentComponent implements OnInit, OnDestroy {
304
218
 
305
219
  getAttachmentKey = (name = '') => (name ? `attachmentsList.${name}` : 'attachmentsList');
306
220
 
307
- CheckForInvalidAttachment() {
308
- let isValid = true;
309
- this.arFileList$.forEach(file => {
310
- if (file.secondary.error) {
311
- isValid = false;
312
- }
313
- });
314
- if (isValid) {
315
- // @ts-ignore
316
- PCore.getMessageManager().clearMessages({
317
- type: PCore.getConstants().MESSAGES.MESSAGES_TYPE_ERROR,
318
- property: (this.pConn$.getStateProps() as any).value,
319
- pageReference: this.pConn$.getPageReference(),
320
- context: this.pConn$.getContextName()
321
- });
322
- }
221
+ getCurrentAttachmentsList(key, context) {
222
+ return PCore.getStoreValue(`.${key}`, 'context_data', context) || [];
323
223
  }
324
224
 
325
- _removeFileFromList(item: any) {
326
- const fileListIndex = this.arFileList$.findIndex(element => element?.id === item?.id);
327
- const fileIndex = this.arFiles$.findIndex(element => element?.ID === item?.id);
225
+ validateMaxSize(fileObj, maxSizeInMB): boolean {
226
+ const fileSize = (fileObj.size / 1048576).toFixed(2);
227
+ return parseFloat(fileSize) < parseFloat(maxSizeInMB);
228
+ }
229
+
230
+ validateFileExtension = (fileObj, allowedExtensions) => {
231
+ if (!allowedExtensions) {
232
+ return true;
233
+ }
234
+ const allowedExtensionList = allowedExtensions
235
+ .toLowerCase()
236
+ .split(',')
237
+ .map(item => item.replaceAll('.', '').trim());
238
+ const extension = fileObj.name.split('.').pop().toLowerCase();
239
+ return allowedExtensionList.includes(extension);
240
+ };
241
+
242
+ updateAttachmentState(pConn, key, attachments) {
243
+ PCore.getStateUtils().updateState(this.pConn$.getContextName(), key, attachments, {
244
+ pageReference: 'context_data',
245
+ isArrayDeepMerge: false
246
+ });
247
+ }
328
248
 
329
- const attachmentsList = [];
330
- let currentAttachmentList = this.getCurrentAttachmentsList(this.getAttachmentKey(this.att_valueRef), this.pConn$.getContextName());
331
- if (this.value$ && this.value$.pxResults && +this.value$.pyCount > 0 && item.actions) {
332
- const updatedAttachments = currentAttachmentList.map(attachment => {
333
- if (attachment?.ID === this.arFileList$[fileListIndex].id || attachment?.props?.ID === this.arFileList$[fileListIndex].id) {
334
- return { ...attachment, delete: true, label: this.valueRef };
249
+ deleteFile(file) {
250
+ const attachmentsList: any[] = [];
251
+ let currentAttachmentList = this.getCurrentAttachmentsList(this.getAttachmentKey(this.valueRef), this.pConn$.getContextName());
252
+
253
+ // If file to be deleted is the one added in previous stage i.e. for which a file instance is created in server
254
+ // no need to filter currentAttachmentList as we will get another entry of file in redux with delete & label
255
+ // eslint-disable-next-line no-unsafe-optional-chaining
256
+ if (this.value$ && this.value$?.pxResults && +this.value$?.pyCount > 0 && file.responseProps && file?.responseProps?.pzInsKey !== 'temp') {
257
+ const updatedAttachments = this.files.map(f => {
258
+ if (f.responseProps && f.responseProps.pzInsKey === file.responseProps.pzInsKey) {
259
+ return { ...f, delete: true, label: this.valueRef };
335
260
  }
336
- return attachment;
261
+ return f;
337
262
  });
338
263
 
339
264
  // updating the redux store to help form-handler in passing the data to delete the file from server
340
- PCore.getStateUtils().updateState(this.pConn$.getContextName(), this.getAttachmentKey(this.att_valueRef), updatedAttachments, {
341
- pageReference: 'context_data',
342
- isArrayDeepMerge: false
343
- });
344
- } else {
345
- currentAttachmentList = currentAttachmentList.filter(f => f.ID !== item.id);
346
- PCore.getStateUtils().updateState(
347
- this.pConn$.getContextName(),
348
- this.getAttachmentKey(this.att_valueRef),
349
- [...currentAttachmentList, ...attachmentsList],
350
- {
351
- pageReference: 'context_data',
352
- isArrayDeepMerge: false
353
- }
265
+ this.updateAttachmentState(this.pConn$, this.getAttachmentKey(this.valueRef), [...updatedAttachments]);
266
+ const newlyAddedFiles = this.files.filter(f => !!f.ID);
267
+ const filesPostDelete = this.files.filter(
268
+ f => f.responseProps?.pzInsKey !== 'temp' && f.responseProps?.pzInsKey !== file.responseProps?.pzInsKey
354
269
  );
270
+ this.files = [...filesPostDelete, ...newlyAddedFiles];
271
+ } // if the file being deleted is the added in this stage i.e. whose data is not yet created in server
272
+ else {
273
+ // filter newly added files in this stage, later the updated current stage files will be added to redux once files state is updated
274
+ currentAttachmentList = currentAttachmentList.filter(f => f.ID !== file.ID);
275
+ this.files = this.files.filter(f => f.ID !== file.ID);
276
+
277
+ this.updateAttachmentState(this.pConn$, this.getAttachmentKey(this.valueRef), [...currentAttachmentList, ...attachmentsList]);
278
+ if (file.inProgress) {
279
+ // @ts-ignore - 3rd parameter "responseEncoding" should be optional
280
+ PCore.getAttachmentUtils().cancelRequest(file.ID, pConn.getContextName());
281
+ }
355
282
  }
356
283
 
357
- if (fileListIndex > -1) {
358
- this.arFileList$.splice(fileListIndex, 1);
284
+ this.filesWithError = this.filesWithError?.filter(f => f.ID !== file.ID);
285
+ if (this.filesWithError.length === 0) {
286
+ this.clearFieldErrorMessages();
359
287
  }
360
- if (fileIndex > -1) {
361
- this.arFiles$.splice(fileIndex, 1);
362
- }
363
-
364
- this.CheckForInvalidAttachment();
288
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
289
+ this.fileInput && this.fileInput.nativeElement.value ? null : '';
290
+ }
365
291
 
366
- this.bShowSelector$ = !(this.arFileList$?.length > 0) || this.allowMultiple$;
292
+ onFileAdded(event) {
293
+ let addedFiles = Array.from(event.target.files);
294
+ addedFiles = this.allowMultiple$ ? addedFiles : [addedFiles[0]];
295
+ const maxAttachmentSize = PCore.getEnvironmentInfo().getMaxAttachmentSize() || '5';
296
+ this.tempFilesToBeUploaded = [
297
+ ...addedFiles.map((f: any, index) => {
298
+ f.ID = `${new Date().getTime()}I${index}`;
299
+ f.inProgress = true;
300
+ f.props = {
301
+ type: f.type,
302
+ name: f.name,
303
+ icon: this.utils.getIconFromFileType(f.type),
304
+ onDelete: () => this.deleteFile(f)
305
+ };
306
+ if (!this.validateMaxSize(f, maxAttachmentSize)) {
307
+ f.props.error = true;
308
+ f.inProgress = false;
309
+ f.props.meta = this.pConn$.getLocalizedValue(`File is too big. Max allowed size is ${maxAttachmentSize}MB.`, '', '');
310
+ } else if (!this.validateFileExtension(f, this.extensions$)) {
311
+ f.props.error = true;
312
+ f.inProgress = false;
313
+ f.props.meta = `${this.pConn$.getLocalizedValue(
314
+ 'File has invalid extension. Allowed extensions are:',
315
+ '',
316
+ ''
317
+ )} ${this.extensions$.replaceAll('.', '')}`;
318
+ }
319
+ if (f.props.error) {
320
+ const fieldName = (this.pConn$.getStateProps() as any).value;
321
+ const context = this.pConn$.getContextName();
322
+ PCore.getMessageManager().addMessages({
323
+ messages: [
324
+ {
325
+ type: 'error',
326
+ // @ts-ignore - Type '{ type: string; message: string; }' is not assignable to type 'MessagesConfigObject'.
327
+ message: pConn.getLocalizedValue('Error with one or more files', '', '')
328
+ }
329
+ ],
330
+ property: fieldName,
331
+ pageReference: this.pConn$.getPageReference(),
332
+ context
333
+ });
334
+ }
335
+ return f;
336
+ })
337
+ ];
338
+ const tempFilesWithError = this.tempFilesToBeUploaded.filter(f => f.props.error);
339
+ if (tempFilesWithError.length > 0) {
340
+ this.filesWithError = tempFilesWithError;
341
+ }
342
+ if (!this.allowMultiple$) {
343
+ this.files = [...this.tempFilesToBeUploaded];
344
+ } else {
345
+ this.files = [...this.files, ...this.tempFilesToBeUploaded];
346
+ }
347
+ this.uploadFiles();
367
348
  }
368
349
 
369
- getCurrentAttachmentsList(key, context) {
370
- return PCore.getStoreValue(`.${key}`, 'context_data', context) || [];
350
+ clearFieldErrorMessages() {
351
+ const fieldName = (this.pConn$.getStateProps() as any).value;
352
+ const context = this.pConn$.getContextName();
353
+ PCore.getMessageManager().clearMessages({
354
+ type: PCore.getConstants().MESSAGES.MESSAGES_TYPE_ERROR,
355
+ property: fieldName,
356
+ pageReference: this.pConn$.getPageReference(),
357
+ context
358
+ });
371
359
  }
372
360
 
373
- errorHandler(isFetchCanceled, file) {
361
+ onUploadProgress() {}
362
+
363
+ errorHandler(isFetchCanceled, attachedFile) {
374
364
  return error => {
375
365
  if (!isFetchCanceled(error)) {
376
366
  let uploadFailMsg = this.pConn$.getLocalizedValue('Something went wrong', '', '');
377
367
  if (error.response && error.response.data && error.response.data.errorDetails) {
378
368
  uploadFailMsg = this.pConn$.getLocalizedValue(error.response.data.errorDetails[0].localizedValue, '', '');
379
369
  }
380
- for (const myFile of this.myFiles) {
381
- if (myFile.ID === file.ID) {
382
- myFile.meta = uploadFailMsg;
383
- myFile.error = true;
384
- myFile.fileName = this.pConn$.getLocalizedValue('Unable to upload file', '', '');
385
- }
386
- }
387
- this.bShowSelector$ = false;
388
- this.arFileList$ = this.myFiles.map(att => {
389
- if (att.id) {
390
- return att;
391
- }
392
- return this.getNewListUtilityItemProps({
393
- att,
394
- downloadFile: null,
395
- cancelFile: null,
396
- deleteFile: null,
397
- removeFile: null
398
- });
399
- });
400
370
 
401
- PCore.getMessageManager().addMessages({
402
- // @ts-ignore
403
- messages: [
404
- {
405
- type: 'error',
406
- message: this.pConn$.getLocalizedValue('Error with one or more files', '', '')
407
- }
408
- ],
409
- property: (this.pConn$.getStateProps() as any).value,
410
- pageReference: this.pConn$.getPageReference(),
411
- context: this.pConn$.getContextName()
371
+ this.files.map(f => {
372
+ if (f.ID === attachedFile.ID) {
373
+ f.props.meta = uploadFailMsg;
374
+ f.props.error = true;
375
+ f.props.onDelete = () => this.deleteFile(f);
376
+ f.props.icon = this.utils.getIconFromFileType(f.type);
377
+ f.props.name = this.pConn$.getLocalizedValue('Unable to upload file', '', '');
378
+ f.inProgress = false;
379
+ const fieldName = (this.pConn$.getStateProps() as any).value;
380
+ const context = this.pConn$.getContextName();
381
+ // set errors to property to block submit even on errors in file upload
382
+ PCore.getMessageManager().addMessages({
383
+ messages: [
384
+ {
385
+ type: 'error',
386
+ // @ts-ignore - Type '{ type: string; message: string; }' is not assignable to type 'MessagesConfigObject'.
387
+ message: this.pConn$.getLocalizedValue('Error with one or more files', '', '')
388
+ }
389
+ ],
390
+ property: fieldName,
391
+ pageReference: this.pConn$.getPageReference(),
392
+ context
393
+ });
394
+ delete f.props.progress;
395
+ }
396
+ return f;
412
397
  });
413
-
414
- this.bShowJustDelete$ = true;
415
- this.bLoading$ = false;
416
398
  }
417
399
  throw error;
418
400
  };
419
401
  }
420
402
 
421
- uploadMyFiles(event: any) {
422
- let arrAttachmentFiles: any = [];
423
- if (this.arFiles$.length) {
424
- arrAttachmentFiles = this.arFiles$;
425
- } else {
426
- arrAttachmentFiles = this.arFileList$;
427
- }
428
- this.arFiles$ = [...arrAttachmentFiles, ...this.getFiles(event.target.files)];
429
-
430
- // convert FileList to an array
431
- this.myFiles = Array.from(this.arFiles$);
432
-
433
- this.bLoading$ = true;
434
-
435
- const filesToBeUploaded = this.myFiles
403
+ uploadFiles() {
404
+ const filesToBeUploaded = this.files
436
405
  .filter(e => {
437
- if (e.id) {
438
- return false;
439
- }
440
- const isFileUploaded = e && e.progress === 100;
441
- const fileHasError = e && e.error;
406
+ const isFileUploaded = e.props && e.props.progress === 100;
407
+ const fileHasError = e.props && e.props.error;
442
408
  const isFileUploadedinLastStep = e.responseProps && e.responseProps.pzInsKey;
443
409
  return !isFileUploaded && !fileHasError && !isFileUploadedinLastStep;
444
410
  })
445
411
  .map(f =>
446
- PCore.getAttachmentUtils().uploadAttachment(
412
+ window.PCore.getAttachmentUtils().uploadAttachment(
447
413
  f,
448
414
  () => {
449
415
  this.onUploadProgress();
@@ -454,252 +420,41 @@ export class AttachmentComponent implements OnInit, OnDestroy {
454
420
  this.pConn$.getContextName()
455
421
  )
456
422
  );
457
-
458
423
  Promise.allSettled(filesToBeUploaded)
459
424
  .then((fileResponses: any) => {
460
- this.handleFileUploadSuccess(fileResponses);
461
- })
462
- .catch(error => {
463
- console.log(error);
464
- this.handleFileUploadFailure();
465
- });
466
- }
467
-
468
- handleFileUploadSuccess(fileResponses) {
469
- const successFileResponses = fileResponses.filter(fr => fr.status !== 'rejected'); // in case of deleting an in progress file, promise gets cancelled but still enters then block
470
- let reqObj;
471
- if (successFileResponses.length > 0) {
472
- const tempFilesUploaded = [...this.arFiles$.filter(file => !file.id)];
473
- let newAttachments: any = [];
474
- tempFilesUploaded.forEach(fileRes => {
475
- const index = successFileResponses.findIndex((fr: any) => fr.value.clientFileID === fileRes.ID);
476
- if (index >= 0) {
477
- reqObj = {
478
- type: 'File',
479
- label: this.att_valueRef,
480
- category: this.att_categoryName,
481
- handle: successFileResponses[index].value.ID,
482
- ID: fileRes.ID,
483
- name: fileRes.name
484
- };
485
- newAttachments = [...newAttachments, reqObj];
486
- }
487
- });
488
- const currentAttachmentList = this.getCurrentAttachmentsList(this.getAttachmentKey(this.att_valueRef), this.pConn$.getContextName()).filter(
489
- f => f.label !== this.att_valueRef
490
- );
491
- PCore.getStateUtils().updateState(
492
- this.pConn$.getContextName(),
493
- this.getAttachmentKey(this.att_valueRef),
494
- [...currentAttachmentList, ...newAttachments],
495
- {
496
- pageReference: 'context_data',
497
- isArrayDeepMerge: false
498
- }
499
- );
500
- this.arFiles$ = tempFilesUploaded;
501
-
502
- this.ngZone.run(() => {
503
- this.bShowSelector$ = this.allowMultiple$;
504
- this.arFiles$.forEach(file => {
505
- if (!file.error) {
506
- file.meta = this.pConn$.getLocalizedValue('File uploaded successfully', '', '');
507
- }
508
- });
509
- this.arFileList$ = this.myFiles.map(att => {
510
- if (att.id) {
511
- return att;
512
- }
513
- return this.getNewListUtilityItemProps({
514
- att,
515
- downloadFile: null,
516
- cancelFile: null,
517
- deleteFile: null,
518
- removeFile: null
425
+ fileResponses = fileResponses.filter(fr => fr.status !== 'rejected'); // in case of deleting an in progress file, promise gets cancelled but still enters then block
426
+ if (fileResponses.length > 0) {
427
+ this.files.forEach(f => {
428
+ const index = fileResponses.findIndex((fr: any) => fr.value.clientFileID === f.ID);
429
+ if (index >= 0) {
430
+ f.props.meta = this.pConn$.getLocalizedValue('Uploaded successfully', '', '');
431
+ f.props.progress = 100;
432
+ f.inProgress = false;
433
+ f.handle = fileResponses[index].value.ID;
434
+ f.label = this.valueRef;
435
+ f.category = this.categoryName;
436
+ f.responseProps = {
437
+ pzInsKey: 'temp',
438
+ pyAttachName: f.props.name
439
+ };
440
+ }
519
441
  });
520
- });
521
-
522
- this.CheckForInvalidAttachment();
523
-
524
- this.bShowJustDelete$ = true;
525
- this.bLoading$ = false;
526
- });
527
- }
528
- }
529
-
530
- handleFileUploadFailure() {
531
- this.bShowJustDelete$ = true;
532
- this.bLoading$ = false;
533
- this.bShowSelector$ = this.allowMultiple$;
534
- this.myFiles.forEach(file => {
535
- if (file?.secondary?.error) {
536
- file.meta = 'File uploaded failed';
537
- }
538
- });
539
-
540
- this.arFileList$ = this.myFiles.map(att => {
541
- return this.getNewListUtilityItemProps({
542
- att,
543
- downloadFile: null,
544
- cancelFile: null,
545
- deleteFile: null,
546
- removeFile: null
547
- });
548
- });
549
-
550
- this.bShowJustDelete$ = true;
551
- this.bLoading$ = false;
552
- }
553
-
554
- getNewListUtilityItemProps = ({ att, cancelFile, downloadFile, deleteFile, removeFile }) => {
555
- let actions;
556
- let isDownloadable = false;
557
-
558
- if (att.progress && att.progress !== 100) {
559
- actions = [
560
- {
561
- id: `Cancel-${att.ID}`,
562
- text: this.pConn$.getLocalizedValue('Cancel', '', ''),
563
- icon: 'times',
564
- onClick: cancelFile
565
- }
566
- ];
567
- } else if (att.links) {
568
- const isFile = att.type === 'FILE';
569
- const ID = att.ID.replace(/\s/gi, '');
570
- const actionsMap = new Map([
571
- [
572
- 'download',
573
- {
574
- id: `download-${ID}`,
575
- text: isFile ? this.pConn$.getLocalizedValue('Download', '', '') : this.pConn$.getLocalizedValue('Open', '', ''),
576
- icon: isFile ? 'download' : 'open',
577
- onClick: downloadFile
578
- }
579
- ],
580
- [
581
- 'delete',
582
- {
583
- id: `Delete-${ID}`,
584
- text: this.pConn$.getLocalizedValue('Delete', '', ''),
585
- icon: 'trash',
586
- onClick: deleteFile
442
+ this.updateAttachments();
443
+ if (this.filesWithError?.length === 0) {
444
+ this.clearFieldErrorMessages();
587
445
  }
588
- ]
589
- ]);
590
- actions = [];
591
- actionsMap.forEach((action, actionKey) => {
592
- if (att.links[actionKey]) {
593
- actions.push(action);
594
446
  }
447
+ })
448
+ .catch(error => {
449
+ console.log(error);
595
450
  });
596
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
597
- isDownloadable = att.links.download;
598
- } else if (att.error) {
599
- actions = [
600
- {
601
- id: `Remove-${att.ID}`,
602
- text: this.pConn$.getLocalizedValue('Remove', '', ''),
603
- icon: 'trash',
604
- onClick: removeFile
605
- }
606
- ];
607
- }
608
-
609
- return {
610
- id: att.ID,
611
- visual: {
612
- icon: this.utils.getIconForAttachment(att),
613
- progress: att.progress == 100 ? undefined : att.progress
614
- },
615
- primary: {
616
- type: att.type,
617
- name: att.error ? att.fileName : att.name,
618
- icon: 'trash',
619
- click: removeFile
620
- },
621
- secondary: {
622
- text: att.meta,
623
- error: att.error
624
- },
625
- actions
626
- };
627
- };
628
-
629
- onUploadProgress() {}
630
-
631
- getFiles(arFiles: any[]): any[] {
632
- const files = this.allowMultiple$ ? arFiles : [arFiles[0]];
633
- return this.setNewFiles(files);
634
- }
635
-
636
- setNewFiles(arFiles) {
637
- let index = 0;
638
- const maxAttachmentSize = PCore.getEnvironmentInfo().getMaxAttachmentSize() || 5;
639
- for (const file of arFiles) {
640
- file.mimeType = file.type;
641
- file.icon = this.utils.getIconFromFileType(file.type);
642
- file.ID = `${new Date().getTime()}I${index}`;
643
-
644
- if (!this.validateMaxSize(file, maxAttachmentSize)) {
645
- file.error = true;
646
- file.meta = this.pConn$.getLocalizedValue('File is too big. Max allowed size is 5MB.', '', '');
647
- } else if (!this.validateFileExtension(file, this.extensions$)) {
648
- file.error = true;
649
- file.meta = `${this.pConn$.getLocalizedValue('File has invalid extension. Allowed extensions are:', '', '')} ${this.extensions$.replaceAll(
650
- '.',
651
- ''
652
- )}`;
653
- }
654
- if (file.error) {
655
- const fieldName = (this.pConn$.getStateProps() as any).value;
656
- const context = this.pConn$.getContextName();
657
- PCore.getMessageManager().addMessages({
658
- // @ts-ignore
659
- messages: [
660
- {
661
- type: 'error',
662
- message: this.pConn$.getLocalizedValue('Error with one or more files', '', '')
663
- }
664
- ],
665
- property: fieldName,
666
- pageReference: this.pConn$.getPageReference(),
667
- context
668
- });
669
- }
670
- index++;
671
- }
672
-
673
- return [...arFiles];
674
451
  }
675
452
 
676
- validateMaxSize(fileObj, maxSizeInMB): boolean {
677
- const fileSize = (fileObj.size / 1048576).toFixed(2);
678
- return parseFloat(fileSize) < parseFloat(maxSizeInMB);
679
- }
680
-
681
- validateFileExtension = (fileObj, allowedExtensions) => {
682
- if (!allowedExtensions) {
683
- return true;
453
+ ngOnDestroy(): void {
454
+ if (this.angularPConnectData.unsubscribeFn) {
455
+ this.angularPConnectData.unsubscribeFn();
684
456
  }
685
- const allowedExtensionList = allowedExtensions
686
- .toLowerCase()
687
- .split(',')
688
- .map(item => item.replaceAll('.', '').trim());
689
- const extension = fileObj.name.split('.').pop().toLowerCase();
690
- return allowedExtensionList.includes(extension);
691
- };
692
457
 
693
- buildFilePropsFromResponse(respObj) {
694
- return {
695
- props: {
696
- meta: `${respObj.pyCategoryName}, ${respObj.pxCreateOperator}`,
697
- name: respObj.pyAttachName,
698
- icon: this.utils.getIconFromFileType(respObj.pyMimeFileExtension)
699
- },
700
- responseProps: {
701
- ...respObj
702
- }
703
- };
458
+ PCore.getPubSubUtils().unsubscribe(PCore.getConstants().PUB_SUB_EVENTS.CASE_EVENTS.ASSIGNMENT_SUBMISSION, this.caseID);
704
459
  }
705
460
  }