|
@@ -1,26 +1,33 @@
|
|
|
'use client'
|
|
|
import type { FC } from 'react'
|
|
|
-import React, { useEffect, useState } from 'react'
|
|
|
-import { ChevronRightIcon } from '@heroicons/react/20/solid'
|
|
|
+import React, { useCallback, useEffect, useState } from 'react'
|
|
|
+import { RiArrowRightSLine, RiCloseLine } from '@remixicon/react'
|
|
|
import Link from 'next/link'
|
|
|
import { Trans, useTranslation } from 'react-i18next'
|
|
|
-import { useContextSelector } from 'use-context-selector'
|
|
|
-import s from './style.module.css'
|
|
|
+import { useContext, useContextSelector } from 'use-context-selector'
|
|
|
+import { SparklesSoft } from '@/app/components/base/icons/src/public/common'
|
|
|
import Modal from '@/app/components/base/modal'
|
|
|
+import ActionButton from '@/app/components/base/action-button'
|
|
|
import Button from '@/app/components/base/button'
|
|
|
+import Divider from '@/app/components/base/divider'
|
|
|
import Input from '@/app/components/base/input'
|
|
|
import Textarea from '@/app/components/base/textarea'
|
|
|
import AppIcon from '@/app/components/base/app-icon'
|
|
|
import Switch from '@/app/components/base/switch'
|
|
|
+import PremiumBadge from '@/app/components/base/premium-badge'
|
|
|
import { SimpleSelect } from '@/app/components/base/select'
|
|
|
import type { AppDetailResponse } from '@/models/app'
|
|
|
import type { AppIconType, AppSSO, Language } from '@/types/app'
|
|
|
import { useToastContext } from '@/app/components/base/toast'
|
|
|
-import { languages } from '@/i18n/language'
|
|
|
+import { LanguagesSupported, languages } from '@/i18n/language'
|
|
|
import Tooltip from '@/app/components/base/tooltip'
|
|
|
import AppContext, { useAppContext } from '@/context/app-context'
|
|
|
+import { useProviderContext } from '@/context/provider-context'
|
|
|
+import { useModalContext } from '@/context/modal-context'
|
|
|
import type { AppIconSelection } from '@/app/components/base/app-icon-picker'
|
|
|
import AppIconPicker from '@/app/components/base/app-icon-picker'
|
|
|
+import I18n from '@/context/i18n'
|
|
|
+import cn from '@/utils/classnames'
|
|
|
|
|
|
export type ISettingsModalProps = {
|
|
|
isChat: boolean
|
|
@@ -84,6 +91,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
|
|
chatColorTheme: chat_color_theme,
|
|
|
chatColorThemeInverted: chat_color_theme_inverted,
|
|
|
copyright,
|
|
|
+ copyrightSwitchValue: !!copyright,
|
|
|
privacyPolicy: privacy_policy,
|
|
|
customDisclaimer: custom_disclaimer,
|
|
|
show_workflow_steps,
|
|
@@ -93,6 +101,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
|
|
const [language, setLanguage] = useState(default_language)
|
|
|
const [saveLoading, setSaveLoading] = useState(false)
|
|
|
const { t } = useTranslation()
|
|
|
+ const { locale } = useContext(I18n)
|
|
|
|
|
|
const [showAppIconPicker, setShowAppIconPicker] = useState(false)
|
|
|
const [appIcon, setAppIcon] = useState<AppIconSelection>(
|
|
@@ -100,7 +109,16 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
|
|
? { type: 'image', url: icon_url!, fileId: icon }
|
|
|
: { type: 'emoji', icon, background: icon_background! },
|
|
|
)
|
|
|
- const isChatBot = appInfo.mode === 'chat' || appInfo.mode === 'advanced-chat' || appInfo.mode === 'agent-chat'
|
|
|
+
|
|
|
+ const { enableBilling, plan } = useProviderContext()
|
|
|
+ const { setShowPricingModal, setShowAccountSettingModal } = useModalContext()
|
|
|
+ const isFreePlan = plan.type === 'sandbox'
|
|
|
+ const handlePlanClick = useCallback(() => {
|
|
|
+ if (isFreePlan)
|
|
|
+ setShowPricingModal()
|
|
|
+ else
|
|
|
+ setShowAccountSettingModal({ payload: 'billing' })
|
|
|
+ }, [isFreePlan, setShowAccountSettingModal, setShowPricingModal])
|
|
|
|
|
|
useEffect(() => {
|
|
|
setInputInfo({
|
|
@@ -109,6 +127,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
|
|
chatColorTheme: chat_color_theme,
|
|
|
chatColorThemeInverted: chat_color_theme_inverted,
|
|
|
copyright,
|
|
|
+ copyrightSwitchValue: !!copyright,
|
|
|
privacyPolicy: privacy_policy,
|
|
|
customDisclaimer: custom_disclaimer,
|
|
|
show_workflow_steps,
|
|
@@ -158,7 +177,11 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
|
|
chat_color_theme: inputInfo.chatColorTheme,
|
|
|
chat_color_theme_inverted: inputInfo.chatColorThemeInverted,
|
|
|
prompt_public: false,
|
|
|
- copyright: inputInfo.copyright,
|
|
|
+ copyright: isFreePlan
|
|
|
+ ? ''
|
|
|
+ : inputInfo.copyrightSwitchValue
|
|
|
+ ? inputInfo.copyright
|
|
|
+ : '',
|
|
|
privacy_policy: inputInfo.privacyPolicy,
|
|
|
custom_disclaimer: inputInfo.customDisclaimer,
|
|
|
icon_type: appIcon.type,
|
|
@@ -192,141 +215,232 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
|
|
return (
|
|
|
<>
|
|
|
<Modal
|
|
|
- title={t(`${prefixSettings}.title`)}
|
|
|
isShow={isShow}
|
|
|
+ closable={false}
|
|
|
onClose={onHide}
|
|
|
- className={`${s.settingsModal}`}
|
|
|
+ className='max-w-[520px] p-0'
|
|
|
>
|
|
|
- <div className={`mt-6 font-medium ${s.settingTitle} text-gray-900`}>{t(`${prefixSettings}.webName`)}</div>
|
|
|
- <div className='flex mt-2'>
|
|
|
- <AppIcon size='large'
|
|
|
- onClick={() => { setShowAppIconPicker(true) }}
|
|
|
- className='cursor-pointer !mr-3 self-center'
|
|
|
- iconType={appIcon.type}
|
|
|
- icon={appIcon.type === 'image' ? appIcon.fileId : appIcon.icon}
|
|
|
- background={appIcon.type === 'image' ? undefined : appIcon.background}
|
|
|
- imageUrl={appIcon.type === 'image' ? appIcon.url : undefined}
|
|
|
- />
|
|
|
- <Input
|
|
|
- className='grow h-10'
|
|
|
- value={inputInfo.title}
|
|
|
- onChange={onChange('title')}
|
|
|
- placeholder={t('app.appNamePlaceholder') || ''}
|
|
|
- />
|
|
|
+ {/* header */}
|
|
|
+ <div className='pl-6 pt-5 pr-5 pb-3'>
|
|
|
+ <div className='flex items-center gap-1'>
|
|
|
+ <div className='grow text-text-primary title-2xl-semi-bold'>{t(`${prefixSettings}.title`)}</div>
|
|
|
+ <ActionButton className='shrink-0' onClick={onHide}>
|
|
|
+ <RiCloseLine className='w-4 h-4' />
|
|
|
+ </ActionButton>
|
|
|
+ </div>
|
|
|
+ <div className='mt-0.5 text-text-tertiary system-xs-regular'>
|
|
|
+ <span>{t(`${prefixSettings}.modalTip`)}</span>
|
|
|
+ <Link href={`${locale === LanguagesSupported[1] ? 'https://docs.dify.ai/zh-hans/guides/application-publishing/launch-your-webapp-quickly#she-zhi-ni-de-ai-zhan-dian' : 'https://docs.dify.ai/guides/application-publishing/launch-your-webapp-quickly#setting-up-your-ai-site'}`} target='_blank' rel='noopener noreferrer' className='text-text-accent'>{t('common.operation.learnMore')}</Link>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div className={`mt-6 font-medium ${s.settingTitle} text-gray-900 `}>{t(`${prefixSettings}.webDesc`)}</div>
|
|
|
- <p className={`mt-1 ${s.settingsTip} text-gray-500`}>{t(`${prefixSettings}.webDescTip`)}</p>
|
|
|
- <Textarea
|
|
|
- className='mt-2'
|
|
|
- value={inputInfo.desc}
|
|
|
- onChange={e => onDesChange(e.target.value)}
|
|
|
- placeholder={t(`${prefixSettings}.webDescPlaceholder`) as string}
|
|
|
- />
|
|
|
- {isChatBot && (
|
|
|
- <div className='w-full mt-4'>
|
|
|
- <div className='flex justify-between items-center'>
|
|
|
- <div className={`font-medium ${s.settingTitle} text-gray-900 `}>{t('app.answerIcon.title')}</div>
|
|
|
- <Switch
|
|
|
- defaultValue={inputInfo.use_icon_as_answer_icon}
|
|
|
- onChange={v => setInputInfo({ ...inputInfo, use_icon_as_answer_icon: v })}
|
|
|
+ {/* form body */}
|
|
|
+ <div className='px-6 py-3 space-y-5'>
|
|
|
+ {/* name & icon */}
|
|
|
+ <div className='flex gap-4'>
|
|
|
+ <div className='grow'>
|
|
|
+ <div className={cn('mb-1 py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.webName`)}</div>
|
|
|
+ <Input
|
|
|
+ className='w-full'
|
|
|
+ value={inputInfo.title}
|
|
|
+ onChange={onChange('title')}
|
|
|
+ placeholder={t('app.appNamePlaceholder') || ''}
|
|
|
/>
|
|
|
</div>
|
|
|
- <p className='body-xs-regular text-gray-500'>{t('app.answerIcon.description')}</p>
|
|
|
- </div>
|
|
|
- )}
|
|
|
- <div className={`mt-6 mb-2 font-medium ${s.settingTitle} text-gray-900 `}>{t(`${prefixSettings}.language`)}</div>
|
|
|
- <SimpleSelect
|
|
|
- items={languages.filter(item => item.supported)}
|
|
|
- defaultValue={language}
|
|
|
- onSelect={item => setLanguage(item.value as Language)}
|
|
|
- />
|
|
|
- <div className='w-full mt-8'>
|
|
|
- <p className='system-xs-medium text-gray-500'>{t(`${prefixSettings}.workflow.title`)}</p>
|
|
|
- <div className='flex justify-between items-center'>
|
|
|
- <div className='font-medium system-sm-semibold flex-grow text-gray-900'>{t(`${prefixSettings}.workflow.subTitle`)}</div>
|
|
|
- <Switch
|
|
|
- disabled={!(appInfo.mode === 'workflow' || appInfo.mode === 'advanced-chat')}
|
|
|
- defaultValue={inputInfo.show_workflow_steps}
|
|
|
- onChange={v => setInputInfo({ ...inputInfo, show_workflow_steps: v })}
|
|
|
+ <AppIcon
|
|
|
+ size='xxl'
|
|
|
+ onClick={() => { setShowAppIconPicker(true) }}
|
|
|
+ className='mt-2 cursor-pointer'
|
|
|
+ iconType={appIcon.type}
|
|
|
+ icon={appIcon.type === 'image' ? appIcon.fileId : appIcon.icon}
|
|
|
+ background={appIcon.type === 'image' ? undefined : appIcon.background}
|
|
|
+ imageUrl={appIcon.type === 'image' ? appIcon.url : undefined}
|
|
|
/>
|
|
|
</div>
|
|
|
- <p className='body-xs-regular text-gray-500'>{t(`${prefixSettings}.workflow.showDesc`)}</p>
|
|
|
- </div>
|
|
|
-
|
|
|
- {isChat && <> <div className={`mt-8 font-medium ${s.settingTitle} text-gray-900`}>{t(`${prefixSettings}.chatColorTheme`)}</div>
|
|
|
- <p className={`mt-1 ${s.settingsTip} text-gray-500`}>{t(`${prefixSettings}.chatColorThemeDesc`)}</p>
|
|
|
- <Input
|
|
|
- className='mt-2 h-10'
|
|
|
- value={inputInfo.chatColorTheme ?? ''}
|
|
|
- onChange={onChange('chatColorTheme')}
|
|
|
- placeholder='E.g #A020F0'
|
|
|
- />
|
|
|
- <div className="mt-1 flex justify-between items-center">
|
|
|
- <p className={`ml-2 ${s.settingsTip} text-gray-500`}>{t(`${prefixSettings}.chatColorThemeInverted`)}</p>
|
|
|
- <Switch defaultValue={inputInfo.chatColorThemeInverted} onChange={v => setInputInfo({ ...inputInfo, chatColorThemeInverted: v })}></Switch>
|
|
|
+ {/* description */}
|
|
|
+ <div className='relative'>
|
|
|
+ <div className={cn('py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.webDesc`)}</div>
|
|
|
+ <Textarea
|
|
|
+ className='mt-1'
|
|
|
+ value={inputInfo.desc}
|
|
|
+ onChange={e => onDesChange(e.target.value)}
|
|
|
+ placeholder={t(`${prefixSettings}.webDescPlaceholder`) as string}
|
|
|
+ />
|
|
|
+ <p className={cn('pb-0.5 text-text-tertiary body-xs-regular')}>{t(`${prefixSettings}.webDescTip`)}</p>
|
|
|
</div>
|
|
|
- </>}
|
|
|
- {systemFeatures.enable_web_sso_switch_component && <div className='w-full mt-8'>
|
|
|
- <p className='system-xs-medium text-gray-500'>{t(`${prefixSettings}.sso.label`)}</p>
|
|
|
- <div className='flex justify-between items-center'>
|
|
|
- <div className='font-medium system-sm-semibold flex-grow text-gray-900'>{t(`${prefixSettings}.sso.title`)}</div>
|
|
|
- <Tooltip
|
|
|
- disabled={systemFeatures.sso_enforced_for_web}
|
|
|
- popupContent={
|
|
|
- <div className='w-[180px]'>{t(`${prefixSettings}.sso.tooltip`)}</div>
|
|
|
- }
|
|
|
- asChild={false}
|
|
|
- >
|
|
|
- <Switch disabled={!systemFeatures.sso_enforced_for_web || !isCurrentWorkspaceEditor} defaultValue={systemFeatures.sso_enforced_for_web && inputInfo.enable_sso} onChange={v => setInputInfo({ ...inputInfo, enable_sso: v })}></Switch>
|
|
|
- </Tooltip>
|
|
|
+ <Divider className="h-px my-0" />
|
|
|
+ {/* answer icon */}
|
|
|
+ {isChat && (
|
|
|
+ <div className='w-full'>
|
|
|
+ <div className='flex justify-between items-center'>
|
|
|
+ <div className={cn('py-1 text-text-secondary system-sm-semibold')}>{t('app.answerIcon.title')}</div>
|
|
|
+ <Switch
|
|
|
+ defaultValue={inputInfo.use_icon_as_answer_icon}
|
|
|
+ onChange={v => setInputInfo({ ...inputInfo, use_icon_as_answer_icon: v })}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <p className='pb-0.5 text-text-tertiary body-xs-regular'>{t('app.answerIcon.description')}</p>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ {/* language */}
|
|
|
+ <div className='flex items-center'>
|
|
|
+ <div className={cn('grow py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.language`)}</div>
|
|
|
+ <SimpleSelect
|
|
|
+ wrapperClassName='w-[200px]'
|
|
|
+ items={languages.filter(item => item.supported)}
|
|
|
+ defaultValue={language}
|
|
|
+ onSelect={item => setLanguage(item.value as Language)}
|
|
|
+ />
|
|
|
</div>
|
|
|
- <p className='body-xs-regular text-gray-500'>{t(`${prefixSettings}.sso.description`)}</p>
|
|
|
- </div>}
|
|
|
- {!isShowMore && <div className='w-full cursor-pointer mt-8' onClick={() => setIsShowMore(true)}>
|
|
|
- <div className='flex justify-between'>
|
|
|
- <div className={`font-medium ${s.settingTitle} flex-grow text-gray-900`}>{t(`${prefixSettings}.more.entry`)}</div>
|
|
|
- <div className='flex-shrink-0 w-4 h-4 text-gray-500'>
|
|
|
- <ChevronRightIcon />
|
|
|
+ {/* theme color */}
|
|
|
+ {isChat && (
|
|
|
+ <div className='flex items-center'>
|
|
|
+ <div className='grow'>
|
|
|
+ <div className={cn('py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.chatColorTheme`)}</div>
|
|
|
+ <div className='pb-0.5 body-xs-regular text-text-tertiary'>{t(`${prefixSettings}.chatColorThemeDesc`)}</div>
|
|
|
+ </div>
|
|
|
+ <div className='shrink-0'>
|
|
|
+ <Input
|
|
|
+ className='mb-1 w-[200px]'
|
|
|
+ value={inputInfo.chatColorTheme ?? ''}
|
|
|
+ onChange={onChange('chatColorTheme')}
|
|
|
+ placeholder='E.g #A020F0'
|
|
|
+ />
|
|
|
+ <div className='flex justify-between items-center'>
|
|
|
+ <p className={cn('body-xs-regular text-text-tertiary')}>{t(`${prefixSettings}.chatColorThemeInverted`)}</p>
|
|
|
+ <Switch defaultValue={inputInfo.chatColorThemeInverted} onChange={v => setInputInfo({ ...inputInfo, chatColorThemeInverted: v })}></Switch>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ {/* workflow detail */}
|
|
|
+ <div className='w-full'>
|
|
|
+ <div className='flex justify-between items-center'>
|
|
|
+ <div className={cn('py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.workflow.subTitle`)}</div>
|
|
|
+ <Switch
|
|
|
+ disabled={!(appInfo.mode === 'workflow' || appInfo.mode === 'advanced-chat')}
|
|
|
+ defaultValue={inputInfo.show_workflow_steps}
|
|
|
+ onChange={v => setInputInfo({ ...inputInfo, show_workflow_steps: v })}
|
|
|
+ />
|
|
|
</div>
|
|
|
+ <p className='pb-0.5 text-text-tertiary body-xs-regular'>{t(`${prefixSettings}.workflow.showDesc`)}</p>
|
|
|
</div>
|
|
|
- <p className={`mt-1 ${s.policy} text-gray-500`}>{t(`${prefixSettings}.more.copyright`)} & {t(`${prefixSettings}.more.privacyPolicy`)}</p>
|
|
|
- </div>}
|
|
|
- {isShowMore && <>
|
|
|
- <hr className='w-full mt-6' />
|
|
|
- <div className={`mt-6 font-medium ${s.settingTitle} text-gray-900`}>{t(`${prefixSettings}.more.copyright`)}</div>
|
|
|
- <Input
|
|
|
- className='mt-2 h-10'
|
|
|
- value={inputInfo.copyright}
|
|
|
- onChange={onChange('copyright')}
|
|
|
- placeholder={t(`${prefixSettings}.more.copyRightPlaceholder`) as string}
|
|
|
- />
|
|
|
- <div className={`mt-8 font-medium ${s.settingTitle} text-gray-900`}>{t(`${prefixSettings}.more.privacyPolicy`)}</div>
|
|
|
- <p className={`mt-1 ${s.settingsTip} text-gray-500`}>
|
|
|
- <Trans
|
|
|
- i18nKey={`${prefixSettings}.more.privacyPolicyTip`}
|
|
|
- components={{ privacyPolicyLink: <Link href={'https://docs.dify.ai/user-agreement/privacy-policy'} target='_blank' rel='noopener noreferrer' className='text-primary-600' /> }}
|
|
|
- />
|
|
|
- </p>
|
|
|
- <Input
|
|
|
- className='mt-2 h-10'
|
|
|
- value={inputInfo.privacyPolicy}
|
|
|
- onChange={onChange('privacyPolicy')}
|
|
|
- placeholder={t(`${prefixSettings}.more.privacyPolicyPlaceholder`) as string}
|
|
|
- />
|
|
|
- <div className={`mt-8 font-medium ${s.settingTitle} text-gray-900`}>{t(`${prefixSettings}.more.customDisclaimer`)}</div>
|
|
|
- <p className={`mt-1 ${s.settingsTip} text-gray-500`}>{t(`${prefixSettings}.more.customDisclaimerTip`)}</p>
|
|
|
- <Input
|
|
|
- className='mt-2 h-10'
|
|
|
- value={inputInfo.customDisclaimer}
|
|
|
- onChange={onChange('customDisclaimer')}
|
|
|
- placeholder={t(`${prefixSettings}.more.customDisclaimerPlaceholder`) as string}
|
|
|
- />
|
|
|
- </>}
|
|
|
- <div className='mt-10 flex justify-end'>
|
|
|
+ {/* SSO */}
|
|
|
+ {systemFeatures.enable_web_sso_switch_component && (
|
|
|
+ <>
|
|
|
+ <Divider className="h-px my-0" />
|
|
|
+ <div className='w-full'>
|
|
|
+ <p className='mb-1 system-xs-medium-uppercase text-text-tertiary'>{t(`${prefixSettings}.sso.label`)}</p>
|
|
|
+ <div className='flex justify-between items-center'>
|
|
|
+ <div className={cn('py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.sso.title`)}</div>
|
|
|
+ <Tooltip
|
|
|
+ disabled={systemFeatures.sso_enforced_for_web}
|
|
|
+ popupContent={
|
|
|
+ <div className='w-[180px]'>{t(`${prefixSettings}.sso.tooltip`)}</div>
|
|
|
+ }
|
|
|
+ asChild={false}
|
|
|
+ >
|
|
|
+ <Switch disabled={!systemFeatures.sso_enforced_for_web || !isCurrentWorkspaceEditor} defaultValue={systemFeatures.sso_enforced_for_web && inputInfo.enable_sso} onChange={v => setInputInfo({ ...inputInfo, enable_sso: v })}></Switch>
|
|
|
+ </Tooltip>
|
|
|
+ </div>
|
|
|
+ <p className='pb-0.5 body-xs-regular text-text-tertiary'>{t(`${prefixSettings}.sso.description`)}</p>
|
|
|
+ </div>
|
|
|
+ </>
|
|
|
+ )}
|
|
|
+ {/* more settings switch */}
|
|
|
+ <Divider className="h-px my-0" />
|
|
|
+ {!isShowMore && (
|
|
|
+ <div className='flex items-center cursor-pointer' onClick={() => setIsShowMore(true)}>
|
|
|
+ <div className='grow'>
|
|
|
+ <div className={cn('py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.more.entry`)}</div>
|
|
|
+ <p className={cn('pb-0.5 text-text-tertiary body-xs-regular')}>{t(`${prefixSettings}.more.copyRightPlaceholder`)} & {t(`${prefixSettings}.more.privacyPolicyPlaceholder`)}</p>
|
|
|
+ </div>
|
|
|
+ <RiArrowRightSLine className='shrink-0 ml-1 w-4 h-4 text-text-secondary'/>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ {/* more settings */}
|
|
|
+ {isShowMore && (
|
|
|
+ <>
|
|
|
+ {/* copyright */}
|
|
|
+ <div className='w-full'>
|
|
|
+ <div className='flex items-center'>
|
|
|
+ <div className='grow flex items-center'>
|
|
|
+ <div className={cn('mr-1 py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.more.copyright`)}</div>
|
|
|
+ {/* upgrade button */}
|
|
|
+ {enableBilling && isFreePlan && (
|
|
|
+ <div className='select-none h-[18px]'>
|
|
|
+ <PremiumBadge size='s' color='blue' allowHover={true} onClick={handlePlanClick}>
|
|
|
+ <SparklesSoft className='flex items-center py-[1px] pl-[3px] w-3.5 h-3.5 text-components-premium-badge-indigo-text-stop-0' />
|
|
|
+ <div className='system-xs-medium'>
|
|
|
+ <span className='p-1'>
|
|
|
+ {t('billing.upgradeBtn.encourageShort')}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </PremiumBadge>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ <Tooltip
|
|
|
+ disabled={!isFreePlan}
|
|
|
+ popupContent={
|
|
|
+ <div className='w-[260px]'>{t(`${prefixSettings}.more.copyrightTooltip`)}</div>
|
|
|
+ }
|
|
|
+ asChild={false}
|
|
|
+ >
|
|
|
+ <Switch
|
|
|
+ disabled={isFreePlan}
|
|
|
+ defaultValue={inputInfo.copyrightSwitchValue}
|
|
|
+ onChange={v => setInputInfo({ ...inputInfo, copyrightSwitchValue: v })}
|
|
|
+ />
|
|
|
+ </Tooltip>
|
|
|
+ </div>
|
|
|
+ <p className='pb-0.5 text-text-tertiary body-xs-regular'>{t(`${prefixSettings}.more.copyrightTip`)}</p>
|
|
|
+ {inputInfo.copyrightSwitchValue && (
|
|
|
+ <Input
|
|
|
+ className='mt-2 h-10'
|
|
|
+ value={inputInfo.copyright}
|
|
|
+ onChange={onChange('copyright')}
|
|
|
+ placeholder={t(`${prefixSettings}.more.copyRightPlaceholder`) as string}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ {/* privacy policy */}
|
|
|
+ <div className='w-full'>
|
|
|
+ <div className={cn('py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.more.privacyPolicy`)}</div>
|
|
|
+ <p className={cn('pb-0.5 body-xs-regular text-text-tertiary')}>
|
|
|
+ <Trans
|
|
|
+ i18nKey={`${prefixSettings}.more.privacyPolicyTip`}
|
|
|
+ components={{ privacyPolicyLink: <Link href={'https://docs.dify.ai/user-agreement/privacy-policy'} target='_blank' rel='noopener noreferrer' className='text-text-accent' /> }}
|
|
|
+ />
|
|
|
+ </p>
|
|
|
+ <Input
|
|
|
+ className='mt-1'
|
|
|
+ value={inputInfo.privacyPolicy}
|
|
|
+ onChange={onChange('privacyPolicy')}
|
|
|
+ placeholder={t(`${prefixSettings}.more.privacyPolicyPlaceholder`) as string}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ {/* custom disclaimer */}
|
|
|
+ <div className='w-full'>
|
|
|
+ <div className={cn('py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.more.customDisclaimer`)}</div>
|
|
|
+ <p className={cn('pb-0.5 body-xs-regular text-text-tertiary')}>{t(`${prefixSettings}.more.customDisclaimerTip`)}</p>
|
|
|
+ <Textarea
|
|
|
+ className='mt-1'
|
|
|
+ value={inputInfo.customDisclaimer}
|
|
|
+ onChange={onChange('customDisclaimer')}
|
|
|
+ placeholder={t(`${prefixSettings}.more.customDisclaimerPlaceholder`) as string}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ {/* footer */}
|
|
|
+ <div className='p-6 pt-5 flex justify-end'>
|
|
|
<Button className='mr-2' onClick={onHide}>{t('common.operation.cancel')}</Button>
|
|
|
<Button variant='primary' onClick={onClickSave} loading={saveLoading}>{t('common.operation.save')}</Button>
|
|
|
</div>
|
|
|
- {showAppIconPicker && <AppIconPicker
|
|
|
+ </Modal >
|
|
|
+ {showAppIconPicker && (
|
|
|
+ <AppIconPicker
|
|
|
onSelect={(payload) => {
|
|
|
setAppIcon(payload)
|
|
|
setShowAppIconPicker(false)
|
|
@@ -337,8 +451,8 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
|
|
: { type: 'emoji', icon, background: icon_background! })
|
|
|
setShowAppIconPicker(false)
|
|
|
}}
|
|
|
- />}
|
|
|
- </Modal >
|
|
|
+ />
|
|
|
+ )}
|
|
|
</>
|
|
|
|
|
|
)
|