index.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import type { FC } from 'react'
  2. import { useState } from 'react'
  3. import { useTranslation } from 'react-i18next'
  4. import { useSelectedLayoutSegment, useRouter } from 'next/navigation'
  5. import classNames from 'classnames'
  6. import { CircleStackIcon, PuzzlePieceIcon } from '@heroicons/react/24/outline'
  7. import { CommandLineIcon, Squares2X2Icon } from '@heroicons/react/24/solid'
  8. import Link from 'next/link'
  9. import AccountDropdown from './account-dropdown'
  10. import Nav from './nav'
  11. import s from './index.module.css'
  12. import type { AppDetailResponse } from '@/models/app'
  13. import type { LangGeniusVersionResponse, UserProfileResponse } from '@/models/common'
  14. import NewAppDialog from '@/app/(commonLayout)/apps/NewAppDialog'
  15. import { WorkspaceProvider } from '@/context/workspace-context'
  16. import { useDatasetsContext } from '@/context/datasets-context'
  17. export type IHeaderProps = {
  18. appItems: AppDetailResponse[]
  19. curApp: AppDetailResponse
  20. userProfile: UserProfileResponse
  21. onLogout: () => void
  22. langeniusVersionInfo: LangGeniusVersionResponse
  23. isBordered: boolean
  24. }
  25. const navClassName = `
  26. flex items-center relative mr-3 px-3 h-8 rounded-xl
  27. font-medium text-[14px]
  28. cursor-pointer
  29. `
  30. const headerEnvClassName: { [k: string]: string } = {
  31. DEVELOPMENT: 'bg-[#FEC84B] border-[#FDB022] text-[#93370D]',
  32. TESTING: 'bg-[#A5F0FC] border-[#67E3F9] text-[#164C63]',
  33. }
  34. const Header: FC<IHeaderProps> = ({ appItems, curApp, userProfile, onLogout, langeniusVersionInfo, isBordered }) => {
  35. const { t } = useTranslation()
  36. const [showNewAppDialog, setShowNewAppDialog] = useState(false)
  37. const { datasets, currentDataset } = useDatasetsContext()
  38. const router = useRouter()
  39. const showEnvTag = langeniusVersionInfo.current_env === 'TESTING' || langeniusVersionInfo.current_env === 'DEVELOPMENT'
  40. const isPluginsComingSoon = useSelectedLayoutSegment() === 'plugins-coming-soon'
  41. return (
  42. <div className={classNames(
  43. 'sticky top-0 left-0 right-0 z-20 flex bg-gray-100 grow-0 shrink-0 basis-auto h-14',
  44. s.header,
  45. isBordered ? 'border-b border-gray-200' : ''
  46. )}>
  47. <div className={classNames(
  48. s[`header-${langeniusVersionInfo.current_env}`],
  49. 'flex flex-1 items-center justify-between px-4'
  50. )}>
  51. <div className='flex items-center'>
  52. <Link href="/apps" className='flex items-center mr-3'>
  53. <div className={s['logo']} />
  54. </Link>
  55. {/* Add it when has many stars */}
  56. <div className='
  57. flex items-center h-[26px] px-2 bg-white
  58. border border-solid border-[#E5E7EB] rounded-l-[6px] rounded-r-[6px]
  59. '>
  60. <div className={s['alpha']} />
  61. <div className='ml-1 text-xs font-semibold text-gray-700'>{t('common.menus.status')}</div>
  62. </div>
  63. </div>
  64. <div className='flex items-center'>
  65. <Nav
  66. icon={<Squares2X2Icon className='mr-1 w-[18px] h-[18px]' />}
  67. text={t('common.menus.apps')}
  68. activeSegment={['apps', 'app']}
  69. link='/apps'
  70. curNav={curApp && { id: curApp.id, name: curApp.name }}
  71. navs={appItems.map(item => ({
  72. id: item.id,
  73. name: item.name,
  74. link: `/app/${item.id}/overview`
  75. }))}
  76. createText={t('common.menus.newApp')}
  77. onCreate={() => setShowNewAppDialog(true)}
  78. />
  79. <Link href="/plugins-coming-soon" className={classNames(
  80. navClassName, 'group',
  81. isPluginsComingSoon && 'bg-white shadow-[0_2px_5px_-1px_rgba(0,0,0,0.05),0_2px_4px_-2px_rgba(0,0,0,0.05)]',
  82. isPluginsComingSoon ? 'text-primary-600' : 'text-gray-500 hover:bg-gray-200 hover:text-gray-700'
  83. )}>
  84. <PuzzlePieceIcon className='mr-1 w-[18px] h-[18px]' />
  85. {t('common.menus.plugins')}
  86. </Link>
  87. <Nav
  88. icon={<CircleStackIcon className='mr-1 w-[18px] h-[18px]' />}
  89. text={t('common.menus.datasets')}
  90. activeSegment='datasets'
  91. link='/datasets'
  92. curNav={currentDataset && { id: currentDataset.id, name: currentDataset.name }}
  93. navs={datasets.map(dataset => ({
  94. id: dataset.id,
  95. name: dataset.name,
  96. link: `/datasets/${dataset.id}/documents`
  97. }))}
  98. createText={t('common.menus.newDataset')}
  99. onCreate={() => router.push('/datasets/create')}
  100. />
  101. </div>
  102. <div className='flex items-center flex-shrink-0'>
  103. {
  104. showEnvTag && (
  105. <div className={`
  106. flex items-center h-[22px] mr-4 rounded-md px-2 text-xs font-medium border
  107. ${headerEnvClassName[langeniusVersionInfo.current_env]}
  108. `}>
  109. {
  110. langeniusVersionInfo.current_env === 'TESTING' && (
  111. <>
  112. <div className={s['beaker-icon']} />
  113. {t('common.environment.testing')}
  114. </>
  115. )
  116. }
  117. {
  118. langeniusVersionInfo.current_env === 'DEVELOPMENT' && (
  119. <>
  120. <CommandLineIcon className='w-3 h-3 mr-1' />
  121. {t('common.environment.development')}
  122. </>
  123. )
  124. }
  125. </div>
  126. )
  127. }
  128. <WorkspaceProvider>
  129. <AccountDropdown userProfile={userProfile} onLogout={onLogout} langeniusVersionInfo={langeniusVersionInfo} />
  130. </WorkspaceProvider>
  131. </div>
  132. </div>
  133. <NewAppDialog show={showNewAppDialog} onClose={() => setShowNewAppDialog(false)} />
  134. </div>
  135. )
  136. }
  137. export default Header