@ -1,7 +1,7 @@
'use client'
import type { MouseEventHandler } from 'react'
import { use Ref, useState } from 'react'
import { use Memo, use Ref, useState } from 'react'
import { useRouter } from 'next/navigation'
import { useContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
@ -10,25 +10,38 @@ import Uploader from './uploader'
import Button from '@/app/components/base/button'
import Modal from '@/app/components/base/modal'
import { ToastContext } from '@/app/components/base/toast'
import { importApp } from '@/service/apps'
import {
importApp ,
importAppFromUrl ,
} 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 { NEED_REFRESH_APP_LIST_KEY } from '@/config'
import { getRedirection } from '@/utils/app-redirection'
import cn from '@/utils/classnames'
type CreateFromDSLModalProps = {
show : boolean
onSuccess ? : ( ) = > void
onClose : ( ) = > void
activeTab? : string
dslUrl? : string
}
const CreateFromDSLModal = ( { show , onSuccess , onClose } : CreateFromDSLModalProps ) = > {
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 < File > ( )
const [ fileContent , setFileContent ] = useState < string > ( )
const [ currentTab , setCurrentTab ] = useState ( activeTab )
const [ dslUrlValue , setDslUrlValue ] = useState ( dslUrl )
const readFile = ( file : File ) = > {
const reader = new FileReader ( )
@ -53,15 +66,26 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose }: CreateFromDSLModalProp
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
if ( ! currentFile )
return
try {
const app = await importApp ( {
data : fileContent || '' ,
} )
let app
if ( currentTab === CreateFromDSLModalTab . FROM_FILE ) {
app = await importApp ( {
data : fileContent || '' ,
} )
}
if ( currentTab === CreateFromDSLModalTab . FROM_URL ) {
app = await importAppFromUrl ( {
url : dslUrlValue || '' ,
} )
}
if ( onSuccess )
onSuccess ( )
if ( onClose )
@ -76,24 +100,95 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose }: CreateFromDSLModalProp
isCreatingRef . current = false
}
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 (
< Modal
className = 'px-8 py-6 max-w-[520px] w-[520px] rounded-xl'
className = 'p -0 w-[520px] rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow -xl'
isShow = { show }
onClose = { ( ) = > { } }
>
< div className = 'relative pb-2 text-xl font-medium leading-[30px] text-gray-900' > { t ( 'app.createFromConfigFile' ) } < / div >
< div className = 'absolute right-4 top-4 p-2 cursor-pointer' onClick = { onClose } >
< RiCloseLine className = 'w-4 h-4 text-gray-500' / >
< div className = 'flex items-center justify-between pt-6 pl-6 pr-5 pb-3 text-text-primary title-2xl-semi-bold' >
{ t ( 'app.importFromDSL' ) }
< div
className = 'flex items-center w-8 h-8 cursor-pointer'
onClick = { ( ) = > onClose ( ) }
>
< RiCloseLine className = 'w-5 h-5 text-text-tertiary' / >
< / div >
< / div >
< div className = 'flex items-center px-6 h-9 space-x-6 system-md-semibold text-text-tertiary border-b border-divider-subtle' >
{
tabs . map ( tab = > (
< div
key = { tab . key }
className = { cn (
'relative flex items-center h-full cursor-pointer' ,
currentTab === tab . key && 'text-text-primary' ,
) }
onClick = { ( ) = > setCurrentTab ( tab . key ) }
>
{ tab . label }
{
currentTab === tab . key && (
< div className = 'absolute bottom-0 w-full h-[2px] bg-util-colors-blue-brand-blue-brand-600' > < / div >
)
}
< / div >
) )
}
< / div >
< div className = 'px-6 py-4' >
{
currentTab === CreateFromDSLModalTab . FROM_FILE && (
< Uploader
className = 'mt-0'
file = { currentFile }
updateFile = { handleFile }
/ >
)
}
{
currentTab === CreateFromDSLModalTab . FROM_URL && (
< div >
< div className = 'mb-1 system-md-semibold leading6' > DSL URL < / div >
< input
placeholder = { t ( 'app.importFromDSLUrlPlaceholder' ) || '' }
className = 'px-2 w-full h-8 border border-components-input-border-active bg-components-input-bg-active rounded-lg outline-none appearance-none placeholder:text-components-input-text-placeholder system-sm-regular'
value = { dslUrlValue }
onChange = { e = > setDslUrlValue ( e . target . value ) }
/ >
< / div >
)
}
< / div >
< Uploader
file = { currentFile }
updateFile = { handleFile }
/ >
{ isAppsFull && < AppsFull loc = 'app-create-dsl' / > }
< div className = 'pt-6 flex justify-end' >
{isAppsFull && (
< div className = 'px-6' >
< AppsFull className = 'mt-0' loc = 'app-create-dsl' / >
< / div >
) }
< div className = ' flex justify-end px-6 py-5 '>
< Button className = 'mr-2' onClick = { onClose } > { t ( 'app.newApp.Cancel' ) } < / Button >
< Button disabled = { isAppsFull || ! currentFile } variant = "primary" onClick = { onCreate } > { t ( 'app.newApp.Create' ) } < / Button >
< Button disabled = { buttonDisabled } variant = "primary" onClick = { onCreate } > { t ( 'app.newApp.Create' ) } < / Button >
< / div >
< / Modal >
)