@evoke-platform/ui-components 1.10.1-dev.5 → 1.11.0-dev.0
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 +20 -1
- package/dist/published/components/custom/Form/FormComponents/FormFieldComponent.js +11 -1
- package/dist/published/components/custom/Form/FormComponents/ViewOnlyComponent.js +32 -6
- package/dist/published/components/custom/Form/types.d.ts +7 -1
- package/dist/published/components/custom/Form/utils.d.ts +15 -1
- package/dist/published/components/custom/Form/utils.js +18 -0
- package/dist/published/components/custom/FormField/FormField.d.ts +5 -1
- package/dist/published/components/custom/FormField/FormField.js +1 -1
- package/dist/published/components/custom/FormV2/components/PropertyProtection.d.ts +5 -1
- package/dist/published/components/custom/FormV2/components/PropertyProtection.js +33 -11
- package/package.json +1 -1
|
@@ -6,9 +6,10 @@ import React, { useEffect, useRef, useState } from 'react';
|
|
|
6
6
|
import '../../../../styles/form-component.css';
|
|
7
7
|
import { Skeleton, Snackbar } from '../../../core';
|
|
8
8
|
import { Box } from '../../../layout';
|
|
9
|
+
import { obfuscateValue } from '../../FormV2/components/utils';
|
|
9
10
|
import { ButtonComponent, DocumentComponent, FormFieldComponent, ImageComponent, ObjectComponent, RepeatableFieldComponent, UserComponent, ViewOnlyComponent, } from '../FormComponents';
|
|
10
11
|
import { CriteriaComponent } from '../FormComponents/CriteriaComponent/CriteriaComponent';
|
|
11
|
-
import { addObjectPropertiesToComponentProps, buildComponentPropsFromDocumentProperties, buildComponentPropsFromObjectProperties, convertFormToComponents, flattenFormComponents, getAllCriteriaInputs, getFlattenEntries, getPrefixedUrl, } from '../utils';
|
|
12
|
+
import { addObjectPropertiesToComponentProps, buildComponentPropsFromDocumentProperties, buildComponentPropsFromObjectProperties, containsCorruptedFiles, convertFormToComponents, flattenFormComponents, getAllCriteriaInputs, getFlattenEntries, getPrefixedUrl, isCorruptedFile, } from '../utils';
|
|
12
13
|
const usePrevious = (value) => {
|
|
13
14
|
const ref = useRef();
|
|
14
15
|
useEffect(() => {
|
|
@@ -25,6 +26,7 @@ export function Form(props) {
|
|
|
25
26
|
const [snackbarError, setSnackbarError] = useState();
|
|
26
27
|
const prevFormKey = usePrevious(formKey);
|
|
27
28
|
const userAccount = user ?? useAuthenticationContext()?.account;
|
|
29
|
+
const originalFilesRef = useRef({});
|
|
28
30
|
const { defaultPages, findDefaultPageSlugFor } = useApp();
|
|
29
31
|
const rebuildFormComponents = () => {
|
|
30
32
|
const viewDetailsComponents = {
|
|
@@ -286,6 +288,13 @@ export function Form(props) {
|
|
|
286
288
|
: object?.properties?.filter((prop) => prop.type === 'document');
|
|
287
289
|
let allEntries = null;
|
|
288
290
|
for (const docProperty of documentProperties ?? []) {
|
|
291
|
+
const currentValue = submittedFields[docProperty.id];
|
|
292
|
+
// Files get corrupted if the document uploads but the form submission fails, in that case replace with non-corrupted file
|
|
293
|
+
if (currentValue && containsCorruptedFiles(currentValue)) {
|
|
294
|
+
const originals = originalFilesRef.current[docProperty.id] ?? [];
|
|
295
|
+
const repaired = currentValue.map((file, index) => (isCorruptedFile(file) ? originals[index] : file));
|
|
296
|
+
submittedFields[docProperty.id] = repaired;
|
|
297
|
+
}
|
|
289
298
|
const value = submittedFields[docProperty.id];
|
|
290
299
|
// skip if the document list did not change
|
|
291
300
|
if (value === undefined || !instance || isEqual(instance?.[docProperty.id], value)) {
|
|
@@ -293,6 +302,8 @@ export function Form(props) {
|
|
|
293
302
|
}
|
|
294
303
|
let savedDocuments = null;
|
|
295
304
|
if (value && value.some((doc) => doc instanceof File)) {
|
|
305
|
+
// Store original non-corrupted files before upload in case form submission fails
|
|
306
|
+
originalFilesRef.current[docProperty.id] = [...value];
|
|
296
307
|
if (!allEntries) {
|
|
297
308
|
allEntries = getFlattenEntries(action?.form?.entries ?? []) ?? [];
|
|
298
309
|
}
|
|
@@ -343,11 +354,19 @@ export function Form(props) {
|
|
|
343
354
|
const submittedFields = {};
|
|
344
355
|
for (const field in submission.data) {
|
|
345
356
|
const value = submission.data[field];
|
|
357
|
+
const protectedProperties = object?.properties?.filter((prop) => prop.protection?.maskChar);
|
|
346
358
|
if (value === '' ||
|
|
347
359
|
(Array.isArray(value) && !value.length) ||
|
|
348
360
|
(typeof value === 'object' && isEmpty(value))) {
|
|
349
361
|
submittedFields[field] = null;
|
|
350
362
|
}
|
|
363
|
+
else if (protectedProperties?.some((prop) => prop.id === field)) {
|
|
364
|
+
// When protected value hasn't been edited or viewed, don't
|
|
365
|
+
// save the obfuscated value.
|
|
366
|
+
const protectedProperty = protectedProperties.find((prop) => prop.id === field);
|
|
367
|
+
const isProtectedValue = value === obfuscateValue(value, protectedProperty);
|
|
368
|
+
submittedFields[field] = isProtectedValue ? undefined : value;
|
|
369
|
+
}
|
|
351
370
|
else {
|
|
352
371
|
if (isObject(value) && 'id' in value && 'name' in value) {
|
|
353
372
|
submittedFields[field] = pick(value, 'id', 'name');
|
|
@@ -5,6 +5,7 @@ import Handlebars from 'no-eval-handlebars';
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import ReactDOM from 'react-dom';
|
|
7
7
|
import { FormField } from '../../../custom';
|
|
8
|
+
import { obfuscateValue } from '../../FormV2/components/utils';
|
|
8
9
|
import { FormComponentWrapper } from '../Common';
|
|
9
10
|
import { isPropertyVisible } from '../utils';
|
|
10
11
|
function isAddressProperties(key) {
|
|
@@ -290,6 +291,15 @@ export class FormFieldComponent extends ReactComponent {
|
|
|
290
291
|
if (!isPropertyVisible(this.component.conditional, this.root.data)) {
|
|
291
292
|
return;
|
|
292
293
|
}
|
|
294
|
+
// When protected value hasn't been edited or viewed, don't validate
|
|
295
|
+
// the obfuscated value
|
|
296
|
+
if (this.component.property.protection) {
|
|
297
|
+
const obfuscatedValue = obfuscateValue(value, this.component.property);
|
|
298
|
+
if (obfuscatedValue === value) {
|
|
299
|
+
delete this.errorDetails['rootError'];
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
293
303
|
const emptyMask = this.component.inputMaskPlaceholderChar &&
|
|
294
304
|
this.component.inputMask
|
|
295
305
|
?.replaceAll('9', this.component.inputMaskPlaceholderChar)
|
|
@@ -490,6 +500,6 @@ export class FormFieldComponent extends ReactComponent {
|
|
|
490
500
|
falsePositiveMaskError &&
|
|
491
501
|
isEmpty(this.errorDetails) &&
|
|
492
502
|
this.emit('changed-' + this.component.key, e.target.value);
|
|
493
|
-
}, ...this.component, id: inputId, defaultValue: this.dataValue, mask: this.component.inputMask, error: this.hasErrors(), size: this.component.fieldHeight ?? 'medium', required: this.component.property.type === 'boolean' ? this.component.strictlyTrue : undefined, isCombobox: this.component.nonStrictEnum }))), root);
|
|
503
|
+
}, ...this.component, id: inputId, defaultValue: this.dataValue, mask: this.component.inputMask, error: this.hasErrors(), size: this.component.fieldHeight ?? 'medium', required: this.component.property.type === 'boolean' ? this.component.strictlyTrue : undefined, isCombobox: this.component.nonStrictEnum, protection: this.component.property.protection }))), root);
|
|
494
504
|
}
|
|
495
505
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { ReactComponent } from '@formio/react';
|
|
2
2
|
import { get, isEmpty } from 'lodash';
|
|
3
3
|
import { DateTime } from 'luxon';
|
|
4
|
-
import React from 'react';
|
|
4
|
+
import React, { useState } from 'react';
|
|
5
5
|
import ReactDOM from 'react-dom';
|
|
6
|
+
import { Box } from '../../../..';
|
|
6
7
|
import { Link, Typography } from '../../../core';
|
|
7
8
|
import BooleanSelect from '../../FormField/BooleanSelect/BooleanSelect';
|
|
9
|
+
import PropertyProtection from '../../FormV2/components/PropertyProtection';
|
|
8
10
|
import { FormComponentWrapper } from '../Common/FormComponentWrapper';
|
|
9
11
|
export class ViewOnlyComponent extends ReactComponent {
|
|
10
12
|
/**
|
|
@@ -26,8 +28,9 @@ export class ViewOnlyComponent extends ReactComponent {
|
|
|
26
28
|
this.showValue = this.showValue.bind(this);
|
|
27
29
|
}
|
|
28
30
|
showValue(value) {
|
|
29
|
-
if ((value === null || value === undefined) && this.component.type !== 'ViewOnlyBoolean')
|
|
31
|
+
if ((value === null || value === undefined) && this.component.type !== 'ViewOnlyBoolean') {
|
|
30
32
|
return React.createElement("span", null, "\u00A0");
|
|
33
|
+
}
|
|
31
34
|
switch (this.component.type) {
|
|
32
35
|
case 'ViewOnlyObject':
|
|
33
36
|
if (this.component.defaultPages && this.component.property.objectId) {
|
|
@@ -76,13 +79,36 @@ export class ViewOnlyComponent extends ReactComponent {
|
|
|
76
79
|
if (!root) {
|
|
77
80
|
root = element;
|
|
78
81
|
}
|
|
82
|
+
const value = this.component.instance
|
|
83
|
+
? get(this.component.instance, this.component.key)
|
|
84
|
+
: this.component.defaultValue;
|
|
79
85
|
/* TODO: You'll see warnings to upgrade to React 18's createRoot();
|
|
80
86
|
* It'll cause issues with: field-level errors not showing up, conditional visibility not working, focus moving out of the form on keypress
|
|
81
87
|
* Will need to be revisited later. Possibly look into using this.ref */
|
|
82
88
|
return ReactDOM.render(React.createElement("div", null,
|
|
83
|
-
React.createElement(FormComponentWrapper, { ...this.component, viewOnly: true },
|
|
84
|
-
React.createElement(Typography, { variant: "body1", key: this.component.key }, this.showValue(this.component.instance
|
|
85
|
-
? get(this.component.instance, this.component.key)
|
|
86
|
-
: this.component.defaultValue)))), root);
|
|
89
|
+
React.createElement(FormComponentWrapper, { ...this.component, viewOnly: true }, this.component.property?.protection ? (React.createElement(ProtectedValue, { property: this.component.property, value: value, instance: this.component.instance, apiServices: this.component.apiServices })) : (React.createElement(Typography, { variant: "body1", key: this.component.key }, this.showValue(value))))), root);
|
|
87
90
|
}
|
|
88
91
|
}
|
|
92
|
+
const ProtectedValue = (props) => {
|
|
93
|
+
const { property, value, instance, apiServices } = props;
|
|
94
|
+
const [currentDisplayValue, setCurrentDisplayValue] = useState(value);
|
|
95
|
+
const [protectionMode, setProtectionMode] = useState('mask');
|
|
96
|
+
const formatValue = (val) => {
|
|
97
|
+
if (typeof val !== 'string') {
|
|
98
|
+
return val;
|
|
99
|
+
}
|
|
100
|
+
else if (property.type === 'date' && protectionMode !== 'mask') {
|
|
101
|
+
return DateTime.fromISO(val).toFormat('MM/dd/yyyy');
|
|
102
|
+
}
|
|
103
|
+
else if (property.type === 'date-time') {
|
|
104
|
+
return DateTime.fromISO(val).toFormat('MM/dd/yyyy hh:mm a');
|
|
105
|
+
}
|
|
106
|
+
else if (property.type === 'time') {
|
|
107
|
+
return DateTime.fromISO(DateTime.now().toISODate() + 'T' + val).toFormat('hh:mm a');
|
|
108
|
+
}
|
|
109
|
+
return val;
|
|
110
|
+
};
|
|
111
|
+
return (React.createElement(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
|
|
112
|
+
React.createElement(Typography, { variant: "body1", key: property.id }, formatValue(currentDisplayValue)),
|
|
113
|
+
!!currentDisplayValue && (React.createElement(PropertyProtection, { parameter: property, protection: property?.protection, mask: property?.mask, value: currentDisplayValue, canEdit: false, setCurrentDisplayValue: setCurrentDisplayValue, mode: protectionMode, setMode: setProtectionMode, instance: instance, apiServices: apiServices }))));
|
|
114
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ApiServices, ObjectInstance, Property, UserAccount, ViewLayoutEntityReference } from '@evoke-platform/context';
|
|
1
|
+
import { ApiServices, ObjectInstance, Property, PropertyProtection, UserAccount, ViewLayoutEntityReference } from '@evoke-platform/context';
|
|
2
2
|
import { ReactComponent } from '@formio/react';
|
|
3
3
|
import { AutocompleteOption } from '../../core';
|
|
4
4
|
export type BaseFormComponentProps = {
|
|
@@ -52,6 +52,7 @@ export type BaseFormComponentProps = {
|
|
|
52
52
|
apiServices: ApiServices;
|
|
53
53
|
description?: string;
|
|
54
54
|
displayOption?: 'radioButton' | 'dropdown' | 'dialogBox' | 'switch' | 'checkbox';
|
|
55
|
+
protection?: PropertyProtection;
|
|
55
56
|
};
|
|
56
57
|
export type ObjectPropertyInputProps = {
|
|
57
58
|
id: string;
|
|
@@ -113,3 +114,8 @@ export type SavedDocumentReference = {
|
|
|
113
114
|
id: string;
|
|
114
115
|
name: string;
|
|
115
116
|
};
|
|
117
|
+
export type CorruptedFile = {
|
|
118
|
+
handle: object;
|
|
119
|
+
path: string;
|
|
120
|
+
relativePath: string;
|
|
121
|
+
};
|
|
@@ -2,7 +2,7 @@ import { ActionInput, ActionInputType, ApiServices, AxiosError, FormEntry, Input
|
|
|
2
2
|
import { ReactComponent } from '@formio/react';
|
|
3
3
|
import { LocalDateTime } from '@js-joda/core';
|
|
4
4
|
import { AutocompleteOption } from '../../core';
|
|
5
|
-
import { Address, ObjectPropertyInputProps } from './types';
|
|
5
|
+
import { Address, CorruptedFile, ObjectPropertyInputProps, SavedDocumentReference } from './types';
|
|
6
6
|
export declare function determineComponentType(properties: Property[], parameter?: InputParameter): ActionInputType | undefined;
|
|
7
7
|
export declare function determineParameterType(componentType: string): PropertyType;
|
|
8
8
|
export declare function getFlattenEntries(entries: FormEntry[]): InputParameterReference[];
|
|
@@ -51,3 +51,17 @@ export declare function isPropertyVisible(conditional: {
|
|
|
51
51
|
export declare function normalizeDateTime(dateTime: LocalDateTime): string;
|
|
52
52
|
export declare function normalizeDates(instances: ObjectInstance[], object?: Obj): void;
|
|
53
53
|
export declare function retrieveCustomErrorMessage(error: AxiosError<any>): string | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Determines whether a submitted value contains any corrupted file entries.
|
|
56
|
+
*
|
|
57
|
+
* A value is considered to contain corrupted files if:
|
|
58
|
+
* - It is an array, and
|
|
59
|
+
* - At least one item in the array matches the `CorruptedFile` shape.
|
|
60
|
+
*
|
|
61
|
+
* This is useful for identifying broken document-upload metadata that
|
|
62
|
+
* should be replaced with the original unmodified files.
|
|
63
|
+
*
|
|
64
|
+
* @param value - The value of the file to inspect.
|
|
65
|
+
* @returns `true` if the value is an array containing at least one `CorruptedFile`, otherwise `false`.
|
|
66
|
+
*/ export declare function containsCorruptedFiles(value: (File | SavedDocumentReference | CorruptedFile)[]): boolean;
|
|
67
|
+
export declare function isCorruptedFile(file: File | SavedDocumentReference | CorruptedFile): file is CorruptedFile;
|
|
@@ -1435,3 +1435,21 @@ function convertVisibilityToConditional(visibility) {
|
|
|
1435
1435
|
}
|
|
1436
1436
|
: undefined;
|
|
1437
1437
|
}
|
|
1438
|
+
/**
|
|
1439
|
+
* Determines whether a submitted value contains any corrupted file entries.
|
|
1440
|
+
*
|
|
1441
|
+
* A value is considered to contain corrupted files if:
|
|
1442
|
+
* - It is an array, and
|
|
1443
|
+
* - At least one item in the array matches the `CorruptedFile` shape.
|
|
1444
|
+
*
|
|
1445
|
+
* This is useful for identifying broken document-upload metadata that
|
|
1446
|
+
* should be replaced with the original unmodified files.
|
|
1447
|
+
*
|
|
1448
|
+
* @param value - The value of the file to inspect.
|
|
1449
|
+
* @returns `true` if the value is an array containing at least one `CorruptedFile`, otherwise `false`.
|
|
1450
|
+
*/ export function containsCorruptedFiles(value) {
|
|
1451
|
+
return value.some((item) => isCorruptedFile(item));
|
|
1452
|
+
}
|
|
1453
|
+
export function isCorruptedFile(file) {
|
|
1454
|
+
return 'handle' in file && isEmpty(file.handle) && !(file instanceof File);
|
|
1455
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PropertyProtection as PropertyProtectionType, SelectOption } from '@evoke-platform/context';
|
|
1
|
+
import { ApiServices, PropertyProtection as PropertyProtectionType, SelectOption } from '@evoke-platform/context';
|
|
2
2
|
import React, { FocusEventHandler, ReactNode } from 'react';
|
|
3
3
|
import { ObjectProperty } from '../../../types';
|
|
4
4
|
import { AutocompleteOption } from '../../core';
|
|
@@ -38,6 +38,10 @@ export type FormFieldProps = {
|
|
|
38
38
|
isCombobox?: boolean;
|
|
39
39
|
endAdornment?: ReactNode;
|
|
40
40
|
protection?: PropertyProtectionType;
|
|
41
|
+
/** @deprecated only used for FormV1 since form context is not available */
|
|
42
|
+
instance?: Record<string, unknown>;
|
|
43
|
+
/** @deprecated only used for FormV1 since form context is not available */
|
|
44
|
+
apiServices?: ApiServices;
|
|
41
45
|
};
|
|
42
46
|
declare const FormField: (props: FormFieldProps) => React.JSX.Element;
|
|
43
47
|
export default FormField;
|
|
@@ -18,7 +18,7 @@ const FormField = (props) => {
|
|
|
18
18
|
setCurrentDisplayValue(defaultValue);
|
|
19
19
|
}
|
|
20
20
|
}, [defaultValue]);
|
|
21
|
-
const protectionComponent = isProtectedProperty && !!defaultValue ? (React.createElement(PropertyProtection, { parameter: property, protection: protection, mask: mask, canEdit: !readOnly, value: defaultValue, handleChange: (value) => onChange?.(property.id, value, property), setCurrentDisplayValue: setCurrentDisplayValue, mode: protectionMode, setMode: setProtectionMode })) : null;
|
|
21
|
+
const protectionComponent = isProtectedProperty && !!defaultValue ? (React.createElement(PropertyProtection, { parameter: property, protection: protection, mask: mask, canEdit: !readOnly, value: defaultValue, handleChange: (value) => onChange?.(property.id, value, property), setCurrentDisplayValue: setCurrentDisplayValue, mode: protectionMode, setMode: setProtectionMode, instance: props.instance, apiServices: props.apiServices })) : null;
|
|
22
22
|
const commonProps = {
|
|
23
23
|
id: id ?? property.id,
|
|
24
24
|
property,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PropertyProtection as PropertyProtectionType } from '@evoke-platform/context';
|
|
1
|
+
import { ApiServices, PropertyProtection as PropertyProtectionType } from '@evoke-platform/context';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { ObjectProperty } from '../../../../types';
|
|
4
4
|
type PropertyProtectionProps = {
|
|
@@ -11,6 +11,10 @@ type PropertyProtectionProps = {
|
|
|
11
11
|
setCurrentDisplayValue: (value: unknown) => void;
|
|
12
12
|
mode: 'mask' | 'full' | 'edit';
|
|
13
13
|
setMode: (mode: 'mask' | 'full' | 'edit') => void;
|
|
14
|
+
/** @deprecated only used for FormV1 since form context is not available */
|
|
15
|
+
instance?: Record<string, unknown>;
|
|
16
|
+
/** @deprecated only used for FormV1 */
|
|
17
|
+
apiServices?: ApiServices;
|
|
14
18
|
};
|
|
15
19
|
declare const PropertyProtection: React.FC<PropertyProtectionProps>;
|
|
16
20
|
export default PropertyProtection;
|
|
@@ -1,25 +1,47 @@
|
|
|
1
1
|
import { useApiServices } from '@evoke-platform/context';
|
|
2
|
+
import { isEmpty } from 'lodash';
|
|
2
3
|
import React, { useEffect, useState } from 'react';
|
|
3
4
|
import { CheckRounded, ClearRounded, EditRounded, VisibilityOffRounded, VisibilityRounded } from '../../../../icons';
|
|
4
5
|
import { useFormContext } from '../../../../theme/hooks';
|
|
5
6
|
import { Divider, IconButton, InputAdornment, Snackbar, Tooltip } from '../../../core';
|
|
6
7
|
import { getPrefixedUrl } from '../../Form/utils';
|
|
7
8
|
import { obfuscateValue } from './utils';
|
|
9
|
+
// Global cache to persist data across component unmount/remount cycles (needed for FormV1)
|
|
10
|
+
const globalCache = new Map();
|
|
8
11
|
const PropertyProtection = (props) => {
|
|
9
|
-
const { parameter, mask, protection, canEdit, value, mode, setMode, setCurrentDisplayValue, handleChange } = props;
|
|
10
|
-
const apiServices = useApiServices();
|
|
12
|
+
const { parameter, mask, protection, canEdit, value, mode, setMode, setCurrentDisplayValue, handleChange, instance: instanceFormV1, apiServices: apiServicesFormV1, } = props;
|
|
13
|
+
const apiServices = apiServicesFormV1 ?? useApiServices();
|
|
11
14
|
const { object, instance, fetchedOptions, setFetchedOptions } = useFormContext();
|
|
12
|
-
const
|
|
13
|
-
const
|
|
15
|
+
const isFormV1 = !!apiServicesFormV1;
|
|
16
|
+
const getCachedValue = (key) => {
|
|
17
|
+
if (isFormV1) {
|
|
18
|
+
const cache = globalCache.get(parameter.id);
|
|
19
|
+
if (key === 'hasViewPermission')
|
|
20
|
+
return cache?.hasViewPermission;
|
|
21
|
+
if (key === 'fullValue')
|
|
22
|
+
return cache?.fullValue;
|
|
23
|
+
}
|
|
24
|
+
return fetchedOptions?.[`${parameter.id}-${key}`];
|
|
25
|
+
};
|
|
26
|
+
const updateCache = (key, value) => {
|
|
27
|
+
if (isFormV1) {
|
|
28
|
+
globalCache.set(parameter.id, { ...globalCache.get(parameter.id), [key]: value });
|
|
29
|
+
}
|
|
30
|
+
else if (setFetchedOptions) {
|
|
31
|
+
setFetchedOptions({ [`${parameter.id}-${key}`]: value });
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const [hasViewPermission, setHasViewPermission] = useState(getCachedValue('hasViewPermission'));
|
|
35
|
+
const [fullValue, setFullValue] = useState(getCachedValue('fullValue'));
|
|
14
36
|
const [isLoading, setIsLoading] = useState(hasViewPermission === undefined);
|
|
15
37
|
const [error, setError] = useState(null);
|
|
16
38
|
useEffect(() => {
|
|
17
|
-
if (hasViewPermission === undefined && instance) {
|
|
39
|
+
if (hasViewPermission === undefined && (instance || instanceFormV1)) {
|
|
18
40
|
apiServices
|
|
19
|
-
.get(getPrefixedUrl(`/objects/${object?.id}/instances/${instance?.id}/checkAccess?action=readProtected&fieldId=${parameter.id}`))
|
|
41
|
+
.get(getPrefixedUrl(`/objects/${object?.id ?? instanceFormV1?.objectId}/instances/${instance?.id ?? instanceFormV1?.id}/checkAccess?action=readProtected&fieldId=${parameter.id}`))
|
|
20
42
|
.then((viewPermissionCheck) => {
|
|
21
43
|
setHasViewPermission(viewPermissionCheck.result);
|
|
22
|
-
|
|
44
|
+
updateCache(`hasViewPermission`, viewPermissionCheck.result);
|
|
23
45
|
})
|
|
24
46
|
.catch(() => {
|
|
25
47
|
setError('Failed to check view permission.');
|
|
@@ -32,13 +54,13 @@ const PropertyProtection = (props) => {
|
|
|
32
54
|
const canViewFull = hasViewPermission || hasValueChangedOrViewed;
|
|
33
55
|
const fetchFullValue = async () => {
|
|
34
56
|
// if instance doesn't exist, cannot fetch full value
|
|
35
|
-
if (
|
|
57
|
+
if (isEmpty(instance) && isEmpty(instanceFormV1)) {
|
|
36
58
|
return undefined;
|
|
37
59
|
}
|
|
38
60
|
try {
|
|
39
|
-
const value = await apiServices.get(getPrefixedUrl(`/objects/${object?.id}/instances/${instance?.id}/properties/${parameter.id}?showProtectedValue=true`));
|
|
61
|
+
const value = await apiServices.get(getPrefixedUrl(`/objects/${object?.id ?? instanceFormV1?.objectId}/instances/${instance?.id ?? instanceFormV1?.id}/properties/${parameter.id}?showProtectedValue=true`));
|
|
40
62
|
setFullValue(value);
|
|
41
|
-
|
|
63
|
+
updateCache('fullValue', value);
|
|
42
64
|
return value;
|
|
43
65
|
}
|
|
44
66
|
catch (error) {
|
|
@@ -75,7 +97,7 @@ const PropertyProtection = (props) => {
|
|
|
75
97
|
// save current value as full value, so value can be reverted as needed
|
|
76
98
|
if (hasValueChangedOrViewed) {
|
|
77
99
|
setFullValue(value);
|
|
78
|
-
|
|
100
|
+
updateCache('fullValue', value);
|
|
79
101
|
}
|
|
80
102
|
}
|
|
81
103
|
setMode(newMode);
|