| | |
| | | 'use client' |
| | | |
| | | import React, { useCallback, useMemo, useState } from 'react' |
| | | import React, { useMemo, useState } from 'react' |
| | | import { useRouter } from 'next/navigation' |
| | | import { useTranslation } from 'react-i18next' |
| | | import { useContext } from 'use-context-selector' |
| | | import useSWR from 'swr' |
| | | import { useDebounceFn } from 'ahooks' |
| | | import Toast from '../../base/toast' |
| | | import s from './style.module.css' |
| | | import cn from '@/utils/classnames' |
| | | import ExploreContext from '@/context/explore-context' |
| | |
| | | import Category from '@/app/components/explore/category' |
| | | import AppCard from '@/app/components/explore/app-card' |
| | | import { fetchAppDetail, fetchAppList } from '@/service/explore' |
| | | import { importDSL } from '@/service/apps' |
| | | import { useTabSearchParams } from '@/hooks/use-tab-searchparams' |
| | | import CreateAppModal from '@/app/components/explore/create-app-modal' |
| | | import AppTypeSelector from '@/app/components/app/type-selector' |
| | | import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal' |
| | | import Loading from '@/app/components/base/loading' |
| | | import { NEED_REFRESH_APP_LIST_KEY } from '@/config' |
| | | import { useAppContext } from '@/context/app-context' |
| | | import { getRedirection } from '@/utils/app-redirection' |
| | | import Input from '@/app/components/base/input' |
| | | import { |
| | | DSLImportMode, |
| | | } from '@/models/app' |
| | | import { useImportDSL } from '@/hooks/use-import-dsl' |
| | | import DSLConfirmModal from '@/app/components/app/create-from-dsl-modal/dsl-confirm-modal' |
| | | import { DSLImportMode } from '@/models/app' |
| | | |
| | | type AppsProps = { |
| | | pageType?: PageType |
| | | onSuccess?: () => void |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | const Apps = ({ |
| | | pageType = PageType.EXPLORE, |
| | | onSuccess, |
| | | }: AppsProps) => { |
| | | const { t } = useTranslation() |
| | | const { isCurrentWorkspaceEditor } = useAppContext() |
| | | const { push } = useRouter() |
| | | const { hasEditPermission } = useContext(ExploreContext) |
| | | const allCategoriesEn = t('explore.apps.allCategories', { lng: 'en' }) |
| | | |
| | |
| | | const [currentType, setCurrentType] = useState<string>('') |
| | | const [currCategory, setCurrCategory] = useTabSearchParams({ |
| | | defaultTab: allCategoriesEn, |
| | | disableSearchParams: false, |
| | | disableSearchParams: pageType !== PageType.EXPLORE, |
| | | }) |
| | | |
| | | const { |
| | |
| | | |
| | | const [currApp, setCurrApp] = React.useState<App | null>(null) |
| | | const [isShowCreateModal, setIsShowCreateModal] = React.useState(false) |
| | | |
| | | const { |
| | | handleImportDSL, |
| | | handleImportDSLConfirm, |
| | | versions, |
| | | isFetching, |
| | | } = useImportDSL() |
| | | const [showDSLConfirmModal, setShowDSLConfirmModal] = useState(false) |
| | | const onCreate: CreateAppModalProps['onConfirm'] = async ({ |
| | | name, |
| | | icon_type, |
| | |
| | | const { export_data } = await fetchAppDetail( |
| | | currApp?.app.id as string, |
| | | ) |
| | | const payload = { |
| | | try { |
| | | const app = await importDSL({ |
| | | mode: DSLImportMode.YAML_CONTENT, |
| | | yaml_content: export_data, |
| | | name, |
| | |
| | | icon, |
| | | icon_background, |
| | | description, |
| | | } |
| | | await handleImportDSL(payload, { |
| | | onSuccess: () => { |
| | | }) |
| | | setIsShowCreateModal(false) |
| | | }, |
| | | onPending: () => { |
| | | setShowDSLConfirmModal(true) |
| | | }, |
| | | Toast.notify({ |
| | | type: 'success', |
| | | message: t('app.newApp.appCreated'), |
| | | }) |
| | | if (onSuccess) |
| | | onSuccess() |
| | | localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1') |
| | | getRedirection(isCurrentWorkspaceEditor, { id: app.app_id }, push) |
| | | } |
| | | |
| | | const onConfirmDSL = useCallback(async () => { |
| | | await handleImportDSLConfirm({ |
| | | onSuccess, |
| | | }) |
| | | }, [handleImportDSLConfirm, onSuccess]) |
| | | catch (e) { |
| | | Toast.notify({ type: 'error', message: t('app.newApp.appCreateFailed') }) |
| | | } |
| | | } |
| | | |
| | | if (!categories || categories.length === 0) { |
| | | return ( |
| | |
| | | |
| | | return ( |
| | | <div className={cn( |
| | | 'flex h-full flex-col border-l-[0.5px] border-divider-regular', |
| | | 'flex flex-col', |
| | | pageType === PageType.EXPLORE ? 'h-full border-l border-gray-200' : 'h-[calc(100%-56px)]', |
| | | )}> |
| | | |
| | | <div className='shrink-0 px-12 pt-6'> |
| | | {pageType === PageType.EXPLORE && ( |
| | | <div className='shrink-0 pt-6 px-12'> |
| | | <div className={`mb-1 ${s.textGradient} text-xl font-semibold`}>{t('explore.apps.title')}</div> |
| | | <div className='text-sm text-text-tertiary'>{t('explore.apps.description')}</div> |
| | | <div className='text-gray-500 text-sm'>{t('explore.apps.description')}</div> |
| | | </div> |
| | | |
| | | )} |
| | | <div className={cn( |
| | | 'mt-6 flex items-center justify-between px-12', |
| | | 'flex items-center justify-between mt-6', |
| | | pageType === PageType.EXPLORE ? 'px-12' : 'px-8', |
| | | )}> |
| | | <> |
| | | {pageType !== PageType.EXPLORE && ( |
| | | <> |
| | | <AppTypeSelector value={currentType} onChange={setCurrentType}/> |
| | | <div className='mx-2 w-[1px] h-3.5 bg-gray-200'/> |
| | | </> |
| | | )} |
| | | <Category |
| | | list={categories} |
| | | value={currCategory} |
| | |
| | | </div> |
| | | |
| | | <div className={cn( |
| | | 'relative mt-4 flex flex-1 shrink-0 grow flex-col overflow-auto pb-6', |
| | | 'relative flex flex-1 pb-6 flex-col overflow-auto bg-gray-100 shrink-0 grow', |
| | | pageType === PageType.EXPLORE ? 'mt-4' : 'mt-0 pt-2', |
| | | )}> |
| | | <nav |
| | | className={cn( |
| | | s.appList, |
| | | 'grid shrink-0 content-start gap-4 px-6 sm:px-12', |
| | | 'grid content-start shrink-0', |
| | | pageType === PageType.EXPLORE ? 'gap-4 px-6 sm:px-12' : 'gap-3 px-8 sm:!grid-cols-2 md:!grid-cols-3 lg:!grid-cols-4', |
| | | )}> |
| | | {searchFilteredList.map(app => ( |
| | | <AppCard |
| | | key={app.app_id} |
| | | isExplore |
| | | isExplore={pageType === PageType.EXPLORE} |
| | | app={app} |
| | | canCreate={hasEditPermission} |
| | | onCreate={() => { |
| | |
| | | appDescription={currApp?.app.description || ''} |
| | | show={isShowCreateModal} |
| | | onConfirm={onCreate} |
| | | confirmDisabled={isFetching} |
| | | onHide={() => setIsShowCreateModal(false)} |
| | | /> |
| | | )} |
| | | { |
| | | showDSLConfirmModal && ( |
| | | <DSLConfirmModal |
| | | versions={versions} |
| | | onCancel={() => setShowDSLConfirmModal(false)} |
| | | onConfirm={onConfirmDSL} |
| | | confirmDisabled={isFetching} |
| | | /> |
| | | ) |
| | | } |
| | | </div> |
| | | ) |
| | | } |