|
@@ -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>
|