Co-authored-by: crazywoola <427733928@qq.com>
This commit is contained in:
committed by
GitHub
parent
8fa6cb5e03
commit
4c0a31d38b
@@ -226,6 +226,7 @@ const AppPublisher = ({
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
<EmbeddedModal
|
||||
siteInfo={appDetail?.site}
|
||||
isShow={embeddingModalOpen}
|
||||
onClose={() => setEmbeddingModalOpen(false)}
|
||||
appBaseUrl={appBaseURL}
|
||||
|
||||
@@ -247,12 +247,14 @@ function AppCard({
|
||||
? (
|
||||
<>
|
||||
<SettingsModal
|
||||
isChat={appMode === 'chat'}
|
||||
appInfo={appInfo}
|
||||
isShow={showSettingsModal}
|
||||
onClose={() => setShowSettingsModal(false)}
|
||||
onSave={onSaveSiteConfig}
|
||||
/>
|
||||
<EmbeddedModal
|
||||
siteInfo={appInfo.site}
|
||||
isShow={showEmbedded}
|
||||
onClose={() => setShowEmbedded(false)}
|
||||
appBaseUrl={app_base_url}
|
||||
|
||||
@@ -8,8 +8,11 @@ import copyStyle from '@/app/components/base/copy-btn/style.module.css'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { IS_CE_EDITION } from '@/config'
|
||||
import type { SiteInfo } from '@/models/share'
|
||||
import { useThemeContext } from '@/app/components/base/chat/embedded-chatbot/theme/theme-context'
|
||||
|
||||
type Props = {
|
||||
siteInfo?: SiteInfo
|
||||
isShow: boolean
|
||||
onClose: () => void
|
||||
accessToken: string
|
||||
@@ -28,7 +31,7 @@ const OPTION_MAP = {
|
||||
</iframe>`,
|
||||
},
|
||||
scripts: {
|
||||
getContent: (url: string, token: string, isTestEnv?: boolean) =>
|
||||
getContent: (url: string, token: string, primaryColor: string, isTestEnv?: boolean) =>
|
||||
`<script>
|
||||
window.difyChatbotConfig = {
|
||||
token: '${token}'${isTestEnv
|
||||
@@ -44,7 +47,12 @@ const OPTION_MAP = {
|
||||
src="${url}/embed.min.js"
|
||||
id="${token}"
|
||||
defer>
|
||||
</script>`,
|
||||
</script>
|
||||
<style>
|
||||
#dify-chatbot-bubble-button {
|
||||
background-color: ${primaryColor} !important;
|
||||
}
|
||||
</style>`,
|
||||
},
|
||||
chromePlugin: {
|
||||
getContent: (url: string, token: string) => `ChatBot URL: ${url}/chatbot/${token}`,
|
||||
@@ -60,12 +68,14 @@ type OptionStatus = {
|
||||
chromePlugin: boolean
|
||||
}
|
||||
|
||||
const Embedded = ({ isShow, onClose, appBaseUrl, accessToken, className }: Props) => {
|
||||
const Embedded = ({ siteInfo, isShow, onClose, appBaseUrl, accessToken, className }: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const [option, setOption] = useState<Option>('iframe')
|
||||
const [isCopied, setIsCopied] = useState<OptionStatus>({ iframe: false, scripts: false, chromePlugin: false })
|
||||
|
||||
const { langeniusVersionInfo } = useAppContext()
|
||||
const themeBuilder = useThemeContext()
|
||||
themeBuilder.buildTheme(siteInfo?.chat_color_theme ?? null, siteInfo?.chat_color_theme_inverted ?? false)
|
||||
const isTestEnv = langeniusVersionInfo.current_env === 'TESTING' || langeniusVersionInfo.current_env === 'DEVELOPMENT'
|
||||
const onClickCopy = () => {
|
||||
if (option === 'chromePlugin') {
|
||||
@@ -74,7 +84,7 @@ const Embedded = ({ isShow, onClose, appBaseUrl, accessToken, className }: Props
|
||||
copy(splitUrl[1])
|
||||
}
|
||||
else {
|
||||
copy(OPTION_MAP[option].getContent(appBaseUrl, accessToken, isTestEnv))
|
||||
copy(OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeBuilder.theme?.primaryColor ?? '#1C64F2', isTestEnv))
|
||||
}
|
||||
setIsCopied({ ...isCopied, [option]: true })
|
||||
}
|
||||
@@ -154,7 +164,7 @@ const Embedded = ({ isShow, onClose, appBaseUrl, accessToken, className }: Props
|
||||
</div>
|
||||
<div className="flex items-start justify-start w-full gap-2 p-3 overflow-x-auto">
|
||||
<div className="grow shrink basis-0 text-slate-700 text-[13px] leading-tight font-mono">
|
||||
<pre className='select-text'>{OPTION_MAP[option].getContent(appBaseUrl, accessToken, isTestEnv)}</pre>
|
||||
<pre className='select-text'>{OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeBuilder.theme?.primaryColor ?? '#1C64F2', isTestEnv)}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -17,6 +17,7 @@ import { useToastContext } from '@/app/components/base/toast'
|
||||
import { languages } from '@/i18n/language'
|
||||
|
||||
export type ISettingsModalProps = {
|
||||
isChat: boolean
|
||||
appInfo: AppDetailResponse
|
||||
isShow: boolean
|
||||
defaultValue?: string
|
||||
@@ -28,6 +29,8 @@ export type ConfigParams = {
|
||||
title: string
|
||||
description: string
|
||||
default_language: string
|
||||
chat_color_theme: string
|
||||
chat_color_theme_inverted: boolean
|
||||
prompt_public: boolean
|
||||
copyright: string
|
||||
privacy_policy: string
|
||||
@@ -40,6 +43,7 @@ export type ConfigParams = {
|
||||
const prefixSettings = 'appOverview.overview.appInfo.settings'
|
||||
|
||||
const SettingsModal: FC<ISettingsModalProps> = ({
|
||||
isChat,
|
||||
appInfo,
|
||||
isShow = false,
|
||||
onClose,
|
||||
@@ -48,8 +52,27 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||
const { notify } = useToastContext()
|
||||
const [isShowMore, setIsShowMore] = useState(false)
|
||||
const { icon, icon_background } = appInfo
|
||||
const { title, description, copyright, privacy_policy, custom_disclaimer, default_language, show_workflow_steps } = appInfo.site
|
||||
const [inputInfo, setInputInfo] = useState({ title, desc: description, copyright, privacyPolicy: privacy_policy, customDisclaimer: custom_disclaimer, show_workflow_steps })
|
||||
const {
|
||||
title,
|
||||
description,
|
||||
chat_color_theme,
|
||||
chat_color_theme_inverted,
|
||||
copyright,
|
||||
privacy_policy,
|
||||
custom_disclaimer,
|
||||
default_language,
|
||||
show_workflow_steps,
|
||||
} = appInfo.site
|
||||
const [inputInfo, setInputInfo] = useState({
|
||||
title,
|
||||
desc: description,
|
||||
chatColorTheme: chat_color_theme,
|
||||
chatColorThemeInverted: chat_color_theme_inverted,
|
||||
copyright,
|
||||
privacyPolicy: privacy_policy,
|
||||
customDisclaimer: custom_disclaimer,
|
||||
show_workflow_steps,
|
||||
})
|
||||
const [language, setLanguage] = useState(default_language)
|
||||
const [saveLoading, setSaveLoading] = useState(false)
|
||||
const { t } = useTranslation()
|
||||
@@ -58,7 +81,16 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||
const [emoji, setEmoji] = useState({ icon, icon_background })
|
||||
|
||||
useEffect(() => {
|
||||
setInputInfo({ title, desc: description, copyright, privacyPolicy: privacy_policy, customDisclaimer: custom_disclaimer, show_workflow_steps })
|
||||
setInputInfo({
|
||||
title,
|
||||
desc: description,
|
||||
chatColorTheme: chat_color_theme,
|
||||
chatColorThemeInverted: chat_color_theme_inverted,
|
||||
copyright,
|
||||
privacyPolicy: privacy_policy,
|
||||
customDisclaimer: custom_disclaimer,
|
||||
show_workflow_steps,
|
||||
})
|
||||
setLanguage(default_language)
|
||||
setEmoji({ icon, icon_background })
|
||||
}, [appInfo])
|
||||
@@ -75,11 +107,30 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||
notify({ type: 'error', message: t('app.newApp.nameNotEmpty') })
|
||||
return
|
||||
}
|
||||
|
||||
const validateColorHex = (hex: string | null) => {
|
||||
if (hex === null || hex.length === 0)
|
||||
return true
|
||||
|
||||
const regex = /#([A-Fa-f0-9]{6})/
|
||||
const check = regex.test(hex)
|
||||
return check
|
||||
}
|
||||
|
||||
if (inputInfo !== null) {
|
||||
if (!validateColorHex(inputInfo.chatColorTheme)) {
|
||||
notify({ type: 'error', message: t(`${prefixSettings}.invalidHexMessage`) })
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
setSaveLoading(true)
|
||||
const params = {
|
||||
title: inputInfo.title,
|
||||
description: inputInfo.desc,
|
||||
default_language: language,
|
||||
chat_color_theme: inputInfo.chatColorTheme,
|
||||
chat_color_theme_inverted: inputInfo.chatColorThemeInverted,
|
||||
prompt_public: false,
|
||||
copyright: inputInfo.copyright,
|
||||
privacy_policy: inputInfo.privacyPolicy,
|
||||
@@ -95,7 +146,13 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||
|
||||
const onChange = (field: string) => {
|
||||
return (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
setInputInfo(item => ({ ...item, [field]: e.target.value }))
|
||||
let value: string | boolean
|
||||
if (e.target.type === 'checkbox')
|
||||
value = (e.target as HTMLInputElement).checked
|
||||
else
|
||||
value = e.target.value
|
||||
|
||||
setInputInfo(item => ({ ...item, [field]: value }))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,6 +201,14 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||
onSelect={item => setInputInfo({ ...inputInfo, show_workflow_steps: item.value === 'true' })}
|
||||
/>
|
||||
</>}
|
||||
{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={`w-full mt-2 rounded-lg h-10 box-border px-3 ${s.projectName} bg-gray-100`}
|
||||
value={inputInfo.chatColorTheme ?? ''}
|
||||
onChange={onChange('chatColorTheme')}
|
||||
placeholder= 'E.g #A020F0'
|
||||
/>
|
||||
</>}
|
||||
{!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>
|
||||
|
||||
Reference in New Issue
Block a user