@evoke-platform/ui-components 1.6.0-testing.13 → 1.6.0-testing.14

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.
Files changed (67) hide show
  1. package/dist/published/components/custom/FormField/AddressFieldComponent/addressFieldComponent.js +1 -1
  2. package/dist/published/components/custom/FormField/BooleanSelect/BooleanSelect.js +3 -3
  3. package/dist/published/components/custom/FormField/DatePickerSelect/DatePickerSelect.js +7 -1
  4. package/dist/published/components/custom/FormField/DateTimePickerSelect/DateTimePickerSelect.js +7 -1
  5. package/dist/published/components/custom/FormField/InputFieldComponent/InputFieldComponent.js +6 -3
  6. package/dist/published/components/custom/FormField/TimePickerSelect/TimePickerSelect.js +13 -3
  7. package/dist/published/components/custom/FormV2/FormRenderer.d.ts +19 -0
  8. package/dist/published/components/custom/FormV2/FormRenderer.js +183 -0
  9. package/dist/published/components/custom/FormV2/components/AccordionSections.d.ts +4 -0
  10. package/dist/published/components/custom/FormV2/components/AccordionSections.js +131 -0
  11. package/dist/published/components/custom/FormV2/components/ActionButtons.d.ts +19 -0
  12. package/dist/published/components/custom/FormV2/components/ActionButtons.js +106 -0
  13. package/dist/published/components/custom/FormV2/components/FieldWrapper.d.ts +24 -0
  14. package/dist/published/components/custom/FormV2/components/FieldWrapper.js +100 -0
  15. package/dist/published/components/custom/FormV2/components/FormContext.d.ts +12 -0
  16. package/dist/published/components/custom/FormV2/components/FormContext.js +8 -0
  17. package/dist/published/components/custom/FormV2/components/FormFieldTypes/AddressFields.d.ts +17 -0
  18. package/dist/published/components/custom/FormV2/components/FormFieldTypes/AddressFields.js +50 -0
  19. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/ActionDialog.d.ts +14 -0
  20. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/ActionDialog.js +88 -0
  21. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DocumentViewerCell.d.ts +13 -0
  22. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DocumentViewerCell.js +140 -0
  23. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableField.d.ts +17 -0
  24. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableField.js +233 -0
  25. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableFieldInput.d.ts +40 -0
  26. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/DropdownRepeatableFieldInput.js +95 -0
  27. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.d.ts +12 -0
  28. package/dist/published/components/custom/FormV2/components/FormFieldTypes/CollectionFiles/RepeatableField.js +526 -0
  29. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.d.ts +12 -0
  30. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.js +93 -0
  31. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.d.ts +16 -0
  32. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +73 -0
  33. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.d.ts +13 -0
  34. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.js +179 -0
  35. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Image.d.ts +12 -0
  36. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Image.js +108 -0
  37. package/dist/published/components/custom/FormV2/components/FormFieldTypes/UserProperty.d.ts +16 -0
  38. package/dist/published/components/custom/FormV2/components/FormFieldTypes/UserProperty.js +129 -0
  39. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/InstanceLookup.d.ts +15 -0
  40. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/InstanceLookup.js +226 -0
  41. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/ObjectPropertyInput.d.ts +4 -0
  42. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/ObjectPropertyInput.js +439 -0
  43. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.d.ts +29 -0
  44. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.js +74 -0
  45. package/dist/published/components/custom/FormV2/components/FormSections.d.ts +4 -0
  46. package/dist/published/components/custom/FormV2/components/FormSections.js +104 -0
  47. package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.d.ts +2 -0
  48. package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +209 -0
  49. package/dist/published/components/custom/FormV2/components/TabNav.d.ts +10 -0
  50. package/dist/published/components/custom/FormV2/components/TabNav.js +23 -0
  51. package/dist/published/components/custom/FormV2/components/ValidationFiles/Validation.d.ts +3 -0
  52. package/dist/published/components/custom/FormV2/components/ValidationFiles/Validation.js +176 -0
  53. package/dist/published/components/custom/FormV2/components/ValidationFiles/ValidationErrorDisplay.d.ts +10 -0
  54. package/dist/published/components/custom/FormV2/components/ValidationFiles/ValidationErrorDisplay.js +45 -0
  55. package/dist/published/components/custom/FormV2/components/types.d.ts +131 -0
  56. package/dist/published/components/custom/FormV2/components/types.js +1 -0
  57. package/dist/published/components/custom/FormV2/components/utils.d.ts +47 -0
  58. package/dist/published/components/custom/FormV2/components/utils.js +434 -0
  59. package/dist/published/components/custom/FormV2/index.d.ts +1 -0
  60. package/dist/published/components/custom/FormV2/index.js +1 -0
  61. package/dist/published/components/custom/index.d.ts +1 -0
  62. package/dist/published/components/custom/index.js +1 -0
  63. package/dist/published/index.d.ts +2 -2
  64. package/dist/published/index.js +2 -2
  65. package/dist/published/theme/hooks.d.ts +7 -0
  66. package/dist/published/theme/hooks.js +9 -0
  67. package/package.json +4 -2
@@ -0,0 +1,73 @@
1
+ import { useApiServices } from '@evoke-platform/context';
2
+ import { isNil } from 'lodash';
3
+ import React, { useEffect, useState } from 'react';
4
+ import { useDropzone } from 'react-dropzone';
5
+ import { UploadCloud } from '../../../../../../icons';
6
+ import { useFormContext } from '../../../../../../theme/hooks';
7
+ import { Skeleton, Snackbar, Typography } from '../../../../../core';
8
+ import { Box, Grid } from '../../../../../layout';
9
+ import { getPrefixedUrl } from '../../utils';
10
+ import { DocumentList } from './DocumentList';
11
+ export const Document = (props) => {
12
+ const { id, handleChange, canUpdateProperty, error, instance, value, validate, hasDescription } = props;
13
+ const apiServices = useApiServices();
14
+ const { fetchedOptions, setFetchedOptions, object } = useFormContext();
15
+ const [snackbarError, setSnackbarError] = useState();
16
+ const [documents, setDocuments] = useState();
17
+ const [hasUpdatePermission, setHasUpdatePermission] = useState(fetchedOptions[`${id}UpdatePermission`]);
18
+ useEffect(() => {
19
+ setDocuments(value);
20
+ }, [value]);
21
+ useEffect(() => {
22
+ checkPermissions();
23
+ }, [object]);
24
+ const checkPermissions = () => {
25
+ if (canUpdateProperty && !fetchedOptions[`${id}UpdatePermission`]) {
26
+ apiServices
27
+ .get(getPrefixedUrl(`/objects/${object?.id}/instances/${instance?.id}/documents/checkAccess?action=update`))
28
+ .then((accessCheck) => {
29
+ setFetchedOptions({
30
+ [`${id}UpdatePermission`]: accessCheck.result,
31
+ });
32
+ setHasUpdatePermission(accessCheck.result);
33
+ });
34
+ }
35
+ };
36
+ const handleUpload = async (files) => {
37
+ const newDocuments = [...(documents ?? []), ...(files ?? [])];
38
+ setDocuments(newDocuments);
39
+ handleChange(id, newDocuments);
40
+ };
41
+ const uploadDisabled = !!validate?.maxDocuments && (documents?.length ?? 0) >= validate.maxDocuments;
42
+ const { getRootProps, getInputProps, open } = useDropzone({
43
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
+ onDrop: (files) => handleUpload(files),
45
+ disabled: uploadDisabled,
46
+ });
47
+ return (React.createElement(React.Fragment, null,
48
+ canUpdateProperty && hasUpdatePermission && (React.createElement(Box, { sx: {
49
+ margin: '5px 0',
50
+ height: '115px',
51
+ borderRadius: '8px',
52
+ display: 'flex',
53
+ justifyContent: 'center',
54
+ alignItems: 'center',
55
+ border: `1px dashed ${error ? 'red' : uploadDisabled ? '#DFE3E8' : '#858585'}`,
56
+ position: 'relative',
57
+ cursor: uploadDisabled ? 'cursor' : 'pointer',
58
+ }, ...getRootProps(), onClick: open },
59
+ React.createElement("input", { ...getInputProps({ id }), disabled: uploadDisabled, ...(hasDescription ? { 'aria-describedby': `${id}-description` } : undefined) }),
60
+ React.createElement(Grid, { container: true, sx: { width: '100%' } },
61
+ React.createElement(Grid, { item: true, xs: 12, sx: { display: 'flex', justifyContent: 'center', paddingBottom: '7px' } },
62
+ React.createElement(UploadCloud, { sx: { color: '#919EAB', width: '50px', height: '30px' } })),
63
+ React.createElement(Grid, { item: true, xs: 12 },
64
+ React.createElement(Typography, { variant: "body2", sx: { color: uploadDisabled ? '#919EAB' : '#212B36', textAlign: 'center' } },
65
+ "Drag and drop or",
66
+ ' ',
67
+ React.createElement(Typography, { component: 'span', color: uploadDisabled ? '#919EAB' : 'primary', sx: { fontSize: '14px' } }, "select file"),
68
+ ' ',
69
+ "to upload"))))),
70
+ canUpdateProperty && isNil(hasUpdatePermission) && (React.createElement(Skeleton, { variant: "rectangular", height: "115px", sx: { margin: '5px 0', borderRadius: '8px' } })),
71
+ React.createElement(DocumentList, { id: id, instance: instance, handleChange: handleChange, value: value, setSnackbarError: (type, message) => setSnackbarError({ message, type }), canUpdateProperty: canUpdateProperty && !!hasUpdatePermission }),
72
+ React.createElement(Snackbar, { open: !!snackbarError?.message, handleClose: () => setSnackbarError(null), message: snackbarError?.message, error: snackbarError?.type === 'error' })));
73
+ };
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import { FieldValues } from 'react-hook-form';
3
+ import { SavedDocumentReference } from '../../types';
4
+ type DocumentListProps = {
5
+ handleChange: (propertyId: string, value: (File | SavedDocumentReference)[] | undefined) => void;
6
+ id: string;
7
+ instance?: FieldValues;
8
+ canUpdateProperty: boolean;
9
+ value: (File | SavedDocumentReference)[] | undefined;
10
+ setSnackbarError: (type: 'error' | 'success', message: string) => void;
11
+ };
12
+ export declare const DocumentList: (props: DocumentListProps) => React.JSX.Element;
13
+ export {};
@@ -0,0 +1,179 @@
1
+ import { useApiServices } from '@evoke-platform/context';
2
+ import { isEqual } from 'lodash';
3
+ import prettyBytes from 'pretty-bytes';
4
+ import React, { useEffect, useState } from 'react';
5
+ import { FileWithExtension, LaunchRounded, TrashCan, WarningRounded } from '../../../../../../icons';
6
+ import { useFormContext } from '../../../../../../theme/hooks';
7
+ import { Chip, IconButton, Typography } from '../../../../../core';
8
+ import { Box, Grid } from '../../../../../layout';
9
+ import { getPrefixedUrl } from '../../utils';
10
+ const styles = {
11
+ icon: {
12
+ padding: '3px',
13
+ color: '#637381',
14
+ },
15
+ };
16
+ const viewableFileTypes = [
17
+ 'application/pdf',
18
+ 'image/jpeg',
19
+ 'image/jpg',
20
+ 'image/png',
21
+ 'image/gif',
22
+ 'image/bmp',
23
+ 'image/webp',
24
+ 'text/plain',
25
+ ];
26
+ export const DocumentList = (props) => {
27
+ const { handleChange, id, canUpdateProperty, instance, value: documents, setSnackbarError } = props;
28
+ const apiServices = useApiServices();
29
+ const { fetchedOptions, setFetchedOptions, object } = useFormContext();
30
+ const [hasViewPermission, setHasViewPermission] = useState(fetchedOptions[`${id}ViewPermission`] ?? true);
31
+ const [savedDocuments, setSavedDocuments] = useState(fetchedOptions[`${id}SavedDocuments`]);
32
+ useEffect(() => {
33
+ const currentValue = instance?.[id];
34
+ if (currentValue?.length) {
35
+ const currentDocumentIds = currentValue.map((doc) => doc.id);
36
+ if (currentDocumentIds.length &&
37
+ // these need to be sorted otherwise it will evaluate as not equal if the ids are in different orders causing unnecessary fetches
38
+ !isEqual(currentDocumentIds.slice().sort(), savedDocuments
39
+ ?.map((doc) => doc.id)
40
+ .slice()
41
+ .sort())) {
42
+ getDocuments(currentDocumentIds);
43
+ }
44
+ }
45
+ }, [id, documents, object]);
46
+ useEffect(() => {
47
+ if (fetchedOptions[`${id}SavedDocuments`]) {
48
+ setSavedDocuments(fetchedOptions[`${id}SavedDocuments`]);
49
+ }
50
+ }, [fetchedOptions]);
51
+ const getDocuments = (currentDocumentIds, shouldRetry = true) => {
52
+ apiServices.get(getPrefixedUrl(`/objects/${object?.id}/instances/${instance?.id}/documents`), {
53
+ params: { filter: { where: { id: { inq: currentDocumentIds } } } },
54
+ }, (error, docs) => {
55
+ // There is a short delay between when a document is uploaded and when
56
+ // it is indexed. Therefore, try again if documents are not found.
57
+ if (shouldRetry &&
58
+ (!docs || currentDocumentIds.some((docId) => !docs.find((doc) => docId === doc.id)))) {
59
+ setTimeout(() => getDocuments(currentDocumentIds, false), 2000);
60
+ }
61
+ else if (error) {
62
+ setSnackbarError('error', 'Error occurred while retrieving saved documents');
63
+ }
64
+ else {
65
+ setSavedDocuments(docs);
66
+ setFetchedOptions({
67
+ [`${id}SavedDocuments`]: docs,
68
+ });
69
+ }
70
+ });
71
+ };
72
+ useEffect(() => {
73
+ if (!fetchedOptions[`${id}ViewPermission`]) {
74
+ checkPermissions();
75
+ }
76
+ }, [object]);
77
+ const checkPermissions = () => {
78
+ if (instance?.[id]?.length) {
79
+ apiServices
80
+ .get(getPrefixedUrl(`/objects/${object?.id}/instances/${instance?.id}/documents/checkAccess?action=view`))
81
+ .then((viewPermissionCheck) => {
82
+ setFetchedOptions({
83
+ [`${id}ViewPermission`]: viewPermissionCheck.result,
84
+ });
85
+ setHasViewPermission(viewPermissionCheck.result);
86
+ });
87
+ }
88
+ };
89
+ const isFile = (doc) => doc instanceof File;
90
+ const fileExists = (doc) => savedDocuments?.find((d) => d.id === doc.id);
91
+ const handleRemove = (index) => {
92
+ const updatedDocuments = documents?.filter((_, i) => i !== index) ?? [];
93
+ handleChange(id, updatedDocuments.length === 0 ? undefined : updatedDocuments);
94
+ };
95
+ const openDocument = async (index) => {
96
+ const doc = documents?.[index];
97
+ if (doc) {
98
+ let url;
99
+ const contentType = doc instanceof File
100
+ ? doc.type
101
+ : savedDocuments?.find((savedDocument) => savedDocument.id === doc.id)?.contentType;
102
+ if (!isFile(doc)) {
103
+ try {
104
+ const documentResponse = await apiServices.get(getPrefixedUrl(`/objects/${object?.id}/instances/${instance?.id}/documents/${doc.id}/content`), { responseType: 'blob' });
105
+ const blob = new Blob([documentResponse], { type: contentType });
106
+ url = window.URL.createObjectURL(blob);
107
+ }
108
+ catch (error) {
109
+ setSnackbarError('error', `Could not open ${doc.name}`);
110
+ return;
111
+ }
112
+ }
113
+ else {
114
+ url = URL.createObjectURL(doc);
115
+ }
116
+ if (contentType && viewableFileTypes.includes(contentType)) {
117
+ window.open(url, '_blank');
118
+ }
119
+ else {
120
+ const link = document.createElement('a');
121
+ link.href = url;
122
+ link.setAttribute('download', doc.name);
123
+ document.body.appendChild(link);
124
+ link.click();
125
+ // Clean up and remove the link
126
+ link.parentNode?.removeChild(link);
127
+ }
128
+ }
129
+ };
130
+ const getDocumentSize = (doc) => {
131
+ let size;
132
+ if (isFile(doc)) {
133
+ size = prettyBytes(doc.size);
134
+ }
135
+ else {
136
+ const savedDoc = savedDocuments?.find((savedDocument) => savedDocument.id === doc.id);
137
+ size = savedDoc ? prettyBytes(savedDoc.size) : '';
138
+ }
139
+ return size;
140
+ };
141
+ return (React.createElement(React.Fragment, null,
142
+ !documents && !canUpdateProperty && (React.createElement(Typography, { variant: "body2", sx: { color: '#637381' } }, "No documents")),
143
+ !!documents?.length && (React.createElement(Box, null, documents.map((doc, index) => (React.createElement(Grid, { container: true, sx: {
144
+ width: '100%',
145
+ border: '1px solid #C4CDD5',
146
+ borderRadius: '6px',
147
+ margin: '5px 2px',
148
+ padding: ' 8px',
149
+ display: 'flex',
150
+ alignItems: 'center',
151
+ } },
152
+ React.createElement(Grid, { item: true, sx: { display: 'flex', justifyContent: 'center', padding: '7px', marginLeft: '4px' } },
153
+ React.createElement(FileWithExtension, { fontFamily: "Arial", fileExtension: doc.name?.split('.')?.pop() ?? '', sx: { height: '1.5em', width: '1.5em' } })),
154
+ React.createElement(Grid, { item: true, sx: { flex: 1, justifyContent: 'center', paddingBottom: '5px' } },
155
+ React.createElement(Grid, { item: true, xs: 12 },
156
+ React.createElement(Typography, { sx: {
157
+ fontSize: '14px',
158
+ fontWeight: 700,
159
+ lineHeight: '15px',
160
+ paddingTop: '8px',
161
+ } }, doc.name)),
162
+ React.createElement(Grid, { item: true, xs: 12 },
163
+ React.createElement(Typography, { sx: { fontSize: '12px', color: '#637381' } }, getDocumentSize(doc)))),
164
+ (isFile(doc) || (hasViewPermission && !isFile(doc) && fileExists(doc))) && (React.createElement(Grid, { item: true },
165
+ React.createElement(IconButton, { "aria-label": "open document", sx: { ...styles.icon, marginRight: '16px' }, onClick: () => openDocument(index) },
166
+ React.createElement(LaunchRounded, { sx: { color: '#637381', fontSize: '22px' } })))),
167
+ !isFile(doc) && savedDocuments && !fileExists(doc) && (React.createElement(Chip, { label: "Deleted", sx: {
168
+ marginRight: '16px',
169
+ backgroundColor: 'rgba(222, 48, 36, 0.16)',
170
+ color: '#A91813',
171
+ borderRadius: '6px',
172
+ height: '25px',
173
+ fontWeight: 700,
174
+ '& .MuiChip-icon': { color: '#A91813', width: '.8em', marginBottom: '2px' },
175
+ }, icon: React.createElement(WarningRounded, null) })),
176
+ canUpdateProperty && (React.createElement(Grid, { item: true },
177
+ React.createElement(IconButton, { "aria-label": "delete document", sx: styles.icon, onClick: () => handleRemove(index) },
178
+ React.createElement(TrashCan, { sx: { ':hover': { color: '#A12723' } } })))))))))));
179
+ };
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ export declare function blobToDataUrl(blob: Blob): Promise<string>;
3
+ type ImageProps = {
4
+ id: string;
5
+ handleChange: (propertyId: string, value: string | null) => void;
6
+ canUpdateProperty?: boolean;
7
+ error?: boolean;
8
+ value?: string;
9
+ hasDescription?: boolean;
10
+ };
11
+ export declare const Image: (props: ImageProps) => React.JSX.Element;
12
+ export {};
@@ -0,0 +1,108 @@
1
+ import { BackupOutlined, ClearRounded } from '@mui/icons-material';
2
+ import { CardMedia } from '@mui/material';
3
+ import React, { useEffect, useState } from 'react';
4
+ import { useDropzone } from 'react-dropzone';
5
+ import { IconButton, Typography } from '../../../../core';
6
+ import { Box, Grid } from '../../../../layout';
7
+ export function blobToDataUrl(blob) {
8
+ const reader = new FileReader();
9
+ return new Promise((resolve) => {
10
+ reader.onloadend = () => resolve(reader.result);
11
+ reader.readAsDataURL(blob);
12
+ });
13
+ }
14
+ const styles = {
15
+ imageContainer: {
16
+ margin: '5px 0',
17
+ height: '160px',
18
+ borderRadius: '8px',
19
+ maxWidth: '100%',
20
+ },
21
+ dropzoneContainer: {
22
+ margin: '5px 0',
23
+ height: '160px',
24
+ borderRadius: '8px',
25
+ display: 'flex',
26
+ justifyContent: 'center',
27
+ alignItems: 'center',
28
+ border: '1px dashed #858585',
29
+ position: 'relative',
30
+ cursor: 'pointer',
31
+ },
32
+ icon: {
33
+ color: '#fff',
34
+ zIndex: 40,
35
+ fontSize: '16px',
36
+ },
37
+ deleteIcon: {
38
+ borderRadius: '50%',
39
+ padding: '3px',
40
+ backgroundColor: '#212B36',
41
+ ':hover': { backgroundColor: '#212B36', cursor: 'pointer' },
42
+ color: '#fff',
43
+ right: '29px',
44
+ bottom: '138px',
45
+ },
46
+ image: {
47
+ borderRadius: '8px',
48
+ width: 'fit-content',
49
+ maxWidth: '95%',
50
+ height: '160px',
51
+ position: 'relative',
52
+ display: 'inline-block',
53
+ objectFit: 'contain',
54
+ },
55
+ };
56
+ export const Image = (props) => {
57
+ const { id, handleChange, canUpdateProperty, error, value, hasDescription } = props;
58
+ const [image, setImage] = useState();
59
+ useEffect(() => {
60
+ if (typeof value === 'string') {
61
+ setImage(value);
62
+ }
63
+ }, [value]);
64
+ const handleUpload = async (file) => {
65
+ if (file?.size && file.size <= 300000) {
66
+ const dataUrl = file ? await blobToDataUrl(file) : null;
67
+ setImage(dataUrl);
68
+ handleChange(id, dataUrl);
69
+ }
70
+ };
71
+ const handleRemove = (e) => {
72
+ setImage(null);
73
+ handleChange(id, '');
74
+ e.stopPropagation();
75
+ };
76
+ const { getRootProps, getInputProps, open } = useDropzone({
77
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
+ onDrop: (files) => handleUpload(files?.[0]),
79
+ accept: { 'image/*': ['.png', '.jpg', '.jpeg', '.gif', '.svg'] },
80
+ });
81
+ return (React.createElement(React.Fragment, null, image ? (React.createElement(Box, { sx: styles.imageContainer },
82
+ React.createElement(Box, { sx: { position: 'relative', left: 0, zIndex: 5 } },
83
+ React.createElement(CardMedia, { component: "img", image: image, alt: 'Uploaded Image', sx: styles.image }),
84
+ canUpdateProperty && (React.createElement(IconButton, { onClick: handleRemove, "aria-label": "Remove image", sx: styles.deleteIcon },
85
+ React.createElement(ClearRounded, { sx: styles.icon })))))) : canUpdateProperty ? (React.createElement(Box, { sx: {
86
+ ...styles.dropzoneContainer,
87
+ borderColor: error ? 'red' : '#858585',
88
+ }, ...getRootProps(), onClick: open },
89
+ React.createElement("input", { ...getInputProps({ id }), multiple: false, ...(hasDescription ? { 'aria-describedby': `${id}-description` } : undefined) }),
90
+ React.createElement(Grid, { container: true, sx: { width: '100%' } },
91
+ React.createElement(Grid, { item: true, xs: 12, sx: {
92
+ display: 'flex',
93
+ justifyContent: 'center',
94
+ paddingBottom: '5px',
95
+ } },
96
+ React.createElement(BackupOutlined, { sx: { color: '#919EAB', height: '1.5em', width: '1.5em' } })),
97
+ React.createElement(Grid, { item: true, xs: 12 },
98
+ React.createElement(Typography, { variant: "body2", sx: { textAlign: 'center' } },
99
+ "Drag and drop or",
100
+ ' ',
101
+ React.createElement(Typography, { component: 'span', color: 'primary', sx: { fontSize: '14px' } }, "select a file"),
102
+ ' ',
103
+ "to upload")),
104
+ React.createElement(Grid, { item: true, xs: 12 },
105
+ React.createElement(Typography, { variant: "body2", sx: { color: '#637381', textAlign: 'center' } }, "Max file size of 300KB")),
106
+ React.createElement(Grid, { item: true, xs: 12 },
107
+ React.createElement(Typography, { variant: "body2", sx: { color: '#637381', textAlign: 'center' } }, "JPG, PNG, or GIF"))))) : (React.createElement(Typography, { variant: "body2", sx: { color: '#637381' } }, "No image"))));
108
+ };
@@ -0,0 +1,16 @@
1
+ import { UserAccount } from '@evoke-platform/context';
2
+ import React from 'react';
3
+ export type UserPropertyProps = {
4
+ id: string;
5
+ handleChange: (id: string, user: UserAccount | undefined) => void;
6
+ error?: boolean;
7
+ value?: {
8
+ id: string;
9
+ name: string;
10
+ };
11
+ fieldHeight?: 'small' | 'medium';
12
+ readOnly?: boolean;
13
+ hasDescription?: boolean;
14
+ };
15
+ declare const UserProperty: (props: UserPropertyProps) => React.JSX.Element;
16
+ export default UserProperty;
@@ -0,0 +1,129 @@
1
+ import { useApiServices } from '@evoke-platform/context';
2
+ import { ExpandMore } from '@mui/icons-material';
3
+ import React, { useEffect, useState } from 'react';
4
+ import { useFormContext } from '../../../../../theme/hooks';
5
+ import { Autocomplete, Paper, TextField, Typography } from '../../../../core';
6
+ import { getPrefixedUrl, isOptionEqualToValue } from '../utils';
7
+ const UserProperty = (props) => {
8
+ const { id, handleChange, error, value, fieldHeight, readOnly, hasDescription } = props;
9
+ const { fetchedOptions, setFetchedOptions } = useFormContext();
10
+ const [loadingOptions, setLoadingOptions] = useState(false);
11
+ const apiServices = useApiServices();
12
+ const [options, setOptions] = useState(fetchedOptions[`${id}Options`] || []);
13
+ const [openOptions, setOpenOptions] = useState(false);
14
+ const [users, setUsers] = useState();
15
+ const [userValue, setUserValue] = useState();
16
+ useEffect(() => {
17
+ if (value && typeof value == 'object' && 'name' in value && 'id' in value) {
18
+ setUserValue({ label: value.name, value: value.id });
19
+ }
20
+ else {
21
+ setUserValue(undefined);
22
+ }
23
+ }, [value]);
24
+ useEffect(() => {
25
+ if (!fetchedOptions[`${id}Options`]) {
26
+ setLoadingOptions(true);
27
+ apiServices.get(getPrefixedUrl(`/users`), (error, userList) => {
28
+ setUsers(userList);
29
+ setOptions((userList ?? []).map((user) => ({
30
+ label: user.name,
31
+ value: user.id,
32
+ })));
33
+ setFetchedOptions({
34
+ [`${id}Options`]: (userList ?? []).map((user) => ({
35
+ label: user.name,
36
+ value: user.id,
37
+ })),
38
+ });
39
+ setLoadingOptions(false);
40
+ });
41
+ }
42
+ }, [id]);
43
+ function handleChangeUserProperty(id, value) {
44
+ const updatedValue = typeof value.value === 'string' ? { name: value.label, id: value.value } : undefined;
45
+ handleChange(id, updatedValue);
46
+ }
47
+ return (options && (React.createElement(Autocomplete, { id: id, fullWidth: true, open: openOptions, popupIcon: userValue || readOnly ? '' : React.createElement(ExpandMore, null), PaperComponent: ({ children }) => {
48
+ return (React.createElement(Paper, { sx: {
49
+ borderRadius: '12px',
50
+ boxShadow: '0px 24px 48px 0px rgba(145, 158, 171, 0.2)',
51
+ '& .MuiAutocomplete-listbox': {
52
+ maxHeight: '25vh',
53
+ },
54
+ '& .MuiAutocomplete-noOptions': {
55
+ fontFamily: 'sans-serif',
56
+ fontSize: '14px',
57
+ paddingLeft: '24px',
58
+ color: 'rgba(145, 158, 171, 1)',
59
+ },
60
+ '& .MuiAutocomplete-loading': {
61
+ fontFamily: 'sans-serif',
62
+ fontSize: '14px',
63
+ paddingLeft: '24px',
64
+ color: 'rgba(145, 158, 171, 1)',
65
+ },
66
+ } }, children));
67
+ }, sx: {
68
+ '& button.MuiButtonBase-root': {
69
+ ...(!loadingOptions && value ? { visibility: 'visible' } : {}),
70
+ },
71
+ '.MuiAutocomplete-clearIndicator': {
72
+ ...(!value ? { display: 'none' } : undefined),
73
+ },
74
+ }, noOptionsText: 'No options available', renderOption: (props, option) => {
75
+ return (React.createElement("li", { ...props, key: option.id },
76
+ React.createElement(Typography, { sx: { marginLeft: '8px', fontSize: '14px' } },
77
+ option.label,
78
+ " ",
79
+ '',
80
+ users?.find((user) => option.value === user.id)?.status === 'Inactive' ? (React.createElement("span", null, "(Inactive)")) : (''))));
81
+ }, onOpen: () => {
82
+ setOpenOptions(true);
83
+ }, onClose: () => setOpenOptions(false), value: userValue ?? '', options: options, getOptionLabel: (option) => {
84
+ if (typeof option === 'string') {
85
+ return options.find((o) => o.value === option)?.label ?? '';
86
+ }
87
+ else {
88
+ {
89
+ if (users?.find((user) => option.value === user.id)?.status === 'Inactive') {
90
+ return option.label + ' (Inactive)';
91
+ }
92
+ else {
93
+ return option.label ?? '';
94
+ }
95
+ }
96
+ }
97
+ }, onKeyDownCapture: (e) => {
98
+ // prevents keyboard trap
99
+ if (e.key === 'Tab') {
100
+ return;
101
+ }
102
+ if (value) {
103
+ e.preventDefault();
104
+ }
105
+ }, loading: loadingOptions, isOptionEqualToValue: isOptionEqualToValue, onChange: (event, value) => {
106
+ handleChangeUserProperty(id, value);
107
+ }, renderInput: (params) => (React.createElement(TextField, { ...params, placeholder: !readOnly ? 'Select' : undefined, readOnly: !loadingOptions && !value && readOnly, inputProps: {
108
+ ...params.inputProps,
109
+ ...(hasDescription ? { 'aria-describedby': `${id}-description` } : undefined),
110
+ }, sx: {
111
+ ...(!loadingOptions && value
112
+ ? {
113
+ '.MuiOutlinedInput-root': {
114
+ background: 'white',
115
+ border: 'auto',
116
+ },
117
+ '& fieldset': { borderColor: 'auto' },
118
+ '&:hover .MuiOutlinedInput-notchedOutline': {
119
+ border: 'auto',
120
+ },
121
+ caretColor: 'white',
122
+ '& svg': {
123
+ display: readOnly ? 'none' : 'block',
124
+ },
125
+ }
126
+ : {}),
127
+ } })), size: fieldHeight ?? 'medium', readOnly: readOnly, error: error })));
128
+ };
129
+ export default UserProperty;
@@ -0,0 +1,15 @@
1
+ import { Obj, ObjectInstance, TableViewLayout } from '@evoke-platform/context';
2
+ import React from 'react';
3
+ import { BaseProps } from '../../types';
4
+ export type InstanceLookUpProps = BaseProps & {
5
+ object: Obj;
6
+ instanceId?: string;
7
+ setSelectedInstance: (selectedInstance: ObjectInstance) => void;
8
+ setRelationType: (relationType: 'new' | 'existing') => void;
9
+ mode: 'default' | 'existingOnly';
10
+ nestedFieldsView?: boolean;
11
+ filter?: Record<string, unknown>;
12
+ layout?: TableViewLayout;
13
+ };
14
+ declare const InstanceLookup: (props: InstanceLookUpProps) => React.JSX.Element;
15
+ export default InstanceLookup;