From 3eaa534e99266daba627bcd355808e84b610c631 Mon Sep 17 00:00:00 2001 From: wangxiaolei Date: Mon, 15 Jun 2026 15:27:25 +0800 Subject: [PATCH] fix: fix human input form logo replace (#37452) --- api/configs/enterprise/__init__.py | 2 +- .../form/[token]/__tests__/form.spec.tsx | 45 +++++++++++++++++++ .../form/[token]/branding-footer.tsx | 30 +++++++++++++ .../form/[token]/form-status-card.tsx | 16 +++---- .../(humanInputLayout)/form/[token]/form.tsx | 5 +++ .../form/[token]/loaded-form-content.tsx | 34 ++++++++------ 6 files changed, 108 insertions(+), 24 deletions(-) create mode 100644 web/app/(humanInputLayout)/form/[token]/branding-footer.tsx diff --git a/api/configs/enterprise/__init__.py b/api/configs/enterprise/__init__.py index b3a93d9773..705ea67bcb 100644 --- a/api/configs/enterprise/__init__.py +++ b/api/configs/enterprise/__init__.py @@ -16,7 +16,7 @@ class EnterpriseFeatureConfig(BaseSettings): CAN_REPLACE_LOGO: bool = Field( description="Allow customization of the enterprise logo.", - default=False, + default=True, ) ENTERPRISE_REQUEST_TIMEOUT: int = Field( diff --git a/web/app/(humanInputLayout)/form/[token]/__tests__/form.spec.tsx b/web/app/(humanInputLayout)/form/[token]/__tests__/form.spec.tsx index ac919ffb05..03e5506b13 100644 --- a/web/app/(humanInputLayout)/form/[token]/__tests__/form.spec.tsx +++ b/web/app/(humanInputLayout)/form/[token]/__tests__/form.spec.tsx @@ -354,4 +354,49 @@ describe('Human input share form', () => { await user.click(screen.getByRole('button', { name: 'share-update-summary' })) expect(approveButton).toBeEnabled() }) + + it('should hide branding when remove_webapp_brand is enabled', () => { + mockUseGetHumanInputForm.mockReturnValue({ + data: { + ...formData, + site: { + ...formData.site, + custom_config: { + remove_webapp_brand: true, + replace_webapp_logo: null, + }, + }, + }, + isLoading: false, + error: null, + }) + + render() + + expect(screen.queryByText('share.chat.poweredBy')).not.toBeInTheDocument() + expect(screen.queryByText('dify-logo')).not.toBeInTheDocument() + }) + + it('should render the custom branding logo when replace_webapp_logo is provided', () => { + mockUseGetHumanInputForm.mockReturnValue({ + data: { + ...formData, + site: { + ...formData.site, + custom_config: { + remove_webapp_brand: false, + replace_webapp_logo: 'https://example.com/custom-logo.png', + }, + }, + }, + isLoading: false, + error: null, + }) + + render() + + expect(screen.getByText('share.chat.poweredBy')).toBeInTheDocument() + expect(screen.getByRole('img', { name: 'logo' })).toHaveAttribute('src', 'https://example.com/custom-logo.png') + expect(screen.queryByText('dify-logo')).not.toBeInTheDocument() + }) }) diff --git a/web/app/(humanInputLayout)/form/[token]/branding-footer.tsx b/web/app/(humanInputLayout)/form/[token]/branding-footer.tsx new file mode 100644 index 0000000000..bd99dde592 --- /dev/null +++ b/web/app/(humanInputLayout)/form/[token]/branding-footer.tsx @@ -0,0 +1,30 @@ +import { useTranslation } from 'react-i18next' +import DifyLogo from '@/app/components/base/logo/dify-logo' + +type BrandingFooterProps = { + removeWebappBrand?: boolean + replaceWebappLogo?: string | null +} + +const BrandingFooter = ({ + removeWebappBrand, + replaceWebappLogo, +}: BrandingFooterProps) => { + const { t } = useTranslation() + + if (removeWebappBrand) + return null + + return ( +
+
+
{t('chat.poweredBy', { ns: 'share' })}
+ {replaceWebappLogo + ? logo + : } +
+
+ ) +} + +export default BrandingFooter diff --git a/web/app/(humanInputLayout)/form/[token]/form-status-card.tsx b/web/app/(humanInputLayout)/form/[token]/form-status-card.tsx index 03efebe1e3..99da6ec2fc 100644 --- a/web/app/(humanInputLayout)/form/[token]/form-status-card.tsx +++ b/web/app/(humanInputLayout)/form/[token]/form-status-card.tsx @@ -1,7 +1,7 @@ import type { ReactNode } from 'react' import { cn } from '@langgenius/dify-ui/cn' import { useTranslation } from 'react-i18next' -import DifyLogo from '@/app/components/base/logo/dify-logo' +import BrandingFooter from './branding-footer' type FormStatusCardProps = { iconClassName: string @@ -9,6 +9,7 @@ type FormStatusCardProps = { subtitle?: ReactNode submissionID?: string removeWebappBrand?: boolean + replaceWebappLogo?: string | null } const FormStatusCard = ({ @@ -17,6 +18,7 @@ const FormStatusCard = ({ subtitle, submissionID, removeWebappBrand, + replaceWebappLogo, }: FormStatusCardProps) => { const { t } = useTranslation() @@ -39,14 +41,10 @@ const FormStatusCard = ({ )} - {!removeWebappBrand && ( -
-
-
{t('chat.poweredBy', { ns: 'share' })}
- -
-
- )} + ) diff --git a/web/app/(humanInputLayout)/form/[token]/form.tsx b/web/app/(humanInputLayout)/form/[token]/form.tsx index ca78b135f9..da50497703 100644 --- a/web/app/(humanInputLayout)/form/[token]/form.tsx +++ b/web/app/(humanInputLayout)/form/[token]/form.tsx @@ -35,6 +35,9 @@ const FormContent = () => { const { isSubmitting, submit, success } = useFormSubmit(token) const removeWebappBrand = formData?.site?.custom_config?.remove_webapp_brand === true + const replaceWebappLogo = typeof formData?.site?.custom_config?.replace_webapp_logo === 'string' + ? formData.site.custom_config.replace_webapp_logo + : null const expired = (error as HumanInputFormError | null)?.code === 'human_input_form_expired' const submitted = (error as HumanInputFormError | null)?.code === 'human_input_form_submitted' @@ -54,6 +57,7 @@ const FormContent = () => { subtitle={t('humanInput.recorded', { ns: 'share' })} submissionID={token} removeWebappBrand={removeWebappBrand} + replaceWebappLogo={replaceWebappLogo} /> ) } @@ -105,6 +109,7 @@ const FormContent = () => { isSubmitting={isSubmitting} onSubmit={submit} removeWebappBrand={removeWebappBrand} + replaceWebappLogo={replaceWebappLogo} /> ) } diff --git a/web/app/(humanInputLayout)/form/[token]/loaded-form-content.tsx b/web/app/(humanInputLayout)/form/[token]/loaded-form-content.tsx index 304e1c625d..9810d0a5b1 100644 --- a/web/app/(humanInputLayout)/form/[token]/loaded-form-content.tsx +++ b/web/app/(humanInputLayout)/form/[token]/loaded-form-content.tsx @@ -6,18 +6,18 @@ import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { produce } from 'immer' import { useMemo, useState } from 'react' -import { useTranslation } from 'react-i18next' import AppIcon from '@/app/components/base/app-icon' import ContentItem from '@/app/components/base/chat/chat/answer/human-input-content/content-item' import ExpirationTime from '@/app/components/base/chat/chat/answer/human-input-content/expiration-time' import { getButtonStyle, getRenderedFormInputs, hasInvalidRequiredHumanInput, initializeInputs, splitByOutputVar } from '@/app/components/base/chat/chat/answer/human-input-content/utils' -import DifyLogo from '@/app/components/base/logo/dify-logo' +import BrandingFooter from './branding-footer' type LoadedFormContentProps = { formData: FormData isSubmitting: boolean onSubmit: (inputs: Record, actionID: string, formInputs: FormData['inputs']) => void removeWebappBrand?: boolean + replaceWebappLogo?: string | null } const LoadedFormContent = ({ @@ -25,15 +25,25 @@ const LoadedFormContent = ({ isSubmitting, onSubmit, removeWebappBrand, + replaceWebappLogo, }: LoadedFormContentProps) => { - const { t } = useTranslation() const renderedFormInputs = getRenderedFormInputs(formData.inputs, formData.form_content) const [inputs, setInputs] = useState>(() => initializeInputs(renderedFormInputs, formData.resolved_default_values), ) const contentList = useMemo(() => { - return splitByOutputVar(formData.form_content) + const contentCounts = new Map() + + return splitByOutputVar(formData.form_content).map((content) => { + const occurrence = (contentCounts.get(content) || 0) + 1 + contentCounts.set(content, occurrence) + + return { + key: `${content}-${occurrence}`, + content, + } + }) }, [formData.form_content]) const handleInputsChange = (name: string, value: HumanInputFieldValue) => { @@ -63,9 +73,9 @@ const LoadedFormContent = ({
- {contentList.map((content, index) => ( + {contentList.map(({ key, content }) => (
- {!removeWebappBrand && ( -
-
-
{t('chat.poweredBy', { ns: 'share' })}
- -
-
- )} +
)