| | |
| | | import Input from '@/app/components/base/input' |
| | | import type { AppMode } from '@/types/app' |
| | | import { DSLImportMode } from '@/models/app' |
| | | import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks' |
| | | |
| | | type AppsProps = { |
| | | onSuccess?: () => void |
| | |
| | | |
| | | const [currApp, setCurrApp] = React.useState<App | null>(null) |
| | | const [isShowCreateModal, setIsShowCreateModal] = React.useState(false) |
| | | const { handleCheckPluginDependencies } = usePluginDependencies() |
| | | const onCreate: CreateAppModalProps['onConfirm'] = async ({ |
| | | name, |
| | | icon_type, |
| | |
| | | icon_background, |
| | | description, |
| | | }) => { |
| | | const { export_data, mode } = await fetchAppDetail( |
| | | const { export_data } = await fetchAppDetail( |
| | | currApp?.app.id as string, |
| | | ) |
| | | try { |
| | |
| | | }) |
| | | if (onSuccess) |
| | | onSuccess() |
| | | if (app.app_id) |
| | | await handleCheckPluginDependencies(app.app_id) |
| | | localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1') |
| | | getRedirection(isCurrentWorkspaceEditor, { id: app.app_id!, mode }, push) |
| | | getRedirection(isCurrentWorkspaceEditor, { id: app.app_id }, push) |
| | | } |
| | | catch { |
| | | catch (e) { |
| | | Toast.notify({ type: 'error', message: t('app.newApp.appCreateFailed') }) |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | return ( |
| | | <div className='flex h-full flex-col'> |
| | | <div className='flex items-center justify-between border-b border-divider-burn py-3'> |
| | | <div className='h-full flex flex-col'> |
| | | <div className='flex justify-between items-center py-3 border-b border-divider-burn'> |
| | | <div className='min-w-[180px] pl-5'> |
| | | <span className='title-xl-semi-bold text-text-primary'>{t('app.newApp.startFromTemplate')}</span> |
| | | </div> |
| | | <div className='flex max-w-[548px] flex-1 items-center rounded-xl border border-components-panel-border bg-components-panel-bg-blur p-1.5 shadow-md'> |
| | | <div className='flex-1 max-w-[548px] p-1.5 flex items-center rounded-xl shadow-md bg-components-panel-bg-blur border border-components-panel-border'> |
| | | <AppTypeSelector value={currentType} onChange={setCurrentType} /> |
| | | <div className='h-[14px]'> |
| | | <Divider type='vertical' /> |
| | |
| | | <Input |
| | | showClearIcon |
| | | wrapperClassName='w-full flex-1' |
| | | className='bg-transparent hover:border-transparent hover:bg-transparent focus:border-transparent focus:bg-transparent focus:shadow-none' |
| | | className='bg-transparent hover:bg-transparent focus:bg-transparent hover:border-transparent focus:border-transparent focus:shadow-none' |
| | | placeholder={t('app.newAppFromTemplate.searchAllTemplate') as string} |
| | | value={keywords} |
| | | onChange={e => handleKeywordsChange(e.target.value)} |
| | | onClear={() => handleKeywordsChange('')} |
| | | /> |
| | | </div> |
| | | <div className='h-8 w-[180px]'></div> |
| | | <div className='w-[180px] h-8'></div> |
| | | </div> |
| | | <div className='relative flex flex-1 overflow-y-auto'> |
| | | {!searchKeywords && <div className='h-full w-[200px] p-4'> |
| | | <Sidebar current={currCategory as AppCategories} categories={categories} onClick={(category) => { setCurrCategory(category) }} onCreateFromBlank={onCreateFromBlank} /> |
| | | {!searchKeywords && <div className='w-[200px] h-full p-4'> |
| | | <Sidebar current={currCategory as AppCategories} onClick={(category) => { setCurrCategory(category) }} onCreateFromBlank={onCreateFromBlank} /> |
| | | </div>} |
| | | <div className='h-full flex-1 shrink-0 grow overflow-auto border-l border-divider-burn p-6 pt-2'> |
| | | <div className='flex-1 h-full overflow-auto shrink-0 grow p-6 pt-2 border-l border-divider-burn'> |
| | | {searchFilteredList && searchFilteredList.length > 0 && <> |
| | | <div className='pb-1 pt-4'> |
| | | <div className='pt-4 pb-1'> |
| | | {searchKeywords |
| | | ? <p className='title-md-semi-bold text-text-tertiary'>{searchFilteredList.length > 1 ? t('app.newApp.foundResults', { count: searchFilteredList.length }) : t('app.newApp.foundResult', { count: searchFilteredList.length })}</p> |
| | | : <div className='flex h-[22px] items-center'> |
| | | <AppCategoryLabel category={currCategory as AppCategories} className='title-md-semi-bold text-text-primary' /> |
| | | </div>} |
| | | : <AppCategoryLabel category={currCategory as AppCategories} className='title-md-semi-bold text-text-primary' />} |
| | | </div> |
| | | <div |
| | | className={cn( |
| | | 'grid shrink-0 grid-cols-1 content-start gap-3 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-5 2k:grid-cols-6', |
| | | 'grid content-start shrink-0 gap-3 grid-cols-1 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-5 2k:grid-cols-6', |
| | | )}> |
| | | {searchFilteredList.map(app => ( |
| | | <AppCard |
| | |
| | | |
| | | function NoTemplateFound() { |
| | | const { t } = useTranslation() |
| | | return <div className='w-full rounded-lg bg-workflow-process-bg p-4'> |
| | | <div className='mb-2 inline-flex h-8 w-8 items-center justify-center rounded-lg bg-components-card-bg shadow-lg'> |
| | | <RiRobot2Line className='h-5 w-5 text-text-tertiary' /> |
| | | return <div className='p-4 rounded-lg w-full bg-workflow-process-bg'> |
| | | <div className='w-8 h-8 rounded-lg inline-flex items-center justify-center mb-2 shadow-lg bg-components-card-bg'> |
| | | <RiRobot2Line className='w-5 h-5 text-text-tertiary' /> |
| | | </div> |
| | | <p className='title-md-semi-bold text-text-primary'>{t('app.newApp.noTemplateFound')}</p> |
| | | <p className='system-sm-regular text-text-tertiary'>{t('app.newApp.noTemplateFoundTip')}</p> |