@pega/angular-sdk-overrides 0.24.1 → 0.24.3

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