@pega/react-sdk-components 0.25.5 → 0.25.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/lib/components/designSystemExtension/CaseSummaryFields/CaseSummaryFields.js +1 -1
  2. package/lib/components/designSystemExtension/CaseSummaryFields/CaseSummaryFields.js.map +1 -1
  3. package/lib/components/field/RadioButtons/RadioButtons.js +1 -1
  4. package/lib/components/field/RadioButtons/RadioButtons.js.map +1 -1
  5. package/lib/components/field/SelectableCard/utils.d.ts +0 -1
  6. package/lib/components/field/SelectableCard/utils.d.ts.map +1 -1
  7. package/lib/components/field/SelectableCard/utils.js +0 -3
  8. package/lib/components/field/SelectableCard/utils.js.map +1 -1
  9. package/lib/components/field/SemanticLink/SemanticLink.js +1 -1
  10. package/lib/components/field/SemanticLink/SemanticLink.js.map +1 -1
  11. package/lib/components/helpers/attachmentShared.d.ts +2 -0
  12. package/lib/components/helpers/attachmentShared.d.ts.map +1 -0
  13. package/lib/components/helpers/attachmentShared.js +6 -0
  14. package/lib/components/helpers/attachmentShared.js.map +1 -0
  15. package/lib/components/helpers/formatters/Currency.d.ts +1 -0
  16. package/lib/components/helpers/formatters/Currency.d.ts.map +1 -1
  17. package/lib/components/helpers/formatters/Currency.js +8 -4
  18. package/lib/components/helpers/formatters/Currency.js.map +1 -1
  19. package/lib/components/helpers/formatters/index.d.ts +1 -0
  20. package/lib/components/helpers/formatters/index.d.ts.map +1 -1
  21. package/lib/components/helpers/object-utils.d.ts +9 -0
  22. package/lib/components/helpers/object-utils.d.ts.map +1 -0
  23. package/lib/components/helpers/object-utils.js +11 -0
  24. package/lib/components/helpers/object-utils.js.map +1 -0
  25. package/lib/components/infra/Containers/ModalViewContainer/ModalViewContainer.d.ts.map +1 -1
  26. package/lib/components/infra/Containers/ModalViewContainer/ModalViewContainer.js +2 -1
  27. package/lib/components/infra/Containers/ModalViewContainer/ModalViewContainer.js.map +1 -1
  28. package/lib/components/infra/MultiStep/MultiStep.css +0 -2
  29. package/lib/components/template/AppShell/AppShell.d.ts.map +1 -1
  30. package/lib/components/template/AppShell/AppShell.js +4 -0
  31. package/lib/components/template/AppShell/AppShell.js.map +1 -1
  32. package/lib/components/template/CaseSummary/CaseSummary.d.ts.map +1 -1
  33. package/lib/components/template/CaseSummary/CaseSummary.js +56 -2
  34. package/lib/components/template/CaseSummary/CaseSummary.js.map +1 -1
  35. package/lib/components/template/SingleReferenceReadOnly/SingleReferenceReadOnly.d.ts.map +1 -1
  36. package/lib/components/template/SingleReferenceReadOnly/SingleReferenceReadOnly.js +3 -2
  37. package/lib/components/template/SingleReferenceReadOnly/SingleReferenceReadOnly.js.map +1 -1
  38. package/lib/components/widget/Attachment/Attachment.d.ts +3 -1
  39. package/lib/components/widget/Attachment/Attachment.d.ts.map +1 -1
  40. package/lib/components/widget/Attachment/Attachment.js +241 -189
  41. package/lib/components/widget/Attachment/Attachment.js.map +1 -1
  42. package/lib/components/widget/Attachment/Attachment.types.d.ts +90 -0
  43. package/lib/components/widget/Attachment/Attachment.types.d.ts.map +1 -0
  44. package/lib/components/widget/Attachment/Attachment.types.js +2 -0
  45. package/lib/components/widget/Attachment/Attachment.types.js.map +1 -0
  46. package/lib/components/widget/Attachment/AttachmentUtils.d.ts +25 -0
  47. package/lib/components/widget/Attachment/AttachmentUtils.d.ts.map +1 -0
  48. package/lib/components/widget/Attachment/AttachmentUtils.js +255 -0
  49. package/lib/components/widget/Attachment/AttachmentUtils.js.map +1 -0
  50. package/lib/components/widget/FileUtility/FileUtility/FileUtility.d.ts +1 -0
  51. package/lib/components/widget/FileUtility/FileUtility/FileUtility.d.ts.map +1 -1
  52. package/lib/components/widget/FileUtility/FileUtility/FileUtility.js +21 -8
  53. package/lib/components/widget/FileUtility/FileUtility/FileUtility.js.map +1 -1
  54. package/lib/components/widget/ToDo/ToDo.js +1 -1
  55. package/lib/components/widget/ToDo/ToDo.js.map +1 -1
  56. package/lib/mediaco/ToDo/ToDo.css +81 -0
  57. package/lib/mediaco/ToDo/ToDo.d.ts +16 -0
  58. package/lib/mediaco/ToDo/ToDo.d.ts.map +1 -0
  59. package/lib/mediaco/ToDo/ToDo.js +187 -0
  60. package/lib/mediaco/ToDo/ToDo.js.map +1 -0
  61. package/lib/mediaco/ToDo/index.d.ts +2 -0
  62. package/lib/mediaco/ToDo/index.d.ts.map +1 -0
  63. package/lib/mediaco/ToDo/index.js +2 -0
  64. package/lib/mediaco/ToDo/index.js.map +1 -0
  65. package/package.json +1 -1
  66. package/lib/components/helpers/attachmentHelpers.d.ts +0 -16
  67. package/lib/components/helpers/attachmentHelpers.d.ts.map +0 -1
  68. package/lib/components/helpers/attachmentHelpers.js +0 -94
  69. package/lib/components/helpers/attachmentHelpers.js.map +0 -1
@@ -2,189 +2,173 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
3
3
  import { CircularProgress, IconButton, Menu, MenuItem, Button } from '@mui/material';
4
4
  import MoreVertIcon from '@mui/icons-material/MoreVert';
5
- import { getIconFromFileType, isFileUploadedToServer, useFileDownload, validateMaxSize } from '../../helpers/attachmentHelpers';
6
5
  import { Utils } from '../../helpers/utils';
6
+ import { clearFieldErrorMessages, deleteAttachments, getIconFromFileType, getMappedValue, insertAttachments, useDeepMemo, useFileDownload, validateFileExtension } from './AttachmentUtils';
7
+ import { validateMaxSize } from '../../helpers/attachmentShared';
7
8
  import './Attachment.css';
8
- const getAttachmentKey = (name, embeddedReference) => {
9
- return `attachmentsList${embeddedReference}.${name}`;
10
- };
11
- const getCurrentAttachmentsList = (key, context) => {
12
- return PCore.getStoreValue(`.${key}`, 'context_data', context) || [];
13
- };
14
- const updateAttachmentState = (pConn, key, attachments) => {
15
- PCore.getStateUtils().updateState(pConn.getContextName(), key, attachments, {
16
- pageReference: 'context_data',
17
- isArrayDeepMerge: false
18
- });
19
- };
20
9
  export default function Attachment(props) {
21
- const { value, getPConnect, label, validatemessage, allowMultiple, extensions, displayMode, helperText } = props;
10
+ const { value, getPConnect, label, validatemessage, extensions, displayMode, helperText, editMode, isTableFormatter } = props;
22
11
  /* this is a temporary fix because required is supposed to be passed as a boolean and NOT as a string */
23
- let { required, disabled } = props;
24
- [required, disabled] = [required, disabled].map(prop => prop === true || (typeof prop === 'string' && prop === 'true'));
12
+ let { required, disabled, allowMultiple } = props;
13
+ [required, disabled, allowMultiple] = [required, disabled, allowMultiple].map(prop => prop === true || (typeof prop === 'string' && prop === 'true'));
25
14
  const pConn = getPConnect();
15
+ const localizationService = pConn.getLocalizationService();
26
16
  const actionSequencer = useMemo(() => PCore.getActionsSequencer(), []);
27
- const caseID = PCore.getStoreValue('.pyID', 'caseInfo.content', pConn.getContextName());
17
+ const rawValue = pConn.getComponentConfig().value;
18
+ const isAttachmentAnnotationPresent = typeof rawValue === 'object' ? false : rawValue?.includes('@ATTACHMENT');
19
+ const { attachments, isOldAttachment } = isAttachmentAnnotationPresent ? value : PCore.getAttachmentUtils().prepareAttachmentData(value);
20
+ let valueRef = pConn.getStateProps().value;
21
+ valueRef = valueRef.indexOf('.') === 0 ? valueRef.substring(1) : valueRef;
22
+ pConn.setReferenceList(`.${valueRef}`);
23
+ const isMultiAttachmentInInlineEditTable = isTableFormatter && allowMultiple && editMode === 'tableRows';
24
+ const [files, setFiles] = useState(attachments);
25
+ const overrideLocalState = useRef(false);
26
+ const attachmentCount = useRef(attachments.length);
27
+ const filesWithError = useRef([]);
28
+ const multiAttachmentsInInlineEdit = useRef([]);
29
+ const thumbnailURLs = useRef([]);
30
+ const contextName = pConn.getContextName();
31
+ const onFileDownload = useFileDownload(contextName);
28
32
  const localizedVal = PCore.getLocaleUtils().getLocaleValue;
29
33
  const localeCategory = 'CosmosFields';
30
34
  const uploadMultipleFilesLabel = localizedVal('file_upload_text_multiple', localeCategory);
31
35
  const uploadSingleFileLabel = localizedVal('file_upload_text_one', localeCategory);
32
36
  const deleteIcon = Utils.getImageSrc('trash', Utils.getSDKStaticConentUrl());
33
37
  const srcImg = Utils.getImageSrc('document-doc', Utils.getSDKStaticConentUrl());
34
- let valueRef = pConn.getStateProps().value;
35
- valueRef = valueRef.indexOf('.') === 0 ? valueRef.substring(1) : valueRef;
36
38
  const [anchorEl, setAnchorEl] = useState(null);
37
39
  const open = Boolean(anchorEl);
38
- const rawValue = pConn.getComponentConfig().value;
39
- const isAttachmentAnnotationPresent = typeof rawValue === 'object' ? false : rawValue?.includes('@ATTACHMENT');
40
- const { hasUploadedFiles, attachments, categoryName } = isAttachmentAnnotationPresent
41
- ? value
42
- : PCore.getAttachmentUtils().prepareAttachmentData(value);
43
40
  const fileInputRef = useRef(null);
44
- const [files, setFiles] = useState(attachments);
45
- const [filesWithError, setFilesWithError] = useState([]);
46
41
  const [toggleUploadBegin, setToggleUploadBegin] = useState(false);
47
- const context = pConn.getContextName();
48
- const onFileDownload = useFileDownload(context);
49
- let embeddedProperty = pConn
50
- .getPageReference()
51
- .replace(PCore.getConstants().CASE_INFO.CASE_INFO_CONTENT, '')
52
- .replace(PCore.getConstants().DATA_INFO.DATA_INFO_CONTENT, '');
53
- if (valueRef?.indexOf('.') > 0) {
54
- embeddedProperty = valueRef.substring(0, valueRef.indexOf('.') + 1);
55
- }
56
- const resetAttachmentStoredState = () => {
57
- PCore.getStateUtils().updateState(pConn.getContextName(), getAttachmentKey(valueRef, embeddedProperty), undefined, {
58
- pageReference: 'context_data',
59
- isArrayDeepMerge: false
60
- });
61
- };
62
- const deleteFile = useCallback(file => {
42
+ const deleteFile = useCallback((file, fileIndex) => {
63
43
  setAnchorEl(null);
64
44
  // reset the file input so that it will allow re-uploading the same file after deletion
65
45
  if (fileInputRef.current) {
66
46
  fileInputRef.current.value = ''; // Reset the input
67
47
  }
68
- let attachmentsList = [];
69
- let currentAttachmentList = getCurrentAttachmentsList(getAttachmentKey(valueRef, embeddedProperty), pConn.getContextName());
70
- // If file to be deleted is the one added in previous stage i.e. for which a file instance is created in server
71
- // no need to filter currentAttachmentList as we will get another entry of file in redux with delete & label
72
- if (hasUploadedFiles && isFileUploadedToServer(file)) {
73
- const updatedAttachments = files.map(f => {
74
- if (f.responseProps && f.responseProps.pzInsKey === file.responseProps.pzInsKey) {
75
- return { ...f, delete: true, label: valueRef };
76
- }
77
- return f;
78
- });
79
- // updating the redux store to help form-handler in passing the data to delete the file from server
80
- updateAttachmentState(pConn, getAttachmentKey(valueRef, embeddedProperty), [...updatedAttachments]);
81
- setFiles(current => {
82
- const newlyAddedFiles = current.filter(f => !!f.ID);
83
- const filesPostDelete = current.filter(f => isFileUploadedToServer(f) && f.responseProps?.ID !== file.responseProps?.ID);
84
- attachmentsList = [...filesPostDelete, ...newlyAddedFiles];
85
- return attachmentsList;
48
+ if (filesWithError.current.length > 0) {
49
+ filesWithError.current = filesWithError.current.filter(fileWithError => fileWithError.props.id !== file.props.id);
50
+ if (filesWithError.current.length === 0) {
51
+ clearFieldErrorMessages(pConn);
52
+ }
53
+ }
54
+ if (file.inProgress) {
55
+ // @ts-ignore - Expected 1 arguments, but got 2.ts(2554)
56
+ PCore.getAttachmentUtils().cancelRequest(file.props.id, contextName);
57
+ actionSequencer.deRegisterBlockingAction(contextName).catch(() => { });
58
+ setFiles(localFiles => {
59
+ return localFiles.filter(localFile => localFile.props.id !== file.props.id);
86
60
  });
87
- } // if the file being deleted is the added in this stage i.e. whose data is not yet created in server
61
+ }
88
62
  else {
89
- // filter newly added files in this stage, later the updated current stage files will be added to redux once files state is updated in below setFiles()
90
- currentAttachmentList = currentAttachmentList.filter(f => !f.props.error && (f.delete || f.label !== valueRef));
91
- setFiles(current => current.filter(f => f.ID !== file.ID));
92
- updateAttachmentState(pConn, getAttachmentKey(valueRef, embeddedProperty), [...currentAttachmentList, ...attachmentsList]);
93
- if (file.inProgress) {
94
- // @ts-expect-error - 3rd parameter "responseEncoding" should be optional
95
- PCore.getAttachmentUtils().cancelRequest(file.ID, pConn.getContextName());
96
- actionSequencer.deRegisterBlockingAction(pConn.getContextName()).catch(error => {
97
- console.log(error);
63
+ deleteAttachments([file], pConn, multiAttachmentsInInlineEdit.current, {
64
+ allowMultiple,
65
+ isOldAttachment,
66
+ isMultiAttachmentInInlineEditTable,
67
+ attachmentCount: attachmentCount.current,
68
+ deleteIndex: fileIndex
69
+ });
70
+ // Filter out without deleted file and reset the file indexes
71
+ setFiles(localFiles => {
72
+ let tempLocalFiles = [...localFiles];
73
+ tempLocalFiles = tempLocalFiles.filter(localFile => localFile.props.id !== file.props.id);
74
+ tempLocalFiles.forEach(localFile => {
75
+ if (!localFile.props.error && !file.props.error) {
76
+ const updatedDeleteIndex = localFile.responseProps.deleteIndex > fileIndex ? localFile.responseProps.deleteIndex - 1 : localFile.responseProps.deleteIndex;
77
+ localFile.props.onDelete = () => deleteFile(localFile, updatedDeleteIndex);
78
+ localFile.responseProps.deleteIndex = updatedDeleteIndex;
79
+ }
98
80
  });
81
+ return tempLocalFiles;
82
+ });
83
+ if (!file.props.error) {
84
+ attachmentCount.current -= 1;
99
85
  }
100
86
  }
101
87
  setToggleUploadBegin(false);
102
- setFilesWithError(prevFilesWithError => {
103
- return prevFilesWithError.filter(f => f.ID !== file.ID);
88
+ }, [pConn]);
89
+ const onUploadProgress = (id, ev) => {
90
+ const progress = Math.floor((ev.loaded / ev.total) * 100);
91
+ setFiles(localFiles => [
92
+ ...localFiles.map(localFile => {
93
+ if (localFile.props?.id === id) {
94
+ localFile.inProgress = true;
95
+ localFile.props.progress = progress;
96
+ }
97
+ return localFile;
98
+ })
99
+ ]);
100
+ };
101
+ const populateErrorAndUpdateRedux = file => {
102
+ const fieldName = pConn.getStateProps().value;
103
+ // set errors to property to block submit even on errors in file upload
104
+ PCore.getMessageManager().addMessages({
105
+ messages: [
106
+ {
107
+ type: 'error',
108
+ message: localizationService.getLocalizedText('Error with one or more files')
109
+ }
110
+ ],
111
+ property: fieldName,
112
+ pageReference: pConn.getPageReference(),
113
+ context: contextName
114
+ });
115
+ insertAttachments([file], pConn, multiAttachmentsInInlineEdit.current, {
116
+ allowMultiple,
117
+ isOldAttachment,
118
+ isMultiAttachmentInInlineEditTable,
119
+ attachmentCount: attachmentCount.current
104
120
  });
105
- }, [valueRef, pConn, hasUploadedFiles, filesWithError, hasUploadedFiles, actionSequencer]);
106
- const onUploadProgress = () => { };
107
- const errorHandler = (isFetchCanceled, attachedFile) => {
121
+ };
122
+ const errorHandler = (isFetchCanceled, file) => {
108
123
  return error => {
109
124
  if (!isFetchCanceled(error)) {
110
- let uploadFailMsg = pConn.getLocalizedValue('Something went wrong', '', '');
125
+ let uploadFailMsg = localizationService.getLocalizedText('Something went wrong');
111
126
  if (error.response && error.response.data && error.response.data.errorDetails) {
112
- uploadFailMsg = pConn.getLocalizedValue(error.response.data.errorDetails[0].localizedValue, '', '');
127
+ uploadFailMsg = localizationService.getLocalizedText(error.response.data.errorDetails[0].localizedValue);
113
128
  }
114
129
  setFiles(current => {
115
- return current.map(f => {
116
- if (f.ID === attachedFile.ID) {
117
- f.props.meta = uploadFailMsg;
118
- f.props.error = true;
119
- f.props.onDelete = () => deleteFile(f);
120
- f.props.icon = getIconFromFileType(f.type);
121
- f.props.name = pConn.getLocalizedValue('Unable to upload file', '', '');
122
- f.inProgress = false;
123
- const fieldName = pConn.getStateProps().value;
124
- // set errors to property to block submit even on errors in file upload
125
- PCore.getMessageManager().addMessages({
126
- messages: [
127
- {
128
- type: 'error',
129
- message: pConn.getLocalizedValue('Error with one or more files', '', '')
130
- }
131
- ],
132
- property: fieldName,
133
- pageReference: pConn.getPageReference(),
134
- context
135
- });
136
- delete f.props.progress;
130
+ return current.map((localFile, index) => {
131
+ if (localFile.props.id === file.props.id) {
132
+ localFile.props.meta = uploadFailMsg;
133
+ localFile.props.error = true;
134
+ localFile.props.onDelete = () => deleteFile(localFile, index);
135
+ localFile.props.icon = getIconFromFileType(localFile.type);
136
+ localFile.props.name = localizationService.getLocalizedText('Unable to upload file');
137
+ localFile.inProgress = false;
138
+ delete localFile.props.progress;
139
+ filesWithError.current.push(localFile);
140
+ populateErrorAndUpdateRedux(localFile);
137
141
  }
138
- return f;
142
+ return localFile;
139
143
  });
140
144
  });
141
145
  }
142
146
  throw error;
143
147
  };
144
148
  };
145
- const validateFileExtension = (fileObj, allowedExtensions) => {
146
- if (!allowedExtensions) {
147
- return true;
148
- }
149
- const allowedExtensionList = allowedExtensions
150
- .toLowerCase()
151
- .split(',')
152
- .map(item => item.replaceAll('.', '').trim());
153
- const extension = fileObj.name.split('.').pop().toLowerCase();
154
- return allowedExtensionList.includes(extension);
155
- };
156
- const clearFieldErrorMessages = () => {
157
- const fieldName = pConn.getStateProps().value;
158
- PCore.getMessageManager().clearMessages({
159
- type: PCore.getConstants().MESSAGES.MESSAGES_TYPE_ERROR,
160
- property: fieldName,
161
- pageReference: pConn.getPageReference(),
162
- context
163
- });
164
- };
165
149
  const onFileAdded = event => {
166
150
  let addedFiles = Array.from(event.target.files);
167
- addedFiles = allowMultiple === 'true' ? addedFiles : [addedFiles[0]];
151
+ addedFiles = allowMultiple ? addedFiles : [addedFiles[0]];
168
152
  const maxAttachmentSize = PCore.getEnvironmentInfo().getMaxAttachmentSize() || '5';
169
153
  const tempFilesToBeUploaded = [
170
154
  ...addedFiles.map((f, index) => {
171
155
  f.ID = `${new Date().getTime()}I${index}`;
172
- f.inProgress = true;
173
156
  f.props = {
174
157
  type: f.type,
175
158
  name: f.name,
159
+ id: f.ID,
160
+ format: f.name.split('.').pop(),
176
161
  icon: getIconFromFileType(f.type),
177
- onDelete: () => deleteFile(f)
162
+ onDelete: () => deleteFile(f, index),
163
+ thumbnail: window.URL.createObjectURL(f)
178
164
  };
179
165
  if (!validateMaxSize(f, maxAttachmentSize)) {
180
166
  f.props.error = true;
181
- f.inProgress = false;
182
- f.props.meta = pConn.getLocalizedValue(`File is too big. Max allowed size is ${maxAttachmentSize}MB.`, '', '');
167
+ f.props.meta = localizationService.getLocalizedText(`File is too big. Max allowed size is ${maxAttachmentSize}MB.`);
183
168
  }
184
169
  else if (!validateFileExtension(f, extensions)) {
185
170
  f.props.error = true;
186
- f.inProgress = false;
187
- f.props.meta = `${pConn.getLocalizedValue('File has invalid extension. Allowed extensions are:', '', '')} ${extensions.replaceAll('.', '')}`;
171
+ f.props.meta = `${localizationService.getLocalizedText('File has invalid extension. Allowed extensions are:')} ${extensions.replaceAll('.', '')}`;
188
172
  }
189
173
  if (f.props.error) {
190
174
  const fieldName = pConn.getStateProps().value;
@@ -192,12 +176,12 @@ export default function Attachment(props) {
192
176
  messages: [
193
177
  {
194
178
  type: 'error',
195
- message: pConn.getLocalizedValue('Error with one or more files', '', '')
179
+ message: localizationService.getLocalizedText('Error with one or more files')
196
180
  }
197
181
  ],
198
182
  property: fieldName,
199
183
  pageReference: pConn.getPageReference(),
200
- context
184
+ context: contextName
201
185
  });
202
186
  }
203
187
  return f;
@@ -205,9 +189,15 @@ export default function Attachment(props) {
205
189
  ];
206
190
  const tempFilesWithError = tempFilesToBeUploaded.filter(f => f.props.error);
207
191
  if (tempFilesWithError.length > 0) {
208
- setFilesWithError(tempFilesWithError);
192
+ filesWithError.current = [...filesWithError.current, ...tempFilesWithError];
193
+ insertAttachments(tempFilesWithError, pConn, multiAttachmentsInInlineEdit.current, {
194
+ allowMultiple,
195
+ isOldAttachment,
196
+ isMultiAttachmentInInlineEditTable,
197
+ attachmentCount: attachmentCount.current
198
+ });
209
199
  }
210
- setFiles(current => (allowMultiple !== 'true' ? [...tempFilesToBeUploaded] : [...current, ...tempFilesToBeUploaded]));
200
+ setFiles(current => (!allowMultiple ? [...tempFilesToBeUploaded] : [...current, ...tempFilesToBeUploaded]));
211
201
  setToggleUploadBegin(true);
212
202
  };
213
203
  const uploadFiles = useCallback(() => {
@@ -215,102 +205,164 @@ export default function Attachment(props) {
215
205
  .filter(e => {
216
206
  const isFileUploaded = e.props && e.props.progress === 100;
217
207
  const fileHasError = e.props && e.props.error;
218
- const isFileUploadedinLastStep = e.responseProps && e.responseProps.pzInsKey;
219
- return !isFileUploaded && !fileHasError && !isFileUploadedinLastStep;
208
+ const isFileUploadedInLastStep = e.responseProps && e.responseProps.ID !== 'temp';
209
+ const isFileUploadInProgress = e.inProgress;
210
+ return !isFileUploadInProgress && !isFileUploaded && !fileHasError && !isFileUploadedInLastStep;
220
211
  })
221
- .map(f => window.PCore.getAttachmentUtils().uploadAttachment(f, () => {
222
- onUploadProgress();
212
+ .map(file => PCore.getAttachmentUtils().uploadAttachment(file, ev => {
213
+ onUploadProgress(file.props.id, ev);
223
214
  }, isFetchCanceled => {
224
- return errorHandler(isFetchCanceled, f);
225
- }, pConn.getContextName()));
215
+ return errorHandler(isFetchCanceled, file);
216
+ }, contextName));
217
+ // allow new files to be added when other files upload is still in progress
218
+ setToggleUploadBegin(false);
226
219
  Promise.allSettled(filesToBeUploaded)
227
220
  .then((fileResponses) => {
228
221
  fileResponses = fileResponses.filter(fr => fr.status !== 'rejected'); // in case of deleting an in progress file, promise gets cancelled but still enters then block
229
222
  if (fileResponses.length > 0) {
230
- setFiles(current => {
231
- const tempFilesUploaded = [...current];
232
- tempFilesUploaded.forEach(f => {
233
- const index = fileResponses.findIndex((fr) => fr.value.clientFileID === f.ID);
223
+ setFiles(localFiles => {
224
+ const tempFilesUploaded = [...localFiles];
225
+ tempFilesUploaded.forEach(localFile => {
226
+ // if attach field has multiple files & in bw any error files are present
227
+ // Example : files = [properFile1, errFile, errFile, properFile2]
228
+ // indexes for delete & preview should be for files [properFile1, properFile2] which is [1,2]
229
+ const index = fileResponses.findIndex(fileResponse => fileResponse.value.clientFileID === localFile.props.id);
234
230
  if (index >= 0) {
235
- f.props.meta = pConn.getLocalizedValue('Uploaded successfully', '', '');
236
- f.props.progress = 100;
237
- f.inProgress = false;
238
- f.handle = fileResponses[index].value.ID;
239
- f.label = valueRef;
240
- f.category = categoryName;
241
- f.responseProps = {
242
- pzInsKey: 'temp',
243
- pyAttachName: f.props.name
231
+ fileResponses[index].value.thumbnail = localFile.props.thumbnail;
232
+ localFile.inProgress = false;
233
+ localFile.ID = fileResponses[index].value.ID;
234
+ localFile.props.meta = localizationService.getLocalizedText('Uploaded successfully');
235
+ localFile.props.progress = 100;
236
+ localFile.handle = fileResponses[index].value.ID;
237
+ localFile.label = valueRef;
238
+ localFile.responseProps = {
239
+ pzInsKey: 'temp'
244
240
  };
245
241
  }
246
242
  });
247
243
  return tempFilesUploaded;
248
244
  });
249
- if (filesWithError.length === 0) {
250
- clearFieldErrorMessages();
245
+ insertAttachments(fileResponses, pConn, multiAttachmentsInInlineEdit.current, {
246
+ allowMultiple,
247
+ isOldAttachment,
248
+ isMultiAttachmentInInlineEditTable,
249
+ attachmentCount: attachmentCount.current,
250
+ insert: true
251
+ });
252
+ attachmentCount.current += fileResponses.length;
253
+ if (filesWithError.current.length === 0) {
254
+ clearFieldErrorMessages(pConn);
251
255
  }
252
256
  }
253
- setToggleUploadBegin(false);
257
+ actionSequencer.deRegisterBlockingAction(contextName).catch(() => { });
254
258
  })
255
259
  .catch(error => {
256
260
  console.log(error);
257
261
  setToggleUploadBegin(false);
258
262
  });
259
- }, [files, filesWithError]);
263
+ }, [files]);
260
264
  useEffect(() => {
261
265
  if (toggleUploadBegin && files.length > 0) {
262
- uploadFiles();
266
+ actionSequencer.registerBlockingAction(contextName).then(() => {
267
+ uploadFiles();
268
+ });
263
269
  }
264
270
  }, [toggleUploadBegin]);
265
271
  useEffect(() => {
266
- if (files.length > 0 && displayMode !== 'DISPLAY_ONLY') {
267
- const currentAttachmentList = getCurrentAttachmentsList(getAttachmentKey(valueRef, embeddedProperty), pConn.getContextName());
268
- // block duplicate files to redux store when added 1 after another to prevent multiple duplicates being added to the case on submit
269
- const tempFiles = files.filter(f => currentAttachmentList.findIndex(fr => fr.ID === f.ID) === -1 && !f.inProgress && f.responseProps);
270
- const updatedAttList = [...currentAttachmentList, ...tempFiles];
271
- updateAttachmentState(pConn, getAttachmentKey(valueRef, embeddedProperty), updatedAttList);
272
- }
273
- }, [files]);
274
- useEffect(() => {
275
- if (filesWithError.length === 0) {
276
- clearFieldErrorMessages();
272
+ if (filesWithError.current.length === 0) {
273
+ clearFieldErrorMessages(pConn);
277
274
  }
278
275
  }, [filesWithError]);
279
- useEffect(() => {
280
- let tempUploadedFiles = getCurrentAttachmentsList(getAttachmentKey(valueRef, embeddedProperty), pConn.getContextName());
281
- tempUploadedFiles = tempUploadedFiles.filter(f => f.label === valueRef);
282
- setFiles(current => {
283
- return [
284
- ...current.map(f => {
285
- return f.responseProps.pzInsKey && !f.responseProps.pzInsKey.includes('temp')
286
- ? {
287
- ...f,
288
- props: {
289
- ...f.props,
290
- onDelete: () => deleteFile(f)
291
- }
292
- }
293
- : { ...f };
294
- }),
295
- ...tempUploadedFiles
296
- ];
276
+ const memoizedAttachments = useDeepMemo(() => {
277
+ return attachments;
278
+ }, [attachments]);
279
+ // Prepares new structure as per Cosmos component
280
+ const transformAttachments = () => {
281
+ const transformedFiles = [...attachments];
282
+ let deleteIndex = -1;
283
+ transformedFiles.forEach(attachment => {
284
+ attachment.props.id = attachment.responseProps.ID;
285
+ attachment.props.format = attachment.props.name.split('.').pop();
286
+ if (attachment.props.error) {
287
+ attachment.responseProps.deleteIndex = deleteIndex;
288
+ }
289
+ else {
290
+ deleteIndex += 1;
291
+ attachment.responseProps.deleteIndex = deleteIndex;
292
+ }
293
+ if (attachment.props.thumbnail) {
294
+ thumbnailURLs.current.push(attachment.props.thumbnail);
295
+ }
297
296
  });
297
+ return transformedFiles;
298
+ };
299
+ useEffect(() => {
300
+ const caseID = PCore.getStoreValue(`.${getMappedValue('pyID')}`, PCore.getResolvedConstantValue('caseInfo.content'), contextName);
298
301
  if (displayMode !== 'DISPLAY_ONLY') {
299
- PCore.getPubSubUtils().subscribe(PCore.getConstants().PUB_SUB_EVENTS.CASE_EVENTS.ASSIGNMENT_SUBMISSION, resetAttachmentStoredState, caseID);
302
+ PCore.getPubSubUtils().subscribe(PCore.getConstants().PUB_SUB_EVENTS.CASE_EVENTS.ASSIGNMENT_SUBMISSION, () => {
303
+ overrideLocalState.current = true;
304
+ }, caseID);
300
305
  }
306
+ // When component mounts, only set local files state from redux.
307
+ const serverFiles = transformAttachments();
308
+ setFiles(serverFiles);
309
+ filesWithError.current = serverFiles.filter(file => file.props.error);
301
310
  return () => {
302
311
  if (displayMode !== 'DISPLAY_ONLY') {
303
312
  PCore.getPubSubUtils().unsubscribe(PCore.getConstants().PUB_SUB_EVENTS.CASE_EVENTS.ASSIGNMENT_SUBMISSION, caseID);
304
313
  }
305
314
  };
306
315
  }, []);
316
+ useEffect(() => {
317
+ if (overrideLocalState.current) {
318
+ const serverFiles = transformAttachments();
319
+ overrideLocalState.current = false;
320
+ attachmentCount.current = attachments.length;
321
+ filesWithError.current = [];
322
+ setFiles(serverFiles);
323
+ }
324
+ else {
325
+ // Determine whether refresh call has overridden any error files in redux, push error files back to redux from local state to perform client side validation during assignment submit
326
+ const errorFiles = attachments.filter(attachment => attachment.props.error);
327
+ if (errorFiles.length === 0 && filesWithError.current.length > 0) {
328
+ // Check if local file state contains error files and push those to redux
329
+ const uniqueKey = getMappedValue('pzInsKey');
330
+ const transformedErrorFiles = filesWithError.current.map(errorFile => {
331
+ const filename = errorFile.props.name;
332
+ return {
333
+ [uniqueKey]: errorFile.props.id,
334
+ FileName: filename,
335
+ Category: '',
336
+ FileExtension: filename.split('.').pop() ?? filename,
337
+ error: errorFile.props.error || null
338
+ };
339
+ });
340
+ let key = '';
341
+ let updatedAttachments = [];
342
+ if (allowMultiple || isOldAttachment) {
343
+ key = isOldAttachment ? `${valueRef}.pxResults` : valueRef;
344
+ const existingAttachments = PCore.getStoreValue(`.${key}`, pConn.getPageReference(), pConn.getContextName()) || [];
345
+ updatedAttachments = [...existingAttachments, ...transformedErrorFiles];
346
+ }
347
+ else {
348
+ key = valueRef;
349
+ updatedAttachments = transformedErrorFiles[0];
350
+ }
351
+ PCore.getStateUtils().updateState(pConn.getContextName(), key, updatedAttachments, {
352
+ pageReference: pConn.getPageReference(),
353
+ isArrayDeepMerge: false,
354
+ removePropertyFromChangedList: true
355
+ });
356
+ }
357
+ }
358
+ }, [memoizedAttachments]);
307
359
  const handleClick = event => {
308
360
  setAnchorEl(event.currentTarget);
309
361
  };
310
362
  const handleClose = () => {
311
363
  setAnchorEl(null);
312
364
  };
313
- const content = (_jsx("div", { style: { marginBottom: '8px' }, children: _jsxs("div", { className: `${disabled ? 'file-disabled' : ''} ${validatemessage === '' ? 'file-div' : 'file-div-error'}`, children: [_jsx("div", { hidden: true, id: 'attachment-ID', children: valueRef }), _jsxs("label", { htmlFor: valueRef, children: [_jsx("input", { style: { display: 'none' }, id: valueRef, name: 'upload-photo', type: 'file', multiple: allowMultiple === 'true', required: required, disabled: disabled, onChange: onFileAdded }), _jsx(Button, { style: { textTransform: 'none' }, variant: 'outlined', color: 'primary', component: 'span', children: allowMultiple === 'true'
365
+ const content = (_jsx("div", { style: { marginBottom: '8px' }, children: _jsxs("div", { className: `${disabled ? 'file-disabled' : ''} ${validatemessage === '' ? 'file-div' : 'file-div-error'}`, children: [_jsx("div", { hidden: true, id: 'attachment-ID', children: valueRef }), _jsxs("label", { htmlFor: valueRef, children: [_jsx("input", { style: { display: 'none' }, id: valueRef, name: 'upload-photo', type: 'file', multiple: allowMultiple, required: required, disabled: disabled, onChange: onFileAdded }), _jsx(Button, { style: { textTransform: 'none' }, variant: 'outlined', color: 'primary', component: 'span', children: allowMultiple
314
366
  ? uploadMultipleFilesLabel === 'file_upload_text_multiple'
315
367
  ? 'Choose files'
316
368
  : uploadMultipleFilesLabel
@@ -320,11 +372,11 @@ export default function Attachment(props) {
320
372
  const fileDisplay = (_jsx("div", { children: files &&
321
373
  files.length > 0 &&
322
374
  files.map((item, index) => {
323
- return (_jsxs("div", { className: 'psdk-utility-card', children: [_jsxs("div", { className: 'psdk-utility-card-icon', children: [!item.inProgress && _jsx("img", { className: 'psdk-utility-card-svg-icon', src: srcImg }), item.inProgress && (_jsx("div", { children: _jsx(CircularProgress, {}) }))] }), _jsxs("div", { className: 'psdk-utility-card-main', children: [_jsx("div", { className: 'psdk-utility-card-main-primary-label', children: item.props.name }), item.props.meta && _jsx("div", { style: { color: item.props.error ? 'red' : undefined }, children: item.props.meta })] }), _jsxs("div", { className: 'psdk-utility-action', children: [item.ID && (_jsx("button", { type: 'button', className: 'psdk-utility-button', "aria-label": 'Delete Attachment', onClick: () => deleteFile(item), children: _jsx("img", { className: 'psdk-utility-card-action-svg-icon', src: deleteIcon }) })), !item.ID && (_jsxs("div", { children: [_jsx(IconButton, { id: 'setting-button', "aria-controls": open ? 'file-menu' : undefined, "aria-expanded": open ? 'true' : undefined, "aria-haspopup": 'true', onClick: handleClick, size: 'large', children: _jsx(MoreVertIcon, {}) }), _jsxs(Menu, { style: { marginTop: '3rem' }, id: 'file-menu', anchorEl: anchorEl, keepMounted: true, open: Boolean(anchorEl), onClose: handleClose, children: [_jsx(MenuItem, { style: { fontSize: '14px' }, onClick: () => {
375
+ return (_jsxs("div", { className: 'psdk-utility-card', children: [_jsxs("div", { className: 'psdk-utility-card-icon', children: [!item.inProgress && _jsx("img", { className: 'psdk-utility-card-svg-icon', src: srcImg }), item.inProgress && (_jsx("div", { children: _jsx(CircularProgress, {}) }))] }), _jsxs("div", { className: 'psdk-utility-card-main', children: [_jsx("div", { className: 'psdk-utility-card-main-primary-label', children: item.props.name }), item.props.meta && _jsx("div", { style: { color: item.props.error ? 'red' : undefined }, children: item.props.meta })] }), _jsxs("div", { className: 'psdk-utility-action', children: [item.ID && (_jsx("button", { type: 'button', className: 'psdk-utility-button', "aria-label": 'Delete Attachment', onClick: () => deleteFile(item, index), children: _jsx("img", { className: 'psdk-utility-card-action-svg-icon', src: deleteIcon }) })), !item.ID && (_jsxs("div", { children: [_jsx(IconButton, { id: 'setting-button', "aria-controls": open ? 'file-menu' : undefined, "aria-expanded": open ? 'true' : undefined, "aria-haspopup": 'true', onClick: handleClick, size: 'large', children: _jsx(MoreVertIcon, {}) }), _jsxs(Menu, { style: { marginTop: '3rem' }, id: 'file-menu', anchorEl: anchorEl, keepMounted: true, open: Boolean(anchorEl), onClose: handleClose, children: [_jsx(MenuItem, { style: { fontSize: '14px' }, onClick: () => {
324
376
  setAnchorEl(null);
325
377
  onFileDownload(item.responseProps ? item.responseProps : {});
326
- }, children: "Download" }, 'download'), _jsx(MenuItem, { style: { fontSize: '14px' }, onClick: () => deleteFile(item), children: "Delete" }, 'delete')] })] }))] })] }, index));
378
+ }, children: "Download" }, 'download'), _jsx(MenuItem, { style: { fontSize: '14px' }, onClick: () => deleteFile(item, index), children: "Delete" }, 'delete')] })] }))] })] }, index));
327
379
  }) }));
328
- return (_jsxs("div", { className: 'file-upload-container', children: [_jsx("span", { className: `label ${required ? 'file-label' : ''}`, children: label }), ((files.length === 0 && allowMultiple !== 'true') || allowMultiple === 'true') && _jsx("section", { children: content }), validatemessage !== '' ? _jsx("span", { className: 'file-error', children: validatemessage }) : _jsx("span", { style: { fontSize: '14px' }, children: helperText }), files && files.length > 0 && _jsx("section", { children: fileDisplay })] }));
380
+ return (_jsxs("div", { className: 'file-upload-container', children: [_jsx("span", { className: `label ${required ? 'file-label' : ''}`, children: label }), ((files.length === 0 && !allowMultiple) || allowMultiple) && _jsx("section", { children: content }), validatemessage !== '' ? _jsx("span", { className: 'file-error', children: validatemessage }) : _jsx("span", { style: { fontSize: '14px' }, children: helperText }), files && files.length > 0 && _jsx("section", { children: fileDisplay })] }));
329
381
  }
330
382
  //# sourceMappingURL=Attachment.js.map