index.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useEffect } from 'react'
  4. import { useRouter } from 'next/navigation'
  5. import { useTranslation } from 'react-i18next'
  6. import { useContext } from 'use-context-selector'
  7. import Toast from '../../base/toast'
  8. import s from './style.module.css'
  9. import ExploreContext from '@/context/explore-context'
  10. import type { App, AppCategory } from '@/models/explore'
  11. import Category from '@/app/components/explore/category'
  12. import AppCard from '@/app/components/explore/app-card'
  13. import { fetchAppDetail, fetchAppList } from '@/service/explore'
  14. import { createApp } from '@/service/apps'
  15. import { useTabSearchParams } from '@/hooks/use-tab-searchparams'
  16. import CreateAppModal from '@/app/components/explore/create-app-modal'
  17. import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal'
  18. import Loading from '@/app/components/base/loading'
  19. import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
  20. import { type AppMode } from '@/types/app'
  21. import { useAppContext } from '@/context/app-context'
  22. const Apps: FC = () => {
  23. const { t } = useTranslation()
  24. const { isCurrentWorkspaceManager } = useAppContext()
  25. const router = useRouter()
  26. const { hasEditPermission } = useContext(ExploreContext)
  27. const [currCategory, setCurrCategory] = useTabSearchParams({
  28. defaultTab: '',
  29. })
  30. const [allList, setAllList] = React.useState<App[]>([])
  31. const [isLoaded, setIsLoaded] = React.useState(false)
  32. const currList = (() => {
  33. if (currCategory === '')
  34. return allList
  35. return allList.filter(item => item.category === currCategory)
  36. })()
  37. const [categories, setCategories] = React.useState<AppCategory[]>([])
  38. useEffect(() => {
  39. (async () => {
  40. const { categories, recommended_apps }: any = await fetchAppList()
  41. const sortedRecommendedApps = [...recommended_apps]
  42. sortedRecommendedApps.sort((a, b) => a.position - b.position) // position from small to big
  43. setCategories(categories)
  44. setAllList(sortedRecommendedApps)
  45. setIsLoaded(true)
  46. })()
  47. }, [])
  48. const [currApp, setCurrApp] = React.useState<App | null>(null)
  49. const [isShowCreateModal, setIsShowCreateModal] = React.useState(false)
  50. const onCreate: CreateAppModalProps['onConfirm'] = async ({ name, icon, icon_background }) => {
  51. const { app_model_config: model_config } = await fetchAppDetail(currApp?.app.id as string)
  52. try {
  53. const app = await createApp({
  54. name,
  55. icon,
  56. icon_background,
  57. mode: currApp?.app.mode as AppMode,
  58. config: model_config,
  59. })
  60. setIsShowCreateModal(false)
  61. Toast.notify({
  62. type: 'success',
  63. message: t('app.newApp.appCreated'),
  64. })
  65. localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
  66. router.push(`/app/${app.id}/${isCurrentWorkspaceManager ? 'configuration' : 'overview'}`)
  67. }
  68. catch (e) {
  69. Toast.notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
  70. }
  71. }
  72. if (!isLoaded) {
  73. return (
  74. <div className='flex h-full items-center'>
  75. <Loading type='area' />
  76. </div>
  77. )
  78. }
  79. return (
  80. <div className='h-full flex flex-col border-l border-gray-200'>
  81. <div className='shrink-0 pt-6 px-12'>
  82. <div className={`mb-1 ${s.textGradient} text-xl font-semibold`}>{t('explore.apps.title')}</div>
  83. <div className='text-gray-500 text-sm'>{t('explore.apps.description')}</div>
  84. </div>
  85. <Category
  86. className='mt-6 px-12'
  87. list={categories}
  88. value={currCategory}
  89. onChange={setCurrCategory}
  90. />
  91. <div className='relative flex flex-1 mt-6 pb-6 flex-col overflow-auto bg-gray-100 shrink-0 grow'>
  92. <nav
  93. className={`${s.appList} grid content-start gap-4 px-6 sm:px-12 shrink-0`}>
  94. {currList.map(app => (
  95. <AppCard
  96. key={app.app_id}
  97. app={app}
  98. canCreate={hasEditPermission}
  99. onCreate={() => {
  100. setCurrApp(app)
  101. setIsShowCreateModal(true)
  102. }}
  103. />
  104. ))}
  105. </nav>
  106. </div>
  107. {isShowCreateModal && (
  108. <CreateAppModal
  109. appName={currApp?.app.name || ''}
  110. show={isShowCreateModal}
  111. onConfirm={onCreate}
  112. onHide={() => setIsShowCreateModal(false)}
  113. />
  114. )}
  115. </div>
  116. )
  117. }
  118. export default React.memo(Apps)