Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

index.tsx 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. 'use client'
  2. import { useTranslation } from 'react-i18next'
  3. import { Fragment, useState } from 'react'
  4. import { useRouter } from 'next/navigation'
  5. import {
  6. RiAccountCircleLine,
  7. RiArrowRightUpLine,
  8. RiBookOpenLine,
  9. RiGithubLine,
  10. RiGraduationCapFill,
  11. RiInformation2Line,
  12. RiLogoutBoxRLine,
  13. RiMap2Line,
  14. RiSettings3Line,
  15. RiStarLine,
  16. RiTShirt2Line,
  17. } from '@remixicon/react'
  18. import Link from 'next/link'
  19. import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'
  20. import Indicator from '../indicator'
  21. import AccountAbout from '../account-about'
  22. import GithubStar from '../github-star'
  23. import Support from './support'
  24. import Compliance from './compliance'
  25. import PremiumBadge from '@/app/components/base/premium-badge'
  26. import Avatar from '@/app/components/base/avatar'
  27. import ThemeSwitcher from '@/app/components/base/theme-switcher'
  28. import { logout } from '@/service/common'
  29. import { useAppContext } from '@/context/app-context'
  30. import { useProviderContext } from '@/context/provider-context'
  31. import { useModalContext } from '@/context/modal-context'
  32. import { IS_CLOUD_EDITION } from '@/config'
  33. import cn from '@/utils/classnames'
  34. import { useGlobalPublicStore } from '@/context/global-public-context'
  35. import { useDocLink } from '@/context/i18n'
  36. export default function AppSelector() {
  37. const itemClassName = `
  38. flex items-center w-full h-8 pl-3 pr-2 text-text-secondary system-md-regular
  39. rounded-lg hover:bg-state-base-hover cursor-pointer gap-1
  40. `
  41. const router = useRouter()
  42. const [aboutVisible, setAboutVisible] = useState(false)
  43. const { systemFeatures } = useGlobalPublicStore()
  44. const { t } = useTranslation()
  45. const docLink = useDocLink()
  46. const { userProfile, langGeniusVersionInfo, isCurrentWorkspaceOwner } = useAppContext()
  47. const { isEducationAccount } = useProviderContext()
  48. const { setShowAccountSettingModal } = useModalContext()
  49. const handleLogout = async () => {
  50. await logout({
  51. url: '/logout',
  52. params: {},
  53. })
  54. localStorage.removeItem('setup_status')
  55. localStorage.removeItem('console_token')
  56. localStorage.removeItem('refresh_token')
  57. router.push('/signin')
  58. }
  59. return (
  60. <div className="">
  61. <Menu as="div" className="relative inline-block text-left">
  62. {
  63. ({ open }) => (
  64. <>
  65. <MenuButton className={cn('inline-flex items-center rounded-[20px] p-0.5 hover:bg-background-default-dodge', open && 'bg-background-default-dodge')}>
  66. <Avatar avatar={userProfile.avatar_url} name={userProfile.name} size={36} />
  67. </MenuButton>
  68. <Transition
  69. as={Fragment}
  70. enter="transition ease-out duration-100"
  71. enterFrom="transform opacity-0 scale-95"
  72. enterTo="transform opacity-100 scale-100"
  73. leave="transition ease-in duration-75"
  74. leaveFrom="transform opacity-100 scale-100"
  75. leaveTo="transform opacity-0 scale-95"
  76. >
  77. <MenuItems
  78. className="
  79. absolute right-0 mt-1.5 w-60 max-w-80
  80. origin-top-right divide-y divide-divider-subtle rounded-xl bg-components-panel-bg-blur shadow-lg
  81. backdrop-blur-sm focus:outline-none
  82. "
  83. >
  84. <div className="px-1 py-1">
  85. <MenuItem disabled>
  86. <div className='flex flex-nowrap items-center py-2 pl-3 pr-2'>
  87. <div className='grow'>
  88. <div className='system-md-medium break-all text-text-primary'>
  89. {userProfile.name}
  90. {isEducationAccount && (
  91. <PremiumBadge size='s' color='blue' className='ml-1 !px-2'>
  92. <RiGraduationCapFill className='mr-1 h-3 w-3' />
  93. <span className='system-2xs-medium'>EDU</span>
  94. </PremiumBadge>
  95. )}
  96. </div>
  97. <div className='system-xs-regular break-all text-text-tertiary'>{userProfile.email}</div>
  98. </div>
  99. <Avatar avatar={userProfile.avatar_url} name={userProfile.name} size={36} />
  100. </div>
  101. </MenuItem>
  102. <MenuItem>
  103. <Link
  104. className={cn(itemClassName, 'group',
  105. 'data-[active]:bg-state-base-hover',
  106. )}
  107. href='/account'
  108. target='_self' rel='noopener noreferrer'>
  109. <RiAccountCircleLine className='size-4 shrink-0 text-text-tertiary' />
  110. <div className='system-md-regular grow px-1 text-text-secondary'>{t('common.account.account')}</div>
  111. <RiArrowRightUpLine className='size-[14px] shrink-0 text-text-tertiary' />
  112. </Link>
  113. </MenuItem>
  114. <MenuItem>
  115. <div className={cn(itemClassName,
  116. 'data-[active]:bg-state-base-hover',
  117. )} onClick={() => setShowAccountSettingModal({ payload: 'members' })}>
  118. <RiSettings3Line className='size-4 shrink-0 text-text-tertiary' />
  119. <div className='system-md-regular grow px-1 text-text-secondary'>{t('common.userProfile.settings')}</div>
  120. </div>
  121. </MenuItem>
  122. </div>
  123. {!systemFeatures.branding.enabled && <>
  124. <div className='p-1'>
  125. <MenuItem>
  126. <Link
  127. className={cn(itemClassName, 'group justify-between',
  128. 'data-[active]:bg-state-base-hover',
  129. )}
  130. href={docLink('/introduction')}
  131. target='_blank' rel='noopener noreferrer'>
  132. <RiBookOpenLine className='size-4 shrink-0 text-text-tertiary' />
  133. <div className='system-md-regular grow px-1 text-text-secondary'>{t('common.userProfile.helpCenter')}</div>
  134. <RiArrowRightUpLine className='size-[14px] shrink-0 text-text-tertiary' />
  135. </Link>
  136. </MenuItem>
  137. <Support />
  138. {IS_CLOUD_EDITION && isCurrentWorkspaceOwner && <Compliance />}
  139. </div>
  140. <div className='p-1'>
  141. <MenuItem>
  142. <Link
  143. className={cn(itemClassName, 'group justify-between',
  144. 'data-[active]:bg-state-base-hover',
  145. )}
  146. href='https://roadmap.dify.ai'
  147. target='_blank' rel='noopener noreferrer'>
  148. <RiMap2Line className='size-4 shrink-0 text-text-tertiary' />
  149. <div className='system-md-regular grow px-1 text-text-secondary'>{t('common.userProfile.roadmap')}</div>
  150. <RiArrowRightUpLine className='size-[14px] shrink-0 text-text-tertiary' />
  151. </Link>
  152. </MenuItem>
  153. <MenuItem>
  154. <Link
  155. className={cn(itemClassName, 'group justify-between',
  156. 'data-[active]:bg-state-base-hover',
  157. )}
  158. href='https://github.com/langgenius/dify'
  159. target='_blank' rel='noopener noreferrer'>
  160. <RiGithubLine className='size-4 shrink-0 text-text-tertiary' />
  161. <div className='system-md-regular grow px-1 text-text-secondary'>{t('common.userProfile.github')}</div>
  162. <div className='flex items-center gap-0.5 rounded-[5px] border border-divider-deep bg-components-badge-bg-dimm px-[5px] py-[3px]'>
  163. <RiStarLine className='size-3 shrink-0 text-text-tertiary' />
  164. <GithubStar className='system-2xs-medium-uppercase text-text-tertiary' />
  165. </div>
  166. </Link>
  167. </MenuItem>
  168. {
  169. document?.body?.getAttribute('data-public-site-about') !== 'hide' && (
  170. <MenuItem>
  171. <div className={cn(itemClassName, 'justify-between',
  172. 'data-[active]:bg-state-base-hover',
  173. )} onClick={() => setAboutVisible(true)}>
  174. <RiInformation2Line className='size-4 shrink-0 text-text-tertiary' />
  175. <div className='system-md-regular grow px-1 text-text-secondary'>{t('common.userProfile.about')}</div>
  176. <div className='flex shrink-0 items-center'>
  177. <div className='system-xs-regular mr-2 text-text-tertiary'>{langGeniusVersionInfo.current_version}</div>
  178. <Indicator color={langGeniusVersionInfo.current_version === langGeniusVersionInfo.latest_version ? 'green' : 'orange'} />
  179. </div>
  180. </div>
  181. </MenuItem>
  182. )
  183. }
  184. </div>
  185. </>}
  186. <MenuItem disabled>
  187. <div className='p-1'>
  188. <div className={cn(itemClassName, 'hover:bg-transparent')}>
  189. <RiTShirt2Line className='size-4 shrink-0 text-text-tertiary' />
  190. <div className='system-md-regular grow px-1 text-text-secondary'>{t('common.theme.theme')}</div>
  191. <ThemeSwitcher />
  192. </div>
  193. </div>
  194. </MenuItem>
  195. <MenuItem>
  196. <div className='p-1' onClick={() => handleLogout()}>
  197. <div
  198. className={cn(itemClassName, 'group justify-between',
  199. 'data-[active]:bg-state-base-hover',
  200. )}
  201. >
  202. <RiLogoutBoxRLine className='size-4 shrink-0 text-text-tertiary' />
  203. <div className='system-md-regular grow px-1 text-text-secondary'>{t('common.userProfile.logout')}</div>
  204. </div>
  205. </div>
  206. </MenuItem>
  207. </MenuItems>
  208. </Transition>
  209. </>
  210. )
  211. }
  212. </Menu>
  213. {
  214. aboutVisible && <AccountAbout onCancel={() => setAboutVisible(false)} langGeniusVersionInfo={langGeniusVersionInfo} />
  215. }
  216. </div >
  217. )
  218. }