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
ff61570b
Commit
ff61570b
authored
Aug 06, 2023
by
Gillian97
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: update app name icon
parent
14147660
Changes
29
Hide whitespace changes
Inline
Side-by-side
Showing
29 changed files
with
401 additions
and
155 deletions
+401
-155
AppCard.tsx
web/app/(commonLayout)/apps/AppCard.tsx
+132
-14
AppModeLabel.tsx
web/app/(commonLayout)/apps/AppModeLabel.tsx
+1
-1
AppSettings.tsx
web/app/(commonLayout)/apps/AppSettings.tsx
+79
-0
Apps.tsx
web/app/(commonLayout)/apps/Apps.tsx
+1
-1
NewAppDialog.tsx
web/app/(commonLayout)/apps/NewAppDialog.tsx
+1
-1
style.module.css
web/app/(commonLayout)/apps/style.module.css
+20
-0
list.module.css
web/app/(commonLayout)/list.module.css
+26
-23
index.tsx
web/app/components/base/confirm-ui/index.tsx
+2
-2
index.tsx
web/app/components/base/confirm/index.tsx
+1
-1
index.tsx
web/app/components/base/popover/index.tsx
+30
-28
list.tsx
web/app/components/datasets/documents/list.tsx
+69
-67
style.module.css
web/app/components/datasets/documents/style.module.css
+10
-10
style.module.css
web/app/components/explore/item-operation/style.module.css
+3
-3
style.module.css
...xplore/universal-chat/config-view/detail/style.module.css
+2
-2
action.svg
web/assets/action.svg
+0
-0
csv.svg
web/assets/csv.svg
+0
-0
html.svg
web/assets/html.svg
+0
-0
json.svg
web/assets/json.svg
+0
-0
md.svg
web/assets/md.svg
+0
-0
pdf.svg
web/assets/pdf.svg
+0
-0
txt.svg
web/assets/txt.svg
+0
-0
xlsx.svg
web/assets/xlsx.svg
+0
-0
app.en.ts
web/i18n/lang/app.en.ts
+4
-0
app.zh.ts
web/i18n/lang/app.zh.ts
+4
-0
common.en.ts
web/i18n/lang/common.en.ts
+3
-0
common.zh.ts
web/i18n/lang/common.zh.ts
+3
-0
app.ts
web/models/app.ts
+2
-0
datasets.ts
web/models/datasets.ts
+1
-0
apps.ts
web/service/apps.ts
+7
-2
No files found.
web/app/(commonLayout)/apps/AppCard.tsx
View file @
ff61570b
...
...
@@ -5,59 +5,167 @@ import Link from 'next/link'
import
type
{
MouseEventHandler
}
from
'react'
import
{
useCallback
,
useState
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
cn
from
'classnames'
import
style
from
'../list.module.css'
import
AppModeLabel
from
'./AppModeLabel'
import
AppSettings
from
'./AppSettings'
import
s
from
'./style.module.css'
import
type
{
App
}
from
'@/types/app'
import
Confirm
from
'@/app/components/base/confirm'
import
{
ToastContext
}
from
'@/app/components/base/toast'
import
{
deleteApp
}
from
'@/service/apps'
import
{
deleteApp
,
updateAppIcon
,
updateAppName
}
from
'@/service/apps'
import
AppIcon
from
'@/app/components/base/app-icon'
import
AppsContext
from
'@/context/app-context'
import
CustomPopover
from
'@/app/components/base/popover'
import
Divider
from
'@/app/components/base/divider'
export
type
AppCardProps
=
{
app
:
App
on
Delete
?:
()
=>
void
on
Refresh
?:
()
=>
void
}
const
AppCard
=
({
app
,
onDelete
,
}:
AppCardProps
)
=>
{
const
AppCard
=
({
app
,
onRefresh
}:
AppCardProps
)
=>
{
const
{
t
}
=
useTranslation
()
const
{
notify
}
=
useContext
(
ToastContext
)
const
mutateApps
=
useContextSelector
(
AppsContext
,
state
=>
state
.
mutateApps
)
const
mutateApps
=
useContextSelector
(
AppsContext
,
state
=>
state
.
mutateApps
,
)
const
[
showConfirmDelete
,
setShowConfirmDelete
]
=
useState
(
false
)
const
onDeleteClick
:
MouseEventHandler
=
useCallback
((
e
)
=>
{
const
[
showSettingsModal
,
setShowSettingsModal
]
=
useState
(
false
)
const
[
updateLoading
,
setUpdateLoading
]
=
useState
(
false
)
const
onClickSettings
:
MouseEventHandler
=
useCallback
((
e
)
=>
{
e
.
preventDefault
()
setShowSettingsModal
(
true
)
},
[])
const
onClickDelete
:
MouseEventHandler
=
useCallback
((
e
)
=>
{
e
.
preventDefault
()
setShowConfirmDelete
(
true
)
},
[])
const
onConfirmDelete
=
useCallback
(
async
()
=>
{
try
{
await
deleteApp
(
app
.
id
)
notify
({
type
:
'success'
,
message
:
t
(
'app.appDeleted'
)
})
if
(
on
Delete
)
on
Delete
()
if
(
on
Refresh
)
on
Refresh
()
mutateApps
()
}
catch
(
e
:
any
)
{
notify
({
type
:
'error'
,
message
:
`
${
t
(
'app.appDeleteFailed'
)}${
'message'
in
e
?
`:
${
e
.
message
}
`
:
''
}
`
})
notify
({
type
:
'error'
,
message
:
`
${
t
(
'app.appDeleteFailed'
)}${
'message'
in
e
?
`:
${
e
.
message
}
`
:
''
}
`
,
})
}
setShowConfirmDelete
(
false
)
},
[
app
.
id
])
const
onUpdate
=
useCallback
(
async
(
params
:
{
name
:
App
[
'name'
];
iconInfo
:
{
icon
:
string
;
icon_background
:
string
}
})
=>
{
const
reqList
=
[]
if
(
params
.
name
!==
app
.
name
)
{
reqList
.
push
(
updateAppName
({
url
:
`/apps/
${
app
.
id
}
/name`
,
body
:
{
name
:
params
.
name
},
}))
}
if
(
params
.
iconInfo
.
icon
!==
app
.
icon
||
params
.
iconInfo
.
icon_background
!==
app
.
icon_background
)
reqList
.
push
(
updateAppIcon
({
url
:
`/apps/
${
app
.
id
}
/icon`
,
body
:
params
.
iconInfo
}))
if
(
!
reqList
.
length
)
{
setShowSettingsModal
(
false
)
notify
({
type
:
'info'
,
message
:
t
(
'common.actionMsg.noModification'
),
})
return
}
setUpdateLoading
(
true
)
const
updateRes
=
await
Promise
.
allSettled
(
reqList
)
if
(
!
updateRes
.
find
(
v
=>
v
.
status
===
'rejected'
))
{
notify
({
type
:
'success'
,
message
:
t
(
'common.actionMsg.modifiedSuccessfully'
),
})
setShowSettingsModal
(
false
)
}
else
{
notify
({
type
:
'error'
,
message
:
t
(
'common.actionMsg.modificationFailed'
),
})
}
if
(
onRefresh
)
onRefresh
()
mutateApps
()
setUpdateLoading
(
false
)
},
[
app
.
id
],
)
const
Operations
=
(
props
:
any
)
=>
{
return
<
div
className=
"w-full py-1"
>
<
div
className=
{
s
.
actionItem
}
onClick=
{
(
e
)
=>
{
props
?.
onClose
()
onClickSettings
(
e
)
}
}
>
<
span
className=
{
s
.
actionName
}
>
{
t
(
'common.operation.settings'
)
}
</
span
>
</
div
>
<
Divider
className=
"my-1"
/>
<
div
className=
{
cn
(
s
.
actionItem
,
s
.
deleteActionItem
,
'group'
)
}
onClick=
{
(
e
)
=>
{
props
?.
onClose
()
onClickDelete
(
e
)
}
}
>
<
span
className=
{
cn
(
s
.
actionName
,
'group-hover:text-red-500'
)
}
>
{
t
(
'common.operation.delete'
)
}
</
span
>
</
div
>
</
div
>
}
return
(
<>
<
Link
href=
{
`/app/${app.id}/overview`
}
className=
{
style
.
listItem
}
>
<
div
className=
{
style
.
listItemTitle
}
>
<
AppIcon
size=
'small'
icon=
{
app
.
icon
}
background=
{
app
.
icon_background
}
/>
<
AppIcon
size=
"small"
icon=
{
app
.
icon
}
background=
{
app
.
icon_background
}
/>
<
div
className=
{
style
.
listItemHeading
}
>
<
div
className=
{
style
.
listItemHeadingContent
}
>
{
app
.
name
}
</
div
>
</
div
>
<
span
className=
{
style
.
deleteAppIcon
}
onClick=
{
onDeleteClick
}
/>
<
CustomPopover
htmlContent=
{
<
Operations
/>
}
position=
"br"
btnElement=
{
<
div
className=
{
cn
(
s
.
actionIcon
,
s
.
commonIcon
)
}
/>
}
btnClassName=
{
open
=>
cn
(
open
?
'!bg-gray-100 !shadow-none'
:
'!bg-transparent'
,
style
.
actionIconWrapper
,
)
}
className=
{
'!w-[128px] h-fit !z-20'
}
/>
</
div
>
<
div
className=
{
style
.
listItemDescription
}
>
{
app
.
model_config
?.
pre_prompt
}
</
div
>
<
div
className=
{
style
.
listItemDescription
}
>
{
app
.
model_config
?.
pre_prompt
}
</
div
>
<
div
className=
{
style
.
listItemFooter
}
>
<
AppModeLabel
mode=
{
app
.
mode
}
/>
</
div
>
...
...
@@ -72,6 +180,16 @@ const AppCard = ({
onCancel=
{
()
=>
setShowConfirmDelete
(
false
)
}
/>
)
}
{
showSettingsModal
&&
(
<
AppSettings
isShow=
{
showSettingsModal
}
onClose=
{
()
=>
setShowSettingsModal
(
false
)
}
appName=
{
app
.
name
}
appIcon=
{
{
icon
:
app
.
icon
,
icon_background
:
app
.
icon_background
}
}
onUpdate=
{
onUpdate
}
loading=
{
updateLoading
}
/>
)
}
</
Link
>
</>
)
...
...
web/app/(commonLayout)/apps/AppModeLabel.tsx
View file @
ff61570b
...
...
@@ -2,8 +2,8 @@
import
classNames
from
'classnames'
import
{
useTranslation
}
from
'react-i18next'
import
{
type
AppMode
}
from
'@/types/app'
import
style
from
'../list.module.css'
import
{
type
AppMode
}
from
'@/types/app'
export
type
AppModeLabelProps
=
{
mode
:
AppMode
...
...
web/app/(commonLayout)/apps/AppSettings.tsx
0 → 100644
View file @
ff61570b
'use client'
import
React
,
{
useState
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
style
from
'../list.module.css'
import
Dialog
from
'@/app/components/base/dialog'
import
Button
from
'@/app/components/base/button'
import
AppIcon
from
'@/app/components/base/app-icon'
import
EmojiPicker
from
'@/app/components/base/emoji-picker'
import
type
{
App
}
from
'@/types/app'
type
Props
=
{
isShow
:
boolean
onClose
:
()
=>
void
appIcon
:
{
icon
:
App
[
'icon'
];
icon_background
:
App
[
'icon_background'
]
}
appName
:
App
[
'name'
]
onUpdate
:
(
v
:
any
)
=>
void
loading
:
boolean
}
const
AppSettings
=
(
props
:
Props
)
=>
{
const
{
isShow
,
onClose
,
appIcon
,
appName
,
onUpdate
,
loading
}
=
props
const
{
t
}
=
useTranslation
()
// Emoji Picker
const
[
showEmojiPicker
,
setShowEmojiPicker
]
=
useState
(
false
)
const
[
emoji
,
setEmoji
]
=
useState
(
appIcon
)
// input name
const
[
inputName
,
setInputName
]
=
useState
(
appName
)
return
(
<>
{
showEmojiPicker
&&
(
<
EmojiPicker
onSelect=
{
(
icon
,
icon_background
)
=>
{
setEmoji
({
icon
,
icon_background
})
setShowEmojiPicker
(
false
)
}
}
onClose=
{
()
=>
{
setEmoji
({
icon
:
'🤖'
,
icon_background
:
'#FFEAD5'
})
setShowEmojiPicker
(
false
)
}
}
/>
)
}
<
Dialog
show=
{
isShow
}
title=
{
t
(
'app.editApp.startToEdit'
)
}
footer=
{
<>
<
Button
onClick=
{
onClose
}
>
{
t
(
'common.operation.cancel'
)
}
</
Button
>
<
Button
type=
"primary"
loading=
{
loading
}
onClick=
{
()
=>
onUpdate
({
name
:
inputName
,
iconInfo
:
emoji
})
}
>
{
t
(
'common.operation.save'
)
}
</
Button
>
</>
}
>
<
h3
className=
{
style
.
newItemCaption
}
>
{
t
(
'explore.appCustomize.subTitle'
)
}
</
h3
>
<
div
className=
"flex items-center justify-between gap-3 mb-8"
>
<
AppIcon
size=
"large"
onClick=
{
()
=>
{
setShowEmojiPicker
(
true
)
}
}
className=
"cursor-pointer"
icon=
{
emoji
.
icon
}
background=
{
emoji
.
icon_background
}
/>
<
input
placeholder=
{
t
(
'app.appNamePlaceholder'
)
||
''
}
onChange=
{
e
=>
setInputName
(
e
.
target
.
value
)
}
value=
{
inputName
}
className=
"h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg grow"
/>
</
div
>
</
Dialog
>
</>
)
}
export
default
AppSettings
web/app/(commonLayout)/apps/Apps.tsx
View file @
ff61570b
...
...
@@ -53,7 +53,7 @@ const Apps = () => {
return
(
<
nav
className=
'grid content-start grid-cols-1 gap-4 px-12 pt-8 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 grow shrink-0'
>
{
data
?.
map
(({
data
:
apps
})
=>
apps
.
map
(
app
=>
(
<
AppCard
key=
{
app
.
id
}
app=
{
app
}
on
Delete
=
{
mutate
}
/>
<
AppCard
key=
{
app
.
id
}
app=
{
app
}
on
Refresh
=
{
mutate
}
/>
)))
}
<
NewAppCard
ref=
{
anchorRef
}
onSuccess=
{
mutate
}
/>
</
nav
>
...
...
web/app/(commonLayout)/apps/NewAppDialog.tsx
View file @
ff61570b
...
...
@@ -119,7 +119,7 @@ const NewAppDialog = ({ show, onSuccess, onClose }: NewAppDialogProps) => {
<
div
className=
'flex items-center justify-between gap-3 mb-8'
>
<
AppIcon
size=
'large'
onClick=
{
()
=>
{
setShowEmojiPicker
(
true
)
}
}
className=
'cursor-pointer'
icon=
{
emoji
.
icon
}
background=
{
emoji
.
icon_background
}
/>
<
input
ref=
{
nameInputRef
}
className=
'h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg grow'
/>
<
input
ref=
{
nameInputRef
}
className=
'h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg grow'
placeholder=
{
t
(
'app.appNamePlaceholder'
)
||
''
}
/>
</
div
>
<
div
className=
'h-[247px]'
>
...
...
web/app/(commonLayout)/apps/style.module.css
0 → 100644
View file @
ff61570b
.commonIcon
{
@apply
w-4
h-4
inline-block
align-middle;
background-repeat
:
no-repeat
;
background-position
:
center
center
;
background-size
:
contain
;
}
.actionIcon
{
@apply
bg-gray-500;
mask-image
:
url(~@/assets/action.svg)
;
}
.actionItem
{
@apply
h-9
py-2
px-3
mx-1
flex
items-center
gap-2
hover
:
bg-gray-100
rounded-lg
cursor-pointer
;
}
.deleteActionItem
{
@apply
hover
:
bg-red-50
!important
;
}
.actionName
{
@apply
text-gray-700
text-sm;
}
web/app/(commonLayout)/list.module.css
View file @
ff61570b
...
...
@@ -14,9 +14,14 @@
@apply
relative;
}
.listItem.selectable
::before
{
content
:
''
;
content
:
""
;
@apply
absolute
top-0
left-0
block
w-full
h-full
rounded-lg
pointer-events-none
opacity-0
transition-opacity
duration-200
ease-in-out
hover
:
opacity-100
;
background
:
linear-gradient
(
0deg
,
rgba
(
235
,
245
,
255
,
0.5
),
rgba
(
235
,
245
,
255
,
0.5
)),
#FFFFFF
;
background
:
linear-gradient
(
0deg
,
rgba
(
235
,
245
,
255
,
0.5
),
rgba
(
235
,
245
,
255
,
0.5
)
),
#ffffff
;
}
.listItem.selectable
:hover::before
{
@apply
opacity-100;
...
...
@@ -65,13 +70,13 @@
@apply
text-primary-600;
}
.newItemIconAdd
{
background-image
:
url(
'./apps/assets/add.svg'
)
;
background-image
:
url(
"./apps/assets/add.svg"
)
;
}
.newItemIconChat
{
background-image
:
url(
'./apps/assets/chat.svg'
)
;
background-image
:
url(
"./apps/assets/chat.svg"
)
;
}
.newItemIconComplete
{
background-image
:
url(
'./apps/assets/completion.svg'
)
;
background-image
:
url(
"./apps/assets/completion.svg"
)
;
}
.listItemTitle
{
...
...
@@ -86,13 +91,11 @@
@apply
absolute
top-0
left-0
w-full
h-full
overflow-hidden
text-ellipsis
whitespace-nowrap;
}
.deleteAppIcon
{
@apply
hidden
grow-0
shrink-0
basis-8
w-8
h-8
rounded-lg
transition-colors
duration-200
ease-in-out
bg-white
border
border-gray-200
hover
:
bg-gray-100
bg-center
bg-no-repeat
;
background-size
:
16px
;
background-image
:
url('./apps/assets/delete.svg')
;
.actionIconWrapper
{
@apply
hidden
h-8
w-8
p-2
rounded-md
border-none
hover
:
bg-gray-100
!important
;
}
.listItem
:hover
.
deleteAppIcon
{
@apply
block
;
.listItem
:hover
.
actionIconWrapper
{
@apply
!inline-flex
;
}
.listItemDescription
{
...
...
@@ -114,19 +117,19 @@
@apply
block
w-3
h-3
bg-center
bg-contain;
}
.solidChatIcon
{
background-image
:
url(
'./apps/assets/chat-solid.svg'
)
;
background-image
:
url(
"./apps/assets/chat-solid.svg"
)
;
}
.solidCompletionIcon
{
background-image
:
url(
'./apps/assets/completion-solid.svg'
)
;
background-image
:
url(
"./apps/assets/completion-solid.svg"
)
;
}
.docIcon
{
background-image
:
url(
'./datasets/assets/doc.svg'
)
;
background-image
:
url(
"./datasets/assets/doc.svg"
)
;
}
.textIcon
{
background-image
:
url(
'./datasets/assets/text.svg'
)
;
background-image
:
url(
"./datasets/assets/text.svg"
)
;
}
.applicationIcon
{
background-image
:
url(
'./datasets/assets/application.svg'
)
;
background-image
:
url(
"./datasets/assets/application.svg"
)
;
}
.newItemCardHeading
{
...
...
@@ -140,24 +143,24 @@
@apply
inline-flex
items-center
gap-1
text-xs
text-gray-400
transition-colors
duration-200
ease-in-out;
}
.listItem
:hover
.listItemLink
{
@apply
text-primary-600
@apply
text-primary-600
;
}
.linkIcon
{
@apply
block
w-[13px]
h-[13px]
bg-center
bg-contain;
background-image
:
url(
'./apps/assets/link.svg'
)
;
background-image
:
url(
"./apps/assets/link.svg"
)
;
}
.linkIcon.grayLinkIcon
{
background-image
:
url(
'./apps/assets/link-gray.svg'
)
;
background-image
:
url(
"./apps/assets/link-gray.svg"
)
;
}
.listItem
:hover
.grayLinkIcon
{
background-image
:
url(
'./apps/assets/link.svg'
)
;
background-image
:
url(
"./apps/assets/link.svg"
)
;
}
.rightIcon
{
@apply
block
w-[13px]
h-[13px]
bg-center
bg-contain;
background-image
:
url(
'./apps/assets/right-arrow.svg'
)
;
background-image
:
url(
"./apps/assets/right-arrow.svg"
)
;
}
.socialMediaLink
{
...
...
@@ -169,11 +172,11 @@
}
.githubIcon
{
background-image
:
url(
'./apps/assets/github.svg'
)
;
background-image
:
url(
"./apps/assets/github.svg"
)
;
}
.discordIcon
{
background-image
:
url(
'./apps/assets/discord.svg'
)
;
background-image
:
url(
"./apps/assets/discord.svg"
)
;
}
/* #region new app dialog */
...
...
web/app/components/base/confirm-ui/index.tsx
View file @
ff61570b
...
...
@@ -41,8 +41,8 @@ const ConfirmUI: FC<IConfirmUIProps> = ({
</
div
>
<
div
className=
'flex gap-3 mt-4 ml-12'
>
<
div
onClick=
{
onConfirm
}
className=
'w-20 leading-
9
text-center text-white border rounded-lg cursor-pointer h-9 border-color-primary-700 bg-primary-700'
>
{
confirmText
||
t
(
'common.operation.confirm'
)
}
</
div
>
<
div
onClick=
{
onCancel
}
className=
'w-20 leading-
9
text-center text-gray-500 border rounded-lg cursor-pointer h-9 border-color-gray-200'
>
{
cancelText
||
t
(
'common.operation.cancel'
)
}
</
div
>
<
div
onClick=
{
onConfirm
}
className=
'w-20 leading-
[34px]
text-center text-white border rounded-lg cursor-pointer h-9 border-color-primary-700 bg-primary-700'
>
{
confirmText
||
t
(
'common.operation.confirm'
)
}
</
div
>
<
div
onClick=
{
onCancel
}
className=
'w-20 leading-
[34px]
text-center text-gray-500 border rounded-lg cursor-pointer h-9 border-color-gray-200'
>
{
cancelText
||
t
(
'common.operation.cancel'
)
}
</
div
>
</
div
>
</
div
>
...
...
web/app/components/base/confirm/index.tsx
View file @
ff61570b
import
{
Dialog
,
Transition
}
from
'@headlessui/react'
import
{
Fragment
}
from
'react'
import
ConfirmUI
from
'../confirm-ui'
import
{
useTranslation
}
from
'react-i18next'
import
ConfirmUI
from
'../confirm-ui'
// https://headlessui.com/react/dialog
...
...
web/app/components/base/popover/index.tsx
View file @
ff61570b
...
...
@@ -46,47 +46,49 @@ export default function CustomPopover({
:
{
onMouseLeave
:
()
=
>
onMouseLeave(open),
onMouseEnter: () =
>
onMouseEnter(open),
})
}
})}
>
<
Popover
.
Button
ref=
{
buttonRef
}
className=
{
`group ${s.popupBtn} ${open ? '' : 'bg-gray-100'} ${!btnClassName ? '' : typeof btnClassName === 'string' ? btnClassName : btnClassName?.(open)}`
}
className=
{
`group ${s.popupBtn} ${open ? '' : 'bg-gray-100'} ${
!btnClassName
? ''
: typeof btnClassName === 'string'
? btnClassName
: btnClassName?.(open)
}`
}
>
{
btnElement
}
</
Popover
.
Button
>
<
Transition
as=
{
Fragment
}
enter=
"transition ease-out duration-200"
enterFrom=
"opacity-0 translate-y-1"
enterTo=
"opacity-100 translate-y-0"
leave=
"transition ease-in duration-150"
leaveFrom=
"opacity-100 translate-y-0"
leaveTo=
"opacity-0 translate-y-1"
>
<
Transition
as=
{
Fragment
}
>
<
Popover
.
Panel
className=
{
`${s.popupPanel} ${position === 'br' ? 'right-0' : 'transform -translate-x-1/2 left-1/2'} ${className}`
}
className=
{
`${s.popupPanel} ${
position === 'br'
? 'right-0'
: 'transform -translate-x-1/2 left-1/2'
} ${className}`
}
{
...
(
trigger
!==
'
hover
'
?
{}
:
{
onMouseLeave
:
()
=
>
onMouseLeave(open),
onMouseEnter: () =
>
onMouseEnter(open),
})
}
>
<
div
className=
{
s
.
panelContainer
}
{
...
(
trigger
!==
'
hover
'
?
{}
:
{
onMouseLeave
:
()
=
>
onMouseLeave(open),
onMouseEnter: () =
>
onMouseEnter(open),
})
}
>
{
cloneElement
(
htmlContent
as
React
.
ReactElement
,
{
onClose
:
()
=>
onMouseLeave
(
open
),
})}
</
div
>
>
{
({
close
})
=>
(
<
div
className=
{
s
.
panelContainer
}
{
...
(
trigger
!==
'
hover
'
?
{}
:
{
onMouseLeave
:
()
=
>
onMouseLeave(open),
onMouseEnter: () =
>
onMouseEnter(open),
})}
>
{
cloneElement
(
htmlContent
as
React
.
ReactElement
,
{
onClose
:
()
=>
close
(),
})
}
</
div
>
)
}
</
Popover
.
Panel
>
</
Transition
>
</
div
>
...
...
web/app/components/datasets/documents/list.tsx
View file @
ff61570b
...
...
@@ -138,10 +138,74 @@ export const OperationAction: FC<{
onUpdate
(
operationName
)
}
return
<
div
className=
'flex items-center'
onClick=
{
e
=>
e
.
stopPropagation
()
}
>
const
Operations
=
(
props
:
any
)
=>
<
div
className=
'w-full py-1'
>
{
!
isListScene
&&
<>
<
div
className=
'flex justify-between items-center mx-4 pt-2'
>
<
span
className=
{
cn
(
s
.
actionName
,
'font-medium'
)
}
>
{
!
archived
&&
enabled
?
t
(
'datasetDocuments.list.index.enable'
)
:
t
(
'datasetDocuments.list.index.disable'
)
}
</
span
>
<
Tooltip
selector=
{
`detail-switch-${id}`
}
content=
{
t
(
'datasetDocuments.list.action.enableWarning'
)
as
string
}
className=
'!font-semibold'
disabled=
{
!
archived
}
>
<
div
>
<
Switch
defaultValue=
{
archived
?
false
:
enabled
}
onChange=
{
v
=>
!
archived
&&
onOperate
(
v
?
'enable'
:
'disable'
)
}
disabled=
{
archived
}
size=
'md'
/>
</
div
>
</
Tooltip
>
</
div
>
<
div
className=
'mx-4 pb-1 pt-0.5 text-xs text-gray-500'
>
{
!
archived
&&
enabled
?
t
(
'datasetDocuments.list.index.enableTip'
)
:
t
(
'datasetDocuments.list.index.disableTip'
)
}
</
div
>
<
Divider
/>
</>
}
{
!
archived
&&
(
<>
<
div
className=
{
s
.
actionItem
}
onClick=
{
()
=>
router
.
push
(
`/datasets/${datasetId}/documents/${detail.id}/settings`
)
}
>
<
SettingsIcon
/>
<
span
className=
{
s
.
actionName
}
>
{
t
(
'datasetDocuments.list.action.settings'
)
}
</
span
>
</
div
>
{
!
isListScene
&&
(
<
div
className=
{
s
.
actionItem
}
onClick=
{
showNewSegmentModal
}
>
<
FilePlus02
className=
'w-4 h-4 text-gray-500'
/>
<
span
className=
{
s
.
actionName
}
>
{
t
(
'datasetDocuments.list.action.add'
)
}
</
span
>
</
div
>
)
}
{
data_source_type
===
'notion_import'
&&
(
<
div
className=
{
s
.
actionItem
}
onClick=
{
()
=>
onOperate
(
'sync'
)
}
>
<
SyncIcon
/>
<
span
className=
{
s
.
actionName
}
>
{
t
(
'datasetDocuments.list.action.sync'
)
}
</
span
>
</
div
>
)
}
<
Divider
className=
'my-1'
/>
</>
)
}
{
!
archived
&&
<
div
className=
{
s
.
actionItem
}
onClick=
{
()
=>
onOperate
(
'archive'
)
}
>
<
ArchiveIcon
/>
<
span
className=
{
s
.
actionName
}
>
{
t
(
'datasetDocuments.list.action.archive'
)
}
</
span
>
</
div
>
}
<
div
className=
{
cn
(
s
.
actionItem
,
s
.
deleteActionItem
,
'group'
)
}
onClick=
{
()
=>
{
setShowModal
(
true
)
props
?.
onClose
()
}
}
>
<
TrashIcon
className=
{
'w-4 h-4 stroke-current text-gray-500 stroke-2 group-hover:text-red-500'
}
/>
<
span
className=
{
cn
(
s
.
actionName
,
'group-hover:text-red-500'
)
}
>
{
t
(
'datasetDocuments.list.action.delete'
)
}
</
span
>
</
div
>
</
div
>
return
<
div
className=
'flex items-center'
onClick=
{
e
=>
e
.
stopPropagation
()
}
>
{
isListScene
&&
<>
{
archived
?
<
Tooltip
selector=
{
`list-switch-${id}`
}
content=
{
t
(
'datasetDocuments.list.action.enableWarning'
)
as
string
}
className=
'!font-semibold'
>
...
...
@@ -154,69 +218,7 @@ export const OperationAction: FC<{
<
Divider
className=
'!ml-4 !mr-2 !h-3'
type=
'vertical'
/>
</>
}
<
Popover
htmlContent=
{
<
div
className=
'w-full py-1'
>
{
!
isListScene
&&
<>
<
div
className=
'flex justify-between items-center mx-4 pt-2'
>
<
span
className=
{
cn
(
s
.
actionName
,
'font-medium'
)
}
>
{
!
archived
&&
enabled
?
t
(
'datasetDocuments.list.index.enable'
)
:
t
(
'datasetDocuments.list.index.disable'
)
}
</
span
>
<
Tooltip
selector=
{
`detail-switch-${id}`
}
content=
{
t
(
'datasetDocuments.list.action.enableWarning'
)
as
string
}
className=
'!font-semibold'
disabled=
{
!
archived
}
>
<
div
>
<
Switch
defaultValue=
{
archived
?
false
:
enabled
}
onChange=
{
v
=>
!
archived
&&
onOperate
(
v
?
'enable'
:
'disable'
)
}
disabled=
{
archived
}
size=
'md'
/>
</
div
>
</
Tooltip
>
</
div
>
<
div
className=
'mx-4 pb-1 pt-0.5 text-xs text-gray-500'
>
{
!
archived
&&
enabled
?
t
(
'datasetDocuments.list.index.enableTip'
)
:
t
(
'datasetDocuments.list.index.disableTip'
)
}
</
div
>
<
Divider
/>
</>
}
{
!
archived
&&
(
<>
<
div
className=
{
s
.
actionItem
}
onClick=
{
()
=>
router
.
push
(
`/datasets/${datasetId}/documents/${detail.id}/settings`
)
}
>
<
SettingsIcon
/>
<
span
className=
{
s
.
actionName
}
>
{
t
(
'datasetDocuments.list.action.settings'
)
}
</
span
>
</
div
>
{
!
isListScene
&&
(
<
div
className=
{
s
.
actionItem
}
onClick=
{
showNewSegmentModal
}
>
<
FilePlus02
className=
'w-4 h-4 text-gray-500'
/>
<
span
className=
{
s
.
actionName
}
>
{
t
(
'datasetDocuments.list.action.add'
)
}
</
span
>
</
div
>
)
}
{
data_source_type
===
'notion_import'
&&
(
<
div
className=
{
s
.
actionItem
}
onClick=
{
()
=>
onOperate
(
'sync'
)
}
>
<
SyncIcon
/>
<
span
className=
{
s
.
actionName
}
>
{
t
(
'datasetDocuments.list.action.sync'
)
}
</
span
>
</
div
>
)
}
<
Divider
className=
'my-1'
/>
</>
)
}
{
!
archived
&&
<
div
className=
{
s
.
actionItem
}
onClick=
{
()
=>
onOperate
(
'archive'
)
}
>
<
ArchiveIcon
/>
<
span
className=
{
s
.
actionName
}
>
{
t
(
'datasetDocuments.list.action.archive'
)
}
</
span
>
</
div
>
}
<
div
className=
{
cn
(
s
.
actionItem
,
s
.
deleteActionItem
,
'group'
)
}
onClick=
{
()
=>
setShowModal
(
true
)
}
>
<
TrashIcon
className=
{
'w-4 h-4 stroke-current text-gray-500 stroke-2 group-hover:text-red-500'
}
/>
<
span
className=
{
cn
(
s
.
actionName
,
'group-hover:text-red-500'
)
}
>
{
t
(
'datasetDocuments.list.action.delete'
)
}
</
span
>
</
div
>
</
div
>
}
htmlContent=
{
<
Operations
/>
}
trigger=
'click'
position=
'br'
btnElement=
{
<
div
className=
{
cn
(
s
.
actionIcon
,
s
.
commonIcon
)
}
/>
}
...
...
web/app/components/datasets/documents/style.module.css
View file @
ff61570b
...
...
@@ -58,34 +58,34 @@
}
.actionIcon
{
@apply
bg-gray-500;
mask-image
:
url(
.
/assets/action.svg)
;
mask-image
:
url(
~@
/assets/action.svg)
;
}
.pdfIcon
{
background-image
:
url(
.
/assets/pdf.svg)
;
background-image
:
url(
~@
/assets/pdf.svg)
;
}
.jsonIcon
{
background-image
:
url(
.
/assets/json.svg)
;
background-image
:
url(
~@
/assets/json.svg)
;
}
.htmlIcon
{
background-image
:
url(
.
/assets/html.svg)
;
background-image
:
url(
~@
/assets/html.svg)
;
}
.txtIcon
{
background-image
:
url(
.
/assets/txt.svg)
;
background-image
:
url(
~@
/assets/txt.svg)
;
}
.markdownIcon
{
background-image
:
url(
.
/assets/md.svg)
;
background-image
:
url(
~@
/assets/md.svg)
;
}
.mdIcon
{
background-image
:
url(
.
/assets/md.svg)
;
background-image
:
url(
~@
/assets/md.svg)
;
}
.xlsIcon
{
background-image
:
url(
.
/assets/xlsx.svg)
;
background-image
:
url(
~@
/assets/xlsx.svg)
;
}
.xlsxIcon
{
background-image
:
url(
.
/assets/xlsx.svg)
;
background-image
:
url(
~@
/assets/xlsx.svg)
;
}
.csvIcon
{
background-image
:
url(
.
/assets/csv.svg)
;
background-image
:
url(
~@
/assets/csv.svg)
;
}
.statusItemDetail
{
@apply
h-8
font-medium
border
border-gray-200
inline-flex
items-center
rounded-lg
pl-3
pr-4
mr-2;
...
...
web/app/components/explore/item-operation/style.module.css
View file @
ff61570b
...
...
@@ -16,11 +16,11 @@
.actionIcon
{
@apply
bg-gray-500;
mask-image
:
url(~@/a
pp/components/datasets/documents/a
ssets/action.svg)
;
mask-image
:
url(~@/assets/action.svg)
;
}
body
.btn
{
background
:
url(~@/a
pp/components/datasets/documents/a
ssets/action.svg)
center
center
no-repeat
transparent
;
background
:
url(~@/assets/action.svg)
center
center
no-repeat
transparent
;
background-size
:
16px
16px
;
/* mask-image: ; */
}
...
...
@@ -32,4 +32,4 @@ body .btn:hover {
.deleteActionItem
:hover
.deleteActionItemChild
{
@apply
text-red-500;
}
\ No newline at end of file
}
web/app/components/explore/universal-chat/config-view/detail/style.module.css
View file @
ff61570b
.btn
{
background
:
url(~@/a
pp/components/datasets/documents/a
ssets/action.svg)
center
center
no-repeat
transparent
;
background
:
url(~@/assets/action.svg)
center
center
no-repeat
transparent
;
background-size
:
16px
16px
;
/* mask-image: ; */
}
.panelBorder
{
border
:
0.5px
solid
rgba
(
0
,
0
,
0
,
.05
);
}
\ No newline at end of file
}
web/a
pp/components/datasets/documents/a
ssets/action.svg
→
web/assets/action.svg
View file @
ff61570b
File moved
web/a
pp/components/datasets/documents/a
ssets/csv.svg
→
web/assets/csv.svg
View file @
ff61570b
File moved
web/a
pp/components/datasets/documents/a
ssets/html.svg
→
web/assets/html.svg
View file @
ff61570b
File moved
web/a
pp/components/datasets/documents/a
ssets/json.svg
→
web/assets/json.svg
View file @
ff61570b
File moved
web/a
pp/components/datasets/documents/a
ssets/md.svg
→
web/assets/md.svg
View file @
ff61570b
File moved
web/a
pp/components/datasets/documents/a
ssets/pdf.svg
→
web/assets/pdf.svg
View file @
ff61570b
File moved
web/a
pp/components/datasets/documents/a
ssets/txt.svg
→
web/assets/txt.svg
View file @
ff61570b
File moved
web/a
pp/components/datasets/documents/a
ssets/xlsx.svg
→
web/assets/xlsx.svg
View file @
ff61570b
File moved
web/i18n/lang/app.en.ts
View file @
ff61570b
...
...
@@ -15,6 +15,7 @@ const translation = {
communityIntro
:
'Discuss with team members, contributors and developers on different channels.'
,
roadmap
:
'See our roadmap'
,
appNamePlaceholder
:
'Please enter the name of the app'
,
newApp
:
{
startToCreate
:
'Let
\'
s start with your new app'
,
captionName
:
'Give your app a name'
,
...
...
@@ -36,6 +37,9 @@ const translation = {
appCreated
:
'App created'
,
appCreateFailed
:
'Failed to create app'
,
},
editApp
:
{
startToEdit
:
'Edit App'
,
},
emoji
:
{
ok
:
'OK'
,
cancel
:
'Cancel'
,
...
...
web/i18n/lang/app.zh.ts
View file @
ff61570b
...
...
@@ -14,6 +14,7 @@ const translation = {
join
:
'参与社区'
,
communityIntro
:
'与团队成员、贡献者和开发者在不同频道中交流'
,
roadmap
:
'产品路线图'
,
appNamePlaceholder
:
'请输入应用名称'
,
newApp
:
{
startToCreate
:
'开始创建一个新应用'
,
captionName
:
'给应用起个名字'
,
...
...
@@ -35,6 +36,9 @@ const translation = {
appCreated
:
'应用已创建'
,
appCreateFailed
:
'应用创建失败'
,
},
editApp
:
{
startToEdit
:
'编辑应用'
,
},
emoji
:
{
ok
:
'确认'
,
cancel
:
'取消'
,
...
...
web/i18n/lang/common.en.ts
View file @
ff61570b
...
...
@@ -23,6 +23,8 @@ const translation = {
lineBreak
:
'Line break'
,
sure
:
'I
\'
m sure'
,
download
:
'Download'
,
delete
:
'Delete'
,
settings
:
'Settings'
,
},
placeholder
:
{
input
:
'Please enter'
,
...
...
@@ -32,6 +34,7 @@ const translation = {
char
:
'chars'
,
},
actionMsg
:
{
noModification
:
'No modifications at the moment.'
,
modifiedSuccessfully
:
'Modified successfully'
,
modificationFailed
:
'Modification failed'
,
copySuccessfully
:
'Copied successfully'
,
...
...
web/i18n/lang/common.zh.ts
View file @
ff61570b
...
...
@@ -23,6 +23,8 @@ const translation = {
lineBreak
:
'换行'
,
sure
:
'我确定'
,
download
:
'下载'
,
delete
:
'删除'
,
settings
:
'设置'
,
},
placeholder
:
{
input
:
'请输入'
,
...
...
@@ -32,6 +34,7 @@ const translation = {
char
:
'个字符'
,
},
actionMsg
:
{
noModification
:
'暂无修改'
,
modifiedSuccessfully
:
'修改成功'
,
modificationFailed
:
'修改失败'
,
copySuccessfully
:
'复制成功'
,
...
...
web/models/app.ts
View file @
ff61570b
...
...
@@ -77,6 +77,8 @@ export type CreateAppResponse = App
export
type
UpdateAppNameResponse
=
App
export
type
UpdateAppIconResponse
=
App
export
type
UpdateAppSiteCodeResponse
=
{
app_id
:
string
}
&
SiteConfig
export
type
AppDailyConversationsResponse
=
{
...
...
web/models/datasets.ts
View file @
ff61570b
...
...
@@ -136,6 +136,7 @@ export type DataSourceInfo = {
created_by
:
string
extension
:
string
}
notion_page_icon
?:
string
}
export
type
InitialDocumentDetail
=
{
...
...
web/service/apps.ts
View file @
ff61570b
import
type
{
Fetcher
}
from
'swr'
import
{
del
,
get
,
post
}
from
'./base'
import
type
{
ApikeysListResponse
,
AppDailyConversationsResponse
,
AppDailyEndUsersResponse
,
AppDetailResponse
,
AppListResponse
,
AppStatisticsResponse
,
AppTemplatesResponse
,
AppTokenCostsResponse
,
CreateApiKeyResponse
,
GenerationIntroductionResponse
,
UpdateAppModelConfigResponse
,
UpdateAppNameResponse
,
UpdateAppSiteCodeResponse
,
UpdateOpenAIKeyResponse
,
ValidateOpenAIKeyResponse
}
from
'@/models/app'
import
type
{
ApikeysListResponse
,
AppDailyConversationsResponse
,
AppDailyEndUsersResponse
,
AppDetailResponse
,
AppListResponse
,
AppStatisticsResponse
,
AppTemplatesResponse
,
AppTokenCostsResponse
,
CreateApiKeyResponse
,
GenerationIntroductionResponse
,
UpdateApp
IconResponse
,
UpdateApp
ModelConfigResponse
,
UpdateAppNameResponse
,
UpdateAppSiteCodeResponse
,
UpdateOpenAIKeyResponse
,
ValidateOpenAIKeyResponse
}
from
'@/models/app'
import
type
{
CommonResponse
}
from
'@/models/common'
import
type
{
AppMode
,
ModelConfig
}
from
'@/types/app'
...
...
@@ -24,8 +24,13 @@ export const deleteApp: Fetcher<CommonResponse, string> = (appID) => {
return
del
(
`apps/
${
appID
}
`
)
as
Promise
<
CommonResponse
>
}
// path: /apps/{appId}/icon
export
const
updateAppIcon
:
Fetcher
<
UpdateAppIconResponse
,
{
url
:
string
;
body
:
{
icon
:
string
;
icon_background
:
string
}
}
>
=
({
url
,
body
})
=>
{
return
post
(
url
,
{
body
})
as
Promise
<
UpdateAppIconResponse
>
}
// path: /apps/{appId}/name
export
const
updateAppName
:
Fetcher
<
UpdateAppNameResponse
,
{
url
:
string
;
body
:
Record
<
string
,
any
>
}
>
=
({
url
,
body
})
=>
{
export
const
updateAppName
:
Fetcher
<
UpdateAppNameResponse
,
{
url
:
string
;
body
:
{
name
:
string
}
}
>
=
({
url
,
body
})
=>
{
return
post
(
url
,
{
body
})
as
Promise
<
UpdateAppNameResponse
>
}
...
...
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