'use client' import type { MouseEventHandler } from 'react' import { useMemo, useRef, useState } from 'react' import { useRouter } from 'next/navigation' import { useContext } from 'use-context-selector' import { useTranslation } from 'react-i18next' import { RiCloseLine, RiCommandLine, RiCornerDownLeftLine } from '@remixicon/react' import { useDebounceFn, useKeyPress } from 'ahooks' import Uploader from './uploader' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' import Modal from '@/app/components/base/modal' import { ToastContext } from '@/app/components/base/toast' import { importDSL, importDSLConfirm, } from '@/service/apps' import { DSLImportMode, DSLImportStatus, } from '@/models/app' import { useAppContext } from '@/context/app-context' import { useProviderContext } from '@/context/provider-context' import AppsFull from '@/app/components/billing/apps-full-in-dialog' import { NEED_REFRESH_APP_LIST_KEY } from '@/config' import { getRedirection } from '@/utils/app-redirection' import cn from '@/utils/classnames' import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks' import { noop } from 'lodash-es' type CreateFromDSLModalProps = { show: boolean onSuccess?: () => void onClose: () => void activeTab?: string dslUrl?: string } export enum CreateFromDSLModalTab { FROM_FILE = 'from-file', FROM_URL = 'from-url', } const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDSLModalTab.FROM_FILE, dslUrl = '' }: CreateFromDSLModalProps) => { const { push } = useRouter() const { t } = useTranslation() const { notify } = useContext(ToastContext) const [currentFile, setDSLFile] = useState() const [fileContent, setFileContent] = useState() const [currentTab, setCurrentTab] = useState(activeTab) const [dslUrlValue, setDslUrlValue] = useState(dslUrl) const [showErrorModal, setShowErrorModal] = useState(false) const [versions, setVersions] = useState<{ importedVersion: string; systemVersion: string }>() const [importId, setImportId] = useState() const { handleCheckPluginDependencies } = usePluginDependencies() const readFile = (file: File) => { const reader = new FileReader() reader.onload = function (event) { const content = event.target?.result setFileContent(content as string) } reader.readAsText(file) } const handleFile = (file?: File) => { setDSLFile(file) if (file) readFile(file) if (!file) setFileContent('') } const { isCurrentWorkspaceEditor } = useAppContext() const { plan, enableBilling } = useProviderContext() const isAppsFull = (enableBilling && plan.usage.buildApps >= plan.total.buildApps) const isCreatingRef = useRef(false) const onCreate: MouseEventHandler = async () => { if (currentTab === CreateFromDSLModalTab.FROM_FILE && !currentFile) return if (currentTab === CreateFromDSLModalTab.FROM_URL && !dslUrlValue) return if (isCreatingRef.current) return isCreatingRef.current = true try { let response if (currentTab === CreateFromDSLModalTab.FROM_FILE) { response = await importDSL({ mode: DSLImportMode.YAML_CONTENT, yaml_content: fileContent || '', }) } if (currentTab === CreateFromDSLModalTab.FROM_URL) { response = await importDSL({ mode: DSLImportMode.YAML_URL, yaml_url: dslUrlValue || '', }) } if (!response) return const { id, status, app_id, app_mode, imported_dsl_version, current_dsl_version } = response if (status === DSLImportStatus.COMPLETED || status === DSLImportStatus.COMPLETED_WITH_WARNINGS) { if (onSuccess) onSuccess() if (onClose) onClose() notify({ type: status === DSLImportStatus.COMPLETED ? 'success' : 'warning', message: t(status === DSLImportStatus.COMPLETED ? 'app.newApp.appCreated' : 'app.newApp.caution'), children: status === DSLImportStatus.COMPLETED_WITH_WARNINGS && t('app.newApp.appCreateDSLWarning'), }) localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1') if (app_id) await handleCheckPluginDependencies(app_id) getRedirection(isCurrentWorkspaceEditor, { id: app_id!, mode: app_mode }, push) } else if (status === DSLImportStatus.PENDING) { setVersions({ importedVersion: imported_dsl_version ?? '', systemVersion: current_dsl_version ?? '', }) if (onClose) onClose() setTimeout(() => { setShowErrorModal(true) }, 300) setImportId(id) } else { notify({ type: 'error', message: t('app.newApp.appCreateFailed') }) } } // eslint-disable-next-line unused-imports/no-unused-vars catch (e) { notify({ type: 'error', message: t('app.newApp.appCreateFailed') }) } isCreatingRef.current = false } const { run: handleCreateApp } = useDebounceFn(onCreate, { wait: 300 }) useKeyPress(['meta.enter', 'ctrl.enter'], () => { if (show && !isAppsFull && ((currentTab === CreateFromDSLModalTab.FROM_FILE && currentFile) || (currentTab === CreateFromDSLModalTab.FROM_URL && dslUrlValue))) handleCreateApp() }) useKeyPress('esc', () => { if (show && !showErrorModal) onClose() }) const onDSLConfirm: MouseEventHandler = async () => { try { if (!importId) return const response = await importDSLConfirm({ import_id: importId, }) const { status, app_id, app_mode } = response if (status === DSLImportStatus.COMPLETED) { if (onSuccess) onSuccess() if (onClose) onClose() notify({ type: 'success', message: t('app.newApp.appCreated'), }) if (app_id) await handleCheckPluginDependencies(app_id) localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1') getRedirection(isCurrentWorkspaceEditor, { id: app_id!, mode: app_mode }, push) } else if (status === DSLImportStatus.FAILED) { notify({ type: 'error', message: t('app.newApp.appCreateFailed') }) } } // eslint-disable-next-line unused-imports/no-unused-vars catch (e) { notify({ type: 'error', message: t('app.newApp.appCreateFailed') }) } } const tabs = [ { key: CreateFromDSLModalTab.FROM_FILE, label: t('app.importFromDSLFile'), }, { key: CreateFromDSLModalTab.FROM_URL, label: t('app.importFromDSLUrl'), }, ] const buttonDisabled = useMemo(() => { if (isAppsFull) return true if (currentTab === CreateFromDSLModalTab.FROM_FILE) return !currentFile if (currentTab === CreateFromDSLModalTab.FROM_URL) return !dslUrlValue return false }, [isAppsFull, currentTab, currentFile, dslUrlValue]) return ( <>
{t('app.importFromDSL')}
onClose()} >
{ tabs.map(tab => (
setCurrentTab(tab.key)} > {tab.label} { currentTab === tab.key && (
) }
)) }
{ currentTab === CreateFromDSLModalTab.FROM_FILE && ( ) } { currentTab === CreateFromDSLModalTab.FROM_URL && (
DSL URL
setDslUrlValue(e.target.value)} />
) }
{isAppsFull && (
)}
setShowErrorModal(false)} className='w-[480px]' >
{t('app.newApp.appCreateDSLErrorTitle')}
{t('app.newApp.appCreateDSLErrorPart1')}
{t('app.newApp.appCreateDSLErrorPart2')}

{t('app.newApp.appCreateDSLErrorPart3')}{versions?.importedVersion}
{t('app.newApp.appCreateDSLErrorPart4')}{versions?.systemVersion}
) } export default CreateFromDSLModal