@evoke-platform/ui-components 1.5.0-testing.2 → 1.5.0-testing.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +17 -20
- package/package.json +1 -1
@@ -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)',
|
@@ -335,9 +336,9 @@ const RepeatableField = (props) => {
|
|
335
336
|
};
|
336
337
|
const getValue = (relatedInstance, propertyId, propertyType) => {
|
337
338
|
const value = get(relatedInstance, propertyId);
|
338
|
-
// If the property is not date-like
|
339
|
+
// If the property is not date-like then just return the
|
339
340
|
// value found at the given path.
|
340
|
-
if (!['date', 'date-time', 'time'
|
341
|
+
if (!['date', 'date-time', 'time'].includes(propertyType)) {
|
341
342
|
return value;
|
342
343
|
}
|
343
344
|
// If the date-like value is empty then there is no need to format.
|
@@ -357,9 +358,6 @@ const RepeatableField = (props) => {
|
|
357
358
|
if (propertyType === 'time') {
|
358
359
|
return DateTime.fromISO(stringValue).toLocaleString(DateTime.TIME_SIMPLE);
|
359
360
|
}
|
360
|
-
if (property.type === 'document') {
|
361
|
-
return Array.isArray(value) ? value.map((v) => v.name).join(', ') : value;
|
362
|
-
}
|
363
361
|
};
|
364
362
|
const columns = retrieveViewLayout();
|
365
363
|
return loading ? (React.createElement(React.Fragment, null,
|
@@ -377,23 +375,22 @@ const RepeatableField = (props) => {
|
|
377
375
|
React.createElement(TableHead, { sx: { backgroundColor: '#F4F6F8' } },
|
378
376
|
React.createElement(TableRow, null,
|
379
377
|
columns?.map((prop) => React.createElement(TableCell, { sx: styles.tableCell }, prop.name)),
|
380
|
-
React.createElement(TableCell, { sx: { ...styles.tableCell, width: '80px' } }))),
|
378
|
+
canUpdateProperty && React.createElement(TableCell, { sx: { ...styles.tableCell, width: '80px' } }))),
|
381
379
|
React.createElement(TableBody, null, relatedInstances?.map((relatedInstance, index) => (React.createElement(TableRow, { key: relatedInstance.id },
|
382
380
|
columns?.map((prop) => {
|
383
|
-
return (React.createElement(TableCell, { sx: { color: '#212B36', fontSize: '16px' } },
|
384
|
-
|
385
|
-
|
386
|
-
'
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
prop.
|
396
|
-
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)'))))));
|
397
394
|
}),
|
398
395
|
canUpdateProperty && (React.createElement(TableCell, { sx: { width: '80px' } },
|
399
396
|
React.createElement(IconButton, { "aria-label": `edit-collection-instance-${index}`, onClick: () => editRow(relatedInstance.id) },
|