@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.
@@ -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
- onClose();
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: `An error occurred while ${actionType === 'delete' ? ' deleting' : ' updating'} an instance`,
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 or document then just return the
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', 'document'].includes(propertyType)) {
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
- React.createElement(Typography, { key: prop.id, sx: prop.id === 'name'
384
- ? {
385
- '&:hover': {
386
- textDecoration: 'underline',
387
- cursor: 'pointer',
388
- },
389
- }
390
- : {}, onClick: canUpdateProperty && prop.id === 'name'
391
- ? () => editRow(relatedInstance.id)
392
- : undefined },
393
- getValue(relatedInstance, prop.id, prop.type),
394
- prop.type === 'user' &&
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) },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evoke-platform/ui-components",
3
- "version": "1.5.0-dev.2",
3
+ "version": "1.5.0-dev.4",
4
4
  "description": "",
5
5
  "main": "dist/published/index.js",
6
6
  "module": "dist/published/index.js",