Commit 8d6984e2 authored by JzoNg's avatar JzoNg

create app by import yaml

parent cd773b8c
......@@ -22,9 +22,9 @@ const CreateAppCard = forwardRef<HTMLAnchorElement, CreateAppCardProps>(({ onSuc
return (
<a
ref={ref}
className='relative col-span-1 flex flex-col justify-between min-h-[160px] bg-gray-200 rounded-xl cursor-pointer duration-200 ease-in-out hover:bg-gray-50 hover:shadow-lg transition-colors'
className='relative col-span-1 flex flex-col justify-between min-h-[160px] bg-gray-200 rounded-xl cursor-pointer duration-200 ease-in-out hover:bg-gray-50 hover:shadow-lg transition-all'
>
<div className='grow rounded-t-xl group hover:bg-white' onClick={() => setShowNewAppDialog(true)}>
<div className='grow rounded-t-xl group transition-all hover:bg-white' onClick={() => setShowNewAppDialog(true)}>
<div className='flex pt-4 px-4 pb-3 h-[66px] items-center gap-3 grow-0 shrink-0'>
<span className='w-10 h-10 p-3 bg-gray-100 rounded-lg border border-gray-200 group-hover:bg-primary-50 group-hover:border-primary-100'>
<Plus className='w-4 h-4 text-gray-500 group-hover:text-primary-600'/>
......@@ -35,7 +35,7 @@ const CreateAppCard = forwardRef<HTMLAnchorElement, CreateAppCardProps>(({ onSuc
</div>
</div>
<div
className='flex items-center px-4 py-3 border-t-[0.5px] border-black/[.05] rounded-b-xl text-xs leading-[18px] text-gray-500 hover:bg-white hover:text-primary-600'
className='flex items-center px-4 py-3 border-t-[0.5px] border-black/[.05] rounded-b-xl text-xs leading-[18px] text-gray-500 transition-all hover:bg-white hover:text-primary-600'
onClick={() => setShowCreateFromDSLModal(true)}
>
{t('app.createFromConfigFile')}
......
'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<File | null>()
const [dragging, setDragging] = useState(false)
const dropRef = useRef<HTMLDivElement>(null)
const dragRef = useRef<HTMLDivElement>(null)
const fileUploader = useRef<HTMLInputElement>(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 = 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<HTMLInputElement>) => {
const files = [...(e.target.files ?? [])] as File[]
console.log(files[0])
setDSLFile(files[0])
}
const removeFile = () => {
if (fileUploader.current)
fileUploader.current.value = ''
setDSLFile(null)
const [currentFile, setDSLFile] = useState<File>()
const [fileContent, setFileContent] = useState<string>()
const readFile = (file: File) => {
const reader = new FileReader()
reader.onload = function (event) {
const content = event.target?.result
setFileContent(content as string)
}
reader.readAsText(file)
}
// 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`
const handleFile = (file?: File) => {
setDSLFile(file)
if (file)
readFile(file)
if (!file)
setFileContent('')
}
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)
}
}, [handleDrop])
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
<div className='absolute right-4 top-4 p-2 cursor-pointer' onClick={onClose}>
<XClose className='w-4 h-4 text-gray-500' />
</div>
<div className='pt-5 pb-7'>
<input
ref={fileUploader}
id="fileUploader"
style={{ display: 'none' }}
type="file"
onChange={fileChangeHandle}
/>
<div ref={dropRef}>
{!currentFile && (
<div className={cn(
'relative flex justify-center items-center h-20 bg-gray-50 rounded-xl border border-dashed border-gray-200',
dragging && '!bg-[#F5F8FF] !border-[#B2CCFF]',
)}>
<div className='flex justify-center items-center'>
<UploadCloud01 className='w-6 h-6 mr-2'/>
<span className='text-sm text-gray-500'>
{t('datasetCreation.stepOne.uploader.button')}
<label className='pl-1 cursor-pointer text-[#155eef]' onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.browse')}</label>
</span>
</div>
</div>
)}
{dragging && <div ref={dragRef} className='absolute top-0 left-0 w-full h-full' />}
</div>
{currentFile && (
<div className='group relative flex justify-center items-center justify-between h-20 pl-[64px] pr-6 bg-gray-50 rounded-xl border border-gray-100 cursor-pointer hover:bg-[#f5f8ff] hover:border-[#d1e0ff]'>
{/* TODO type icon */}
<div className='absolute top-[24px] left-[24px] w-8 h-8'/>
<div className='grow truncate'>
<div className='truncate text-sm leading-[20px] font-medium text-gray-800'>{currentFile.name}</div>
<div className='text-xs leading-[18px] text-gray-500'>{getFileSize(currentFile.size)}</div>
</div>
<div className='shrink-0 hidden group-hover:flex'>
<Trash03 className='w-4 h-4 text-gray-500 cursor-pointer' onClick={(e) => {
e.stopPropagation()
removeFile()
}} />
</div>
</div>
)}
</div>
<Uploader
file={currentFile}
updateFile={handleFile}
/>
{isAppsFull && <AppsFull loc='app-create-dsl' />}
<div className='pt-6 flex justify-end'>
<Button className='mr-2 text-gray-700 text-sm font-medium' onClick={onClose}>{t('app.newApp.Cancel')}</Button>
......
'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<Props> = ({
file,
updateFile,
}) => {
const { t } = useTranslation()
const { notify } = useContext(ToastContext)
const [dragging, setDragging] = useState(false)
const dropRef = useRef<HTMLDivElement>(null)
const dragRef = useRef<HTMLDivElement>(null)
const fileUploader = useRef<HTMLInputElement>(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<HTMLInputElement>) => {
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 (
<div className='mt-6'>
<input
ref={fileUploader}
style={{ display: 'none' }}
type="file"
id="fileUploader"
accept='.yml'
onChange={fileChangeHandle}
/>
<div ref={dropRef}>
{!file && (
<div className={cn('flex items-center h-20 rounded-xl bg-gray-50 border border-dashed border-gray-200 text-sm font-normal', dragging && 'bg-[#F5F8FF] border border-[#B2CCFF]')}>
<div className='w-full flex items-center justify-center space-x-2'>
<UploadCloud01 className='w-6 h-6 mr-2'/>
<div className='text-gray-500'>
{t('datasetCreation.stepOne.uploader.button')}
<span className='pl-1 text-[#155eef] cursor-pointer' onClick={selectHandle}>{t('datasetDocuments.list.batchModal.browse')}</span>
</div>
</div>
{dragging && <div ref={dragRef} className='absolute w-full h-full top-0 left-0'/>}
</div>
)}
{file && (
<div className={cn('flex items-center h-20 px-6 rounded-xl bg-gray-50 border border-gray-200 text-sm font-normal group', 'hover:bg-[#F5F8FF] hover:border-[#B2CCFF]')}>
<CSVIcon className="shrink-0" />
<div className='flex ml-2 w-0 grow'>
<span className='max-w-[calc(100%_-_30px)] text-ellipsis whitespace-nowrap overflow-hidden text-gray-800'>{file.name.replace(/(.yaml|.yml)$/, '')}</span>
<span className='shrink-0 text-gray-500'>.yml</span>
</div>
<div className='hidden group-hover:flex items-center'>
<Button className='!h-8 !px-3 !py-[6px] bg-white !text-[13px] !leading-[18px] text-gray-700' onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.change')}</Button>
<div className='mx-2 w-px h-4 bg-gray-200' />
<div className='p-2 cursor-pointer' onClick={removeFile}>
<Trash03 className='w-4 h-4 text-gray-500' />
</div>
</div>
</div>
)}
</div>
</div>
)
}
export default React.memo(Uploader)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment