diff --git a/web/app/components/base/form/components/base/base-field.tsx b/web/app/components/base/form/components/base/base-field.tsx index 124a0fbf52..406df330d6 100644 --- a/web/app/components/base/form/components/base/base-field.tsx +++ b/web/app/components/base/form/components/base/base-field.tsx @@ -1,5 +1,5 @@ import CheckboxList from '@/app/components/base/checkbox-list' -import type { FieldState, FormSchema } from '@/app/components/base/form/types' +import type { FieldState, FormSchema, TypeWithI18N } from '@/app/components/base/form/types' import { FormItemValidateStatusEnum, FormTypeEnum } from '@/app/components/base/form/types' import Input from '@/app/components/base/input' import Radio from '@/app/components/base/radio' @@ -31,6 +31,19 @@ const getExtraProps = (type: FormTypeEnum) => { } } +const getTranslatedContent = ({ content, render }: { + content: React.ReactNode | string | null | undefined | TypeWithI18N | Record + render: (content: TypeWithI18N | Record) => string +}): string => { + if (isValidElement(content) || typeof content === 'string') + return content as string + + if (typeof content === 'object' && content !== null) + return render(content as TypeWithI18N) + + return '' +} + const VALIDATE_STATUS_STYLE_MAP: Record = { [FormItemValidateStatusEnum.Error]: { componentClassName: 'border-components-input-border-destructive focus:border-components-input-border-destructive', @@ -91,24 +104,21 @@ const BaseField = ({ multiple = false, tooltip, showCopy, + description, + url, + help, } = formSchema const disabled = propsDisabled || formSchemaDisabled - const memorizedLabel = useMemo(() => { - if (isValidElement(label) || typeof label === 'string') - return label - - if (typeof label === 'object' && label !== null) - return renderI18nObject(label as Record) - }, [label, renderI18nObject]) - - const memorizedPlaceholder = useMemo(() => { - if (typeof placeholder === 'string') - return placeholder - - if (typeof placeholder === 'object' && placeholder !== null) - return renderI18nObject(placeholder as Record) - }, [placeholder, renderI18nObject]) + const [translatedLabel, translatedPlaceholder, translatedTooltip, translatedDescription, translatedHelp] = useMemo(() => { + return [ + label, + placeholder, + tooltip, + description, + help, + ].map(v => getTranslatedContent({ content: v, render: renderI18nObject })) + }, [label, placeholder, tooltip, description, help, renderI18nObject]) const watchedVariables = useMemo(() => { const variables = new Set() @@ -139,7 +149,7 @@ const BaseField = ({ }) }).map((option) => { return { - label: typeof option.label === 'string' ? option.label : renderI18nObject(option.label), + label: getTranslatedContent({ content: option.label, render: renderI18nObject }), value: option.value, } }) || [] @@ -162,7 +172,7 @@ const BaseField = ({ if (!dynamicOptionsData?.options) return [] return dynamicOptionsData.options.map(option => ({ - name: typeof option.label === 'string' ? option.label : renderI18nObject(option.label), + name: getTranslatedContent({ content: option.label, render: renderI18nObject }), value: option.value, })) }, [dynamicOptionsData, renderI18nObject]) @@ -173,150 +183,158 @@ const BaseField = ({ }, [field, onChange]) return ( -
-
- {memorizedLabel} - { - required && !isValidElement(label) && ( - * - ) - } - {tooltip && ( - {typeof tooltip === 'string' ? tooltip : renderI18nObject(tooltip as Record)}
} - triggerClassName='ml-0.5 w-4 h-4' - /> - )} -
-
- { - [FormTypeEnum.textInput, FormTypeEnum.secretInput, FormTypeEnum.textNumber].includes(formItemType) && ( - { - handleChange(e.target.value) - }} - onBlur={field.handleBlur} - disabled={disabled} - placeholder={memorizedPlaceholder} - {...getExtraProps(formItemType)} - showCopyIcon={showCopy} + <> +
+
+ {translatedLabel} + { + required && !isValidElement(label) && ( + * + ) + } + {tooltip && ( + {translatedTooltip}
} + triggerClassName='ml-0.5 w-4 h-4' /> - ) - } - { - formItemType === FormTypeEnum.select && !multiple && ( - handleChange(v)} - disabled={disabled} - placeholder={memorizedPlaceholder} - options={memorizedOptions} - triggerPopupSameWidth - popupProps={{ - className: 'max-h-[320px] overflow-y-auto', - }} - /> - ) - } - { - formItemType === FormTypeEnum.checkbox /* && multiple */ && ( - field.handleChange(v)} - options={memorizedOptions} - maxHeight='200px' - /> - ) - } - { - formItemType === FormTypeEnum.dynamicSelect && ( - field.handleChange(item.value)} - readonly={disabled || isDynamicOptionsLoading} - placeholder={ - isDynamicOptionsLoading - ? 'Loading options...' - : memorizedPlaceholder || 'Select an option' - } - items={dynamicOptions} - popupClassName="z-[9999]" - /> - ) - } - { - formItemType === FormTypeEnum.radio && ( + )} +
+
+ { + [FormTypeEnum.textInput, FormTypeEnum.secretInput, FormTypeEnum.textNumber].includes(formItemType) && ( + { + handleChange(e.target.value) + }} + onBlur={field.handleBlur} + disabled={disabled} + placeholder={translatedPlaceholder} + {...getExtraProps(formItemType)} + showCopyIcon={showCopy} + /> + ) + } + { + formItemType === FormTypeEnum.select && !multiple && ( + handleChange(v)} + disabled={disabled} + placeholder={translatedPlaceholder} + options={memorizedOptions} + triggerPopupSameWidth + popupProps={{ + className: 'max-h-[320px] overflow-y-auto', + }} + /> + ) + } + { + formItemType === FormTypeEnum.checkbox /* && multiple */ && ( + field.handleChange(v)} + options={memorizedOptions} + maxHeight='200px' + /> + ) + } + { + formItemType === FormTypeEnum.dynamicSelect && ( + field.handleChange(item.value)} + readonly={disabled || isDynamicOptionsLoading} + placeholder={ + isDynamicOptionsLoading + ? 'Loading options...' + : translatedPlaceholder || 'Select an option' + } + items={dynamicOptions} + popupClassName="z-[9999]" + /> + ) + } + { + formItemType === FormTypeEnum.radio && ( +
+ { + memorizedOptions.map(option => ( +
!disabled && handleChange(option.value)} + > + { + formSchema.showRadioUI && ( + + ) + } + {option.label} +
+ )) + } +
+ ) + } + { + formItemType === FormTypeEnum.boolean && ( + field.handleChange(v)} + > + True + False + + ) + } + {fieldState?.validateStatus && [FormItemValidateStatusEnum.Error, FormItemValidateStatusEnum.Warning].includes(fieldState?.validateStatus) && (
- { - memorizedOptions.map(option => ( -
!disabled && handleChange(option.value)} - > - { - formSchema.showRadioUI && ( - - ) - } - {option.label} -
- )) - } + {fieldState?.[VALIDATE_STATUS_STYLE_MAP[fieldState?.validateStatus].infoFieldName as keyof FieldState]}
- ) - } - { - formItemType === FormTypeEnum.boolean && ( - field.handleChange(v)} - > - True - False - - ) - } - {fieldState?.validateStatus && [FormItemValidateStatusEnum.Error, FormItemValidateStatusEnum.Warning].includes(fieldState?.validateStatus) && ( -
- {fieldState?.[VALIDATE_STATUS_STYLE_MAP[fieldState?.validateStatus].infoFieldName as keyof FieldState]} -
- )} - { - formSchema.url && ( - - - {renderI18nObject(formSchema?.help as any)} - - - - ) - } + )} +
- + {description && ( +
+ {translatedDescription} +
+ )} + { + url && ( + + + {translatedHelp} + + + + ) + } + + ) } diff --git a/web/app/components/base/modal/modal.tsx b/web/app/components/base/modal/modal.tsx index 3ab941b0f4..369661b5a1 100644 --- a/web/app/components/base/modal/modal.tsx +++ b/web/app/components/base/modal/modal.tsx @@ -26,6 +26,7 @@ type ModalProps = { footerSlot?: React.ReactNode bottomSlot?: React.ReactNode disabled?: boolean + containerClassName?: string } const Modal = ({ onClose, @@ -44,6 +45,7 @@ const Modal = ({ footerSlot, bottomSlot, disabled, + containerClassName, }: ModalProps) => { const { t } = useTranslation() @@ -55,9 +57,10 @@ const Modal = ({ >
e.stopPropagation()} > diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.tsx index 1b51dfef9d..c006c8d26e 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.tsx @@ -370,6 +370,7 @@ export const CommonCreateModal = ({ onClose, createType, builder }: Props) => { disabled={isVerifyingCredentials || isBuilding} bottomSlot={currentStep === ApiKeyStep.Verify ? : null} size={createType === SupportedCreationMethods.MANUAL ? 'md' : 'sm'} + containerClassName='min-h-[360px]' > {createType === SupportedCreationMethods.APIKEY && } {currentStep === ApiKeyStep.Verify && ( @@ -432,6 +433,7 @@ export const CommonCreateModal = ({ onClose, createType, builder }: Props) => { credential_id: subscriptionBuilder?.id || '', } : undefined, fieldClassName: schema.type === FormTypeEnum.boolean ? 'flex items-center justify-between' : undefined, + labelClassName: schema.type === FormTypeEnum.boolean ? 'mb-0' : undefined, } })} ref={autoCommonParametersFormRef} diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth-client.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth-client.tsx index c171600353..3a5ab85881 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth-client.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth-client.tsx @@ -189,7 +189,7 @@ export const OAuthClientSettingsModal = ({ oauthConfig, onClose, showOAuthCreate onCancel={() => handleSave(false)} onConfirm={() => handleSave(true)} footerSlot={ - oauthConfig?.custom_enabled && oauthConfig?.params && ( + oauthConfig?.custom_enabled && oauthConfig?.params && clientType === ClientTypeEnum.Custom && (