Commit f607a334 authored by JzoNg's avatar JzoNg

create from DSL

parent 117b8411
......@@ -3,6 +3,7 @@
import { forwardRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import NewAppDialog from './NewAppDialog'
import CreateFromDSLModal from '@/app/components/app/create-from-dsl-modal'
import { useProviderContext } from '@/context/provider-context'
import { Plus } from '@/app/components/base/icons/src/vender/line/general'
import { ArrowUpRight } from '@/app/components/base/icons/src/vender/line/arrows'
......@@ -17,13 +18,13 @@ const CreateAppCard = forwardRef<HTMLAnchorElement, CreateAppCardProps>(({ onSuc
const { onPlanInfoChanged } = useProviderContext()
const [showNewAppDialog, setShowNewAppDialog] = useState(false)
const [showCreateFromDSLModal, setShowCreateFromDSLModal] = useState(false)
return (
<a
ref={ref}
onClick={() => setShowNewAppDialog(true)}
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'
>
<div className='grow rounded-t-xl group hover:bg-white'>
<div className='grow rounded-t-xl group 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'/>
......@@ -33,10 +34,22 @@ const CreateAppCard = forwardRef<HTMLAnchorElement, CreateAppCardProps>(({ onSuc
</div>
</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'>
<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'
onClick={() => setShowCreateFromDSLModal(true)}
>
{t('app.createFromConfigFile')}
<ArrowUpRight className='ml-1 w-3 h-3'/>
</div>
<CreateFromDSLModal
show={showCreateFromDSLModal}
onClose={() => setShowCreateFromDSLModal(false)}
onSuccess={() => {
onPlanInfoChanged()
if (onSuccess)
onSuccess()
}}
/>
<NewAppDialog show={showNewAppDialog} onSuccess={
() => {
onPlanInfoChanged()
......
'use client'
import type { MouseEventHandler } from 'react'
import { useCallback, useEffect, useRef, useState } from 'react'
// import useSWR from 'swr'
import cn from 'classnames'
import { useRouter } from 'next/navigation'
import { useContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
// import Dialog from '@/app/components/base/dialog'
import Modal from '@/app/components/base/modal'
// import Input from '@/app/components/base/input'
// import type { AppMode } from '@/types/app'
import { ToastContext } from '@/app/components/base/toast'
// import { createApp, fetchAppTemplates } 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'
type CreateFromDSLModalProps = {
show: boolean
onSuccess?: () => void
onClose: () => void
}
const CreateFromDSLModal = ({ show, onSuccess, onClose }: CreateFromDSLModalProps) => {
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)
}
// 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)
}
}, [handleDrop])
const { isCurrentWorkspaceManager } = useAppContext()
const { plan, enableBilling } = useProviderContext()
const isAppsFull = (enableBilling && plan.usage.buildApps >= plan.total.buildApps)
const isCreatingRef = useRef(false)
// TODO
const onCreate: MouseEventHandler = async () => {
if (isCreatingRef.current)
return
isCreatingRef.current = true
try {
// const app = await createApp()
if (onSuccess)
onSuccess()
if (onClose)
onClose()
notify({ type: 'success', message: t('app.newApp.appCreated') })
// router.push(`/app/${app.id}/${isCurrentWorkspaceManager ? 'configuration' : 'overview'}`)
}
catch (e) {
notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
}
isCreatingRef.current = false
}
return (
<Modal
wrapperClassName='z-20'
className='px-8 py-6 max-w-[520px] w-[520px] rounded-xl'
isShow={show}
onClose={() => {}}
>
<div className='relative pb-2 text-xl font-medium leading-[30px] text-gray-900'>Create from DSL file</div>
<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>
{isAppsFull && <AppsFull loc='app-create' />}
<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>
<Button className='text-sm font-medium' disabled={isAppsFull || !currentFile} type="primary" onClick={onCreate}>{t('app.newApp.Create')}</Button>
</div>
</Modal>
)
}
export default CreateFromDSLModal
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.4" d="M4 16.2422C2.79401 15.435 2 14.0602 2 12.5C2 10.1564 3.79151 8.23129 6.07974 8.01937C6.54781 5.17213 9.02024 3 12 3C14.9798 3 17.4522 5.17213 17.9203 8.01937C20.2085 8.23129 22 10.1564 22 12.5C22 14.0602 21.206 15.435 20 16.2422" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 16L12 12M12 12L16 16M12 12L12 21" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "24",
"height": "24",
"viewBox": "0 0 24 24",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"opacity": "0.4",
"d": "M4 16.2422C2.79401 15.435 2 14.0602 2 12.5C2 10.1564 3.79151 8.23129 6.07974 8.01937C6.54781 5.17213 9.02024 3 12 3C14.9798 3 17.4522 5.17213 17.9203 8.01937C20.2085 8.23129 22 10.1564 22 12.5C22 14.0602 21.206 15.435 20 16.2422",
"stroke": "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round"
},
"children": []
},
{
"type": "element",
"name": "path",
"attributes": {
"d": "M8 16L12 12M12 12L16 16M12 12L12 21",
"stroke": "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round"
},
"children": []
}
]
},
"name": "UploadCloud01"
}
\ No newline at end of file
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './UploadCloud01.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
Icon.displayName = 'UploadCloud01'
export default Icon
......@@ -28,5 +28,6 @@ export { default as Settings04 } from './Settings04'
export { default as Target04 } from './Target04'
export { default as Trash03 } from './Trash03'
export { default as Upload03 } from './Upload03'
export { default as UploadCloud01 } from './UploadCloud01'
export { default as XClose } from './XClose'
export { default as X } from './X'
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