@pega/react-sdk-overrides 0.25.5 → 0.25.7
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.
- package/lib/designSystemExtension/CaseSummaryFields/CaseSummaryFields.tsx +1 -1
- package/lib/field/RadioButtons/RadioButtons.tsx +1 -1
- package/lib/field/SelectableCard/utils.tsx +2 -5
- package/lib/field/SemanticLink/SemanticLink.tsx +1 -1
- package/lib/helpers/attachmentShared.ts +6 -0
- package/lib/helpers/common-utils.ts +1 -2
- package/lib/helpers/formatters/Currency.ts +9 -4
- package/lib/helpers/object-utils.ts +10 -0
- package/lib/infra/Assignment/Assignment.tsx +1 -1
- package/lib/infra/Containers/FlowContainer/FlowContainer.tsx +2 -2
- package/lib/infra/Containers/ModalViewContainer/ModalViewContainer.tsx +4 -3
- package/lib/infra/MultiStep/MultiStep.css +0 -2
- package/lib/infra/NavBar/NavBar.tsx +2 -3
- package/lib/infra/Stages/Stages.tsx +1 -1
- package/lib/template/AppShell/AppShell.tsx +5 -0
- package/lib/template/CaseSummary/CaseSummary.tsx +65 -4
- package/lib/template/CaseView/CaseView.tsx +3 -10
- package/lib/template/DataReference/DataReference.tsx +1 -2
- package/lib/template/ListView/ListView.tsx +1 -1
- package/lib/template/SimpleTable/SimpleTableManual/SimpleTableManual.tsx +1 -1
- package/lib/template/SingleReferenceReadOnly/SingleReferenceReadOnly.tsx +9 -1
- package/lib/widget/Attachment/Attachment.tsx +284 -210
- package/lib/widget/Attachment/Attachment.types.ts +96 -0
- package/lib/widget/Attachment/AttachmentUtils.ts +316 -0
- package/lib/widget/FileUtility/FileUtility/FileUtility.tsx +25 -16
- package/lib/widget/ToDo/ToDo.tsx +10 -5
- package/package.json +1 -1
- package/lib/helpers/attachmentHelpers.ts +0 -97
|
@@ -2,9 +2,19 @@ import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
|
|
2
2
|
import { CircularProgress, IconButton, Menu, MenuItem, Button } from '@mui/material';
|
|
3
3
|
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
|
4
4
|
|
|
5
|
-
import { getIconFromFileType, isFileUploadedToServer, useFileDownload, validateMaxSize } from '@pega/react-sdk-components/lib/components/helpers/attachmentHelpers';
|
|
6
|
-
|
|
7
5
|
import { Utils } from '@pega/react-sdk-components/lib/components/helpers/utils';
|
|
6
|
+
import {
|
|
7
|
+
clearFieldErrorMessages,
|
|
8
|
+
deleteAttachments,
|
|
9
|
+
getIconFromFileType,
|
|
10
|
+
getMappedValue,
|
|
11
|
+
insertAttachments,
|
|
12
|
+
useDeepMemo,
|
|
13
|
+
useFileDownload,
|
|
14
|
+
validateFileExtension
|
|
15
|
+
} from './AttachmentUtils';
|
|
16
|
+
import { validateMaxSize } from '@pega/react-sdk-components/lib/components/helpers/attachmentShared';
|
|
17
|
+
import type { PageInstructionOptions } from './Attachment.types';
|
|
8
18
|
import type { PConnFieldProps } from '@pega/react-sdk-components/lib/types/PConnProps';
|
|
9
19
|
|
|
10
20
|
import './Attachment.css';
|
|
@@ -12,77 +22,58 @@ import './Attachment.css';
|
|
|
12
22
|
interface AttachmentProps extends Omit<PConnFieldProps, 'value'> {
|
|
13
23
|
// If any, enter additional props that only exist on this component
|
|
14
24
|
value: any;
|
|
15
|
-
allowMultiple: string;
|
|
25
|
+
allowMultiple: boolean | string;
|
|
16
26
|
extensions: string;
|
|
27
|
+
editMode: string;
|
|
28
|
+
isTableFormatter: boolean;
|
|
17
29
|
}
|
|
18
30
|
|
|
19
|
-
const getAttachmentKey = (name, embeddedReference) => {
|
|
20
|
-
return `attachmentsList${embeddedReference}.${name}`;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const getCurrentAttachmentsList = (key, context) => {
|
|
24
|
-
return PCore.getStoreValue(`.${key}`, 'context_data', context) || [];
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const updateAttachmentState = (pConn, key, attachments) => {
|
|
28
|
-
PCore.getStateUtils().updateState(pConn.getContextName(), key, attachments, {
|
|
29
|
-
pageReference: 'context_data',
|
|
30
|
-
isArrayDeepMerge: false
|
|
31
|
-
});
|
|
32
|
-
};
|
|
33
|
-
|
|
34
31
|
export default function Attachment(props: AttachmentProps) {
|
|
35
|
-
const { value, getPConnect, label, validatemessage,
|
|
32
|
+
const { value, getPConnect, label, validatemessage, extensions, displayMode, helperText, editMode, isTableFormatter } = props;
|
|
36
33
|
/* this is a temporary fix because required is supposed to be passed as a boolean and NOT as a string */
|
|
37
|
-
let { required, disabled } = props;
|
|
38
|
-
[required, disabled] = [required, disabled].map(
|
|
34
|
+
let { required, disabled, allowMultiple } = props;
|
|
35
|
+
[required, disabled, allowMultiple] = [required, disabled, allowMultiple].map(
|
|
36
|
+
prop => prop === true || (typeof prop === 'string' && prop === 'true')
|
|
37
|
+
);
|
|
39
38
|
const pConn = getPConnect();
|
|
39
|
+
const localizationService = pConn.getLocalizationService();
|
|
40
40
|
|
|
41
41
|
const actionSequencer = useMemo(() => PCore.getActionsSequencer(), []);
|
|
42
|
-
const
|
|
42
|
+
const rawValue = pConn.getComponentConfig().value;
|
|
43
|
+
const isAttachmentAnnotationPresent = typeof rawValue === 'object' ? false : rawValue?.includes('@ATTACHMENT');
|
|
44
|
+
const { attachments, isOldAttachment } = isAttachmentAnnotationPresent ? value : PCore.getAttachmentUtils().prepareAttachmentData(value);
|
|
45
|
+
|
|
46
|
+
let valueRef = (pConn.getStateProps() as any).value;
|
|
47
|
+
valueRef = valueRef.indexOf('.') === 0 ? valueRef.substring(1) : valueRef;
|
|
48
|
+
|
|
49
|
+
pConn.setReferenceList(`.${valueRef}`);
|
|
50
|
+
|
|
51
|
+
const isMultiAttachmentInInlineEditTable = isTableFormatter && allowMultiple && editMode === 'tableRows';
|
|
52
|
+
|
|
53
|
+
const [files, setFiles] = useState<any[]>(attachments);
|
|
54
|
+
const overrideLocalState = useRef(false);
|
|
55
|
+
const attachmentCount = useRef(attachments.length);
|
|
56
|
+
const filesWithError = useRef<any[]>([]);
|
|
57
|
+
const multiAttachmentsInInlineEdit = useRef([]);
|
|
58
|
+
const thumbnailURLs = useRef<any[]>([]);
|
|
59
|
+
const contextName = pConn.getContextName();
|
|
60
|
+
const onFileDownload = useFileDownload(contextName);
|
|
61
|
+
|
|
43
62
|
const localizedVal = PCore.getLocaleUtils().getLocaleValue;
|
|
44
63
|
const localeCategory = 'CosmosFields';
|
|
45
64
|
const uploadMultipleFilesLabel = localizedVal('file_upload_text_multiple', localeCategory);
|
|
46
65
|
const uploadSingleFileLabel = localizedVal('file_upload_text_one', localeCategory);
|
|
47
66
|
const deleteIcon = Utils.getImageSrc('trash', Utils.getSDKStaticConentUrl());
|
|
48
67
|
const srcImg = Utils.getImageSrc('document-doc', Utils.getSDKStaticConentUrl());
|
|
49
|
-
|
|
50
|
-
valueRef = valueRef.indexOf('.') === 0 ? valueRef.substring(1) : valueRef;
|
|
68
|
+
|
|
51
69
|
const [anchorEl, setAnchorEl] = useState(null);
|
|
52
70
|
const open = Boolean(anchorEl);
|
|
53
71
|
|
|
54
|
-
const rawValue = pConn.getComponentConfig().value;
|
|
55
|
-
const isAttachmentAnnotationPresent = typeof rawValue === 'object' ? false : rawValue?.includes('@ATTACHMENT');
|
|
56
|
-
const { hasUploadedFiles, attachments, categoryName } = isAttachmentAnnotationPresent
|
|
57
|
-
? value
|
|
58
|
-
: PCore.getAttachmentUtils().prepareAttachmentData(value);
|
|
59
|
-
|
|
60
72
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
61
|
-
const [files, setFiles] = useState<any[]>(attachments);
|
|
62
|
-
const [filesWithError, setFilesWithError] = useState<any[]>([]);
|
|
63
73
|
const [toggleUploadBegin, setToggleUploadBegin] = useState(false);
|
|
64
74
|
|
|
65
|
-
const context = pConn.getContextName();
|
|
66
|
-
const onFileDownload = useFileDownload(context);
|
|
67
|
-
|
|
68
|
-
let embeddedProperty = pConn
|
|
69
|
-
.getPageReference()
|
|
70
|
-
.replace(PCore.getConstants().CASE_INFO.CASE_INFO_CONTENT, '')
|
|
71
|
-
.replace(PCore.getConstants().DATA_INFO.DATA_INFO_CONTENT, '');
|
|
72
|
-
|
|
73
|
-
if (valueRef?.indexOf('.') > 0) {
|
|
74
|
-
embeddedProperty = valueRef.substring(0, valueRef.indexOf('.') + 1);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const resetAttachmentStoredState = () => {
|
|
78
|
-
PCore.getStateUtils().updateState(pConn.getContextName(), getAttachmentKey(valueRef, embeddedProperty), undefined, {
|
|
79
|
-
pageReference: 'context_data',
|
|
80
|
-
isArrayDeepMerge: false
|
|
81
|
-
});
|
|
82
|
-
};
|
|
83
|
-
|
|
84
75
|
const deleteFile = useCallback(
|
|
85
|
-
file => {
|
|
76
|
+
(file, fileIndex) => {
|
|
86
77
|
setAnchorEl(null);
|
|
87
78
|
|
|
88
79
|
// reset the file input so that it will allow re-uploading the same file after deletion
|
|
@@ -90,84 +81,112 @@ export default function Attachment(props: AttachmentProps) {
|
|
|
90
81
|
fileInputRef.current.value = ''; // Reset the input
|
|
91
82
|
}
|
|
92
83
|
|
|
93
|
-
|
|
94
|
-
|
|
84
|
+
if (filesWithError.current.length > 0) {
|
|
85
|
+
filesWithError.current = filesWithError.current.filter(fileWithError => fileWithError.props.id !== file.props.id);
|
|
86
|
+
if (filesWithError.current.length === 0) {
|
|
87
|
+
clearFieldErrorMessages(pConn);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
95
90
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
return f;
|
|
91
|
+
if (file.inProgress) {
|
|
92
|
+
// @ts-ignore - Expected 1 arguments, but got 2.ts(2554)
|
|
93
|
+
PCore.getAttachmentUtils().cancelRequest(file.props.id, contextName);
|
|
94
|
+
actionSequencer.deRegisterBlockingAction(contextName).catch(() => {});
|
|
95
|
+
setFiles(localFiles => {
|
|
96
|
+
return localFiles.filter(localFile => localFile.props.id !== file.props.id);
|
|
104
97
|
});
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
98
|
+
} else {
|
|
99
|
+
deleteAttachments([file], pConn, multiAttachmentsInInlineEdit.current, {
|
|
100
|
+
allowMultiple,
|
|
101
|
+
isOldAttachment,
|
|
102
|
+
isMultiAttachmentInInlineEditTable,
|
|
103
|
+
attachmentCount: attachmentCount.current,
|
|
104
|
+
deleteIndex: fileIndex
|
|
105
|
+
} as any);
|
|
106
|
+
// Filter out without deleted file and reset the file indexes
|
|
107
|
+
setFiles(localFiles => {
|
|
108
|
+
let tempLocalFiles = [...localFiles];
|
|
109
|
+
tempLocalFiles = tempLocalFiles.filter(localFile => localFile.props.id !== file.props.id);
|
|
110
|
+
tempLocalFiles.forEach(localFile => {
|
|
111
|
+
if (!localFile.props.error && !file.props.error) {
|
|
112
|
+
const updatedDeleteIndex =
|
|
113
|
+
localFile.responseProps.deleteIndex > fileIndex ? localFile.responseProps.deleteIndex - 1 : localFile.responseProps.deleteIndex;
|
|
114
|
+
|
|
115
|
+
localFile.props.onDelete = () => deleteFile(localFile, updatedDeleteIndex);
|
|
116
|
+
|
|
117
|
+
localFile.responseProps.deleteIndex = updatedDeleteIndex;
|
|
118
|
+
}
|
|
125
119
|
});
|
|
120
|
+
return tempLocalFiles;
|
|
121
|
+
});
|
|
122
|
+
if (!file.props.error) {
|
|
123
|
+
attachmentCount.current -= 1;
|
|
126
124
|
}
|
|
127
125
|
}
|
|
128
126
|
|
|
129
127
|
setToggleUploadBegin(false);
|
|
130
|
-
setFilesWithError(prevFilesWithError => {
|
|
131
|
-
return prevFilesWithError.filter(f => f.ID !== file.ID);
|
|
132
|
-
});
|
|
133
128
|
},
|
|
134
|
-
[
|
|
129
|
+
[pConn]
|
|
135
130
|
);
|
|
136
131
|
|
|
137
|
-
const onUploadProgress = () => {
|
|
132
|
+
const onUploadProgress = (id, ev) => {
|
|
133
|
+
const progress = Math.floor((ev.loaded / ev.total) * 100);
|
|
134
|
+
setFiles(localFiles => [
|
|
135
|
+
...localFiles.map(localFile => {
|
|
136
|
+
if (localFile.props?.id === id) {
|
|
137
|
+
localFile.inProgress = true;
|
|
138
|
+
localFile.props.progress = progress;
|
|
139
|
+
}
|
|
140
|
+
return localFile;
|
|
141
|
+
})
|
|
142
|
+
]);
|
|
143
|
+
};
|
|
138
144
|
|
|
139
|
-
const
|
|
145
|
+
const populateErrorAndUpdateRedux = file => {
|
|
146
|
+
const fieldName = (pConn.getStateProps() as any).value;
|
|
147
|
+
// set errors to property to block submit even on errors in file upload
|
|
148
|
+
PCore.getMessageManager().addMessages({
|
|
149
|
+
messages: [
|
|
150
|
+
{
|
|
151
|
+
type: 'error',
|
|
152
|
+
message: localizationService.getLocalizedText('Error with one or more files')
|
|
153
|
+
}
|
|
154
|
+
],
|
|
155
|
+
property: fieldName,
|
|
156
|
+
pageReference: pConn.getPageReference(),
|
|
157
|
+
context: contextName
|
|
158
|
+
});
|
|
159
|
+
insertAttachments([file], pConn, multiAttachmentsInInlineEdit.current, {
|
|
160
|
+
allowMultiple,
|
|
161
|
+
isOldAttachment,
|
|
162
|
+
isMultiAttachmentInInlineEditTable,
|
|
163
|
+
attachmentCount: attachmentCount.current
|
|
164
|
+
} as any);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const errorHandler = (isFetchCanceled, file) => {
|
|
140
168
|
return error => {
|
|
141
169
|
if (!isFetchCanceled(error)) {
|
|
142
|
-
let uploadFailMsg =
|
|
170
|
+
let uploadFailMsg = localizationService.getLocalizedText('Something went wrong');
|
|
143
171
|
if (error.response && error.response.data && error.response.data.errorDetails) {
|
|
144
|
-
uploadFailMsg =
|
|
172
|
+
uploadFailMsg = localizationService.getLocalizedText(error.response.data.errorDetails[0].localizedValue);
|
|
145
173
|
}
|
|
174
|
+
|
|
146
175
|
setFiles(current => {
|
|
147
|
-
return current.map(
|
|
148
|
-
if (
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
{
|
|
160
|
-
type: 'error',
|
|
161
|
-
message: pConn.getLocalizedValue('Error with one or more files', '', '')
|
|
162
|
-
}
|
|
163
|
-
],
|
|
164
|
-
property: fieldName,
|
|
165
|
-
pageReference: pConn.getPageReference(),
|
|
166
|
-
context
|
|
167
|
-
});
|
|
168
|
-
delete f.props.progress;
|
|
176
|
+
return current.map((localFile, index) => {
|
|
177
|
+
if (localFile.props.id === file.props.id) {
|
|
178
|
+
localFile.props.meta = uploadFailMsg;
|
|
179
|
+
localFile.props.error = true;
|
|
180
|
+
localFile.props.onDelete = () => deleteFile(localFile, index);
|
|
181
|
+
localFile.props.icon = getIconFromFileType(localFile.type);
|
|
182
|
+
localFile.props.name = localizationService.getLocalizedText('Unable to upload file');
|
|
183
|
+
localFile.inProgress = false;
|
|
184
|
+
delete localFile.props.progress;
|
|
185
|
+
filesWithError.current.push(localFile);
|
|
186
|
+
|
|
187
|
+
populateErrorAndUpdateRedux(localFile);
|
|
169
188
|
}
|
|
170
|
-
return
|
|
189
|
+
return localFile;
|
|
171
190
|
});
|
|
172
191
|
});
|
|
173
192
|
}
|
|
@@ -175,50 +194,28 @@ export default function Attachment(props: AttachmentProps) {
|
|
|
175
194
|
};
|
|
176
195
|
};
|
|
177
196
|
|
|
178
|
-
const validateFileExtension = (fileObj, allowedExtensions) => {
|
|
179
|
-
if (!allowedExtensions) {
|
|
180
|
-
return true;
|
|
181
|
-
}
|
|
182
|
-
const allowedExtensionList = allowedExtensions
|
|
183
|
-
.toLowerCase()
|
|
184
|
-
.split(',')
|
|
185
|
-
.map(item => item.replaceAll('.', '').trim());
|
|
186
|
-
const extension = fileObj.name.split('.').pop().toLowerCase();
|
|
187
|
-
return allowedExtensionList.includes(extension);
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
const clearFieldErrorMessages = () => {
|
|
191
|
-
const fieldName = (pConn.getStateProps() as any).value;
|
|
192
|
-
PCore.getMessageManager().clearMessages({
|
|
193
|
-
type: PCore.getConstants().MESSAGES.MESSAGES_TYPE_ERROR,
|
|
194
|
-
property: fieldName,
|
|
195
|
-
pageReference: pConn.getPageReference(),
|
|
196
|
-
context
|
|
197
|
-
});
|
|
198
|
-
};
|
|
199
|
-
|
|
200
197
|
const onFileAdded = event => {
|
|
201
198
|
let addedFiles = Array.from(event.target.files);
|
|
202
|
-
addedFiles = allowMultiple
|
|
199
|
+
addedFiles = allowMultiple ? addedFiles : [addedFiles[0]];
|
|
203
200
|
const maxAttachmentSize = PCore.getEnvironmentInfo().getMaxAttachmentSize() || '5';
|
|
204
201
|
const tempFilesToBeUploaded = [
|
|
205
202
|
...addedFiles.map((f: any, index) => {
|
|
206
203
|
f.ID = `${new Date().getTime()}I${index}`;
|
|
207
|
-
f.inProgress = true;
|
|
208
204
|
f.props = {
|
|
209
205
|
type: f.type,
|
|
210
206
|
name: f.name,
|
|
207
|
+
id: f.ID,
|
|
208
|
+
format: f.name.split('.').pop(),
|
|
211
209
|
icon: getIconFromFileType(f.type),
|
|
212
|
-
onDelete: () => deleteFile(f)
|
|
210
|
+
onDelete: () => deleteFile(f, index),
|
|
211
|
+
thumbnail: window.URL.createObjectURL(f)
|
|
213
212
|
};
|
|
214
213
|
if (!validateMaxSize(f, maxAttachmentSize)) {
|
|
215
214
|
f.props.error = true;
|
|
216
|
-
f.
|
|
217
|
-
f.props.meta = pConn.getLocalizedValue(`File is too big. Max allowed size is ${maxAttachmentSize}MB.`, '', '');
|
|
215
|
+
f.props.meta = localizationService.getLocalizedText(`File is too big. Max allowed size is ${maxAttachmentSize}MB.`);
|
|
218
216
|
} else if (!validateFileExtension(f, extensions)) {
|
|
219
217
|
f.props.error = true;
|
|
220
|
-
f.
|
|
221
|
-
f.props.meta = `${pConn.getLocalizedValue('File has invalid extension. Allowed extensions are:', '', '')} ${extensions.replaceAll(
|
|
218
|
+
f.props.meta = `${localizationService.getLocalizedText('File has invalid extension. Allowed extensions are:')} ${extensions.replaceAll(
|
|
222
219
|
'.',
|
|
223
220
|
''
|
|
224
221
|
)}`;
|
|
@@ -229,22 +226,30 @@ export default function Attachment(props: AttachmentProps) {
|
|
|
229
226
|
messages: [
|
|
230
227
|
{
|
|
231
228
|
type: 'error',
|
|
232
|
-
message:
|
|
229
|
+
message: localizationService.getLocalizedText('Error with one or more files')
|
|
233
230
|
}
|
|
234
231
|
],
|
|
235
232
|
property: fieldName,
|
|
236
233
|
pageReference: pConn.getPageReference(),
|
|
237
|
-
context
|
|
234
|
+
context: contextName
|
|
238
235
|
});
|
|
239
236
|
}
|
|
240
237
|
return f;
|
|
241
238
|
})
|
|
242
239
|
];
|
|
240
|
+
|
|
243
241
|
const tempFilesWithError = tempFilesToBeUploaded.filter(f => f.props.error);
|
|
244
242
|
if (tempFilesWithError.length > 0) {
|
|
245
|
-
|
|
243
|
+
filesWithError.current = [...filesWithError.current, ...tempFilesWithError];
|
|
244
|
+
|
|
245
|
+
insertAttachments(tempFilesWithError, pConn, multiAttachmentsInInlineEdit.current, {
|
|
246
|
+
allowMultiple,
|
|
247
|
+
isOldAttachment,
|
|
248
|
+
isMultiAttachmentInInlineEditTable,
|
|
249
|
+
attachmentCount: attachmentCount.current
|
|
250
|
+
} as PageInstructionOptions);
|
|
246
251
|
}
|
|
247
|
-
setFiles(current => (allowMultiple
|
|
252
|
+
setFiles(current => (!allowMultiple ? [...tempFilesToBeUploaded] : [...current, ...tempFilesToBeUploaded]));
|
|
248
253
|
setToggleUploadBegin(true);
|
|
249
254
|
};
|
|
250
255
|
|
|
@@ -253,102 +258,129 @@ export default function Attachment(props: AttachmentProps) {
|
|
|
253
258
|
.filter(e => {
|
|
254
259
|
const isFileUploaded = e.props && e.props.progress === 100;
|
|
255
260
|
const fileHasError = e.props && e.props.error;
|
|
256
|
-
const
|
|
257
|
-
|
|
261
|
+
const isFileUploadedInLastStep = e.responseProps && e.responseProps.ID !== 'temp';
|
|
262
|
+
const isFileUploadInProgress = e.inProgress;
|
|
263
|
+
return !isFileUploadInProgress && !isFileUploaded && !fileHasError && !isFileUploadedInLastStep;
|
|
258
264
|
})
|
|
259
|
-
.map(
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
onUploadProgress();
|
|
265
|
+
.map(file =>
|
|
266
|
+
PCore.getAttachmentUtils().uploadAttachment(
|
|
267
|
+
file,
|
|
268
|
+
ev => {
|
|
269
|
+
onUploadProgress(file.props.id, ev);
|
|
264
270
|
},
|
|
265
271
|
isFetchCanceled => {
|
|
266
|
-
return errorHandler(isFetchCanceled,
|
|
272
|
+
return errorHandler(isFetchCanceled, file);
|
|
267
273
|
},
|
|
268
|
-
|
|
274
|
+
contextName
|
|
269
275
|
)
|
|
270
276
|
);
|
|
277
|
+
|
|
278
|
+
// allow new files to be added when other files upload is still in progress
|
|
279
|
+
setToggleUploadBegin(false);
|
|
271
280
|
Promise.allSettled(filesToBeUploaded)
|
|
272
281
|
.then((fileResponses: any) => {
|
|
273
282
|
fileResponses = fileResponses.filter(fr => fr.status !== 'rejected'); // in case of deleting an in progress file, promise gets cancelled but still enters then block
|
|
274
283
|
if (fileResponses.length > 0) {
|
|
275
|
-
setFiles(
|
|
276
|
-
const tempFilesUploaded = [...
|
|
277
|
-
tempFilesUploaded.forEach(
|
|
278
|
-
|
|
284
|
+
setFiles(localFiles => {
|
|
285
|
+
const tempFilesUploaded = [...localFiles];
|
|
286
|
+
tempFilesUploaded.forEach(localFile => {
|
|
287
|
+
// if attach field has multiple files & in bw any error files are present
|
|
288
|
+
// Example : files = [properFile1, errFile, errFile, properFile2]
|
|
289
|
+
// indexes for delete & preview should be for files [properFile1, properFile2] which is [1,2]
|
|
290
|
+
const index = fileResponses.findIndex(fileResponse => fileResponse.value.clientFileID === localFile.props.id);
|
|
279
291
|
if (index >= 0) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
292
|
+
fileResponses[index].value.thumbnail = localFile.props.thumbnail;
|
|
293
|
+
localFile.inProgress = false;
|
|
294
|
+
localFile.ID = fileResponses[index].value.ID;
|
|
295
|
+
localFile.props.meta = localizationService.getLocalizedText('Uploaded successfully');
|
|
296
|
+
localFile.props.progress = 100;
|
|
297
|
+
localFile.handle = fileResponses[index].value.ID;
|
|
298
|
+
localFile.label = valueRef;
|
|
299
|
+
localFile.responseProps = {
|
|
300
|
+
pzInsKey: 'temp'
|
|
289
301
|
};
|
|
290
302
|
}
|
|
291
303
|
});
|
|
292
304
|
return tempFilesUploaded;
|
|
293
305
|
});
|
|
294
306
|
|
|
295
|
-
|
|
296
|
-
|
|
307
|
+
insertAttachments(fileResponses, pConn, multiAttachmentsInInlineEdit.current, {
|
|
308
|
+
allowMultiple,
|
|
309
|
+
isOldAttachment,
|
|
310
|
+
isMultiAttachmentInInlineEditTable,
|
|
311
|
+
attachmentCount: attachmentCount.current,
|
|
312
|
+
insert: true
|
|
313
|
+
} as any);
|
|
314
|
+
attachmentCount.current += fileResponses.length;
|
|
315
|
+
|
|
316
|
+
if (filesWithError.current.length === 0) {
|
|
317
|
+
clearFieldErrorMessages(pConn);
|
|
297
318
|
}
|
|
298
319
|
}
|
|
299
|
-
|
|
320
|
+
actionSequencer.deRegisterBlockingAction(contextName).catch(() => {});
|
|
300
321
|
})
|
|
301
322
|
.catch(error => {
|
|
302
323
|
console.log(error);
|
|
303
324
|
setToggleUploadBegin(false);
|
|
304
325
|
});
|
|
305
|
-
}, [files
|
|
326
|
+
}, [files]);
|
|
306
327
|
|
|
307
328
|
useEffect(() => {
|
|
308
329
|
if (toggleUploadBegin && files.length > 0) {
|
|
309
|
-
|
|
330
|
+
actionSequencer.registerBlockingAction(contextName).then(() => {
|
|
331
|
+
uploadFiles();
|
|
332
|
+
});
|
|
310
333
|
}
|
|
311
334
|
}, [toggleUploadBegin]);
|
|
312
335
|
|
|
313
336
|
useEffect(() => {
|
|
314
|
-
if (
|
|
315
|
-
|
|
316
|
-
// block duplicate files to redux store when added 1 after another to prevent multiple duplicates being added to the case on submit
|
|
317
|
-
const tempFiles = files.filter(f => currentAttachmentList.findIndex(fr => fr.ID === f.ID) === -1 && !f.inProgress && f.responseProps);
|
|
318
|
-
|
|
319
|
-
const updatedAttList = [...currentAttachmentList, ...tempFiles];
|
|
320
|
-
updateAttachmentState(pConn, getAttachmentKey(valueRef, embeddedProperty), updatedAttList);
|
|
321
|
-
}
|
|
322
|
-
}, [files]);
|
|
323
|
-
|
|
324
|
-
useEffect(() => {
|
|
325
|
-
if (filesWithError.length === 0) {
|
|
326
|
-
clearFieldErrorMessages();
|
|
337
|
+
if (filesWithError.current.length === 0) {
|
|
338
|
+
clearFieldErrorMessages(pConn);
|
|
327
339
|
}
|
|
328
340
|
}, [filesWithError]);
|
|
329
341
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
342
|
+
const memoizedAttachments = useDeepMemo(() => {
|
|
343
|
+
return attachments;
|
|
344
|
+
}, [attachments]);
|
|
345
|
+
|
|
346
|
+
// Prepares new structure as per Cosmos component
|
|
347
|
+
const transformAttachments = () => {
|
|
348
|
+
const transformedFiles = [...attachments];
|
|
349
|
+
let deleteIndex = -1;
|
|
350
|
+
transformedFiles.forEach(attachment => {
|
|
351
|
+
attachment.props.id = attachment.responseProps.ID;
|
|
352
|
+
attachment.props.format = attachment.props.name.split('.').pop();
|
|
353
|
+
if (attachment.props.error) {
|
|
354
|
+
attachment.responseProps.deleteIndex = deleteIndex;
|
|
355
|
+
} else {
|
|
356
|
+
deleteIndex += 1;
|
|
357
|
+
attachment.responseProps.deleteIndex = deleteIndex;
|
|
358
|
+
}
|
|
359
|
+
if (attachment.props.thumbnail) {
|
|
360
|
+
thumbnailURLs.current.push(attachment.props.thumbnail);
|
|
361
|
+
}
|
|
348
362
|
});
|
|
363
|
+
|
|
364
|
+
return transformedFiles;
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
useEffect(() => {
|
|
368
|
+
const caseID = PCore.getStoreValue(`.${getMappedValue('pyID')}`, PCore.getResolvedConstantValue('caseInfo.content'), contextName);
|
|
349
369
|
if (displayMode !== 'DISPLAY_ONLY') {
|
|
350
|
-
PCore.getPubSubUtils().subscribe(
|
|
370
|
+
PCore.getPubSubUtils().subscribe(
|
|
371
|
+
PCore.getConstants().PUB_SUB_EVENTS.CASE_EVENTS.ASSIGNMENT_SUBMISSION,
|
|
372
|
+
() => {
|
|
373
|
+
overrideLocalState.current = true;
|
|
374
|
+
},
|
|
375
|
+
caseID
|
|
376
|
+
);
|
|
351
377
|
}
|
|
378
|
+
|
|
379
|
+
// When component mounts, only set local files state from redux.
|
|
380
|
+
const serverFiles = transformAttachments();
|
|
381
|
+
setFiles(serverFiles);
|
|
382
|
+
filesWithError.current = serverFiles.filter(file => file.props.error);
|
|
383
|
+
|
|
352
384
|
return () => {
|
|
353
385
|
if (displayMode !== 'DISPLAY_ONLY') {
|
|
354
386
|
PCore.getPubSubUtils().unsubscribe(PCore.getConstants().PUB_SUB_EVENTS.CASE_EVENTS.ASSIGNMENT_SUBMISSION, caseID);
|
|
@@ -356,6 +388,48 @@ export default function Attachment(props: AttachmentProps) {
|
|
|
356
388
|
};
|
|
357
389
|
}, []);
|
|
358
390
|
|
|
391
|
+
useEffect(() => {
|
|
392
|
+
if (overrideLocalState.current) {
|
|
393
|
+
const serverFiles = transformAttachments();
|
|
394
|
+
overrideLocalState.current = false;
|
|
395
|
+
attachmentCount.current = attachments.length;
|
|
396
|
+
filesWithError.current = [];
|
|
397
|
+
setFiles(serverFiles);
|
|
398
|
+
} else {
|
|
399
|
+
// 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
|
|
400
|
+
const errorFiles = attachments.filter(attachment => attachment.props.error);
|
|
401
|
+
if (errorFiles.length === 0 && filesWithError.current.length > 0) {
|
|
402
|
+
// Check if local file state contains error files and push those to redux
|
|
403
|
+
const uniqueKey = getMappedValue('pzInsKey');
|
|
404
|
+
const transformedErrorFiles = filesWithError.current.map(errorFile => {
|
|
405
|
+
const filename = errorFile.props.name;
|
|
406
|
+
return {
|
|
407
|
+
[uniqueKey]: errorFile.props.id,
|
|
408
|
+
FileName: filename,
|
|
409
|
+
Category: '',
|
|
410
|
+
FileExtension: filename.split('.').pop() ?? filename,
|
|
411
|
+
error: errorFile.props.error || null
|
|
412
|
+
};
|
|
413
|
+
});
|
|
414
|
+
let key = '';
|
|
415
|
+
let updatedAttachments: any = [];
|
|
416
|
+
if (allowMultiple || isOldAttachment) {
|
|
417
|
+
key = isOldAttachment ? `${valueRef}.pxResults` : valueRef;
|
|
418
|
+
const existingAttachments = PCore.getStoreValue(`.${key}`, pConn.getPageReference(), pConn.getContextName()) || [];
|
|
419
|
+
updatedAttachments = [...existingAttachments, ...transformedErrorFiles];
|
|
420
|
+
} else {
|
|
421
|
+
key = valueRef;
|
|
422
|
+
updatedAttachments = transformedErrorFiles[0];
|
|
423
|
+
}
|
|
424
|
+
PCore.getStateUtils().updateState(pConn.getContextName(), key, updatedAttachments, {
|
|
425
|
+
pageReference: pConn.getPageReference(),
|
|
426
|
+
isArrayDeepMerge: false,
|
|
427
|
+
removePropertyFromChangedList: true
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}, [memoizedAttachments]);
|
|
432
|
+
|
|
359
433
|
const handleClick = event => {
|
|
360
434
|
setAnchorEl(event.currentTarget);
|
|
361
435
|
};
|
|
@@ -376,13 +450,13 @@ export default function Attachment(props: AttachmentProps) {
|
|
|
376
450
|
id={valueRef}
|
|
377
451
|
name='upload-photo'
|
|
378
452
|
type='file'
|
|
379
|
-
multiple={allowMultiple
|
|
453
|
+
multiple={allowMultiple}
|
|
380
454
|
required={required}
|
|
381
455
|
disabled={disabled}
|
|
382
456
|
onChange={onFileAdded}
|
|
383
457
|
/>
|
|
384
458
|
<Button style={{ textTransform: 'none' }} variant='outlined' color='primary' component='span'>
|
|
385
|
-
{allowMultiple
|
|
459
|
+
{allowMultiple
|
|
386
460
|
? uploadMultipleFilesLabel === 'file_upload_text_multiple'
|
|
387
461
|
? 'Choose files'
|
|
388
462
|
: uploadMultipleFilesLabel
|
|
@@ -416,7 +490,7 @@ export default function Attachment(props: AttachmentProps) {
|
|
|
416
490
|
</div>
|
|
417
491
|
<div className='psdk-utility-action'>
|
|
418
492
|
{item.ID && (
|
|
419
|
-
<button type='button' className='psdk-utility-button' aria-label='Delete Attachment' onClick={() => deleteFile(item)}>
|
|
493
|
+
<button type='button' className='psdk-utility-button' aria-label='Delete Attachment' onClick={() => deleteFile(item, index)}>
|
|
420
494
|
<img className='psdk-utility-card-action-svg-icon' src={deleteIcon} />
|
|
421
495
|
</button>
|
|
422
496
|
)}
|
|
@@ -443,7 +517,7 @@ export default function Attachment(props: AttachmentProps) {
|
|
|
443
517
|
>
|
|
444
518
|
Download
|
|
445
519
|
</MenuItem>
|
|
446
|
-
<MenuItem style={{ fontSize: '14px' }} key='delete' onClick={() => deleteFile(item)}>
|
|
520
|
+
<MenuItem style={{ fontSize: '14px' }} key='delete' onClick={() => deleteFile(item, index)}>
|
|
447
521
|
Delete
|
|
448
522
|
</MenuItem>
|
|
449
523
|
</Menu>
|
|
@@ -459,7 +533,7 @@ export default function Attachment(props: AttachmentProps) {
|
|
|
459
533
|
return (
|
|
460
534
|
<div className='file-upload-container'>
|
|
461
535
|
<span className={`label ${required ? 'file-label' : ''}`}>{label}</span>
|
|
462
|
-
{((files.length === 0 && allowMultiple
|
|
536
|
+
{((files.length === 0 && !allowMultiple) || allowMultiple) && <section>{content}</section>}
|
|
463
537
|
{validatemessage !== '' ? <span className='file-error'>{validatemessage}</span> : <span style={{ fontSize: '14px' }}>{helperText}</span>}
|
|
464
538
|
{files && files.length > 0 && <section>{fileDisplay}</section>}
|
|
465
539
|
</div>
|