@evoke-platform/ui-components 1.5.0-dev.2 → 1.5.0-dev.4
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/FormComponents/ObjectComponent/RelatedObjectInstance.js +3 -3
- package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/DocumentViewerCell.d.ts +13 -0
- package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/DocumentViewerCell.js +113 -0
- package/dist/published/components/custom/Form/FormComponents/RepeatableFieldComponent/RepeatableField.js +24 -26
- package/package.json +1 -1
@@ -41,7 +41,7 @@ export const RelatedObjectInstance = (props) => {
|
|
41
41
|
handleClose();
|
42
42
|
setErrors([]);
|
43
43
|
};
|
44
|
-
const createNewInstance = async (submission) => {
|
44
|
+
const createNewInstance = async (submission, setSubmitting) => {
|
45
45
|
if (!relatedObject) {
|
46
46
|
// Handle the case where relatedObject is undefined
|
47
47
|
return { isSuccessful: false };
|
@@ -74,12 +74,12 @@ export const RelatedObjectInstance = (props) => {
|
|
74
74
|
setSnackbarError({
|
75
75
|
showAlert: true,
|
76
76
|
message: err.response?.data?.error?.details?.[0]?.message ??
|
77
|
-
err.data?.error?.message ??
|
77
|
+
err.response?.data?.error?.message ??
|
78
78
|
`An error occurred. The new instance was not created.`,
|
79
79
|
isError: true,
|
80
80
|
});
|
81
81
|
error = err.response?.data?.error;
|
82
|
-
|
82
|
+
setSubmitting && setSubmitting(false);
|
83
83
|
}
|
84
84
|
return { isSuccessful, error };
|
85
85
|
};
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { ApiServices, ObjectInstance } from '@evoke-platform/context';
|
2
|
+
import React from 'react';
|
3
|
+
export type DocumentViewerCellProps = {
|
4
|
+
instance: ObjectInstance;
|
5
|
+
propertyId: string;
|
6
|
+
apiServices: ApiServices;
|
7
|
+
setSnackbarError: (error: {
|
8
|
+
showAlert: boolean;
|
9
|
+
message?: string;
|
10
|
+
isError?: boolean;
|
11
|
+
}) => void;
|
12
|
+
};
|
13
|
+
export declare const DocumentViewerCell: (props: DocumentViewerCellProps) => React.JSX.Element;
|
@@ -0,0 +1,113 @@
|
|
1
|
+
import React, { useState } from 'react';
|
2
|
+
import { AutorenewRounded, FileWithExtension, LaunchRounded } from '../../../../../icons';
|
3
|
+
import { Button, Menu, MenuItem, Typography } from '../../../../core';
|
4
|
+
import { Grid } from '../../../../layout';
|
5
|
+
import { getPrefixedUrl } from '../../utils';
|
6
|
+
export const DocumentViewerCell = (props) => {
|
7
|
+
const { instance, propertyId, apiServices, setSnackbarError } = props;
|
8
|
+
const [anchorEl, setAnchorEl] = useState(null);
|
9
|
+
const [isLoading, setIsLoading] = useState(false);
|
10
|
+
const downloadDocument = async (doc, instance) => {
|
11
|
+
setIsLoading(true);
|
12
|
+
try {
|
13
|
+
const documentResponse = await apiServices.get(getPrefixedUrl(`/objects/${instance.objectId}/instances/${instance.id}/documents/${doc.id}/content`), { responseType: 'blob' });
|
14
|
+
const contentType = documentResponse.type;
|
15
|
+
const blob = new Blob([documentResponse], { type: contentType });
|
16
|
+
const url = URL.createObjectURL(blob);
|
17
|
+
// Let the browser handle whether to open the document to view in a new tab or download it.
|
18
|
+
window.open(url, '_blank');
|
19
|
+
setIsLoading(false);
|
20
|
+
URL.revokeObjectURL(url);
|
21
|
+
}
|
22
|
+
catch (error) {
|
23
|
+
const status = error.status;
|
24
|
+
let message = 'An error occurred while downloading the document.';
|
25
|
+
if (status === 403) {
|
26
|
+
message = 'You do not have permission to download this document.';
|
27
|
+
}
|
28
|
+
else if (status === 404) {
|
29
|
+
message = 'Document not found.';
|
30
|
+
}
|
31
|
+
setIsLoading(false);
|
32
|
+
setSnackbarError({
|
33
|
+
showAlert: true,
|
34
|
+
message,
|
35
|
+
isError: true,
|
36
|
+
});
|
37
|
+
}
|
38
|
+
};
|
39
|
+
return (React.createElement(React.Fragment, null, instance[propertyId]?.length ? (React.createElement(React.Fragment, null,
|
40
|
+
React.createElement(Button, { sx: {
|
41
|
+
display: 'flex',
|
42
|
+
alignItems: 'center',
|
43
|
+
justifyContent: 'flex-start',
|
44
|
+
padding: '6px 10px',
|
45
|
+
}, color: 'inherit', onClick: async (event) => {
|
46
|
+
event.stopPropagation();
|
47
|
+
const documents = instance[propertyId];
|
48
|
+
if (documents.length === 1) {
|
49
|
+
await downloadDocument(documents[0], instance);
|
50
|
+
}
|
51
|
+
else {
|
52
|
+
setAnchorEl(event.currentTarget);
|
53
|
+
}
|
54
|
+
}, variant: "text", "aria-haspopup": "menu", "aria-controls": `document-menu-${instance.id}-${propertyId}`, "aria-expanded": anchorEl ? 'true' : 'false' },
|
55
|
+
isLoading ? (React.createElement(AutorenewRounded, { sx: {
|
56
|
+
color: '#637381',
|
57
|
+
width: '20px',
|
58
|
+
height: '20px',
|
59
|
+
} })) : (React.createElement(LaunchRounded, { sx: {
|
60
|
+
color: '#637381',
|
61
|
+
width: '20px',
|
62
|
+
height: '20px',
|
63
|
+
} })),
|
64
|
+
React.createElement(Typography, { sx: {
|
65
|
+
marginLeft: '8px',
|
66
|
+
fontWeight: 400,
|
67
|
+
fontSize: '14px',
|
68
|
+
} }, isLoading ? 'Preparing document...' : 'View Document')),
|
69
|
+
React.createElement(Menu, { id: `document-menu-${instance.id}-${propertyId}`, anchorEl: anchorEl, open: Boolean(anchorEl), onClose: () => {
|
70
|
+
setAnchorEl(null);
|
71
|
+
}, sx: {
|
72
|
+
'& .MuiPaper-root': {
|
73
|
+
borderRadius: '12px',
|
74
|
+
boxShadow: 'rgba(145, 158, 171, 0.2)',
|
75
|
+
},
|
76
|
+
}, variant: 'menu', slotProps: {
|
77
|
+
paper: {
|
78
|
+
style: {
|
79
|
+
maxHeight: 200,
|
80
|
+
maxWidth: 300,
|
81
|
+
minWidth: 300,
|
82
|
+
},
|
83
|
+
},
|
84
|
+
} }, instance[propertyId].map((document) => (React.createElement(MenuItem, { key: document.id, onClick: async (e) => {
|
85
|
+
setAnchorEl(null);
|
86
|
+
await downloadDocument(document, instance);
|
87
|
+
}, "aria-label": document.name },
|
88
|
+
React.createElement(Grid, { item: true, sx: {
|
89
|
+
display: 'flex',
|
90
|
+
justifyContent: 'center',
|
91
|
+
padding: '7px',
|
92
|
+
} },
|
93
|
+
React.createElement(FileWithExtension, { fontFamily: "Arial", fileExtension: document.name?.split('.')?.pop() ?? '', sx: {
|
94
|
+
height: '1rem',
|
95
|
+
width: '1rem',
|
96
|
+
} })),
|
97
|
+
React.createElement(Grid, { item: true, xs: 12, sx: {
|
98
|
+
width: '100%',
|
99
|
+
overflow: 'hidden',
|
100
|
+
textOverflow: 'ellipsis',
|
101
|
+
} },
|
102
|
+
React.createElement(Typography, { noWrap: true, sx: {
|
103
|
+
fontSize: '14px',
|
104
|
+
fontWeight: 700,
|
105
|
+
color: '#212B36',
|
106
|
+
lineHeight: '15px',
|
107
|
+
width: '100%',
|
108
|
+
} }, document.name)))))))) : (React.createElement(Typography, { sx: {
|
109
|
+
fontStyle: 'italic',
|
110
|
+
marginLeft: '12px',
|
111
|
+
fontSize: '14px',
|
112
|
+
} }, "No documents"))));
|
113
|
+
};
|
@@ -9,6 +9,7 @@ import { Button, IconButton, Skeleton, Snackbar, Table, TableBody, TableCell, Ta
|
|
9
9
|
import { Box } from '../../../../layout';
|
10
10
|
import { getPrefixedUrl, normalizeDateTime } from '../../utils';
|
11
11
|
import { ActionDialog } from './ActionDialog';
|
12
|
+
import { DocumentViewerCell } from './DocumentViewerCell';
|
12
13
|
const styles = {
|
13
14
|
addButton: {
|
14
15
|
backgroundColor: 'rgba(0, 117, 167, 0.08)',
|
@@ -250,13 +251,13 @@ const RepeatableField = (props) => {
|
|
250
251
|
isSuccessful = true;
|
251
252
|
}
|
252
253
|
catch (err) {
|
254
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
255
|
+
error = err.response?.data?.error;
|
253
256
|
setSnackbarError({
|
254
257
|
showAlert: true,
|
255
|
-
message: `An error occurred while creating an instance`,
|
258
|
+
message: error?.message ?? `An error occurred while creating an instance`,
|
256
259
|
isError: true,
|
257
260
|
});
|
258
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
259
|
-
error = err.response?.data?.error;
|
260
261
|
setSubmitting && setSubmitting(false);
|
261
262
|
}
|
262
263
|
}
|
@@ -284,13 +285,14 @@ const RepeatableField = (props) => {
|
|
284
285
|
isSuccessful = true;
|
285
286
|
}
|
286
287
|
catch (err) {
|
288
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
289
|
+
error = err.response?.data?.error;
|
287
290
|
setSnackbarError({
|
288
291
|
showAlert: true,
|
289
|
-
message:
|
292
|
+
message: error?.message ??
|
293
|
+
`An error occurred while ${actionType === 'delete' ? ' deleting' : ' updating'} an instance`,
|
290
294
|
isError: true,
|
291
295
|
});
|
292
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
293
|
-
error = err.response?.data?.error;
|
294
296
|
}
|
295
297
|
}
|
296
298
|
return { isSuccessful, error };
|
@@ -334,9 +336,9 @@ const RepeatableField = (props) => {
|
|
334
336
|
};
|
335
337
|
const getValue = (relatedInstance, propertyId, propertyType) => {
|
336
338
|
const value = get(relatedInstance, propertyId);
|
337
|
-
// If the property is not date-like
|
339
|
+
// If the property is not date-like then just return the
|
338
340
|
// value found at the given path.
|
339
|
-
if (!['date', 'date-time', 'time'
|
341
|
+
if (!['date', 'date-time', 'time'].includes(propertyType)) {
|
340
342
|
return value;
|
341
343
|
}
|
342
344
|
// If the date-like value is empty then there is no need to format.
|
@@ -356,9 +358,6 @@ const RepeatableField = (props) => {
|
|
356
358
|
if (propertyType === 'time') {
|
357
359
|
return DateTime.fromISO(stringValue).toLocaleString(DateTime.TIME_SIMPLE);
|
358
360
|
}
|
359
|
-
if (property.type === 'document') {
|
360
|
-
return Array.isArray(value) ? value.map((v) => v.name).join(', ') : value;
|
361
|
-
}
|
362
361
|
};
|
363
362
|
const columns = retrieveViewLayout();
|
364
363
|
return loading ? (React.createElement(React.Fragment, null,
|
@@ -376,23 +375,22 @@ const RepeatableField = (props) => {
|
|
376
375
|
React.createElement(TableHead, { sx: { backgroundColor: '#F4F6F8' } },
|
377
376
|
React.createElement(TableRow, null,
|
378
377
|
columns?.map((prop) => React.createElement(TableCell, { sx: styles.tableCell }, prop.name)),
|
379
|
-
React.createElement(TableCell, { sx: { ...styles.tableCell, width: '80px' } }))),
|
378
|
+
canUpdateProperty && React.createElement(TableCell, { sx: { ...styles.tableCell, width: '80px' } }))),
|
380
379
|
React.createElement(TableBody, null, relatedInstances?.map((relatedInstance, index) => (React.createElement(TableRow, { key: relatedInstance.id },
|
381
380
|
columns?.map((prop) => {
|
382
|
-
return (React.createElement(TableCell, { sx: { color: '#212B36', fontSize: '16px' } },
|
383
|
-
|
384
|
-
|
385
|
-
'
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
prop.
|
395
|
-
users?.find((user) => get(relatedInstance, `${prop.id.split('.')[0]}.id`) === user.id)?.status === 'Inactive' && React.createElement("span", null, ' (Inactive)'))));
|
381
|
+
return (React.createElement(TableCell, { sx: { color: '#212B36', fontSize: '16px' } }, prop.type === 'document' ? (React.createElement(DocumentViewerCell, { instance: relatedInstance, propertyId: prop.id, apiServices: apiServices, setSnackbarError: setSnackbarError })) : (React.createElement(Typography, { key: prop.id, sx: prop.id === 'name'
|
382
|
+
? {
|
383
|
+
'&:hover': {
|
384
|
+
textDecoration: 'underline',
|
385
|
+
cursor: 'pointer',
|
386
|
+
},
|
387
|
+
}
|
388
|
+
: {}, onClick: canUpdateProperty && prop.id === 'name'
|
389
|
+
? () => editRow(relatedInstance.id)
|
390
|
+
: undefined },
|
391
|
+
getValue(relatedInstance, prop.id, prop.type),
|
392
|
+
prop.type === 'user' &&
|
393
|
+
users?.find((user) => get(relatedInstance, `${prop.id.split('.')[0]}.id`) === user.id)?.status === 'Inactive' && (React.createElement("span", null, ' (Inactive)'))))));
|
396
394
|
}),
|
397
395
|
canUpdateProperty && (React.createElement(TableCell, { sx: { width: '80px' } },
|
398
396
|
React.createElement(IconButton, { "aria-label": `edit-collection-instance-${index}`, onClick: () => editRow(relatedInstance.id) },
|