index.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  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. isLoading,
  35. } = useSWR(
  36. ['/explore/apps'],
  37. () =>
  38. fetchAppList().then(({ categories, recommended_apps }) => ({
  39. categories,
  40. allList: recommended_apps.sort((a, b) => a.position - b.position),
  41. })),
  42. {
  43. fallbackData: {
  44. categories: [],
  45. allList: [],
  46. },
  47. },
  48. )
  49. const currList = (() => {
  50. if (currCategory === allCategoriesEn)
  51. return allList
  52. return allList.filter(item => item.category === currCategory)
  53. })()
  54. const [currApp, setCurrApp] = React.useState<App | null>(null)
  55. const [isShowCreateModal, setIsShowCreateModal] = React.useState(false)
  56. const onCreate: CreateAppModalProps['onConfirm'] = async ({
  57. name,
  58. icon,
  59. icon_background,
  60. }) => {
  61. const { app_model_config: model_config } = await fetchAppDetail(
  62. currApp?.app.id as string,
  63. )
  64. try {
  65. const app = await createApp({
  66. name,
  67. icon,
  68. icon_background,
  69. mode: currApp?.app.mode as AppMode,
  70. config: model_config,
  71. })
  72. setIsShowCreateModal(false)
  73. Toast.notify({
  74. type: 'success',
  75. message: t('app.newApp.appCreated'),
  76. })
  77. localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
  78. router.push(
  79. `/app/${app.id}/${
  80. isCurrentWorkspaceManager ? 'configuration' : 'overview'
  81. }`,
  82. )
  83. }
  84. catch (e) {
  85. Toast.notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
  86. }
  87. }
  88. if (!isLoading) {
  89. return (
  90. <div className="flex h-full items-center">
  91. <Loading type="area" />
  92. </div>
  93. )
  94. }
  95. return (
  96. <div className="h-full flex flex-col border-l border-gray-200">
  97. <div className="shrink-0 pt-6 px-12">
  98. <div className={`mb-1 ${s.textGradient} text-xl font-semibold`}>
  99. {t('explore.apps.title')}
  100. </div>
  101. <div className="text-gray-500 text-sm">
  102. {t('explore.apps.description')}
  103. </div>
  104. </div>
  105. <Category
  106. className="mt-6 px-12"
  107. list={categories}
  108. value={currCategory}
  109. onChange={setCurrCategory}
  110. />
  111. <div className="relative flex flex-1 mt-6 pb-6 flex-col overflow-auto bg-gray-100 shrink-0 grow">
  112. <nav
  113. className={`${s.appList} grid content-start gap-4 px-6 sm:px-12 shrink-0`}
  114. >
  115. {currList.map(app => (
  116. <AppCard
  117. key={app.app_id}
  118. app={app}
  119. canCreate={hasEditPermission}
  120. onCreate={() => {
  121. setCurrApp(app)
  122. setIsShowCreateModal(true)
  123. }}
  124. />
  125. ))}
  126. </nav>
  127. </div>
  128. {isShowCreateModal && (
  129. <CreateAppModal
  130. appName={currApp?.app.name || ''}
  131. show={isShowCreateModal}
  132. onConfirm={onCreate}
  133. onHide={() => setIsShowCreateModal(false)}
  134. />
  135. )}
  136. </div>
  137. )
  138. }
  139. export default React.memo(Apps)