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
e61c84ca
Unverified
Commit
e61c84ca
authored
Jun 06, 2023
by
zxhlyh
Committed by
GitHub
Jun 06, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: header nav load more app (#296)
parent
d70086b8
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
95 additions
and
61 deletions
+95
-61
_layout-client.tsx
web/app/(commonLayout)/_layout-client.tsx
+11
-5
index.tsx
web/app/components/header/index.tsx
+31
-6
index.tsx
web/app/components/header/nav/index.tsx
+2
-0
index.tsx
web/app/components/header/nav/nav-selector/index.tsx
+51
-50
No files found.
web/app/(commonLayout)/_layout-client.tsx
View file @
e61c84ca
'use client'
'use client'
import
{
FC
,
useRef
}
from
'react'
import
type
{
FC
}
from
'react'
import
React
,
{
useEffect
,
useState
}
from
'react'
import
React
,
{
useEffect
,
use
Ref
,
use
State
}
from
'react'
import
{
usePathname
,
useRouter
,
useSelectedLayoutSegments
}
from
'next/navigation'
import
{
usePathname
,
useRouter
,
useSelectedLayoutSegments
}
from
'next/navigation'
import
useSWR
,
{
SWRConfig
}
from
'swr'
import
useSWR
,
{
SWRConfig
}
from
'swr'
import
Header
from
'../components/header'
import
Header
from
'../components/header'
...
@@ -50,7 +50,7 @@ const CommonLayout: FC<ICommonLayoutProps> = ({ children }) => {
...
@@ -50,7 +50,7 @@ const CommonLayout: FC<ICommonLayoutProps> = ({ children }) => {
if
(
!
appList
||
!
userProfile
||
!
langeniusVersionInfo
)
if
(
!
appList
||
!
userProfile
||
!
langeniusVersionInfo
)
return
<
Loading
type=
'app'
/>
return
<
Loading
type=
'app'
/>
const
curApp
=
appList
?.
data
.
find
(
opt
=>
opt
.
id
===
appId
)
const
curApp
Id
=
segments
[
0
]
===
'app'
&&
segments
[
2
]
const
currentDatasetId
=
segments
[
0
]
===
'datasets'
&&
segments
[
2
]
const
currentDatasetId
=
segments
[
0
]
===
'datasets'
&&
segments
[
2
]
const
currentDataset
=
datasetList
?.
data
?.
find
(
opt
=>
opt
.
id
===
currentDatasetId
)
const
currentDataset
=
datasetList
?.
data
?.
find
(
opt
=>
opt
.
id
===
currentDatasetId
)
...
@@ -70,12 +70,18 @@ const CommonLayout: FC<ICommonLayoutProps> = ({ children }) => {
...
@@ -70,12 +70,18 @@ const CommonLayout: FC<ICommonLayoutProps> = ({ children }) => {
return
(
return
(
<
SWRConfig
value=
{
{
<
SWRConfig
value=
{
{
shouldRetryOnError
:
false
shouldRetryOnError
:
false
,
}
}
>
}
}
>
<
AppContextProvider
value=
{
{
apps
:
appList
.
data
,
mutateApps
,
userProfile
,
mutateUserProfile
,
pageContainerRef
}
}
>
<
AppContextProvider
value=
{
{
apps
:
appList
.
data
,
mutateApps
,
userProfile
,
mutateUserProfile
,
pageContainerRef
}
}
>
<
DatasetsContext
.
Provider
value=
{
{
datasets
:
datasetList
?.
data
||
[],
mutateDatasets
,
currentDataset
}
}
>
<
DatasetsContext
.
Provider
value=
{
{
datasets
:
datasetList
?.
data
||
[],
mutateDatasets
,
currentDataset
}
}
>
<
div
ref=
{
pageContainerRef
}
className=
'relative flex flex-col h-full overflow-auto bg-gray-100'
>
<
div
ref=
{
pageContainerRef
}
className=
'relative flex flex-col h-full overflow-auto bg-gray-100'
>
<
Header
isBordered=
{
[
'/apps'
,
'/datasets'
].
includes
(
pathname
)
}
curApp=
{
curApp
as
any
}
appItems=
{
appList
.
data
}
userProfile=
{
userProfile
}
onLogout=
{
onLogout
}
langeniusVersionInfo=
{
langeniusVersionInfo
}
/>
<
Header
isBordered=
{
[
'/apps'
,
'/datasets'
].
includes
(
pathname
)
}
curAppId=
{
curAppId
||
''
}
userProfile=
{
userProfile
}
onLogout=
{
onLogout
}
langeniusVersionInfo=
{
langeniusVersionInfo
}
/>
{
children
}
{
children
}
</
div
>
</
div
>
</
DatasetsContext
.
Provider
>
</
DatasetsContext
.
Provider
>
...
...
web/app/components/header/index.tsx
View file @
e61c84ca
'use client'
import
{
useCallback
,
useState
}
from
'react'
import
type
{
FC
}
from
'react'
import
type
{
FC
}
from
'react'
import
{
useState
}
from
'react
'
import
useSWRInfinite
from
'swr/infinite
'
import
{
useTranslation
}
from
'react-i18next'
import
{
useTranslation
}
from
'react-i18next'
import
{
flatten
}
from
'lodash-es'
import
{
useRouter
,
useSelectedLayoutSegment
}
from
'next/navigation'
import
{
useRouter
,
useSelectedLayoutSegment
}
from
'next/navigation'
import
classNames
from
'classnames'
import
classNames
from
'classnames'
import
{
CircleStackIcon
,
PuzzlePieceIcon
}
from
'@heroicons/react/24/outline'
import
{
CircleStackIcon
,
PuzzlePieceIcon
}
from
'@heroicons/react/24/outline'
...
@@ -9,11 +12,12 @@ import Link from 'next/link'
...
@@ -9,11 +12,12 @@ import Link from 'next/link'
import
AccountDropdown
from
'./account-dropdown'
import
AccountDropdown
from
'./account-dropdown'
import
Nav
from
'./nav'
import
Nav
from
'./nav'
import
s
from
'./index.module.css'
import
s
from
'./index.module.css'
import
type
{
App
Detail
Response
}
from
'@/models/app'
import
type
{
App
List
Response
}
from
'@/models/app'
import
type
{
LangGeniusVersionResponse
,
UserProfileResponse
}
from
'@/models/common'
import
type
{
LangGeniusVersionResponse
,
UserProfileResponse
}
from
'@/models/common'
import
NewAppDialog
from
'@/app/(commonLayout)/apps/NewAppDialog'
import
NewAppDialog
from
'@/app/(commonLayout)/apps/NewAppDialog'
import
{
WorkspaceProvider
}
from
'@/context/workspace-context'
import
{
WorkspaceProvider
}
from
'@/context/workspace-context'
import
{
useDatasetsContext
}
from
'@/context/datasets-context'
import
{
useDatasetsContext
}
from
'@/context/datasets-context'
import
{
fetchAppList
}
from
'@/service/apps'
const
BuildAppsIcon
=
({
isSelected
}:
{
isSelected
:
boolean
})
=>
(
const
BuildAppsIcon
=
({
isSelected
}:
{
isSelected
:
boolean
})
=>
(
<
svg
className=
'mr-1'
width=
"16"
height=
"16"
viewBox=
"0 0 16 16"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<
svg
className=
'mr-1'
width=
"16"
height=
"16"
viewBox=
"0 0 16 16"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
...
@@ -22,8 +26,7 @@ const BuildAppsIcon = ({ isSelected }: { isSelected: boolean }) => (
...
@@ -22,8 +26,7 @@ const BuildAppsIcon = ({ isSelected }: { isSelected: boolean }) => (
)
)
export
type
IHeaderProps
=
{
export
type
IHeaderProps
=
{
appItems
:
AppDetailResponse
[]
curAppId
?:
string
curApp
:
AppDetailResponse
userProfile
:
UserProfileResponse
userProfile
:
UserProfileResponse
onLogout
:
()
=>
void
onLogout
:
()
=>
void
langeniusVersionInfo
:
LangGeniusVersionResponse
langeniusVersionInfo
:
LangGeniusVersionResponse
...
@@ -38,15 +41,36 @@ const headerEnvClassName: { [k: string]: string } = {
...
@@ -38,15 +41,36 @@ const headerEnvClassName: { [k: string]: string } = {
DEVELOPMENT
:
'bg-[#FEC84B] border-[#FDB022] text-[#93370D]'
,
DEVELOPMENT
:
'bg-[#FEC84B] border-[#FDB022] text-[#93370D]'
,
TESTING
:
'bg-[#A5F0FC] border-[#67E3F9] text-[#164C63]'
,
TESTING
:
'bg-[#A5F0FC] border-[#67E3F9] text-[#164C63]'
,
}
}
const
Header
:
FC
<
IHeaderProps
>
=
({
appItems
,
curApp
,
userProfile
,
onLogout
,
langeniusVersionInfo
,
isBordered
})
=>
{
const
getKey
=
(
pageIndex
:
number
,
previousPageData
:
AppListResponse
)
=>
{
if
(
!
pageIndex
||
previousPageData
.
has_more
)
return
{
url
:
'apps'
,
params
:
{
page
:
pageIndex
+
1
,
limit
:
30
}
}
return
null
}
const
Header
:
FC
<
IHeaderProps
>
=
({
curAppId
,
userProfile
,
onLogout
,
langeniusVersionInfo
,
isBordered
,
})
=>
{
const
{
t
}
=
useTranslation
()
const
{
t
}
=
useTranslation
()
const
[
showNewAppDialog
,
setShowNewAppDialog
]
=
useState
(
false
)
const
[
showNewAppDialog
,
setShowNewAppDialog
]
=
useState
(
false
)
const
{
data
:
appsData
,
isLoading
,
setSize
}
=
useSWRInfinite
(
curAppId
?
getKey
:
()
=>
null
,
fetchAppList
,
{
revalidateFirstPage
:
false
})
const
{
datasets
,
currentDataset
}
=
useDatasetsContext
()
const
{
datasets
,
currentDataset
}
=
useDatasetsContext
()
const
router
=
useRouter
()
const
router
=
useRouter
()
const
showEnvTag
=
langeniusVersionInfo
.
current_env
===
'TESTING'
||
langeniusVersionInfo
.
current_env
===
'DEVELOPMENT'
const
showEnvTag
=
langeniusVersionInfo
.
current_env
===
'TESTING'
||
langeniusVersionInfo
.
current_env
===
'DEVELOPMENT'
const
selectedSegment
=
useSelectedLayoutSegment
()
const
selectedSegment
=
useSelectedLayoutSegment
()
const
isPluginsComingSoon
=
selectedSegment
===
'plugins-coming-soon'
const
isPluginsComingSoon
=
selectedSegment
===
'plugins-coming-soon'
const
isExplore
=
selectedSegment
===
'explore'
const
isExplore
=
selectedSegment
===
'explore'
const
appItems
=
flatten
(
appsData
?.
map
(
appData
=>
appData
.
data
))
const
handleLoadmore
=
useCallback
(()
=>
{
if
(
isLoading
)
return
setSize
(
size
=>
size
+
1
)
},
[
setSize
,
isLoading
])
return
(
return
(
<
div
className=
{
classNames
(
<
div
className=
{
classNames
(
'sticky top-0 left-0 right-0 z-20 flex bg-gray-100 grow-0 shrink-0 basis-auto h-14'
,
'sticky top-0 left-0 right-0 z-20 flex bg-gray-100 grow-0 shrink-0 basis-auto h-14'
,
...
@@ -84,7 +108,7 @@ const Header: FC<IHeaderProps> = ({ appItems, curApp, userProfile, onLogout, lan
...
@@ -84,7 +108,7 @@ const Header: FC<IHeaderProps> = ({ appItems, curApp, userProfile, onLogout, lan
text=
{
t
(
'common.menus.apps'
)
}
text=
{
t
(
'common.menus.apps'
)
}
activeSegment=
{
[
'apps'
,
'app'
]
}
activeSegment=
{
[
'apps'
,
'app'
]
}
link=
'/apps'
link=
'/apps'
curNav=
{
curApp
&&
{
id
:
curApp
.
id
,
name
:
curApp
.
name
,
icon
:
curApp
.
icon
,
icon_background
:
curApp
.
icon_background
}
}
curNav=
{
appItems
.
find
(
appItem
=>
appItem
.
id
===
curAppId
)
}
navs=
{
appItems
.
map
(
item
=>
({
navs=
{
appItems
.
map
(
item
=>
({
id
:
item
.
id
,
id
:
item
.
id
,
name
:
item
.
name
,
name
:
item
.
name
,
...
@@ -94,6 +118,7 @@ const Header: FC<IHeaderProps> = ({ appItems, curApp, userProfile, onLogout, lan
...
@@ -94,6 +118,7 @@ const Header: FC<IHeaderProps> = ({ appItems, curApp, userProfile, onLogout, lan
}))
}
}))
}
createText=
{
t
(
'common.menus.newApp'
)
}
createText=
{
t
(
'common.menus.newApp'
)
}
onCreate=
{
()
=>
setShowNewAppDialog
(
true
)
}
onCreate=
{
()
=>
setShowNewAppDialog
(
true
)
}
onLoadmore=
{
handleLoadmore
}
/>
/>
<
Link
href=
"/plugins-coming-soon"
className=
{
classNames
(
<
Link
href=
"/plugins-coming-soon"
className=
{
classNames
(
navClassName
,
'group'
,
navClassName
,
'group'
,
...
...
web/app/components/header/nav/index.tsx
View file @
e61c84ca
...
@@ -24,6 +24,7 @@ const Nav = ({
...
@@ -24,6 +24,7 @@ const Nav = ({
navs
,
navs
,
createText
,
createText
,
onCreate
,
onCreate
,
onLoadmore
,
}:
INavProps
)
=>
{
}:
INavProps
)
=>
{
const
[
hovered
,
setHovered
]
=
useState
(
false
)
const
[
hovered
,
setHovered
]
=
useState
(
false
)
const
segment
=
useSelectedLayoutSegment
()
const
segment
=
useSelectedLayoutSegment
()
...
@@ -62,6 +63,7 @@ const Nav = ({
...
@@ -62,6 +63,7 @@ const Nav = ({
navs=
{
navs
}
navs=
{
navs
}
createText=
{
createText
}
createText=
{
createText
}
onCreate=
{
onCreate
}
onCreate=
{
onCreate
}
onLoadmore=
{
onLoadmore
}
/>
/>
</>
</>
)
)
...
...
web/app/components/header/nav/nav-selector/index.tsx
View file @
e61c84ca
'use client'
'use client'
import
{
Fragment
}
from
'react'
import
{
useCallback
}
from
'react'
import
{
ChevronDownIcon
,
PlusIcon
}
from
'@heroicons/react/24/solid'
import
{
ChevronDownIcon
,
PlusIcon
}
from
'@heroicons/react/24/solid'
import
{
Menu
,
Transition
}
from
'@headlessui/react'
import
{
Menu
}
from
'@headlessui/react'
import
{
useRouter
}
from
'next/navigation'
import
{
useRouter
}
from
'next/navigation'
import
{
debounce
}
from
'lodash-es'
import
Indicator
from
'../../indicator'
import
Indicator
from
'../../indicator'
import
AppIcon
from
'@/app/components/base/app-icon'
import
AppIcon
from
'@/app/components/base/app-icon'
...
@@ -13,11 +14,12 @@ type NavItem = {
...
@@ -13,11 +14,12 @@ type NavItem = {
icon
:
string
icon
:
string
icon_background
:
string
icon_background
:
string
}
}
export
interface
INavSelectorProps
{
export
type
INavSelectorProps
=
{
navs
:
NavItem
[]
navs
:
NavItem
[]
curNav
?:
Omit
<
NavItem
,
'link'
>
curNav
?:
Omit
<
NavItem
,
'link'
>
createText
:
string
createText
:
string
onCreate
:
()
=>
void
onCreate
:
()
=>
void
onLoadmore
?:
()
=>
void
}
}
const
itemClassName
=
`
const
itemClassName
=
`
...
@@ -25,9 +27,18 @@ const itemClassName = `
...
@@ -25,9 +27,18 @@ const itemClassName = `
rounded-lg font-normal hover:bg-gray-100 cursor-pointer
rounded-lg font-normal hover:bg-gray-100 cursor-pointer
`
`
const
NavSelector
=
({
curNav
,
navs
,
createText
,
onCreate
}:
INavSelectorProps
)
=>
{
const
NavSelector
=
({
curNav
,
navs
,
createText
,
onCreate
,
onLoadmore
}:
INavSelectorProps
)
=>
{
const
router
=
useRouter
()
const
router
=
useRouter
()
const
handleScroll
=
useCallback
(
debounce
((
e
)
=>
{
if
(
typeof
onLoadmore
===
'function'
)
{
const
{
clientHeight
,
scrollHeight
,
scrollTop
}
=
e
.
target
if
(
clientHeight
+
scrollTop
>
scrollHeight
-
50
)
onLoadmore
()
}
},
50
),
[])
return
(
return
(
<
div
className=
""
>
<
div
className=
""
>
<
Menu
as=
"div"
className=
"relative inline-block text-left"
>
<
Menu
as=
"div"
className=
"relative inline-block text-left"
>
...
@@ -46,59 +57,49 @@ const NavSelector = ({ curNav, navs, createText, onCreate }: INavSelectorProps)
...
@@ -46,59 +57,49 @@ const NavSelector = ({ curNav, navs, createText, onCreate }: INavSelectorProps)
/>
/>
</
Menu
.
Button
>
</
Menu
.
Button
>
</
div
>
</
div
>
<
Transition
<
Menu
.
Items
as=
{
Fragment
}
className=
"
enter=
"transition ease-out duration-100"
absolute -left-11 right-0 mt-1.5 w-60 max-w-80
enterFrom=
"transform opacity-0 scale-95"
divide-y divide-gray-100 origin-top-right rounded-lg bg-white
enterTo=
"transform opacity-100 scale-100"
shadow-[0_10px_15px_-3px_rgba(0,0,0,0.1),0_4px_6px_rgba(0,0,0,0.05)]
leave=
"transition ease-in duration-75"
"
leaveFrom=
"transform opacity-100 scale-100"
leaveTo=
"transform opacity-0 scale-95"
>
>
<
Menu
.
Items
<
div
className=
"px-1 py-1 overflow-auto"
style=
{
{
maxHeight
:
'50vh'
}
}
onScroll=
{
handleScroll
}
>
className=
"
{
absolute -left-11 right-0 mt-1.5 w-60 max-w-80
navs
.
map
(
nav
=>
(
divide-y divide-gray-100 origin-top-right rounded-lg bg-white
<
Menu
.
Item
key=
{
nav
.
id
}
>
shadow-[0_10px_15px_-3px_rgba(0,0,0,0.1),0_4px_6px_rgba(0,0,0,0.05)]
<
div
className=
{
itemClassName
}
onClick=
{
()
=>
router
.
push
(
nav
.
link
)
}
>
"
<
div
className=
'relative w-6 h-6 mr-2 bg-[#D5F5F6] rounded-[6px]'
>
>
<
AppIcon
size=
'tiny'
icon=
{
nav
.
icon
}
background=
{
nav
.
icon_background
}
/>
<
div
className=
"px-1 py-1 overflow-auto"
style=
{
{
maxHeight
:
'50vh'
}
}
>
<
div
className=
'flex justify-center items-center absolute -right-0.5 -bottom-0.5 w-2.5 h-2.5 bg-white rounded'
>
{
<
Indicator
/>
navs
.
map
((
nav
)
=>
(
<
Menu
.
Item
key=
{
nav
.
id
}
>
<
div
className=
{
itemClassName
}
onClick=
{
()
=>
router
.
push
(
nav
.
link
)
}
>
<
div
className=
'relative w-6 h-6 mr-2 bg-[#D5F5F6] rounded-[6px]'
>
<
AppIcon
size=
'tiny'
icon=
{
nav
.
icon
}
background=
{
nav
.
icon_background
}
/>
<
div
className=
'flex justify-center items-center absolute -right-0.5 -bottom-0.5 w-2.5 h-2.5 bg-white rounded'
>
<
Indicator
/>
</
div
>
</
div
>
</
div
>
{
nav
.
name
}
</
div
>
</
div
>
</
Menu
.
Item
>
{
nav
.
name
}
))
</
div
>
}
</
Menu
.
Item
>
</
div
>
))
<
Menu
.
Item
>
}
<
div
className=
'p-1'
onClick=
{
onCreate
}
>
</
div
>
<
Menu
.
Item
>
<
div
className=
'p-1'
onClick=
{
onCreate
}
>
<
div
className=
'flex items-center h-12 rounded-lg cursor-pointer hover:bg-gray-100'
>
<
div
<
div
className=
'flex items-center h-12 rounded-lg cursor-pointer hover:bg-gray-100'
className=
'
flex justify-center items-center
ml-4 mr-2 w-6 h-6 bg-gray-100 rounded-[6px]
border-[0.5px] border-gray-200 border-dashed
'
>
>
<
div
<
PlusIcon
className=
'w-4 h-4 text-gray-500'
/>
className=
'
flex justify-center items-center
ml-4 mr-2 w-6 h-6 bg-gray-100 rounded-[6px]
border-[0.5px] border-gray-200 border-dashed
'
>
<
PlusIcon
className=
'w-4 h-4 text-gray-500'
/>
</
div
>
<
div
className=
'font-normal text-[14px] text-gray-700'
>
{
createText
}
</
div
>
</
div
>
</
div
>
<
div
className=
'font-normal text-[14px] text-gray-700'
>
{
createText
}
</
div
>
</
div
>
</
div
>
</
Menu
.
Item
>
</
div
>
</
Menu
.
Item
s
>
</
Menu
.
Item
>
</
Transition
>
</
Menu
.
Items
>
</
Menu
>
</
Menu
>
</
div
>
</
div
>
)
)
...
...
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