mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-06 06:48:21 +00:00
Make encrypted fields editable (#5008)
* fix: data sources update * fix: default state for custom s3 url * refactor: move state to parent * make encrypted fields default enabled for new datasources * show edit toggle only while editing datasource * chore: change toggle placement * chore: replace edit toggle with button * fix: password reset
This commit is contained in:
parent
68393cecb2
commit
307f869ff9
3 changed files with 87 additions and 15 deletions
|
|
@ -27,6 +27,8 @@ const DynamicForm = ({
|
|||
optionsChanged,
|
||||
queryName,
|
||||
}) => {
|
||||
const [computedProps, setComputedProps] = React.useState({});
|
||||
|
||||
// if(schema.properties) todo add empty check
|
||||
React.useLayoutEffect(() => {
|
||||
if (!isEditMode || isEmpty(options)) {
|
||||
|
|
@ -35,6 +37,34 @@ const DynamicForm = ({
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
const { properties } = schema;
|
||||
if (isEmpty(properties)) return null;
|
||||
|
||||
let fields = {};
|
||||
let encrpytedFieldsProps = {};
|
||||
const flipComponentDropdown = find(properties, ['type', 'dropdown-component-flip']);
|
||||
|
||||
if (flipComponentDropdown) {
|
||||
const selector = options?.[flipComponentDropdown?.key]?.value;
|
||||
fields = { ...flipComponentDropdown?.commonFields, ...properties[selector] };
|
||||
} else {
|
||||
fields = { ...properties };
|
||||
}
|
||||
|
||||
Object.keys(fields).map((key) => {
|
||||
const { type, encrypted } = fields[key];
|
||||
if ((type === 'password' || encrypted) && !(key in computedProps)) {
|
||||
//Editable encrypted fields only if datasource doesn't exists
|
||||
encrpytedFieldsProps[key] = {
|
||||
disabled: !!selectedDataSource?.id,
|
||||
};
|
||||
}
|
||||
});
|
||||
setComputedProps({ ...computedProps, ...encrpytedFieldsProps });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [options]);
|
||||
|
||||
const getElement = (type) => {
|
||||
switch (type) {
|
||||
case 'password':
|
||||
|
|
@ -97,7 +127,7 @@ const DynamicForm = ({
|
|||
case 'textarea':
|
||||
return {
|
||||
type,
|
||||
placeholder: description,
|
||||
placeholder: options?.[key]?.encrypted ? '**************' : description,
|
||||
className: `form-control${handleToggle(controller)}`,
|
||||
value: options?.[key]?.value,
|
||||
...(type === 'textarea' && { rows: rows }),
|
||||
|
|
@ -207,6 +237,26 @@ const DynamicForm = ({
|
|||
return flipComponentDropdown;
|
||||
}
|
||||
|
||||
const handleEncryptedFieldsToggle = (event, field) => {
|
||||
const isEditing = computedProps[field]['disabled'];
|
||||
setComputedProps({
|
||||
...computedProps,
|
||||
[field]: {
|
||||
...computedProps[field],
|
||||
disabled: !isEditing,
|
||||
},
|
||||
});
|
||||
|
||||
if (isEditing) {
|
||||
optionchanged(field, '');
|
||||
} else {
|
||||
//Send old field value if editing mode disabled for encrypted fields
|
||||
const newOptions = { ...options };
|
||||
const oldFieldValue = selectedDataSource?.['options']?.[field];
|
||||
optionsChanged({ ...newOptions, [field]: oldFieldValue });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="row">
|
||||
{Object.keys(obj).map((key) => {
|
||||
|
|
@ -216,14 +266,30 @@ const DynamicForm = ({
|
|||
|
||||
return (
|
||||
<div className={cx('my-2', { 'col-md-12': !className, [className]: !!className })} key={key}>
|
||||
{label && (
|
||||
<label
|
||||
className="form-label"
|
||||
data-cy={`label-${String(label).toLocaleLowerCase().replace(/\s+/g, '-')}`}
|
||||
>
|
||||
{label}
|
||||
{(type === 'password' || encrypted) && (
|
||||
<small className="text-green mx-2">
|
||||
<div className="d-flex align-items-center">
|
||||
{label && (
|
||||
<label
|
||||
className="form-label"
|
||||
data-cy={`label-${String(label).toLocaleLowerCase().replace(/\s+/g, '-')}`}
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
{(type === 'password' || encrypted) && selectedDataSource?.id && (
|
||||
<div className="mx-1 col">
|
||||
<button
|
||||
className="btn btn-sm font-500 color-primary border-1 mb-2 mx-2"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
onClick={(event) => handleEncryptedFieldsToggle(event, key)}
|
||||
>
|
||||
{computedProps?.[key]?.['disabled'] ? 'Edit' : 'Cancel'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{(type === 'password' || encrypted) && (
|
||||
<div className="col-auto mb-2">
|
||||
<small className="text-green">
|
||||
<img
|
||||
className="mx-2 encrypted-icon"
|
||||
src="assets/images/icons/padlock.svg"
|
||||
|
|
@ -232,11 +298,12 @@ const DynamicForm = ({
|
|||
/>
|
||||
Encrypted
|
||||
</small>
|
||||
)}
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Element
|
||||
{...getElementProps(obj[key])}
|
||||
{...computedProps[key]}
|
||||
data-cy={`${String(label).toLocaleLowerCase().replace(/\s+/g, '-')}-text-field`}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
export default ({ defaultChecked, onChange, checked = false }) => {
|
||||
export default ({ defaultChecked, onChange, checked = false, classes = {} }) => {
|
||||
return (
|
||||
<label className="form-switch">
|
||||
<label className={`form-switch ${classes?.wrapper}`}>
|
||||
<input
|
||||
className="form-check-input"
|
||||
checked={checked}
|
||||
|
|
|
|||
|
|
@ -119,7 +119,12 @@ export class DataSourcesService {
|
|||
const sourceOptions = {};
|
||||
|
||||
for (const key of Object.keys(options)) {
|
||||
sourceOptions[key] = options[key]['value'];
|
||||
const credentialId = options[key]?.['credential_id'];
|
||||
if (credentialId) {
|
||||
sourceOptions[key] = await this.credentialsService.getValue(credentialId);
|
||||
} else {
|
||||
sourceOptions[key] = options[key]['value'];
|
||||
}
|
||||
}
|
||||
|
||||
const service = await this.pluginsHelper.getService(plugin_id, kind);
|
||||
|
|
|
|||
Loading…
Reference in a new issue