| .modal { | |||||
| padding: 24px 32px !important; | |||||
| width: 400px !important; | |||||
| } | |||||
| .bg { | |||||
| background: linear-gradient(180deg, rgba(217, 45, 32, 0.05) 0%, rgba(217, 45, 32, 0.00) 24.02%), #F9FAFB; | |||||
| } | |||||
| 'use client' | |||||
| import { useState } from 'react' | |||||
| import { useTranslation } from 'react-i18next' | |||||
| import { useContext } from 'use-context-selector' | |||||
| import s from './index.module.css' | |||||
| import Collapse from '@/app/components/header/account-setting/collapse' | |||||
| import type { IItem } from '@/app/components/header/account-setting/collapse' | |||||
| import Modal from '@/app/components/base/modal' | |||||
| import Confirm from '@/app/components/base/confirm' | |||||
| import Button from '@/app/components/base/button' | |||||
| import { updateUserProfile } from '@/service/common' | |||||
| import { useAppContext } from '@/context/app-context' | |||||
| import { ToastContext } from '@/app/components/base/toast' | |||||
| import AppIcon from '@/app/components/base/app-icon' | |||||
| import Avatar from '@/app/components/base/avatar' | |||||
| import { IS_CE_EDITION } from '@/config' | |||||
| const titleClassName = ` | |||||
| text-sm font-medium text-gray-900 | |||||
| ` | |||||
| const descriptionClassName = ` | |||||
| mt-1 text-xs font-normal text-gray-500 | |||||
| ` | |||||
| const inputClassName = ` | |||||
| mt-2 w-full px-3 py-2 bg-gray-100 rounded | |||||
| text-sm font-normal text-gray-800 | |||||
| ` | |||||
| const validPassword = /^(?=.*[a-zA-Z])(?=.*\d).{8,}$/ | |||||
| export default function AccountPage() { | |||||
| const { t } = useTranslation() | |||||
| const { mutateUserProfile, userProfile, apps } = useAppContext() | |||||
| const { notify } = useContext(ToastContext) | |||||
| const [editNameModalVisible, setEditNameModalVisible] = useState(false) | |||||
| const [editName, setEditName] = useState('') | |||||
| const [editing, setEditing] = useState(false) | |||||
| const [editPasswordModalVisible, setEditPasswordModalVisible] = useState(false) | |||||
| const [currentPassword, setCurrentPassword] = useState('') | |||||
| const [password, setPassword] = useState('') | |||||
| const [confirmPassword, setConfirmPassword] = useState('') | |||||
| const [showDeleteAccountModal, setShowDeleteAccountModal] = useState(false) | |||||
| const handleEditName = () => { | |||||
| setEditNameModalVisible(true) | |||||
| setEditName(userProfile.name) | |||||
| } | |||||
| const handleSaveName = async () => { | |||||
| try { | |||||
| setEditing(true) | |||||
| await updateUserProfile({ url: 'account/name', body: { name: editName } }) | |||||
| notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) | |||||
| mutateUserProfile() | |||||
| setEditNameModalVisible(false) | |||||
| setEditing(false) | |||||
| } | |||||
| catch (e) { | |||||
| notify({ type: 'error', message: (e as Error).message }) | |||||
| setEditNameModalVisible(false) | |||||
| setEditing(false) | |||||
| } | |||||
| } | |||||
| const showErrorMessage = (message: string) => { | |||||
| notify({ | |||||
| type: 'error', | |||||
| message, | |||||
| }) | |||||
| } | |||||
| const valid = () => { | |||||
| if (!password.trim()) { | |||||
| showErrorMessage(t('login.error.passwordEmpty')) | |||||
| return false | |||||
| } | |||||
| if (!validPassword.test(password)) { | |||||
| showErrorMessage(t('login.error.passwordInvalid')) | |||||
| return false | |||||
| } | |||||
| if (password !== confirmPassword) { | |||||
| showErrorMessage(t('common.account.notEqual')) | |||||
| return false | |||||
| } | |||||
| return true | |||||
| } | |||||
| const resetPasswordForm = () => { | |||||
| setCurrentPassword('') | |||||
| setPassword('') | |||||
| setConfirmPassword('') | |||||
| } | |||||
| const handleSavePassword = async () => { | |||||
| if (!valid()) | |||||
| return | |||||
| try { | |||||
| setEditing(true) | |||||
| await updateUserProfile({ | |||||
| url: 'account/password', | |||||
| body: { | |||||
| password: currentPassword, | |||||
| new_password: password, | |||||
| repeat_new_password: confirmPassword, | |||||
| }, | |||||
| }) | |||||
| notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) | |||||
| mutateUserProfile() | |||||
| setEditPasswordModalVisible(false) | |||||
| resetPasswordForm() | |||||
| setEditing(false) | |||||
| } | |||||
| catch (e) { | |||||
| notify({ type: 'error', message: (e as Error).message }) | |||||
| setEditPasswordModalVisible(false) | |||||
| setEditing(false) | |||||
| } | |||||
| } | |||||
| const renderAppItem = (item: IItem) => { | |||||
| return ( | |||||
| <div className='flex px-3 py-1'> | |||||
| <div className='mr-3'> | |||||
| <AppIcon size='tiny' /> | |||||
| </div> | |||||
| <div className='mt-[3px] text-xs font-medium text-gray-700 leading-[18px]'>{item.name}</div> | |||||
| </div> | |||||
| ) | |||||
| } | |||||
| return ( | |||||
| <> | |||||
| <div className='pt-2 pb-3'> | |||||
| <h4 className='title-2xl-semi-bold text-primary'>{t('common.account.myAccount')}</h4> | |||||
| </div> | |||||
| <div className='mb-8 p-6 rounded-xl flex items-center bg-gradient-to-r from-background-gradient-bg-fill-chat-bg-2 to-background-gradient-bg-fill-chat-bg-1'> | |||||
| <Avatar name={userProfile.name} size={64} /> | |||||
| <div className='ml-4'> | |||||
| <p className='system-xl-semibold text-text-primary'>{userProfile.name}</p> | |||||
| <p className='system-xs-regular text-text-tertiary'>{userProfile.email}</p> | |||||
| </div> | |||||
| </div> | |||||
| <div className='mb-8'> | |||||
| <div className={titleClassName}>{t('common.account.name')}</div> | |||||
| <div className='flex items-center justify-between gap-2 w-full mt-2'> | |||||
| <div className='flex-1 bg-gray-100 rounded-md p-2 system-sm-regular text-components-input-text-filled '> | |||||
| <span className='pl-1'>{userProfile.name}</span> | |||||
| </div> | |||||
| <div className=' bg-gray-100 rounded-md py-2 px-3 cursor-pointer system-sm-medium text-components-input-text-filled' onClick={handleEditName}> | |||||
| {t('common.operation.edit')} | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div className='mb-8'> | |||||
| <div className={titleClassName}>{t('common.account.email')}</div> | |||||
| <div className='flex items-center justify-between gap-2 w-full mt-2'> | |||||
| <div className='flex-1 bg-gray-100 rounded-md p-2 system-sm-regular text-components-input-text-filled '> | |||||
| <span className='pl-1'>{userProfile.email}</span> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| { | |||||
| IS_CE_EDITION && ( | |||||
| <div className='mb-8 flex justify-between'> | |||||
| <div> | |||||
| <div className='mb-1 text-sm font-medium text-gray-900'>{t('common.account.password')}</div> | |||||
| <div className='mb-2 text-xs text-gray-500'>{t('common.account.passwordTip')}</div> | |||||
| </div> | |||||
| <Button onClick={() => setEditPasswordModalVisible(true)}>{userProfile.is_password_set ? t('common.account.resetPassword') : t('common.account.setPassword')}</Button> | |||||
| </div> | |||||
| ) | |||||
| } | |||||
| <div className='mb-6 border-[0.5px] border-gray-100' /> | |||||
| <div className='mb-8'> | |||||
| <div className={titleClassName}>{t('common.account.langGeniusAccount')}</div> | |||||
| <div className={descriptionClassName}>{t('common.account.langGeniusAccountTip')}</div> | |||||
| {!!apps.length && ( | |||||
| <Collapse | |||||
| title={`${t('common.account.showAppLength', { length: apps.length })}`} | |||||
| items={apps.map(app => ({ key: app.id, name: app.name }))} | |||||
| renderItem={renderAppItem} | |||||
| wrapperClassName='mt-2' | |||||
| /> | |||||
| )} | |||||
| {!IS_CE_EDITION && <Button className='mt-2 text-[#D92D20]' onClick={() => setShowDeleteAccountModal(true)}>{t('common.account.delete')}</Button>} | |||||
| </div> | |||||
| { | |||||
| editNameModalVisible && ( | |||||
| <Modal | |||||
| isShow | |||||
| onClose={() => setEditNameModalVisible(false)} | |||||
| className={s.modal} | |||||
| > | |||||
| <div className='mb-6 text-lg font-medium text-gray-900'>{t('common.account.editName')}</div> | |||||
| <div className={titleClassName}>{t('common.account.name')}</div> | |||||
| <input | |||||
| className={inputClassName} | |||||
| value={editName} | |||||
| onChange={e => setEditName(e.target.value)} | |||||
| /> | |||||
| <div className='flex justify-end mt-10'> | |||||
| <Button className='mr-2' onClick={() => setEditNameModalVisible(false)}>{t('common.operation.cancel')}</Button> | |||||
| <Button | |||||
| disabled={editing || !editName} | |||||
| variant='primary' | |||||
| onClick={handleSaveName} | |||||
| > | |||||
| {t('common.operation.save')} | |||||
| </Button> | |||||
| </div> | |||||
| </Modal> | |||||
| ) | |||||
| } | |||||
| { | |||||
| editPasswordModalVisible && ( | |||||
| <Modal | |||||
| isShow | |||||
| onClose={() => { | |||||
| setEditPasswordModalVisible(false) | |||||
| resetPasswordForm() | |||||
| }} | |||||
| className={s.modal} | |||||
| > | |||||
| <div className='mb-6 text-lg font-medium text-gray-900'>{userProfile.is_password_set ? t('common.account.resetPassword') : t('common.account.setPassword')}</div> | |||||
| {userProfile.is_password_set && ( | |||||
| <> | |||||
| <div className={titleClassName}>{t('common.account.currentPassword')}</div> | |||||
| <input | |||||
| type="password" | |||||
| className={inputClassName} | |||||
| value={currentPassword} | |||||
| onChange={e => setCurrentPassword(e.target.value)} | |||||
| /> | |||||
| </> | |||||
| )} | |||||
| <div className='mt-8 text-sm font-medium text-gray-900'> | |||||
| {userProfile.is_password_set ? t('common.account.newPassword') : t('common.account.password')} | |||||
| </div> | |||||
| <input | |||||
| type="password" | |||||
| className={inputClassName} | |||||
| value={password} | |||||
| onChange={e => setPassword(e.target.value)} | |||||
| /> | |||||
| <div className='mt-8 text-sm font-medium text-gray-900'>{t('common.account.confirmPassword')}</div> | |||||
| <input | |||||
| type="password" | |||||
| className={inputClassName} | |||||
| value={confirmPassword} | |||||
| onChange={e => setConfirmPassword(e.target.value)} | |||||
| /> | |||||
| <div className='flex justify-end mt-10'> | |||||
| <Button className='mr-2' onClick={() => { | |||||
| setEditPasswordModalVisible(false) | |||||
| resetPasswordForm() | |||||
| }}>{t('common.operation.cancel')}</Button> | |||||
| <Button | |||||
| disabled={editing} | |||||
| variant='primary' | |||||
| onClick={handleSavePassword} | |||||
| > | |||||
| {userProfile.is_password_set ? t('common.operation.reset') : t('common.operation.save')} | |||||
| </Button> | |||||
| </div> | |||||
| </Modal> | |||||
| ) | |||||
| } | |||||
| { | |||||
| showDeleteAccountModal && ( | |||||
| <Confirm | |||||
| isShow | |||||
| onCancel={() => setShowDeleteAccountModal(false)} | |||||
| onConfirm={() => setShowDeleteAccountModal(false)} | |||||
| showCancel={false} | |||||
| type='warning' | |||||
| title={t('common.account.delete')} | |||||
| content={ | |||||
| <> | |||||
| <div className='my-1 text-[#D92D20] text-sm leading-5'> | |||||
| {t('common.account.deleteTip')} | |||||
| </div> | |||||
| <div className='mt-3 text-sm leading-5'> | |||||
| <span>{t('common.account.deleteConfirmTip')}</span> | |||||
| <a | |||||
| className='text-primary-600 cursor' | |||||
| href={`mailto:support@dify.ai?subject=Delete Account Request&body=Delete Account: ${userProfile.email}`} | |||||
| target='_blank' | |||||
| rel='noreferrer noopener' | |||||
| onClick={(e) => { | |||||
| e.preventDefault() | |||||
| window.location.href = e.currentTarget.href | |||||
| }} | |||||
| > | |||||
| support@dify.ai | |||||
| </a> | |||||
| </div> | |||||
| <div className='my-2 px-3 py-2 rounded-lg bg-gray-100 text-sm font-medium leading-5 text-gray-800'>{`${t('common.account.delete')}: ${userProfile.email}`}</div> | |||||
| </> | |||||
| } | |||||
| confirmText={t('common.operation.ok') as string} | |||||
| /> | |||||
| ) | |||||
| } | |||||
| </> | |||||
| ) | |||||
| } |
| 'use client' | |||||
| import { useTranslation } from 'react-i18next' | |||||
| import { Fragment } from 'react' | |||||
| import { useRouter } from 'next/navigation' | |||||
| import { Menu, Transition } from '@headlessui/react' | |||||
| import Avatar from '@/app/components/base/avatar' | |||||
| import { logout } from '@/service/common' | |||||
| import { useAppContext } from '@/context/app-context' | |||||
| import { LogOut01 } from '@/app/components/base/icons/src/vender/line/general' | |||||
| export type IAppSelector = { | |||||
| isMobile: boolean | |||||
| } | |||||
| export default function AppSelector() { | |||||
| const router = useRouter() | |||||
| const { t } = useTranslation() | |||||
| const { userProfile } = useAppContext() | |||||
| const handleLogout = async () => { | |||||
| await logout({ | |||||
| url: '/logout', | |||||
| params: {}, | |||||
| }) | |||||
| if (localStorage?.getItem('console_token')) | |||||
| localStorage.removeItem('console_token') | |||||
| router.push('/signin') | |||||
| } | |||||
| return ( | |||||
| <Menu as="div" className="relative inline-block text-left"> | |||||
| { | |||||
| ({ open }) => ( | |||||
| <> | |||||
| <div> | |||||
| <Menu.Button | |||||
| className={` | |||||
| inline-flex items-center | |||||
| rounded-[20px] p-1x text-sm | |||||
| text-gray-700 hover:bg-gray-200 | |||||
| mobile:px-1 | |||||
| ${open && 'bg-gray-200'} | |||||
| `} | |||||
| > | |||||
| <Avatar name={userProfile.name} size={32} /> | |||||
| </Menu.Button> | |||||
| </div> | |||||
| <Transition | |||||
| as={Fragment} | |||||
| enter="transition ease-out duration-100" | |||||
| enterFrom="transform opacity-0 scale-95" | |||||
| enterTo="transform opacity-100 scale-100" | |||||
| leave="transition ease-in duration-75" | |||||
| leaveFrom="transform opacity-100 scale-100" | |||||
| leaveTo="transform opacity-0 scale-95" | |||||
| > | |||||
| <Menu.Items | |||||
| className=" | |||||
| absolute -right-3 -top-3 w-60 max-w-80 | |||||
| divide-y divide-gray-100 origin-top-right rounded-lg bg-white | |||||
| shadow-lg | |||||
| " | |||||
| > | |||||
| <Menu.Item> | |||||
| <div className='p-1'> | |||||
| <div className='flex flex-nowrap items-center px-3 py-2'> | |||||
| <div className='grow'> | |||||
| <div className='system-md-medium text-text-primary break-all'>{userProfile.name}</div> | |||||
| <div className='system-xs-regular text-text-tertiary break-all'>{userProfile.email}</div> | |||||
| </div> | |||||
| <Avatar name={userProfile.name} size={32} /> | |||||
| </div> | |||||
| </div> | |||||
| </Menu.Item> | |||||
| <Menu.Item> | |||||
| <div className='p-1' onClick={() => handleLogout()}> | |||||
| <div | |||||
| className='flex items-center justify-start h-9 px-3 rounded-lg cursor-pointer group hover:bg-gray-50' | |||||
| > | |||||
| <LogOut01 className='w-4 h-4 text-gray-500 flex mr-1' /> | |||||
| <div className='font-normal text-[14px] text-gray-700'>{t('common.userProfile.logout')}</div> | |||||
| </div> | |||||
| </div> | |||||
| </Menu.Item> | |||||
| </Menu.Items> | |||||
| </Transition> | |||||
| </> | |||||
| ) | |||||
| } | |||||
| </Menu> | |||||
| ) | |||||
| } |
| 'use client' | |||||
| import { useTranslation } from 'react-i18next' | |||||
| import { RiArrowRightUpLine, RiRobot2Line } from '@remixicon/react' | |||||
| import { useRouter } from 'next/navigation' | |||||
| import Button from '../components/base/button' | |||||
| import Avatar from './avatar' | |||||
| import LogoSite from '@/app/components/base/logo/logo-site' | |||||
| const Header = () => { | |||||
| const { t } = useTranslation() | |||||
| const router = useRouter() | |||||
| const back = () => { | |||||
| router.back() | |||||
| } | |||||
| return ( | |||||
| <div className='flex flex-1 items-center justify-between px-4'> | |||||
| <div className='flex items-center gap-3'> | |||||
| <div className='flex items-center cursor-pointer' onClick={back}> | |||||
| <LogoSite className='object-contain' /> | |||||
| </div> | |||||
| <div className='w-[1px] h-4 bg-divider-regular' /> | |||||
| <p className='text-text-primary title-3xl-semi-bold'>{t('common.account.account')}</p> | |||||
| </div> | |||||
| <div className='flex items-center flex-shrink-0 gap-3'> | |||||
| <Button className='gap-2 py-2 px-3 system-sm-medium' onClick={back}> | |||||
| <RiRobot2Line className='w-4 h-4' /> | |||||
| <p>{t('common.account.studio')}</p> | |||||
| <RiArrowRightUpLine className='w-4 h-4' /> | |||||
| </Button> | |||||
| <div className='w-[1px] h-4 bg-divider-regular' /> | |||||
| <Avatar /> | |||||
| </div> | |||||
| </div> | |||||
| ) | |||||
| } | |||||
| export default Header |
| import React from 'react' | |||||
| import type { ReactNode } from 'react' | |||||
| import Header from './header' | |||||
| import SwrInitor from '@/app/components/swr-initor' | |||||
| import { AppContextProvider } from '@/context/app-context' | |||||
| import GA, { GaType } from '@/app/components/base/ga' | |||||
| import HeaderWrapper from '@/app/components/header/header-wrapper' | |||||
| import { EventEmitterContextProvider } from '@/context/event-emitter' | |||||
| import { ProviderContextProvider } from '@/context/provider-context' | |||||
| import { ModalContextProvider } from '@/context/modal-context' | |||||
| const Layout = ({ children }: { children: ReactNode }) => { | |||||
| return ( | |||||
| <> | |||||
| <GA gaType={GaType.admin} /> | |||||
| <SwrInitor> | |||||
| <AppContextProvider> | |||||
| <EventEmitterContextProvider> | |||||
| <ProviderContextProvider> | |||||
| <ModalContextProvider> | |||||
| <HeaderWrapper> | |||||
| <Header /> | |||||
| </HeaderWrapper> | |||||
| <div className='relative flex flex-col overflow-y-auto bg-white shrink-0 h-0 grow'> | |||||
| {children} | |||||
| </div> | |||||
| </ModalContextProvider> | |||||
| </ProviderContextProvider> | |||||
| </EventEmitterContextProvider> | |||||
| </AppContextProvider> | |||||
| </SwrInitor> | |||||
| </> | |||||
| ) | |||||
| } | |||||
| export const metadata = { | |||||
| title: 'Dify', | |||||
| } | |||||
| export default Layout |
| import AccountPage from './account-page' | |||||
| export default function Account() { | |||||
| return <div className='max-w-[640px] w-full mx-auto pt-12 px-6'> | |||||
| <AccountPage /> | |||||
| </div> | |||||
| } |
| </div> | </div> | ||||
| <div className="px-1 py-1"> | <div className="px-1 py-1"> | ||||
| <Menu.Item> | <Menu.Item> | ||||
| <div className={itemClassName} onClick={() => setShowAccountSettingModal({ payload: 'account' })}> | |||||
| <Link | |||||
| className={classNames(itemClassName, 'group justify-between')} | |||||
| href='/account' | |||||
| target='_self' rel='noopener noreferrer'> | |||||
| <div>{t('common.account.account')}</div> | |||||
| <ArrowUpRight className='hidden w-[14px] h-[14px] text-gray-500 group-hover:flex' /> | |||||
| </Link> | |||||
| </Menu.Item> | |||||
| <Menu.Item> | |||||
| <div className={itemClassName} onClick={() => setShowAccountSettingModal({ payload: 'members' })}> | |||||
| <div>{t('common.userProfile.settings')}</div> | <div>{t('common.userProfile.settings')}</div> | ||||
| </div> | </div> | ||||
| </Menu.Item> | </Menu.Item> |
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||
| import { useEffect, useRef, useState } from 'react' | import { useEffect, useRef, useState } from 'react' | ||||
| import { | import { | ||||
| RiAccountCircleFill, | |||||
| RiAccountCircleLine, | |||||
| RiApps2AddFill, | |||||
| RiApps2AddLine, | |||||
| RiBox3Fill, | RiBox3Fill, | ||||
| RiBox3Line, | RiBox3Line, | ||||
| RiCloseLine, | RiCloseLine, | ||||
| RiPuzzle2Line, | RiPuzzle2Line, | ||||
| RiTranslate2, | RiTranslate2, | ||||
| } from '@remixicon/react' | } from '@remixicon/react' | ||||
| import AccountPage from './account-page' | |||||
| import MembersPage from './members-page' | import MembersPage from './members-page' | ||||
| import IntegrationsPage from './Integrations-page' | |||||
| import LanguagePage from './language-page' | import LanguagePage from './language-page' | ||||
| import ApiBasedExtensionPage from './api-based-extension-page' | import ApiBasedExtensionPage from './api-based-extension-page' | ||||
| import DataSourcePage from './data-source-page' | import DataSourcePage from './data-source-page' | ||||
| export default function AccountSetting({ | export default function AccountSetting({ | ||||
| onCancel, | onCancel, | ||||
| activeTab = 'account', | |||||
| activeTab = 'members', | |||||
| }: IAccountSettingProps) { | }: IAccountSettingProps) { | ||||
| const [activeMenu, setActiveMenu] = useState(activeTab) | const [activeMenu, setActiveMenu] = useState(activeTab) | ||||
| const { t } = useTranslation() | const { t } = useTranslation() | ||||
| key: 'account-group', | key: 'account-group', | ||||
| name: t('common.settings.accountGroup'), | name: t('common.settings.accountGroup'), | ||||
| items: [ | items: [ | ||||
| { | |||||
| key: 'account', | |||||
| name: t('common.settings.account'), | |||||
| icon: <RiAccountCircleLine className={iconClassName} />, | |||||
| activeIcon: <RiAccountCircleFill className={iconClassName} />, | |||||
| }, | |||||
| { | |||||
| key: 'integrations', | |||||
| name: t('common.settings.integrations'), | |||||
| icon: <RiApps2AddLine className={iconClassName} />, | |||||
| activeIcon: <RiApps2AddFill className={iconClassName} />, | |||||
| }, | |||||
| { | { | ||||
| key: 'language', | key: 'language', | ||||
| name: t('common.settings.language'), | name: t('common.settings.language'), | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div className='px-4 sm:px-8 pt-2'> | <div className='px-4 sm:px-8 pt-2'> | ||||
| {activeMenu === 'account' && <AccountPage />} | |||||
| {activeMenu === 'members' && <MembersPage />} | {activeMenu === 'members' && <MembersPage />} | ||||
| {activeMenu === 'billing' && <BillingPage />} | {activeMenu === 'billing' && <BillingPage />} | ||||
| {activeMenu === 'integrations' && <IntegrationsPage />} | |||||
| {activeMenu === 'language' && <LanguagePage />} | {activeMenu === 'language' && <LanguagePage />} | ||||
| {activeMenu === 'provider' && <ModelProviderPage />} | {activeMenu === 'provider' && <ModelProviderPage />} | ||||
| {activeMenu === 'data-source' && <DataSourcePage />} | {activeMenu === 'data-source' && <DataSourcePage />} |
| children, | children, | ||||
| }: HeaderWrapperProps) => { | }: HeaderWrapperProps) => { | ||||
| const pathname = usePathname() | const pathname = usePathname() | ||||
| const isBordered = ['/apps', '/datasets', '/datasets/create', '/tools'].includes(pathname) | |||||
| const isBordered = ['/apps', '/datasets', '/datasets/create', '/tools', '/account'].includes(pathname) | |||||
| return ( | return ( | ||||
| <div className={classNames( | <div className={classNames( |
| logout: 'Log out', | logout: 'Log out', | ||||
| }, | }, | ||||
| settings: { | settings: { | ||||
| accountGroup: 'ACCOUNT', | |||||
| accountGroup: 'GENERAL', | |||||
| workplaceGroup: 'WORKSPACE', | workplaceGroup: 'WORKSPACE', | ||||
| account: 'My account', | account: 'My account', | ||||
| members: 'Members', | members: 'Members', | ||||
| apiBasedExtension: 'API Extension', | apiBasedExtension: 'API Extension', | ||||
| }, | }, | ||||
| account: { | account: { | ||||
| account: 'Account', | |||||
| myAccount: 'My Account', | |||||
| studio: 'Dify Studio', | |||||
| avatar: 'Avatar', | avatar: 'Avatar', | ||||
| name: 'Name', | name: 'Name', | ||||
| email: 'Email', | email: 'Email', |
| logout: '登出', | logout: '登出', | ||||
| }, | }, | ||||
| settings: { | settings: { | ||||
| accountGroup: '账户', | |||||
| accountGroup: '通用', | |||||
| workplaceGroup: '工作空间', | workplaceGroup: '工作空间', | ||||
| account: '我的账户', | |||||
| members: '成员', | members: '成员', | ||||
| billing: '账单', | billing: '账单', | ||||
| integrations: '集成', | integrations: '集成', | ||||
| apiBasedExtension: 'API 扩展', | apiBasedExtension: 'API 扩展', | ||||
| }, | }, | ||||
| account: { | account: { | ||||
| account: '账户', | |||||
| myAccount: '我的账户', | |||||
| studio: 'Dify 工作室', | |||||
| avatar: '头像', | avatar: '头像', | ||||
| name: '用户名', | name: '用户名', | ||||
| email: '邮箱', | email: '邮箱', |