index.tsx 4.3 KB

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