Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
D
dify
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ai-tech
dify
Commits
8d6984e2
Commit
8d6984e2
authored
Mar 03, 2024
by
JzoNg
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
create app by import yaml
parent
cd773b8c
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
168 additions
and
128 deletions
+168
-128
NewAppCard.tsx
web/app/(commonLayout)/apps/NewAppCard.tsx
+3
-3
index.tsx
web/app/components/app/create-from-dsl-modal/index.tsx
+39
-125
uploader.tsx
web/app/components/app/create-from-dsl-modal/uploader.tsx
+126
-0
No files found.
web/app/(commonLayout)/apps/NewAppCard.tsx
View file @
8d6984e2
...
...
@@ -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'
)
}
...
...
web/app/components/app/create-from-dsl-modal/index.tsx
View file @
8d6984e2
'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
>
...
...
web/app/components/app/create-from-dsl-modal/uploader.tsx
0 → 100644
View file @
8d6984e2
'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
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment