@evoke-platform/ui-components 1.12.0-dev.1 → 1.12.0-dev.2
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/dist/published/components/custom/Form/Common/Form.js +15 -7
- package/dist/published/components/custom/Form/FormComponents/DocumentComponent/Document.js +6 -1
- package/dist/published/components/custom/Form/FormComponents/DocumentComponent/DocumentList.js +20 -6
- package/dist/published/components/custom/Form/utils.js +16 -12
- package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.js +18 -3
- package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +1 -1
- package/dist/published/components/custom/FormV2/components/types.d.ts +8 -0
- package/dist/published/components/custom/FormV2/components/utils.d.ts +4 -0
- package/dist/published/components/custom/FormV2/components/utils.js +48 -10
- package/dist/published/components/custom/ViewDetailsV2/InstanceEntryRenderer.js +2 -2
- package/package.json +1 -1
|
@@ -62,6 +62,7 @@ export function Form(props) {
|
|
|
62
62
|
TextField: FormFieldComponent,
|
|
63
63
|
Decimal: FormFieldComponent,
|
|
64
64
|
Document: DocumentComponent,
|
|
65
|
+
File: DocumentComponent,
|
|
65
66
|
Integer: FormFieldComponent,
|
|
66
67
|
Buttons: ButtonComponent,
|
|
67
68
|
Content: Components.components.content,
|
|
@@ -241,7 +242,8 @@ export function Form(props) {
|
|
|
241
242
|
else if (object?.properties) {
|
|
242
243
|
// If form.io form is not configured and no inputProperties are
|
|
243
244
|
// set, use object properties to build form.
|
|
244
|
-
const propertiesInForm = object.properties.filter((prop) => prop.id !== associatedObject?.propertyId &&
|
|
245
|
+
const propertiesInForm = object.properties.filter((prop) => prop.id !== associatedObject?.propertyId &&
|
|
246
|
+
(action?.type !== 'create' || !['document', 'file'].includes(prop.type)));
|
|
245
247
|
const components = buildComponentPropsFromObjectProperties(propertiesInForm, object.id, instance, {
|
|
246
248
|
...objectInputCommonProps,
|
|
247
249
|
defaultPages: allDefaultPages,
|
|
@@ -284,8 +286,8 @@ export function Form(props) {
|
|
|
284
286
|
};
|
|
285
287
|
const saveDocuments = async (submittedFields, action) => {
|
|
286
288
|
const documentProperties = action?.parameters
|
|
287
|
-
? action.parameters.filter((param) => param.type
|
|
288
|
-
: object?.properties?.filter((prop) => prop.type
|
|
289
|
+
? action.parameters.filter((param) => ['document', 'file'].includes(param.type))
|
|
290
|
+
: object?.properties?.filter((prop) => ['document', 'file'].includes(prop.type));
|
|
289
291
|
let allEntries = null;
|
|
290
292
|
for (const docProperty of documentProperties ?? []) {
|
|
291
293
|
const currentValue = submittedFields[docProperty.id];
|
|
@@ -330,8 +332,8 @@ export function Form(props) {
|
|
|
330
332
|
};
|
|
331
333
|
const deleteDocuments = async (submittedFields, requestSuccess, action) => {
|
|
332
334
|
const documentProperties = action?.parameters
|
|
333
|
-
? action.parameters.filter((param) => param.type
|
|
334
|
-
: object?.properties?.filter((prop) => prop.type
|
|
335
|
+
? action.parameters.filter((param) => ['document', 'file'].includes(param.type))
|
|
336
|
+
: object?.properties?.filter((prop) => ['document', 'file'].includes(prop.type));
|
|
335
337
|
for (const docProperty of documentProperties ?? []) {
|
|
336
338
|
const savedValue = submittedFields[docProperty.id];
|
|
337
339
|
const originalValue = instance?.[docProperty.id];
|
|
@@ -340,11 +342,17 @@ export function Form(props) {
|
|
|
340
342
|
: (savedValue?.filter((file) => !originalValue?.some((f) => f.id === file.id)) ?? []);
|
|
341
343
|
for (const doc of documentsToRemove) {
|
|
342
344
|
try {
|
|
343
|
-
|
|
345
|
+
// Use different endpoints based on property type
|
|
346
|
+
const deleteEndpoint = docProperty.type === 'file'
|
|
347
|
+
? getPrefixedUrl(`/files/${doc.id}`)
|
|
348
|
+
: getPrefixedUrl(`/objects/${object?.id}/instances/${instance?.id}/documents/${doc.id}`);
|
|
349
|
+
await apiServices?.delete(deleteEndpoint);
|
|
344
350
|
}
|
|
345
351
|
catch (error) {
|
|
346
352
|
if (error.response?.status !== 404) {
|
|
347
|
-
setSnackbarError({
|
|
353
|
+
setSnackbarError({
|
|
354
|
+
message: `An error occurred while removing ${docProperty.type === 'file' ? 'file' : 'document'} '${doc.name}'`,
|
|
355
|
+
});
|
|
348
356
|
}
|
|
349
357
|
}
|
|
350
358
|
}
|
|
@@ -40,8 +40,13 @@ export const Document = (props) => {
|
|
|
40
40
|
}, []);
|
|
41
41
|
const checkPermissions = () => {
|
|
42
42
|
if (canUpdateProperty) {
|
|
43
|
+
// For 'file' type properties, check permissions on the sys__file object
|
|
44
|
+
// For 'document' type properties, check document attachment permissions
|
|
45
|
+
const endpoint = property.type === 'file'
|
|
46
|
+
? getPrefixedUrl(`/objects/sys__file/instances/${instance.id}/checkAccess?action=update`)
|
|
47
|
+
: getPrefixedUrl(`/objects/${objectId}/instances/${instance.id}/documents/checkAccess?action=update`);
|
|
43
48
|
apiServices
|
|
44
|
-
.get(
|
|
49
|
+
.get(endpoint)
|
|
45
50
|
.then((accessCheck) => setHasUpdatePermission(accessCheck.result))
|
|
46
51
|
.catch(() => setHasUpdatePermission(false));
|
|
47
52
|
}
|
package/dist/published/components/custom/Form/FormComponents/DocumentComponent/DocumentList.js
CHANGED
|
@@ -36,13 +36,19 @@ export const DocumentList = (props) => {
|
|
|
36
36
|
}
|
|
37
37
|
}, [property]);
|
|
38
38
|
const getDocuments = (currentDocumentIds, shouldRetry = true) => {
|
|
39
|
-
|
|
39
|
+
// For 'file' type properties, fetch sys__file instances directly
|
|
40
|
+
// For 'document' type properties, fetch attachment documents
|
|
41
|
+
const endpoint = property.type === 'file'
|
|
42
|
+
? getPrefixedUrl(`/objects/sys__file/instances`)
|
|
43
|
+
: getPrefixedUrl(`/objects/${objectId}/instances/${instance.id}/documents`);
|
|
44
|
+
apiServices.get(endpoint, {
|
|
40
45
|
params: { filter: { where: { id: { inq: currentDocumentIds } } } },
|
|
41
46
|
}, (error, docs) => {
|
|
42
47
|
// There is a short delay between when a document is uploaded and when
|
|
43
48
|
// it is indexed. Therefore, try again if documents are not found.
|
|
44
49
|
if (shouldRetry &&
|
|
45
|
-
(!docs ||
|
|
50
|
+
(!docs ||
|
|
51
|
+
currentDocumentIds.some((docId) => !docs.find((doc) => docId === doc.id)))) {
|
|
46
52
|
setTimeout(() => getDocuments(currentDocumentIds, false), 2000);
|
|
47
53
|
}
|
|
48
54
|
else if (error) {
|
|
@@ -58,9 +64,12 @@ export const DocumentList = (props) => {
|
|
|
58
64
|
}, []);
|
|
59
65
|
const checkPermissions = () => {
|
|
60
66
|
if (instance?.[property.id]?.length) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
// For 'file' type properties, check permissions on the sys__file object
|
|
68
|
+
// For 'document' type properties, check document attachment permissions
|
|
69
|
+
const endpoint = property.type === 'file'
|
|
70
|
+
? getPrefixedUrl(`/objects/sys__file/instances/${instance.id}/checkAccess?action=view`)
|
|
71
|
+
: getPrefixedUrl(`/objects/${objectId}/instances/${instance.id}/documents/checkAccess?action=view`);
|
|
72
|
+
apiServices.get(endpoint).then((accessCheck) => setHasViewPermission(accessCheck.result));
|
|
64
73
|
}
|
|
65
74
|
};
|
|
66
75
|
const isFile = (doc) => doc instanceof File;
|
|
@@ -78,7 +87,12 @@ export const DocumentList = (props) => {
|
|
|
78
87
|
: savedDocuments?.find((savedDocument) => savedDocument.id === doc.id)?.contentType;
|
|
79
88
|
if (!isFile(doc)) {
|
|
80
89
|
try {
|
|
81
|
-
|
|
90
|
+
// For 'file' type properties, use /files/{id}/content endpoint
|
|
91
|
+
// For 'document' type properties, use /objects/{id}/instances/{id}/documents/{id}/content endpoint
|
|
92
|
+
const contentEndpoint = property.type === 'file'
|
|
93
|
+
? getPrefixedUrl(`/files/${doc.id}/content`)
|
|
94
|
+
: getPrefixedUrl(`/objects/${objectId}/instances/${instance.id}/documents/${doc.id}/content`);
|
|
95
|
+
const documentResponse = await apiServices.get(contentEndpoint, { responseType: 'blob' });
|
|
82
96
|
const blob = new Blob([documentResponse], { type: contentType });
|
|
83
97
|
url = window.URL.createObjectURL(blob);
|
|
84
98
|
}
|
|
@@ -25,6 +25,8 @@ export function determineComponentType(properties, parameter) {
|
|
|
25
25
|
}
|
|
26
26
|
case 'document':
|
|
27
27
|
return 'Document';
|
|
28
|
+
case 'file':
|
|
29
|
+
return 'File';
|
|
28
30
|
case 'array':
|
|
29
31
|
return 'MultiSelect';
|
|
30
32
|
case 'number':
|
|
@@ -161,7 +163,7 @@ export function convertFormToComponents(entries, parameters, object) {
|
|
|
161
163
|
rows: displayOptions?.rowCount,
|
|
162
164
|
key: parameter.id,
|
|
163
165
|
type,
|
|
164
|
-
multiple: ['array', 'document'].includes(parameter.type),
|
|
166
|
+
multiple: ['array', 'document', 'file'].includes(parameter.type),
|
|
165
167
|
data: {
|
|
166
168
|
values: entry.type === 'input' ? entry.enumWithLabels : undefined,
|
|
167
169
|
},
|
|
@@ -227,19 +229,19 @@ export function convertFormToComponents(entries, parameters, object) {
|
|
|
227
229
|
max: ['integer', 'number'].includes(parameter.type ?? '')
|
|
228
230
|
? parameter.validation?.maximum
|
|
229
231
|
: undefined,
|
|
230
|
-
minDocuments: parameter.type
|
|
232
|
+
minDocuments: ['document', 'file'].includes(parameter.type ?? '')
|
|
231
233
|
? parameter.validation?.minDocuments
|
|
232
234
|
: undefined,
|
|
233
|
-
maxDocuments: parameter.type
|
|
235
|
+
maxDocuments: ['document', 'file'].includes(parameter.type ?? '')
|
|
234
236
|
? parameter.validation?.maxDocuments
|
|
235
237
|
: undefined,
|
|
236
|
-
allowedFileExtensions: parameter.type
|
|
238
|
+
allowedFileExtensions: ['document', 'file'].includes(parameter.type ?? '')
|
|
237
239
|
? parameter.validation?.allowedFileExtensions
|
|
238
240
|
: undefined,
|
|
239
|
-
maxSizeInKB: parameter.type
|
|
241
|
+
maxSizeInKB: ['document', 'file'].includes(parameter.type ?? '')
|
|
240
242
|
? parameter.validation?.maxSizeInKB
|
|
241
243
|
: undefined,
|
|
242
|
-
customMessage: ['integer', 'number', 'date', 'time', 'document'].includes(parameter.type ?? '')
|
|
244
|
+
customMessage: ['integer', 'number', 'date', 'time', 'document', 'file'].includes(parameter.type ?? '')
|
|
243
245
|
? parameter.validation
|
|
244
246
|
?.errorMessage
|
|
245
247
|
: '',
|
|
@@ -254,7 +256,9 @@ export function convertFormToComponents(entries, parameters, object) {
|
|
|
254
256
|
conditional: convertVisibilityToConditional(displayOptions?.visibility),
|
|
255
257
|
viewLayout: displayOptions?.viewLayout,
|
|
256
258
|
strictlyTrue: parameter.type === 'boolean' && parameter.strictlyTrue,
|
|
257
|
-
documentMetadata: parameter.type
|
|
259
|
+
documentMetadata: ['document', 'file'].includes(parameter.type ?? '')
|
|
260
|
+
? entry.documentMetadata
|
|
261
|
+
: undefined,
|
|
258
262
|
};
|
|
259
263
|
}
|
|
260
264
|
})
|
|
@@ -454,7 +458,7 @@ export function getMiddleInstance(instanceId, property, middleObjectInstances) {
|
|
|
454
458
|
// The following function is used to prefix the URL with the appropriate path.
|
|
455
459
|
export function getPrefixedUrl(url) {
|
|
456
460
|
const wcsMatchers = ['/apps', '/pages', '/widgets'];
|
|
457
|
-
const dataMatchers = ['/objects', '/correspondenceTemplates', '/documents', '/payments', '/locations'];
|
|
461
|
+
const dataMatchers = ['/objects', '/correspondenceTemplates', '/documents', '/files', '/payments', '/locations'];
|
|
458
462
|
const signalrMatchers = ['/hubs'];
|
|
459
463
|
const accessManagementMatchers = ['/users'];
|
|
460
464
|
const workflowMatchers = ['/workflows'];
|
|
@@ -664,7 +668,7 @@ formComponents, allCriteriaInputs, instance, objectPropertyInputProps, associate
|
|
|
664
668
|
properties,
|
|
665
669
|
type: `${readOnly ? 'ViewOnly' : ''}${component.type}`,
|
|
666
670
|
readOnly: component.readOnly || readOnly || property?.formula,
|
|
667
|
-
multiple: ['array', 'document'].includes(property.type),
|
|
671
|
+
multiple: ['array', 'document', 'file'].includes(property.type),
|
|
668
672
|
instance: instance,
|
|
669
673
|
fromFormBuilder: true,
|
|
670
674
|
apiServices: objectPropertyInputProps?.apiServices,
|
|
@@ -708,7 +712,7 @@ formComponents, allCriteriaInputs, instance, objectPropertyInputProps, associate
|
|
|
708
712
|
property,
|
|
709
713
|
associatedObject,
|
|
710
714
|
readOnly: component.readOnly || readOnly || property?.formula,
|
|
711
|
-
multiple: ['array', 'document'].includes(property.type),
|
|
715
|
+
multiple: ['array', 'document', 'file'].includes(property.type),
|
|
712
716
|
instance: instance,
|
|
713
717
|
fromFormBuilder: true,
|
|
714
718
|
apiServices: objectPropertyInputProps?.apiServices,
|
|
@@ -1115,7 +1119,7 @@ export const buildComponentPropsFromObjectProperties = (properties, objectId, in
|
|
|
1115
1119
|
key: property.id,
|
|
1116
1120
|
label: property.name,
|
|
1117
1121
|
inputMask: property.mask,
|
|
1118
|
-
multiple: ['array', 'document'].includes(property.type),
|
|
1122
|
+
multiple: ['array', 'document', 'file'].includes(property.type),
|
|
1119
1123
|
readOnly: !hasActionPermissions || readOnly || !!property?.formula,
|
|
1120
1124
|
instance: instance,
|
|
1121
1125
|
validate: {
|
|
@@ -1155,7 +1159,7 @@ export const buildComponentPropsFromObjectProperties = (properties, objectId, in
|
|
|
1155
1159
|
: 'Property is not in a valid format',
|
|
1156
1160
|
})),
|
|
1157
1161
|
}),
|
|
1158
|
-
...(property.type
|
|
1162
|
+
...(['document', 'file'].includes(property.type) &&
|
|
1159
1163
|
property.validation && {
|
|
1160
1164
|
minDocuments: property.validation.minDocuments,
|
|
1161
1165
|
maxDocuments: property.validation.maxDocuments,
|
|
@@ -27,7 +27,11 @@ export const DocumentList = (props) => {
|
|
|
27
27
|
const { handleChange, onAutosave, id, canUpdateProperty, value: documents, setSnackbarError } = props;
|
|
28
28
|
const apiServices = useApiServices();
|
|
29
29
|
const { fetchedOptions, setFetchedOptions, object, instance } = useFormContext();
|
|
30
|
+
// Determine property type once at component level
|
|
31
|
+
const propertyType = object?.properties?.find((p) => p.id === id)?.type;
|
|
32
|
+
const isFileType = propertyType === 'file';
|
|
30
33
|
const [hasViewPermission, setHasViewPermission] = useState(fetchedOptions[`${id}ViewPermission`] ?? true);
|
|
34
|
+
// savedDocuments is either FileInstance[] or DocumentType[], never a mix
|
|
31
35
|
const [savedDocuments, setSavedDocuments] = useState(fetchedOptions[`${id}SavedDocuments`]);
|
|
32
36
|
useEffect(() => {
|
|
33
37
|
const currentValue = instance?.[id];
|
|
@@ -49,13 +53,19 @@ export const DocumentList = (props) => {
|
|
|
49
53
|
}
|
|
50
54
|
}, [fetchedOptions]);
|
|
51
55
|
const getDocuments = (currentDocumentIds, shouldRetry = true) => {
|
|
52
|
-
|
|
56
|
+
// For 'file' type properties, fetch sys__file instances directly
|
|
57
|
+
// For 'document' type properties, fetch attachment documents
|
|
58
|
+
const endpoint = isFileType
|
|
59
|
+
? getPrefixedUrl(`/objects/sys__file/instances`)
|
|
60
|
+
: getPrefixedUrl(`/objects/${object?.id}/instances/${instance?.id}/documents`);
|
|
61
|
+
apiServices.get(endpoint, {
|
|
53
62
|
params: { filter: { where: { id: { inq: currentDocumentIds } } } },
|
|
54
63
|
}, (error, docs) => {
|
|
55
64
|
// There is a short delay between when a document is uploaded and when
|
|
56
65
|
// it is indexed. Therefore, try again if documents are not found.
|
|
57
66
|
if (shouldRetry &&
|
|
58
|
-
(!docs ||
|
|
67
|
+
(!docs ||
|
|
68
|
+
currentDocumentIds.some((docId) => !docs.find((doc) => docId === doc.id)))) {
|
|
59
69
|
setTimeout(() => getDocuments(currentDocumentIds, false), 2000);
|
|
60
70
|
}
|
|
61
71
|
else if (error) {
|
|
@@ -114,7 +124,12 @@ export const DocumentList = (props) => {
|
|
|
114
124
|
: savedDocuments?.find((savedDocument) => savedDocument.id === doc.id)?.contentType;
|
|
115
125
|
if (!isFile(doc)) {
|
|
116
126
|
try {
|
|
117
|
-
|
|
127
|
+
// Determine property type to use the correct endpoint
|
|
128
|
+
const propertyType = object?.properties?.find((p) => p.id === id)?.type;
|
|
129
|
+
const contentEndpoint = propertyType === 'file'
|
|
130
|
+
? getPrefixedUrl(`/files/${doc.id}/content`)
|
|
131
|
+
: getPrefixedUrl(`/objects/${object?.id}/instances/${instance?.id}/documents/${doc.id}/content`);
|
|
132
|
+
const documentResponse = await apiServices.get(contentEndpoint, { responseType: 'blob' });
|
|
118
133
|
const blob = new Blob([documentResponse], { type: contentType });
|
|
119
134
|
url = window.URL.createObjectURL(blob);
|
|
120
135
|
}
|
|
@@ -134,7 +134,7 @@ export function RecursiveEntryRenderer(props) {
|
|
|
134
134
|
});
|
|
135
135
|
}, readOnly: entry.type === 'readonlyField', placeholder: display?.placeholder, error: !!errors?.[entryId], errorMessage: errors?.[entryId]?.message, isMultiLineText: !!display?.rowCount, rows: display?.rowCount, size: fieldHeight }))));
|
|
136
136
|
}
|
|
137
|
-
else if (fieldDefinition.type === 'document') {
|
|
137
|
+
else if (fieldDefinition.type === 'document' || fieldDefinition.type === 'file') {
|
|
138
138
|
return (React.createElement(FieldWrapper, { ...getFieldWrapperProps(fieldDefinition, entry, entryId, fieldValue, display, errors) },
|
|
139
139
|
React.createElement(Document, { id: entryId, error: !!errors?.[entryId], value: fieldValue, canUpdateProperty: !(entry.type === 'readonlyField'), hasDescription: !!display?.description, validate: validation })));
|
|
140
140
|
}
|
|
@@ -27,6 +27,14 @@ export type Document = {
|
|
|
27
27
|
};
|
|
28
28
|
versionId?: string;
|
|
29
29
|
};
|
|
30
|
+
export type FileInstance = {
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
contentType: string;
|
|
34
|
+
size: number;
|
|
35
|
+
dateUploaded: string;
|
|
36
|
+
uploadedBy: string;
|
|
37
|
+
};
|
|
30
38
|
export type SimpleEditorProps = {
|
|
31
39
|
id: string;
|
|
32
40
|
value: string;
|
|
@@ -49,6 +49,10 @@ export declare const convertPropertiesToParams: (object: Obj) => InputParameter[
|
|
|
49
49
|
export declare function getUnnestedEntries(entries: FormEntry[]): FormEntry[];
|
|
50
50
|
export declare const isEmptyWithDefault: (fieldValue: unknown, entry: InputParameterReference | InputField, instance: Record<string, unknown> | object) => boolean | "" | 0 | undefined;
|
|
51
51
|
export declare const docProperties: Property[];
|
|
52
|
+
/**
|
|
53
|
+
* Upload files using the POST /files endpoint for sys__file objects
|
|
54
|
+
*/
|
|
55
|
+
export declare const uploadFiles: (files: (File | SavedDocumentReference)[], apiServices: ApiServices, actionId?: string, metadata?: Record<string, string>) => Promise<SavedDocumentReference[]>;
|
|
52
56
|
export declare const uploadDocuments: (files: (File | SavedDocumentReference)[], metadata: Record<string, string>, apiServices: ApiServices, instanceId: string, objectId: string) => Promise<SavedDocumentReference[]>;
|
|
53
57
|
export declare const deleteDocuments: (submittedFields: FieldValues, requestSuccess: boolean, apiServices: ApiServices, object: Obj, instance: FieldValues, action?: Action, setSnackbarError?: React.Dispatch<React.SetStateAction<{
|
|
54
58
|
showAlert: boolean;
|
|
@@ -469,6 +469,33 @@ export const docProperties = [
|
|
|
469
469
|
type: 'string',
|
|
470
470
|
},
|
|
471
471
|
];
|
|
472
|
+
/**
|
|
473
|
+
* Upload files using the POST /files endpoint for sys__file objects
|
|
474
|
+
*/
|
|
475
|
+
export const uploadFiles = async (files, apiServices, actionId = '_create', metadata) => {
|
|
476
|
+
// Separate already uploaded files from files that need uploading
|
|
477
|
+
const alreadyUploaded = files.filter((file) => !('size' in file));
|
|
478
|
+
const filesToUpload = files.filter((file) => 'size' in file);
|
|
479
|
+
// Upload all files in parallel
|
|
480
|
+
const uploadPromises = filesToUpload.map(async (file) => {
|
|
481
|
+
const formData = new FormData();
|
|
482
|
+
formData.append('file', file);
|
|
483
|
+
formData.append('actionId', actionId);
|
|
484
|
+
formData.append('objectId', 'sys__file');
|
|
485
|
+
if (metadata) {
|
|
486
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
487
|
+
formData.append(key, value);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
const fileInstance = await apiServices.post(getPrefixedUrl(`/files`), formData);
|
|
491
|
+
return {
|
|
492
|
+
id: fileInstance.id,
|
|
493
|
+
name: fileInstance.name,
|
|
494
|
+
};
|
|
495
|
+
});
|
|
496
|
+
const uploadedFiles = await Promise.all(uploadPromises);
|
|
497
|
+
return [...alreadyUploaded, ...uploadedFiles];
|
|
498
|
+
};
|
|
472
499
|
export const uploadDocuments = async (files, metadata, apiServices, instanceId, objectId) => {
|
|
473
500
|
const allDocuments = [];
|
|
474
501
|
const formData = new FormData();
|
|
@@ -494,8 +521,8 @@ export const uploadDocuments = async (files, metadata, apiServices, instanceId,
|
|
|
494
521
|
};
|
|
495
522
|
export const deleteDocuments = async (submittedFields, requestSuccess, apiServices, object, instance, action, setSnackbarError) => {
|
|
496
523
|
const documentProperties = action?.parameters
|
|
497
|
-
? action.parameters.filter((param) => param.type
|
|
498
|
-
: object?.properties?.filter((prop) => prop.type
|
|
524
|
+
? action.parameters.filter((param) => ['document', 'file'].includes(param.type))
|
|
525
|
+
: object?.properties?.filter((prop) => ['document', 'file'].includes(prop.type));
|
|
499
526
|
for (const docProperty of documentProperties ?? []) {
|
|
500
527
|
const savedValue = submittedFields[docProperty.id];
|
|
501
528
|
const originalValue = instance?.[docProperty.id];
|
|
@@ -504,14 +531,18 @@ export const deleteDocuments = async (submittedFields, requestSuccess, apiServic
|
|
|
504
531
|
: (savedValue?.filter((file) => !originalValue?.some((f) => f.id === file.id)) ?? []);
|
|
505
532
|
for (const doc of documentsToRemove) {
|
|
506
533
|
try {
|
|
507
|
-
|
|
534
|
+
// Use different endpoints based on property type
|
|
535
|
+
const deleteEndpoint = docProperty.type === 'file'
|
|
536
|
+
? getPrefixedUrl(`/files/${doc.id}`)
|
|
537
|
+
: getPrefixedUrl(`/objects/${object.id}/instances/${instance.id}/documents/${doc.id}`);
|
|
538
|
+
await apiServices?.delete(deleteEndpoint);
|
|
508
539
|
}
|
|
509
540
|
catch (error) {
|
|
510
541
|
if (error) {
|
|
511
542
|
setSnackbarError &&
|
|
512
543
|
setSnackbarError({
|
|
513
544
|
showAlert: true,
|
|
514
|
-
message: `An error occurred while removing document '${doc.name}'`,
|
|
545
|
+
message: `An error occurred while removing ${docProperty.type === 'file' ? 'file' : 'document'} '${doc.name}'`,
|
|
515
546
|
isError: true,
|
|
516
547
|
});
|
|
517
548
|
}
|
|
@@ -543,12 +574,19 @@ export async function formatSubmission(submission, apiServices, objectId, instan
|
|
|
543
574
|
// Only upload if array contains File instances (not SavedDocumentReference)
|
|
544
575
|
const fileInArray = value.some((item) => item instanceof File);
|
|
545
576
|
if (fileInArray && instanceId && apiServices && objectId) {
|
|
577
|
+
// Determine property type from the entry
|
|
578
|
+
const propertyType = entry?.input?.type;
|
|
546
579
|
try {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
580
|
+
// Use uploadFiles for 'file' type, uploadDocuments for 'document' type
|
|
581
|
+
const uploadedDocuments = propertyType === 'file'
|
|
582
|
+
? await uploadFiles(value, apiServices, '_create', {
|
|
583
|
+
...entry?.documentMetadata,
|
|
584
|
+
})
|
|
585
|
+
: await uploadDocuments(value, {
|
|
586
|
+
type: '',
|
|
587
|
+
view_permission: '',
|
|
588
|
+
...entry?.documentMetadata,
|
|
589
|
+
}, apiServices, instanceId, objectId);
|
|
552
590
|
submission[key] = uploadedDocuments;
|
|
553
591
|
}
|
|
554
592
|
catch (err) {
|
|
@@ -556,7 +594,7 @@ export async function formatSubmission(submission, apiServices, objectId, instan
|
|
|
556
594
|
setSnackbarError &&
|
|
557
595
|
setSnackbarError({
|
|
558
596
|
showAlert: true,
|
|
559
|
-
message: `An error occurred while uploading associated documents`,
|
|
597
|
+
message: `An error occurred while uploading associated ${propertyType === 'file' ? 'files' : 'documents'}`,
|
|
560
598
|
isError: true,
|
|
561
599
|
});
|
|
562
600
|
}
|
|
@@ -129,8 +129,8 @@ function ViewOnlyEntryRenderer(props) {
|
|
|
129
129
|
// RichTexts get a uniqueId when the form is loaded to prevent issues with multiple rich text fields on one form
|
|
130
130
|
id: entry.uniqueId, value: fieldValue, format: "rtf", disabled: true, rows: display?.rowCount, hasError: false })) : (React.createElement(Typography, { variant: "body1", key: entryId, sx: { height: '24px' } }, fieldValue))));
|
|
131
131
|
}
|
|
132
|
-
else if (fieldDefinition.type === 'document') {
|
|
133
|
-
return (React.createElement(FieldWrapper, { inputId: entryId, inputType:
|
|
132
|
+
else if (fieldDefinition.type === 'document' || fieldDefinition.type === 'file') {
|
|
133
|
+
return (React.createElement(FieldWrapper, { inputId: entryId, inputType: fieldDefinition.type, label: display?.label || fieldDefinition?.name || 'default', value: fieldValue, required: display?.required || false, prefix: display?.prefix, suffix: display?.suffix, viewOnly: true },
|
|
134
134
|
React.createElement(Document, { id: entryId, error: false, value: fieldValue, canUpdateProperty: false })));
|
|
135
135
|
}
|
|
136
136
|
else if (fieldDefinition.type === 'collection') {
|