From d80b54143ee8a9ac3f729dd13b75e6424c67abdc Mon Sep 17 00:00:00 2001 From: parthy007 Date: Fri, 9 May 2025 17:03:39 +0530 Subject: [PATCH 001/196] Add logic to preview workspace constants in V2-inputs --- frontend/src/_components/DynamicFormV2.jsx | 8 ++++---- frontend/src/_ui/Input-V3/index.js | 12 +++++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/frontend/src/_components/DynamicFormV2.jsx b/frontend/src/_components/DynamicFormV2.jsx index bada082e16..fa043ba3ad 100644 --- a/frontend/src/_components/DynamicFormV2.jsx +++ b/frontend/src/_components/DynamicFormV2.jsx @@ -320,10 +320,10 @@ const DynamicFormV2 = ({ value: currentValue || '', onChange: (e) => optionchanged(key, e.target.value, true), isGDS: true, - workspaceVariables: [], - workspaceConstants: [], encrypted: isEncrypted, onBlur, + workspaceVariables, + workspaceConstants: currentOrgEnvironmentConstants, }; } case 'password-v3': @@ -342,8 +342,6 @@ const DynamicFormV2 = ({ value: currentValue || '', onChange: (e) => handleOptionChange(key, e.target.value, true), isGDS: true, - workspaceVariables: [], - workspaceConstants: [], encrypted: isEncrypted, onBlur, isRequired: isRequired, @@ -355,6 +353,8 @@ const DynamicFormV2 = ({ ? { valid: true, message: '' } : { valid: null, message: '' }, // handle optional && encrypted fields isDisabled: !canUpdateDataSource(selectedDataSource?.id) && !canDeleteDataSource(), + workspaceVariables, + workspaceConstants: currentOrgEnvironmentConstants, }; } case 'react-component-headers': { diff --git a/frontend/src/_ui/Input-V3/index.js b/frontend/src/_ui/Input-V3/index.js index 71b2b3cf2a..1140ebc57c 100644 --- a/frontend/src/_ui/Input-V3/index.js +++ b/frontend/src/_ui/Input-V3/index.js @@ -6,7 +6,7 @@ import { toast } from 'react-hot-toast'; import InputComponent from '@/components/ui/Input/Index'; const InputV3 = ({ helpText, ...props }) => { - const { workspaceVariables, workspaceConstants, value, widget, disabled, encrypted } = props; + const { workspaceVariables, workspaceConstants, value, widget, disabled, encrypted, onBlur } = props; const [isFocused, setIsFocused] = useState(false); const [isCopied, setIsCopied] = useState(false); @@ -37,6 +37,11 @@ const InputV3 = ({ helpText, ...props }) => { setIsFocused(true)} + onBlur={(event) => { + setIsFocused(false); + onBlur(event); + }} styles="tw-bg-transparent" label={props.label} placeholder={props.placeholder} @@ -49,6 +54,11 @@ const InputV3 = ({ helpText, ...props }) => { {...props} type="password" value={value} + onFocus={() => setIsFocused(true)} + onBlur={(event) => { + setIsFocused(false); + onBlur(event); + }} styles="tw-bg-transparent" label={props.label} placeholder={props.placeholder} From 09e0a94de1d1d34c0a6c6be0160338523aabea78 Mon Sep 17 00:00:00 2001 From: parthy007 Date: Fri, 9 May 2025 17:04:49 +0530 Subject: [PATCH 002/196] Add in-mem-cache setup to reuse auth code --- server/src/helpers/in_memory_cache.service.ts | 22 +++++ server/src/modules/data-sources/module.ts | 2 + .../src/modules/data-sources/util.service.ts | 97 +++++++++++-------- 3 files changed, 81 insertions(+), 40 deletions(-) create mode 100644 server/src/helpers/in_memory_cache.service.ts diff --git a/server/src/helpers/in_memory_cache.service.ts b/server/src/helpers/in_memory_cache.service.ts new file mode 100644 index 0000000000..b1cfde0184 --- /dev/null +++ b/server/src/helpers/in_memory_cache.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class InMemoryCacheService { + private static cacheStore: Map> = new Map(); + + set(key: string, value: Promise): void { + InMemoryCacheService.cacheStore.set(key, value); + } + + get(key: string): Promise | undefined { + return InMemoryCacheService.cacheStore.get(key); + } + + has(key: string): boolean { + return InMemoryCacheService.cacheStore.has(key); + } + + clear(): void { + InMemoryCacheService.cacheStore.clear(); + } +} diff --git a/server/src/modules/data-sources/module.ts b/server/src/modules/data-sources/module.ts index f914bbcd2e..8c1692b3ef 100644 --- a/server/src/modules/data-sources/module.ts +++ b/server/src/modules/data-sources/module.ts @@ -12,6 +12,7 @@ import { AppsRepository } from '@modules/apps/repository'; import { TooljetDbModule } from '@modules/tooljet-db/module'; import { SessionModule } from '@modules/session/module'; import { SampleDBScheduler } from './schedulers/sample-db.scheduler'; +import { InMemoryCacheService } from '@helpers/in_memory_cache.service'; export class DataSourcesModule { static async register(configs?: { IS_GET_CONTEXT: boolean }): Promise { @@ -43,6 +44,7 @@ export class DataSourcesModule { SampleDataSourceService, FeatureAbilityFactory, SampleDBScheduler, + InMemoryCacheService, ], controllers: [DataSourcesController], exports: [DataSourcesUtilService, SampleDataSourceService, PluginsServiceSelector], diff --git a/server/src/modules/data-sources/util.service.ts b/server/src/modules/data-sources/util.service.ts index 03ad5ddd52..b66b97fe29 100644 --- a/server/src/modules/data-sources/util.service.ts +++ b/server/src/modules/data-sources/util.service.ts @@ -21,6 +21,7 @@ import { PluginsServiceSelector } from './services/plugin-selector.service'; import { OrganizationConstantsUtilService } from '@modules/organization-constants/util.service'; import { DataSourceOptions } from '@entities/data_source_options.entity'; import { IDataSourcesUtilService } from './interfaces/IUtilService'; +import { InMemoryCacheService } from '@helpers/in_memory_cache.service'; @Injectable() export class DataSourcesUtilService implements IDataSourcesUtilService { @@ -31,7 +32,8 @@ export class DataSourcesUtilService implements IDataSourcesUtilService { protected readonly licenseTermsService: LicenseTermsService, protected readonly encryptionService: EncryptionService, protected readonly pluginsServiceSelector: PluginsServiceSelector, - protected readonly organizationConstantsUtilService: OrganizationConstantsUtilService + protected readonly organizationConstantsUtilService: OrganizationConstantsUtilService, + protected readonly inMemoryCacheService: InMemoryCacheService ) {} async create(createArgumentsDto: CreateArgumentsDto, user: User): Promise { return await dbTransactionWrap(async (manager: EntityManager) => { @@ -135,7 +137,17 @@ export class DataSourcesUtilService implements IDataSourcesUtilService { const queryService = await this.pluginsServiceSelector.getService(plugin_id, provider); // const queryService = new allPlugins[provider](); - const accessDetails = await queryService.accessDetailsFrom(authCode, options, resetSecureData); + let accessDetailsPromise: Promise; + + const cacheKey = `${provider}_${authCode}`; + + if (this.inMemoryCacheService.has(cacheKey)) { + accessDetailsPromise = this.inMemoryCacheService.get(cacheKey); + } else { + accessDetailsPromise = queryService.accessDetailsFrom(authCode, options, resetSecureData); + this.inMemoryCacheService.set(cacheKey, accessDetailsPromise); + } + const accessDetails = await accessDetailsPromise; for (const row of accessDetails) { const option = {}; @@ -165,52 +177,56 @@ export class DataSourcesUtilService implements IDataSourcesUtilService { throw new BadRequestException('Cannot update configuration of sample data source'); } - await dbTransactionWrap(async (manager: EntityManager) => { - const isMultiEnvEnabled = await this.licenseTermsService.getLicenseTerms(LICENSE_FIELD.MULTI_ENVIRONMENT); - const envToUpdate = await this.appEnvironmentUtilService.get(organizationId, environmentId, false, manager); + try { + await dbTransactionWrap(async (manager: EntityManager) => { + const isMultiEnvEnabled = await this.licenseTermsService.getLicenseTerms(LICENSE_FIELD.MULTI_ENVIRONMENT); + const envToUpdate = await this.appEnvironmentUtilService.get(organizationId, environmentId, false, manager); - // if datasource is restapi then reset the token data - if (dataSource.kind === 'restapi') - options.push({ - key: 'tokenData', - value: undefined, - encrypted: false, - }); + // if datasource is restapi then reset the token data + if (dataSource.kind === 'restapi') + options.push({ + key: 'tokenData', + value: undefined, + encrypted: false, + }); - if (isMultiEnvEnabled) { - dataSource.options = ( - await this.appEnvironmentUtilService.getOptions(dataSourceId, organizationId, envToUpdate.id) - ).options; - - const newOptions = await this.parseOptionsForUpdate(dataSource, options, manager); - await this.appEnvironmentUtilService.updateOptions(newOptions, envToUpdate.id, dataSource.id, manager); - } else { - const allEnvs = await this.appEnvironmentUtilService.getAll(organizationId); - /* - Basic plan customer. lets update all environment options. - this will help us to run the queries successfully when the user buys enterprise plan - */ - - const newOptions = await this.parseOptionsForUpdate(dataSource, options, manager); - for (const env of allEnvs) { + if (isMultiEnvEnabled) { dataSource.options = ( - await this.appEnvironmentUtilService.getOptions(dataSourceId, organizationId, env.id) + await this.appEnvironmentUtilService.getOptions(dataSourceId, organizationId, envToUpdate.id) ).options; - await this.appEnvironmentUtilService.updateOptions(newOptions, env.id, dataSource.id, manager); + const newOptions = await this.parseOptionsForUpdate(dataSource, options, manager); + await this.appEnvironmentUtilService.updateOptions(newOptions, envToUpdate.id, dataSource.id, manager); + } else { + const allEnvs = await this.appEnvironmentUtilService.getAll(organizationId); + /* + Basic plan customer. lets update all environment options. + this will help us to run the queries successfully when the user buys enterprise plan + */ + + for (const env of allEnvs) { + dataSource.options = ( + await this.appEnvironmentUtilService.getOptions(dataSourceId, organizationId, env.id) + ).options; + const newOptions = await this.parseOptionsForUpdate(dataSource, options, manager); + + await this.appEnvironmentUtilService.updateOptions(newOptions, env.id, dataSource.id, manager); + } } - } - const updatableParams = { - id: dataSourceId, - name, - updatedAt: new Date(), - }; + const updatableParams = { + id: dataSourceId, + name, + updatedAt: new Date(), + }; - // Remove keys with undefined values - cleanObject(updatableParams); + // Remove keys with undefined values + cleanObject(updatableParams); - await manager.save(DataSource, updatableParams); - }); + await manager.save(DataSource, updatableParams); + }); + } finally { + this.inMemoryCacheService.clear(); + } } async decrypt(options: Record) { @@ -612,6 +628,7 @@ export class DataSourcesUtilService implements IDataSourcesUtilService { for (const key of Object.keys(options)) { const currentOption = options[key]?.['value']; + constantMatcher.lastIndex = 0; //! request options are nested arrays with constants and variables if (Array.isArray(currentOption)) { From 571326f7e9e46a166b0a9ad878b8cb1e8fba0bc6 Mon Sep 17 00:00:00 2001 From: parthy007 Date: Fri, 9 May 2025 17:05:12 +0530 Subject: [PATCH 003/196] Update git submodule --- server/ee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/ee b/server/ee index b9e73f87b9..427cb39489 160000 --- a/server/ee +++ b/server/ee @@ -1 +1 @@ -Subproject commit b9e73f87b9062e06c49c2c73add6b82ba21dcacf +Subproject commit 427cb394892ced272529c32c8bbac697e1a02ed4 From 3a90ce6139b5a8ecb81128953381b613f05d9f4b Mon Sep 17 00:00:00 2001 From: parthy007 Date: Tue, 13 May 2025 17:23:06 +0530 Subject: [PATCH 004/196] Add backend logic to include workspace_constant key while importing and saving datasource --- .../services/app-import-export.service.ts | 1 + .../src/modules/data-sources/util.service.ts | 56 +++++++++++++------ 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/server/src/modules/apps/services/app-import-export.service.ts b/server/src/modules/apps/services/app-import-export.service.ts index 6cd6b3ce8f..03ca965ac4 100644 --- a/server/src/modules/apps/services/app-import-export.service.ts +++ b/server/src/modules/apps/services/app-import-export.service.ts @@ -1435,6 +1435,7 @@ export class AppImportExportService { key: key, value: options[key]['value'], encrypted: options[key]['encrypted'], + workspace_constant: options[key]['workspace_constant'], }; }); } diff --git a/server/src/modules/data-sources/util.service.ts b/server/src/modules/data-sources/util.service.ts index b66b97fe29..7ecabef083 100644 --- a/server/src/modules/data-sources/util.service.ts +++ b/server/src/modules/data-sources/util.service.ts @@ -105,15 +105,25 @@ export class DataSourcesUtilService implements IDataSourcesUtilService { for (const option of optionsWithOauth) { if (option['encrypted']) { - const credential = await this.credentialService.create( - resetSecureData ? '' : option['value'] || '', - entityManager - ); + if (option['workspace_constant']) { + const credential = await this.credentialService.create(option['workspace_constant'], entityManager); - parsedOptions[option['key']] = { - credential_id: credential.id, - encrypted: option['encrypted'], - }; + parsedOptions[option['key']] = { + credential_id: credential.id, + workspace_constant: option['workspace_constant'], + encrypted: option['encrypted'], + }; + } else { + const credential = await this.credentialService.create( + resetSecureData ? '' : option['value'] || '', + entityManager + ); + + parsedOptions[option['key']] = { + credential_id: credential.id, + encrypted: option['encrypted'], + }; + } } else { parsedOptions[option['key']] = { value: option['value'], @@ -251,31 +261,40 @@ export class DataSourcesUtilService implements IDataSourcesUtilService { const parsedOptions = {}; return await dbTransactionWrap(async (entityManager: EntityManager) => { for (const option of optionsWithOauth) { + const key = option['key']; + const credentialValue = option['value']; + if (option['encrypted']) { const existingCredentialId = - dataSource?.options && - dataSource.options[option['key']] && - dataSource.options[option['key']]['credential_id']; + dataSource?.options && dataSource.options[key] && dataSource.options[key]['credential_id']; + + if (credentialValue && (credentialValue.includes('{{constants') || credentialValue.includes('{{secrets'))) { + parsedOptions[key] = { + workspace_constant: credentialValue, + }; + } if (existingCredentialId) { - (option['value'] || option['value'] === '') && - (await this.credentialService.update(existingCredentialId, option['value'] || '')); + (credentialValue || credentialValue === '') && + (await this.credentialService.update(existingCredentialId, credentialValue || '')); - parsedOptions[option['key']] = { + parsedOptions[key] = { + ...parsedOptions[key], credential_id: existingCredentialId, encrypted: option['encrypted'], }; } else { - const credential = await this.credentialService.create(option['value'] || '', entityManager); + const credential = await this.credentialService.create(credentialValue || '', entityManager); - parsedOptions[option['key']] = { + parsedOptions[key] = { + ...parsedOptions[key], credential_id: credential.id, encrypted: option['encrypted'], }; } } else { - parsedOptions[option['key']] = { - value: option['value'], + parsedOptions[key] = { + value: credentialValue, encrypted: false, }; } @@ -428,6 +447,7 @@ export class DataSourcesUtilService implements IDataSourcesUtilService { const credentialId = parsedOptions[key]?.['credential_id']; if (credentialId) { const encryptedKeyValue = await this.credentialService.getValue(credentialId); + constantMatcher.lastIndex = 0; //check if encrypted key value is a constant if (constantMatcher.test(encryptedKeyValue)) { From 4f73f5ee9ac8123efcc3753a2b95be2e56be74cb Mon Sep 17 00:00:00 2001 From: parthy007 Date: Tue, 13 May 2025 17:23:45 +0530 Subject: [PATCH 005/196] Add frontend changed to render workspace constant variable names on encrypted fields --- frontend/src/_components/DynamicForm.jsx | 3 ++- frontend/src/_components/DynamicFormV2.jsx | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/src/_components/DynamicForm.jsx b/frontend/src/_components/DynamicForm.jsx index 0f5db30e9b..f1b4909c04 100644 --- a/frontend/src/_components/DynamicForm.jsx +++ b/frontend/src/_components/DynamicForm.jsx @@ -252,6 +252,7 @@ const DynamicForm = ({ }) => { const source = schema?.source?.kind; const darkMode = localStorage.getItem('darkMode') === 'true'; + const workspaceConstant = options?.[key]?.workspace_constant; if (!options) return; @@ -263,7 +264,7 @@ const DynamicForm = ({ (options?.[key]?.encrypted !== undefined ? options?.[key].encrypted : encrypted) || type === 'password'; return { type, - placeholder: useEncrypted ? '**************' : description, + placeholder: workspaceConstant ? workspaceConstant : useEncrypted ? '**************' : description, className: `form-control${handleToggle(controller)} ${useEncrypted && 'dynamic-form-encrypted-field'}`, style: { marginBottom: '0px !important' }, value: options?.[key]?.value || '', diff --git a/frontend/src/_components/DynamicFormV2.jsx b/frontend/src/_components/DynamicFormV2.jsx index fa043ba3ad..f7074114fa 100644 --- a/frontend/src/_components/DynamicFormV2.jsx +++ b/frontend/src/_components/DynamicFormV2.jsx @@ -294,6 +294,7 @@ const DynamicFormV2 = ({ const currentValue = options?.[key]?.value; const skipValidation = (!hasUserInteracted && !showValidationErrors) || (!interactedFields.has(key) && !showValidationErrors); + const workspaceConstant = options?.[key]?.workspace_constant; const handleOptionChange = (key, value, flag = true) => { if (!hasUserInteracted) { @@ -311,7 +312,7 @@ const DynamicFormV2 = ({ key, widget, label, - placeholder: isEncrypted ? '**************' : description, + placeholder: workspaceConstant ? workspaceConstant : isEncrypted ? '**************' : description, className: cx('form-control', { 'dynamic-form-encrypted-field': isEncrypted, }), @@ -333,7 +334,7 @@ const DynamicFormV2 = ({ key, widget, label, - placeholder: isEncrypted ? '**************' : description, + placeholder: workspaceConstant ? workspaceConstant : isEncrypted ? '**************' : description, className: cx('form-control', { 'dynamic-form-encrypted-field': isEncrypted, }), From 165a43f83599bbeb6cbb52c8ae2303ca5f522b00 Mon Sep 17 00:00:00 2001 From: parthy007 Date: Thu, 15 May 2025 15:06:15 +0530 Subject: [PATCH 006/196] Persists encryted field value when triggered --- frontend/src/_components/DynamicForm.jsx | 12 ++- frontend/src/_ui/Input/index.js | 35 +++--- .../components/ui/Input/CommonInput/Index.jsx | 20 +++- frontend/src/components/ui/Input/Input.jsx | 101 ++++++++++-------- .../src/modules/data-sources/util.service.ts | 51 +++++---- 5 files changed, 132 insertions(+), 87 deletions(-) diff --git a/frontend/src/_components/DynamicForm.jsx b/frontend/src/_components/DynamicForm.jsx index 00d7542c78..6309689984 100644 --- a/frontend/src/_components/DynamicForm.jsx +++ b/frontend/src/_components/DynamicForm.jsx @@ -254,6 +254,7 @@ const DynamicForm = ({ const source = schema?.source?.kind; const darkMode = localStorage.getItem('darkMode') === 'true'; const workspaceConstant = options?.[key]?.workspace_constant; + const isWorkspaceConstant = !!workspaceConstant; if (!options) return; @@ -277,6 +278,7 @@ const DynamicForm = ({ workspaceVariables, workspaceConstants: currentOrgEnvironmentConstants, encrypted: useEncrypted, + isWorkspaceConstant: isWorkspaceConstant, }; } case 'toggle': @@ -510,10 +512,16 @@ const DynamicForm = ({ return; } const isEditing = computedProps[field]['disabled']; + const workspaceConstant = options?.[field]?.workspace_constant; + const isWorkspaceConstant = !!workspaceConstant; + if (isEditing) { - optionchanged(field, ''); + if (isWorkspaceConstant) { + optionchanged(field, workspaceConstant); + } else { + optionchanged(field, ''); + } } else { - //Send old field value if editing mode disabled for encrypted fields const newOptions = { ...options }; const oldFieldValue = selectedDataSource?.['options']?.[field]; if (oldFieldValue) { diff --git a/frontend/src/_ui/Input/index.js b/frontend/src/_ui/Input/index.js index 7b876d4e45..2e632c74e7 100644 --- a/frontend/src/_ui/Input/index.js +++ b/frontend/src/_ui/Input/index.js @@ -5,20 +5,21 @@ import SolidIcon from '../Icon/SolidIcons'; import { toast } from 'react-hot-toast'; const Input = ({ helpText, onBlur, ...props }) => { - const { workspaceVariables, workspaceConstants, value, type, disabled, encrypted } = props; + const { workspaceVariables, workspaceConstants, value, type, disabled, encrypted, isWorkspaceConstant } = props; const [isFocused, setIsFocused] = useState(false); const [isCopied, setIsCopied] = useState(false); - const [showPasswordProps, setShowPasswordProps] = useState({ - inputType: type, - iconType: 'eyedisable', - }); + const [showPassword, setShowPassword] = useState(false); + const inputType = type === 'password' || encrypted ? (showPassword ? 'text' : 'password') : type; + const iconType = showPassword ? 'eye' : 'eyedisable'; + + useEffect(() => { + if (isWorkspaceConstant) { + setShowPassword(true); + } + }, [isWorkspaceConstant]); const toggleShowPassword = () => { - if (inputType !== 'text') { - setShowPasswordProps({ inputType: 'text', iconType: 'eye' }); - } else { - setShowPasswordProps({ inputType: 'password', iconType: 'eyedisable' }); - } + setShowPassword(!showPassword); }; const handleCopyToClipboard = async () => { @@ -36,12 +37,6 @@ const Input = ({ helpText, onBlur, ...props }) => { } }; - useEffect(() => { - if (disabled && encrypted) setShowPasswordProps({ inputType: 'password', iconType: 'eyedisable' }); - }, [disabled]); - - const { inputType, iconType } = showPasswordProps; - return (
{ }} /> {(type === 'password' || encrypted) && ( -
- {' '} +
)} @@ -66,12 +63,10 @@ const Input = ({ helpText, onBlur, ...props }) => { value && (!isCopied ? (
- {' '}
) : (
- {' '} Copied!
))} diff --git a/frontend/src/components/ui/Input/CommonInput/Index.jsx b/frontend/src/components/ui/Input/CommonInput/Index.jsx index 2b968a0940..e484b77c30 100644 --- a/frontend/src/components/ui/Input/CommonInput/Index.jsx +++ b/frontend/src/components/ui/Input/CommonInput/Index.jsx @@ -12,8 +12,12 @@ const CommonInput = ({ label, helperText, disabled, required, onChange: change, const [isValid, setIsValid] = useState(null); const [message, setMessage] = useState(''); const [isEditing, setIsEditing] = useState(false); + const [originalValue, setOriginalValue] = useState(null); const isEncrypted = type === 'password' || encrypted; + const isWorkspaceConstant = + restProps.placeholder && + (restProps.placeholder.includes('{{constants') || restProps.placeholder.includes('{{secrets')); const handleChange = (e) => { if (validation) { @@ -44,7 +48,20 @@ const CommonInput = ({ label, helperText, disabled, required, onChange: change, const willBeInEditMode = !isEditing; setIsEditing(willBeInEditMode); - change({ target: { value: '' } }); + + if (willBeInEditMode) { + setOriginalValue(restProps.value); + + if (isWorkspaceConstant) { + change({ target: { value: restProps.placeholder } }); + } else { + change({ target: { value: '' } }); + } + } else { + if (restProps.value === restProps.placeholder && isWorkspaceConstant) { + change({ target: { value: originalValue } }); + } + } }; return ( @@ -86,6 +103,7 @@ const CommonInput = ({ label, helperText, disabled, required, onChange: change, required={required} response={isValid} onChange={handleChange} + isWorkspaceConstant={isWorkspaceConstant} {...restProps} /> {helperText && ( diff --git a/frontend/src/components/ui/Input/Input.jsx b/frontend/src/components/ui/Input/Input.jsx index 9d09b97009..24090abc2f 100644 --- a/frontend/src/components/ui/Input/Input.jsx +++ b/frontend/src/components/ui/Input/Input.jsx @@ -2,56 +2,65 @@ import * as React from 'react'; import { cn } from '@/lib/utils'; import { inputVariants } from './InputUtils/Variants'; import SolidIcon from '../../../_ui/Icon/SolidIcons'; +import { useEffect } from 'react'; -const Input = React.forwardRef(({ className, size, type, multiline, response, rows = 3, ...props }, ref) => { - const [isPasswordVisible, setIsPasswordVisible] = React.useState(false); - const isPasswordField = type === 'password'; +const Input = React.forwardRef( + ({ className, size, type, multiline, response, isWorkspaceConstant, rows = 3, ...props }, ref) => { + const [isPasswordVisible, setIsPasswordVisible] = React.useState(false); + const isPasswordField = type === 'password'; - const togglePasswordVisibility = () => { - if (!props.disabled) { - setIsPasswordVisible((prev) => !prev); - } - }; + const togglePasswordVisibility = () => { + if (!props.disabled) { + setIsPasswordVisible((prev) => !prev); + } + }; - const validationClass = response === true ? 'valid-textarea' : response === false ? 'invalid-textarea' : ''; + useEffect(() => { + if (isWorkspaceConstant) { + setIsPasswordVisible(true); + } + }, [isWorkspaceConstant]); - return ( -
- {multiline ? ( -