mirror of
https://github.com/ToolJet/ToolJet
synced 2026-04-21 21:47:17 +00:00
805 lines
No EOL
28 KiB
JavaScript
805 lines
No EOL
28 KiB
JavaScript
import React from 'react';
|
|
import cx from 'classnames';
|
|
import Input from '@/_ui/Input';
|
|
import Textarea from '@/_ui/Textarea';
|
|
import Select from '@/_ui/Select';
|
|
import Headers from '@/_ui/HttpHeaders';
|
|
import Sort from '@/_ui/Sort';
|
|
import OAuth from '@/_ui/OAuth';
|
|
import Toggle from '@/_ui/Toggle';
|
|
import OpenApi from '@/_ui/OpenAPI';
|
|
import { Checkbox, CheckboxGroup } from '@/_ui/CheckBox';
|
|
import CodeHinter from '@/AppBuilder/CodeEditor';
|
|
import GoogleSheets from '@/_components/Googlesheets';
|
|
import Slack from '@/_components/Slack';
|
|
import Zendesk from '@/_components/Zendesk';
|
|
import ApiEndpointInput from '@/_components/ApiEndpointInput';
|
|
import ApiEndpointInputOld from './ApiEndpointInputOld';
|
|
import { ConditionFilter, CondtionSort, MultiColumn } from '@/_components/MultiConditions';
|
|
import ToolJetDbOperations from '@/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/ToolJetDbOperations';
|
|
import { orgEnvironmentConstantService } from '../_services';
|
|
import { filter, find, isEmpty } from 'lodash';
|
|
import { ButtonSolid } from './AppButton';
|
|
import { useGlobalDataSourcesStatus } from '@/_stores/dataSourcesStore';
|
|
import { canDeleteDataSource, canUpdateDataSource } from '@/_helpers';
|
|
import { Constants } from '@/_helpers/utils';
|
|
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
|
|
import Sharepoint from '@/_components/Sharepoint';
|
|
import AccordionForm from './AccordionForm';
|
|
import { generateCypressDataCy } from '../modules/common/helpers/cypressHelpers';
|
|
import OAuthWrapper from './OAuthWrapper';
|
|
|
|
const DynamicForm = ({
|
|
schema,
|
|
isGDS,
|
|
optionchanged,
|
|
createDataSource,
|
|
options,
|
|
isSaving,
|
|
selectedDataSource,
|
|
isEditMode,
|
|
optionsChanged,
|
|
queryName,
|
|
computeSelectStyles = false,
|
|
currentAppEnvironmentId,
|
|
setDefaultOptions,
|
|
disableMenuPortal = false,
|
|
onBlur,
|
|
layout = 'vertical',
|
|
renderCopilot,
|
|
elementsProps = null,
|
|
}) => {
|
|
const [computedProps, setComputedProps] = React.useState({});
|
|
const isHorizontalLayout = layout === 'horizontal';
|
|
const prevDataSourceIdRef = React.useRef(selectedDataSource?.id);
|
|
|
|
const globalDataSourcesStatus = useGlobalDataSourcesStatus();
|
|
const { isEditing: isDataSourceEditing } = globalDataSourcesStatus;
|
|
|
|
const [workspaceVariables, setWorkspaceVariables] = React.useState([]);
|
|
const [currentOrgEnvironmentConstants, setCurrentOrgEnvironmentConstants] = React.useState([]);
|
|
|
|
// if(schema.properties) todo add empty check
|
|
React.useLayoutEffect(() => {
|
|
if (!isEditMode || isEmpty(options)) {
|
|
typeof setDefaultOptions === 'function' && setDefaultOptions(schema?.defaults);
|
|
optionsChanged(schema?.defaults ?? {});
|
|
}
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, []);
|
|
|
|
React.useEffect(() => {
|
|
if (isGDS) {
|
|
orgEnvironmentConstantService.getConstantsFromEnvironment(currentAppEnvironmentId).then((data) => {
|
|
const constants = {
|
|
globals: {},
|
|
secrets: {},
|
|
};
|
|
data.constants.forEach((constant) => {
|
|
if (constant.type === Constants.Secret) {
|
|
constants.secrets[constant.name] = constant.value;
|
|
} else {
|
|
constants.globals[constant.name] = constant.value;
|
|
}
|
|
});
|
|
|
|
setCurrentOrgEnvironmentConstants(constants);
|
|
});
|
|
}
|
|
|
|
return () => {
|
|
setWorkspaceVariables([]);
|
|
setCurrentOrgEnvironmentConstants([]);
|
|
};
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [currentAppEnvironmentId]);
|
|
|
|
React.useEffect(() => {
|
|
const prevDataSourceId = prevDataSourceIdRef.current;
|
|
prevDataSourceIdRef.current = selectedDataSource?.id;
|
|
const { properties } = schema;
|
|
if (!isEmpty(properties)) {
|
|
let fields = {};
|
|
let encryptedFieldsProps = {};
|
|
const flipComponentDropdown = find(properties, ['type', 'dropdown-component-flip']);
|
|
|
|
if (flipComponentDropdown) {
|
|
const selector = options?.[flipComponentDropdown?.key]?.value;
|
|
const commonFieldsFromSslCertificate = properties[selector]?.ssl_certificate?.commonFields;
|
|
fields = { ...commonFieldsFromSslCertificate, ...flipComponentDropdown?.commonFields, ...properties[selector] };
|
|
} else {
|
|
fields = { ...properties };
|
|
}
|
|
|
|
const processFields = (fieldsObject) => {
|
|
Object.keys(fieldsObject).forEach((key) => {
|
|
const field = fieldsObject[key];
|
|
const { type, encrypted, key: propertyKey } = field;
|
|
|
|
if (!canUpdateDataSource(selectedDataSource?.id) && !canDeleteDataSource()) {
|
|
encryptedFieldsProps[propertyKey] = {
|
|
disabled: !!selectedDataSource?.id,
|
|
};
|
|
} else if (!isDataSourceEditing) {
|
|
if (type === 'password' || encrypted) {
|
|
encryptedFieldsProps[propertyKey] = {
|
|
disabled: true,
|
|
};
|
|
}
|
|
} else {
|
|
if ((type === 'password' || encrypted) && !(propertyKey in computedProps)) {
|
|
encryptedFieldsProps[propertyKey] = {
|
|
disabled: !!selectedDataSource?.id,
|
|
};
|
|
}
|
|
}
|
|
|
|
// To check for nested dropdown-component-flip
|
|
if (type === 'dropdown-component-flip') {
|
|
const selectedOption = options?.[field.key]?.value;
|
|
|
|
if (field.commonFields) {
|
|
processFields(field.commonFields);
|
|
}
|
|
|
|
if (selectedOption && fieldsObject[selectedOption]) {
|
|
processFields(fieldsObject[selectedOption]);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
processFields(fields);
|
|
|
|
if (properties.renderForm) {
|
|
Object.keys(properties.renderForm).forEach((sectionKey) => {
|
|
const section = properties.renderForm[sectionKey];
|
|
const { inputs } = section;
|
|
if (inputs) {
|
|
processFields(inputs);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (prevDataSourceId !== selectedDataSource?.id) {
|
|
setComputedProps({ ...encryptedFieldsProps });
|
|
} else {
|
|
setComputedProps({ ...computedProps, ...encryptedFieldsProps });
|
|
}
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [selectedDataSource?.id, options, isDataSourceEditing]);
|
|
|
|
const getElement = (type) => {
|
|
switch (type) {
|
|
case 'password':
|
|
case 'text':
|
|
return Input;
|
|
case 'textarea':
|
|
return Textarea;
|
|
case 'dropdown':
|
|
return Select;
|
|
case 'toggle':
|
|
return Toggle;
|
|
case 'checkbox':
|
|
return Checkbox;
|
|
case 'checkbox-group':
|
|
return CheckboxGroup;
|
|
case 'tooljetdb-operations':
|
|
return ToolJetDbOperations;
|
|
case 'react-component-headers':
|
|
return Headers;
|
|
case 'react-component-sort':
|
|
return Sort;
|
|
case 'react-component-oauth-authentication':
|
|
return OAuth;
|
|
case 'react-component-google-sheets':
|
|
return GoogleSheets;
|
|
case 'react-component-slack':
|
|
return Slack;
|
|
case 'codehinter':
|
|
return CodeHinter;
|
|
case 'react-component-openapi-validator':
|
|
return OpenApi;
|
|
case 'react-component-zendesk':
|
|
return Zendesk;
|
|
case 'columns':
|
|
return MultiColumn;
|
|
case 'filters':
|
|
return ConditionFilter;
|
|
case 'sorts':
|
|
return CondtionSort;
|
|
case 'react-component-api-endpoint':
|
|
return ApiEndpointInput;
|
|
case 'react-component-api-endpoint-old':
|
|
return ApiEndpointInputOld;
|
|
case 'react-component-sharepoint':
|
|
return Sharepoint;
|
|
case 'react-component-oauth':
|
|
return OAuthWrapper;
|
|
default:
|
|
return <div>Type is invalid</div>;
|
|
}
|
|
};
|
|
|
|
const handleToggle = (controller) => {
|
|
if (controller) {
|
|
return !options?.[controller]?.value ? ' d-none' : '';
|
|
} else {
|
|
return '';
|
|
}
|
|
};
|
|
|
|
const getElementProps = ({
|
|
key,
|
|
list,
|
|
rows = 5,
|
|
helpText: helpTextProp, // For marketplace compatibility
|
|
help_text,
|
|
description,
|
|
type,
|
|
placeholder = '',
|
|
mode = 'sql',
|
|
lineNumbers = true,
|
|
initialValue,
|
|
height = 'auto',
|
|
width,
|
|
ignoreBraces = false,
|
|
className,
|
|
controller,
|
|
encrypted,
|
|
placeholders = {},
|
|
editorType: editorTypeProp, // For marketplace plugins, it currently receives editorType instead of editor_type
|
|
editor_type,
|
|
spec_url = '',
|
|
disabled = false,
|
|
buttonText: buttonTextProp,
|
|
button_text, // For marketplace plugins, it currently receives button_text instead of buttonText
|
|
text,
|
|
subtext,
|
|
oauth_configs,
|
|
}) => {
|
|
const source = schema?.source?.kind;
|
|
const darkMode = localStorage.getItem('darkMode') === 'true';
|
|
const workspaceConstant = options?.[key]?.workspace_constant;
|
|
const isWorkspaceConstant = !!workspaceConstant;
|
|
|
|
if (!options) return;
|
|
|
|
// Select snake_case for marketplace plugins if camelCase is undefined
|
|
const buttonText = buttonTextProp || button_text;
|
|
const editorType = editorTypeProp || editor_type;
|
|
const helpText = helpTextProp || help_text;
|
|
|
|
switch (type) {
|
|
case 'password':
|
|
case 'text':
|
|
case 'textarea': {
|
|
const useEncrypted =
|
|
(options?.[key]?.encrypted !== undefined ? options?.[key].encrypted : encrypted) || type === 'password';
|
|
return {
|
|
type,
|
|
placeholder: workspaceConstant ? workspaceConstant : useEncrypted ? '**************' : description,
|
|
className: `form-control${handleToggle(controller)} ${useEncrypted && 'dynamic-form-encrypted-field'}`,
|
|
style: { marginBottom: '0px !important' },
|
|
value: options?.[key]?.value || '',
|
|
...(type === 'textarea' && { rows: rows }),
|
|
...(helpText && { helpText }),
|
|
onChange: (e) => optionchanged(key, e.target.value, true), //shouldNotAutoSave is true because autosave should occur during onBlur, not after each character change (in optionchanged).
|
|
onblur: () => onBlur(),
|
|
isGDS,
|
|
workspaceVariables,
|
|
workspaceConstants: currentOrgEnvironmentConstants,
|
|
encrypted: useEncrypted,
|
|
isWorkspaceConstant: isWorkspaceConstant,
|
|
};
|
|
}
|
|
case 'toggle':
|
|
return {
|
|
defaultChecked: options?.[key],
|
|
checked: options?.[key]?.value,
|
|
onChange: (e) => optionchanged(key, e.target.checked),
|
|
text,
|
|
subtext,
|
|
};
|
|
case 'dropdown':
|
|
case 'dropdown-component-flip':
|
|
return {
|
|
options: list,
|
|
value: options?.[key]?.value || options?.[key],
|
|
onChange: (value) => optionchanged(key, value),
|
|
width: width || '100%',
|
|
useMenuPortal: disableMenuPortal ? false : queryName ? true : false,
|
|
styles: computeSelectStyles ? computeSelectStyles('100%') : {},
|
|
useCustomStyles: computeSelectStyles ? true : false,
|
|
isDisabled: !canUpdateDataSource(selectedDataSource?.id) && !canDeleteDataSource(),
|
|
encrypted: options?.[key]?.encrypted,
|
|
};
|
|
case 'checkbox-group':
|
|
return {
|
|
options: list,
|
|
values: options?.[key] ?? [],
|
|
onChange: (value) => {
|
|
optionchanged(key, [...value]);
|
|
},
|
|
};
|
|
|
|
case 'react-component-headers': {
|
|
let isRenderedAsQueryEditor;
|
|
if (isGDS) {
|
|
isRenderedAsQueryEditor = false;
|
|
} else {
|
|
isRenderedAsQueryEditor = !isGDS;
|
|
}
|
|
return {
|
|
getter: key,
|
|
options: isRenderedAsQueryEditor
|
|
? options?.[key] ?? schema?.defaults?.[key]
|
|
: options?.[key]?.value ?? schema?.defaults?.[key]?.value,
|
|
optionchanged,
|
|
isRenderedAsQueryEditor,
|
|
workspaceConstants: currentOrgEnvironmentConstants,
|
|
isDisabled: !canUpdateDataSource(selectedDataSource?.id) && !canDeleteDataSource(),
|
|
encrypted: options?.[key]?.encrypted,
|
|
buttonText,
|
|
width: width,
|
|
...elementsProps?.[key],
|
|
};
|
|
}
|
|
case 'react-component-sort': {
|
|
let isRenderedAsQueryEditor;
|
|
if (isGDS) {
|
|
isRenderedAsQueryEditor = false;
|
|
} else {
|
|
isRenderedAsQueryEditor = !isGDS;
|
|
}
|
|
return {
|
|
getter: key,
|
|
options: isRenderedAsQueryEditor
|
|
? options?.[key] ?? schema?.defaults?.[key]
|
|
: options?.[key]?.value ?? schema?.defaults?.[key]?.value,
|
|
optionchanged,
|
|
isRenderedAsQueryEditor,
|
|
workspaceConstants: currentOrgEnvironmentConstants,
|
|
isDisabled: !canUpdateDataSource(selectedDataSource?.id) && !canDeleteDataSource(),
|
|
encrypted: options?.[key]?.encrypted,
|
|
buttonText,
|
|
width: width,
|
|
};
|
|
}
|
|
case 'react-component-oauth-authentication':
|
|
return {
|
|
isGrpc: source === 'grpc' || source === 'grpcv2',
|
|
grant_type: options?.grant_type?.value,
|
|
auth_type: options?.auth_type?.value,
|
|
add_token_to: options?.add_token_to?.value,
|
|
header_prefix: options?.header_prefix?.value,
|
|
access_token_url: options?.access_token_url?.value,
|
|
access_token_custom_headers: options?.access_token_custom_headers?.value,
|
|
client_id: options?.client_id?.value,
|
|
client_secret: options?.client_secret?.value,
|
|
client_auth: options?.client_auth?.value,
|
|
scopes: options?.scopes?.value,
|
|
username: options?.username?.value,
|
|
password: options?.password?.value,
|
|
grpc_apiKey_key: options?.grpc_apikey_key?.value,
|
|
grpc_apiKey_value: options?.grpc_apikey_value?.value,
|
|
bearer_token: options?.bearer_token?.value,
|
|
auth_url: options?.auth_url?.value,
|
|
auth_key: options?.auth_key?.value,
|
|
audience: options?.audience?.value,
|
|
custom_auth_params: options?.custom_auth_params?.value,
|
|
custom_query_params: options?.custom_query_params?.value,
|
|
multiple_auth_enabled: options?.multiple_auth_enabled?.value,
|
|
optionchanged,
|
|
workspaceConstants: currentOrgEnvironmentConstants,
|
|
isDisabled: !canUpdateDataSource(selectedDataSource?.id) && !canDeleteDataSource(),
|
|
options,
|
|
optionsChanged,
|
|
selectedDataSource,
|
|
};
|
|
case 'react-component-google-sheets':
|
|
case 'react-component-slack':
|
|
case 'react-component-zendesk':
|
|
case 'react-component-sharepoint':
|
|
case 'react-component-oauth':
|
|
return {
|
|
optionchanged,
|
|
createDataSource,
|
|
options,
|
|
isSaving,
|
|
selectedDataSource,
|
|
currentAppEnvironmentId,
|
|
workspaceConstants: currentOrgEnvironmentConstants,
|
|
isDisabled: !canUpdateDataSource(selectedDataSource?.id) && !canDeleteDataSource(),
|
|
optionsChanged,
|
|
multiple_auth_enabled: options?.multiple_auth_enabled?.value,
|
|
scopes: options?.scopes?.value,
|
|
oauth_configs,
|
|
};
|
|
case 'tooljetdb-operations':
|
|
return {
|
|
optionchanged,
|
|
createDataSource,
|
|
options,
|
|
isSaving,
|
|
selectedDataSource,
|
|
darkMode,
|
|
optionsChanged,
|
|
renderCopilot,
|
|
};
|
|
case 'codehinter': {
|
|
let theme;
|
|
if (darkMode) {
|
|
theme = 'monokai';
|
|
} else if (lineNumbers) {
|
|
theme = 'duotone-light';
|
|
} else {
|
|
theme = 'default';
|
|
}
|
|
return {
|
|
type: editorType,
|
|
initialValue: options[key]
|
|
? typeof options[key] === 'string'
|
|
? options[key]
|
|
: JSON.stringify(options[key])
|
|
: initialValue,
|
|
lang: mode,
|
|
lineNumbers,
|
|
className: className ? className : lineNumbers ? 'query-hinter' : 'codehinter-query-editor-input',
|
|
onChange: (value) => optionchanged(key, value),
|
|
theme: theme,
|
|
placeholder,
|
|
height,
|
|
width,
|
|
componentName: queryName ? `${queryName}::${key ?? ''}` : null,
|
|
cyLabel: key ? `${String(key).toLocaleLowerCase().replace(/\s+/g, '-')}` : '',
|
|
disabled,
|
|
delayOnChange: false,
|
|
renderCopilot,
|
|
...(helpText && { helpText }),
|
|
};
|
|
}
|
|
case 'react-component-openapi-validator':
|
|
return {
|
|
selectedDataSource,
|
|
isSaving,
|
|
currentAppEnvironmentId,
|
|
multiple_auth_enabled: options?.multiple_auth_enabled?.value,
|
|
format: options.format?.value,
|
|
definition: options.definition?.value,
|
|
auth_type: options.auth_type?.value,
|
|
auth_key: options.auth_key?.value,
|
|
username: options.username?.value,
|
|
password: options.password?.value,
|
|
bearer_token: options.bearer_token?.value,
|
|
api_keys: options.api_keys?.value,
|
|
optionchanged,
|
|
grant_type: options.grant_type?.value,
|
|
add_token_to: options.add_token_to?.value,
|
|
header_prefix: options.header_prefix?.value,
|
|
access_token_url: options.access_token_url?.value,
|
|
access_token_custom_headers: options.access_token_custom_headers?.value,
|
|
client_id: options.client_id?.value,
|
|
client_secret: options.client_secret?.value,
|
|
client_auth: options.client_auth?.value,
|
|
scopes: options.scopes?.value,
|
|
auth_url: options.auth_url?.value,
|
|
custom_auth_params: options.custom_auth_params?.value,
|
|
custom_query_params: options.custom_query_params?.value,
|
|
spec: options.spec?.value,
|
|
audience: options?.audience?.value,
|
|
workspaceConstants: currentOrgEnvironmentConstants,
|
|
isDisabled: !canUpdateDataSource(selectedDataSource?.id) && !canDeleteDataSource(),
|
|
optionsChanged,
|
|
};
|
|
case 'filters':
|
|
return {
|
|
operators: list || [],
|
|
value: options?.[key] ?? {},
|
|
onChange: (value) => optionchanged(key, value),
|
|
placeholders,
|
|
};
|
|
case 'sorts':
|
|
return {
|
|
orders: list || [],
|
|
value: options?.[key] ?? {},
|
|
onChange: (value) => optionchanged(key, value),
|
|
placeholders,
|
|
};
|
|
case 'columns':
|
|
return {
|
|
value: options?.[key] ?? {},
|
|
onChange: (value) => optionchanged(key, value),
|
|
placeholders,
|
|
};
|
|
case 'react-component-api-endpoint':
|
|
return {
|
|
specUrl: spec_url,
|
|
optionsChanged,
|
|
options,
|
|
darkMode,
|
|
};
|
|
case 'react-component-api-endpoint-old':
|
|
return {
|
|
specUrl: spec_url,
|
|
optionsChanged,
|
|
options,
|
|
darkMode,
|
|
};
|
|
default:
|
|
return {};
|
|
}
|
|
};
|
|
|
|
const getLayout = (obj) => {
|
|
if (isEmpty(obj)) return null;
|
|
const flipComponentDropdown = isFlipComponentDropdown(obj);
|
|
|
|
if (flipComponentDropdown) {
|
|
return flipComponentDropdown;
|
|
}
|
|
|
|
const handleEncryptedFieldsToggle = (event, field) => {
|
|
if (!canUpdateDataSource(selectedDataSource?.id) && !canDeleteDataSource()) {
|
|
return;
|
|
}
|
|
const isEditing = computedProps[field]['disabled'];
|
|
const workspaceConstant = options?.[field]?.workspace_constant;
|
|
const isWorkspaceConstant = !!workspaceConstant;
|
|
|
|
if (isEditing) {
|
|
if (isWorkspaceConstant) {
|
|
optionchanged(field, workspaceConstant);
|
|
} else {
|
|
optionchanged(field, '');
|
|
}
|
|
} else {
|
|
const newOptions = { ...options };
|
|
const oldFieldValue = selectedDataSource?.['options']?.[field];
|
|
if (oldFieldValue) {
|
|
optionsChanged({ ...newOptions, [field]: oldFieldValue });
|
|
} else {
|
|
delete newOptions[field];
|
|
optionsChanged({ ...newOptions });
|
|
}
|
|
}
|
|
setComputedProps({
|
|
...computedProps,
|
|
[field]: {
|
|
...computedProps[field],
|
|
disabled: !isEditing,
|
|
},
|
|
});
|
|
};
|
|
|
|
const renderLabel = (label, tooltip) => {
|
|
const labelElement = (
|
|
<label
|
|
className="form-label"
|
|
data-cy={`label-${generateCypressDataCy(label)}`}
|
|
style={{
|
|
textDecoration: tooltip ? 'underline 2px dashed' : 'none',
|
|
textDecorationColor: 'var(--slate8)',
|
|
marginBottom: '2px'
|
|
}}
|
|
>
|
|
{label}
|
|
</label>
|
|
);
|
|
|
|
if (tooltip) {
|
|
return (
|
|
<OverlayTrigger
|
|
placement="top"
|
|
trigger="click"
|
|
rootClose
|
|
overlay={<Tooltip id={`tooltip-${label}`}>{tooltip}</Tooltip>}
|
|
>
|
|
{labelElement}
|
|
</OverlayTrigger>
|
|
);
|
|
}
|
|
|
|
return labelElement;
|
|
};
|
|
|
|
return (
|
|
<div className={`${isHorizontalLayout ? '' : 'row'}`}>
|
|
{Object.keys(obj).map((key) => {
|
|
const { label, type, encrypted, className, key: propertyKey, shouldRenderTheProperty = '' } = obj[key];
|
|
const Element = getElement(type);
|
|
const isSpecificComponent = [
|
|
'tooljetdb-operations',
|
|
'react-component-api-endpoint',
|
|
'react-component-api-endpoint-old',
|
|
'react-component-oauth',
|
|
].includes(type);
|
|
// shouldRenderTheProperty - key is used for Dynamic connection parameters
|
|
const enabled = shouldRenderTheProperty
|
|
? selectedDataSource?.options?.[shouldRenderTheProperty]?.value ?? false
|
|
: true;
|
|
|
|
return (
|
|
enabled && (
|
|
<div
|
|
className={cx({
|
|
'my-2': type !== 'react-component-oauth', // Remove my-2 for react-component-oauth to prevent gap
|
|
'col-md-12': !className && !isHorizontalLayout && type !== 'react-component-oauth',
|
|
[className]: !!className,
|
|
'd-flex': isHorizontalLayout,
|
|
'dynamic-form-row': isHorizontalLayout,
|
|
})}
|
|
data-cy={`${generateCypressDataCy(label ?? key)}-section`}
|
|
key={key}
|
|
>
|
|
{!isSpecificComponent && (
|
|
<div
|
|
className={cx('d-flex', {
|
|
'form-label': isHorizontalLayout,
|
|
'align-items-center': !isHorizontalLayout,
|
|
})}
|
|
style={{ minWidth: '100px', marginBottom: '0' }}
|
|
>
|
|
{label && renderLabel(label, obj[key].tooltip)}
|
|
|
|
{(type === 'password' || encrypted) && selectedDataSource?.id && (
|
|
<div className="mx-1 col">
|
|
<ButtonSolid
|
|
className="datasource-edit-btn mb-2"
|
|
type="a"
|
|
variant="tertiary"
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
disabled={!canUpdateDataSource() && !canDeleteDataSource()}
|
|
onClick={(event) => handleEncryptedFieldsToggle(event, propertyKey)}
|
|
data-cy={`button-${generateCypressDataCy(computedProps?.[propertyKey]?.['disabled'] ? 'Edit' : 'Cancel')}`}
|
|
>
|
|
{computedProps?.[propertyKey]?.['disabled'] ? 'Edit' : 'Cancel'}
|
|
</ButtonSolid>
|
|
</div>
|
|
)}
|
|
{(type === 'password' || encrypted) && (
|
|
<div className="col-auto mb-2">
|
|
<small className="text-green" data-cy="encrypted-text">
|
|
<img
|
|
className="mx-2 encrypted-icon"
|
|
src="assets/images/icons/padlock.svg"
|
|
width="12"
|
|
height="12"
|
|
/>
|
|
Encrypted
|
|
</small>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
<div
|
|
className={cx(
|
|
{
|
|
'flex-grow-1': isHorizontalLayout && !isSpecificComponent,
|
|
'w-100': isHorizontalLayout && type !== 'codehinter',
|
|
},
|
|
'dynamic-form-element'
|
|
)}
|
|
style={{ width: '100%' }}
|
|
>
|
|
<Element
|
|
key={`${selectedDataSource?.id}-${propertyKey}`}
|
|
{...getElementProps(obj[key])}
|
|
{...computedProps[propertyKey]}
|
|
data-cy={`${generateCypressDataCy(label)}-text-field`}
|
|
dataCy={generateCypressDataCy(obj[key].label ?? obj[key].key)}
|
|
//to be removed after whole ui is same
|
|
isHorizontalLayout={isHorizontalLayout}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const FlipComponentDropdown = (obj) => {
|
|
const flipComponentDropdowns = filter(obj, ['type', 'dropdown-component-flip']);
|
|
|
|
const dropdownComponents = flipComponentDropdowns.map((flipComponentDropdown) => {
|
|
const selector = options?.[flipComponentDropdown?.key]?.value || options?.[flipComponentDropdown?.key];
|
|
|
|
return (
|
|
<div key={flipComponentDropdown.key}>
|
|
<div className={isHorizontalLayout ? '' : 'row'} >
|
|
{flipComponentDropdown.commonFields && getLayout(flipComponentDropdown.commonFields)}
|
|
|
|
<div
|
|
className={cx('my-2', {
|
|
'col-md-12': !flipComponentDropdown.className && !isHorizontalLayout,
|
|
'd-flex': isHorizontalLayout,
|
|
'dynamic-form-row': isHorizontalLayout,
|
|
[flipComponentDropdown.className]: !!flipComponentDropdown.className,
|
|
})}
|
|
data-cy={`${generateCypressDataCy(flipComponentDropdown.label)}-section`}
|
|
>
|
|
{(flipComponentDropdown.label || isHorizontalLayout) && (
|
|
<label
|
|
className={cx('form-label')}
|
|
data-cy={`${generateCypressDataCy(flipComponentDropdown.label)}-dropdown-label`}
|
|
style={{ marginBottom: '2px' }}
|
|
>
|
|
{flipComponentDropdown.label}
|
|
</label>
|
|
)}
|
|
|
|
<div
|
|
data-cy={`${generateCypressDataCy(flipComponentDropdown.label)}-select-dropdown`}
|
|
className={cx({ 'flex-grow-1': isHorizontalLayout })}
|
|
>
|
|
<Select
|
|
{...getElementProps(flipComponentDropdown)}
|
|
styles={computeSelectStyles ? computeSelectStyles('100%') : {}}
|
|
useCustomStyles={computeSelectStyles ? true : false}
|
|
dataCy={generateCypressDataCy(flipComponentDropdown.label)}
|
|
/>
|
|
</div>
|
|
{flipComponentDropdown.helpText && (
|
|
<span className="flip-dropdown-help-text">{flipComponentDropdown.helpText}</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{getLayout(obj[selector])}
|
|
</div>
|
|
);
|
|
});
|
|
|
|
const normalComponents = Object.keys(obj).map((key) => {
|
|
const component = obj[key];
|
|
|
|
if (component.type && component.type !== 'dropdown-component-flip') {
|
|
return <div key={key}>{getLayout({ [key]: component })}</div>;
|
|
}
|
|
return null;
|
|
});
|
|
|
|
return (
|
|
<>
|
|
{normalComponents}
|
|
{dropdownComponents}
|
|
</>
|
|
);
|
|
};
|
|
|
|
const isFormComponent = (obj, getLayout) => {
|
|
const formComponent = find(obj, ['type', 'react-form-component']);
|
|
if (formComponent) {
|
|
return <AccordionForm formComponent={formComponent} getLayout={getLayout} />;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
const isFlipComponentDropdown = (obj) => {
|
|
const checkFlipComponents = filter(obj, ['type', 'dropdown-component-flip']);
|
|
if (checkFlipComponents.length > 0) {
|
|
return FlipComponentDropdown(obj);
|
|
} else {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
const flipComponentDropdown = isFlipComponentDropdown(schema.properties);
|
|
const formComponent = isFormComponent(schema.properties, getLayout);
|
|
|
|
if (flipComponentDropdown) {
|
|
return flipComponentDropdown;
|
|
}
|
|
if (formComponent) {
|
|
return formComponent;
|
|
}
|
|
|
|
return getLayout(schema.properties);
|
|
};
|
|
|
|
export default DynamicForm; |