From 8d6984e28679bf9c48ca353192ad1a3eee0c3b22 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Sun, 3 Mar 2024 15:37:47 +0800 Subject: [PATCH] create app by import yaml --- web/app/(commonLayout)/apps/NewAppCard.tsx | 6 +- .../app/create-from-dsl-modal/index.tsx | 166 +++++------------- .../app/create-from-dsl-modal/uploader.tsx | 126 +++++++++++++ 3 files changed, 169 insertions(+), 129 deletions(-) create mode 100644 web/app/components/app/create-from-dsl-modal/uploader.tsx diff --git a/web/app/(commonLayout)/apps/NewAppCard.tsx b/web/app/(commonLayout)/apps/NewAppCard.tsx index 2586eb0cd1..d4f3f86d43 100644 --- a/web/app/(commonLayout)/apps/NewAppCard.tsx +++ b/web/app/(commonLayout)/apps/NewAppCard.tsx @@ -22,9 +22,9 @@ const CreateAppCard = forwardRef(({ onSuc return ( -
setShowNewAppDialog(true)}> +
setShowNewAppDialog(true)}>
@@ -35,7 +35,7 @@ const CreateAppCard = forwardRef(({ onSuc
setShowCreateFromDSLModal(true)} > {t('app.createFromConfigFile')} diff --git a/web/app/components/app/create-from-dsl-modal/index.tsx b/web/app/components/app/create-from-dsl-modal/index.tsx index 03ad060b18..896a99ccac 100644 --- a/web/app/components/app/create-from-dsl-modal/index.tsx +++ b/web/app/components/app/create-from-dsl-modal/index.tsx @@ -1,20 +1,20 @@ 'use client' import type { MouseEventHandler } from 'react' -import { useCallback, useEffect, useRef, useState } from 'react' -import cn from 'classnames' +import { useRef, useState } from 'react' import { useRouter } from 'next/navigation' import { useContext } from 'use-context-selector' import { useTranslation } from 'react-i18next' +import Uploader from './uploader' import Button from '@/app/components/base/button' import Modal from '@/app/components/base/modal' -// import type { AppMode } from '@/types/app' import { ToastContext } from '@/app/components/base/toast' -// import { createApp, fetchAppTemplates } from '@/service/apps' +import { importApp } from '@/service/apps' import { useAppContext } from '@/context/app-context' import { useProviderContext } from '@/context/provider-context' import AppsFull from '@/app/components/billing/apps-full-in-dialog' -import { Trash03, UploadCloud01, XClose } from '@/app/components/base/icons/src/vender/line/general' +import { XClose } from '@/app/components/base/icons/src/vender/line/general' +import { NEED_REFRESH_APP_LIST_KEY } from '@/config' type CreateFromDSLModalProps = { show: boolean @@ -26,102 +26,55 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose }: CreateFromDSLModalProp const router = useRouter() const { t } = useTranslation() const { notify } = useContext(ToastContext) - const [currentFile, setDSLFile] = useState() - const [dragging, setDragging] = useState(false) - const dropRef = useRef(null) - const dragRef = useRef(null) - const fileUploader = useRef(null) + const [currentFile, setDSLFile] = useState() + const [fileContent, setFileContent] = useState() - const handleDragEnter = (e: DragEvent) => { - e.preventDefault() - e.stopPropagation() - e.target !== dragRef.current && setDragging(true) - } - const handleDragOver = (e: DragEvent) => { - e.preventDefault() - e.stopPropagation() - } - const handleDragLeave = (e: DragEvent) => { - e.preventDefault() - e.stopPropagation() - e.target === dragRef.current && setDragging(false) - } - - const handleDrop = useCallback((e: DragEvent) => { - e.preventDefault() - e.stopPropagation() - setDragging(false) - if (!e.dataTransfer) - return - - const files = [...e.dataTransfer.files] as File[] - setDSLFile(files[0]) - }, [setDSLFile]) - - const selectHandle = () => { - if (fileUploader.current) - fileUploader.current.click() - } - - const fileChangeHandle = (e: React.ChangeEvent) => { - const files = [...(e.target.files ?? [])] as File[] - console.log(files[0]) - setDSLFile(files[0]) - } - - const removeFile = () => { - if (fileUploader.current) - fileUploader.current.value = '' - setDSLFile(null) - } - - // utils - // const getFileType = (currentFile: File) => { - // if (!currentFile) - // return '' - - // const arr = currentFile.name.split('.') - // return arr[arr.length - 1] - // } - - const getFileSize = (size: number) => { - if (size / 1024 < 10) - return `${(size / 1024).toFixed(2)}KB` - - return `${(size / 1024 / 1024).toFixed(2)}MB` - } - - useEffect(() => { - dropRef.current?.addEventListener('dragenter', handleDragEnter) - dropRef.current?.addEventListener('dragover', handleDragOver) - dropRef.current?.addEventListener('dragleave', handleDragLeave) - dropRef.current?.addEventListener('drop', handleDrop) - return () => { - dropRef.current?.removeEventListener('dragenter', handleDragEnter) - dropRef.current?.removeEventListener('dragover', handleDragOver) - dropRef.current?.removeEventListener('dragleave', handleDragLeave) - dropRef.current?.removeEventListener('drop', handleDrop) + const readFile = (file: File) => { + const reader = new FileReader() + reader.onload = function (event) { + const content = event.target?.result + setFileContent(content as string) } - }, [handleDrop]) + reader.readAsText(file) + } + + const handleFile = (file?: File) => { + setDSLFile(file) + if (file) + readFile(file) + if (!file) + setFileContent('') + } const { isCurrentWorkspaceManager } = useAppContext() const { plan, enableBilling } = useProviderContext() const isAppsFull = (enableBilling && plan.usage.buildApps >= plan.total.buildApps) const isCreatingRef = useRef(false) - // #TODO# use import api const onCreate: MouseEventHandler = async () => { if (isCreatingRef.current) return isCreatingRef.current = true + if (!currentFile) + return try { - // const app = await createApp() + const app = await importApp({ + data: fileContent || '', + }) if (onSuccess) onSuccess() if (onClose) onClose() notify({ type: 'success', message: t('app.newApp.appCreated') }) - // router.push(`/app/${app.id}/${isCurrentWorkspaceManager ? 'configuration' : 'overview'}`) + localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1') + if (!isCurrentWorkspaceManager) { + router.push(`/app/${app.id}/'overview'`) + } + else { + if (app.mode === 'workflow' || app.mode === 'advanced-chat') + router.push(`/app/${app.id}/'workflow'`) + router.push(`/app/${app.id}/'configuration'`) + } } catch (e) { notify({ type: 'error', message: t('app.newApp.appCreateFailed') }) @@ -140,49 +93,10 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose }: CreateFromDSLModalProp
-
- -
- {!currentFile && ( -
- -
- - - {t('datasetCreation.stepOne.uploader.button')} - - -
-
- )} - {dragging &&
} -
- {currentFile && ( -
- {/* TODO type icon */} -
-
-
{currentFile.name}
-
{getFileSize(currentFile.size)}
-
-
- { - e.stopPropagation() - removeFile() - }} /> -
-
- )} -
+ {isAppsFull && }
diff --git a/web/app/components/app/create-from-dsl-modal/uploader.tsx b/web/app/components/app/create-from-dsl-modal/uploader.tsx new file mode 100644 index 0000000000..a93e6f6332 --- /dev/null +++ b/web/app/components/app/create-from-dsl-modal/uploader.tsx @@ -0,0 +1,126 @@ +'use client' +import type { FC } from 'react' +import React, { useEffect, useRef, useState } from 'react' +import cn from 'classnames' +import { useTranslation } from 'react-i18next' +import { useContext } from 'use-context-selector' +import { Csv as CSVIcon } from '@/app/components/base/icons/src/public/files' +import { ToastContext } from '@/app/components/base/toast' +import { Trash03, UploadCloud01 } from '@/app/components/base/icons/src/vender/line/general' +import Button from '@/app/components/base/button' + +export type Props = { + file: File | undefined + updateFile: (file?: File) => void +} + +const Uploader: FC = ({ + file, + updateFile, +}) => { + const { t } = useTranslation() + const { notify } = useContext(ToastContext) + const [dragging, setDragging] = useState(false) + const dropRef = useRef(null) + const dragRef = useRef(null) + const fileUploader = useRef(null) + + const handleDragEnter = (e: DragEvent) => { + e.preventDefault() + e.stopPropagation() + e.target !== dragRef.current && setDragging(true) + } + const handleDragOver = (e: DragEvent) => { + e.preventDefault() + e.stopPropagation() + } + const handleDragLeave = (e: DragEvent) => { + e.preventDefault() + e.stopPropagation() + e.target === dragRef.current && setDragging(false) + } + const handleDrop = (e: DragEvent) => { + e.preventDefault() + e.stopPropagation() + setDragging(false) + if (!e.dataTransfer) + return + const files = [...e.dataTransfer.files] + if (files.length > 1) { + notify({ type: 'error', message: t('datasetCreation.stepOne.uploader.validation.count') }) + return + } + updateFile(files[0]) + } + const selectHandle = () => { + if (fileUploader.current) + fileUploader.current.click() + } + const removeFile = () => { + if (fileUploader.current) + fileUploader.current.value = '' + updateFile() + } + const fileChangeHandle = (e: React.ChangeEvent) => { + const currentFile = e.target.files?.[0] + updateFile(currentFile) + } + + useEffect(() => { + dropRef.current?.addEventListener('dragenter', handleDragEnter) + dropRef.current?.addEventListener('dragover', handleDragOver) + dropRef.current?.addEventListener('dragleave', handleDragLeave) + dropRef.current?.addEventListener('drop', handleDrop) + return () => { + dropRef.current?.removeEventListener('dragenter', handleDragEnter) + dropRef.current?.removeEventListener('dragover', handleDragOver) + dropRef.current?.removeEventListener('dragleave', handleDragLeave) + dropRef.current?.removeEventListener('drop', handleDrop) + } + }, []) + + return ( +
+ +
+ {!file && ( +
+
+ +
+ {t('datasetCreation.stepOne.uploader.button')} + {t('datasetDocuments.list.batchModal.browse')} +
+
+ {dragging &&
} +
+ )} + {file && ( +
+ +
+ {file.name.replace(/(.yaml|.yml)$/, '')} + .yml +
+
+ +
+
+ +
+
+
+ )} +
+
+ ) +} + +export default React.memo(Uploader)