mirror of
https://github.com/langgenius/dify.git
synced 2024-11-16 11:42:29 +08:00
233 lines
7.5 KiB
TypeScript
233 lines
7.5 KiB
TypeScript
'use client'
|
|
import type { FC } from 'react'
|
|
import React, { useEffect, useState } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import cn from 'classnames'
|
|
import Button from '../base/button'
|
|
import { Plus } from '../base/icons/src/vender/line/general'
|
|
import Toast from '../base/toast'
|
|
import type { Collection, CustomCollectionBackend, Tool } from './types'
|
|
import { CollectionType, LOC } from './types'
|
|
import ToolNavList from './tool-nav-list'
|
|
import Search from './search'
|
|
import Contribute from './contribute'
|
|
import ToolList from './tool-list'
|
|
import EditCustomToolModal from './edit-custom-collection-modal'
|
|
import NoCustomTool from './info/no-custom-tool'
|
|
import NoSearchRes from './info/no-search-res'
|
|
import TabSlider from '@/app/components/base/tab-slider'
|
|
import { createCustomCollection, fetchCollectionList as doFetchCollectionList, fetchBuiltInToolList, fetchCustomToolList } from '@/service/tools'
|
|
import type { AgentTool } from '@/types/app'
|
|
|
|
type Props = {
|
|
loc: LOC
|
|
addedTools?: AgentTool[]
|
|
onAddTool?: (collection: Collection, payload: Tool) => void
|
|
selectedProviderId?: string
|
|
}
|
|
|
|
const Tools: FC<Props> = ({
|
|
loc,
|
|
addedTools,
|
|
onAddTool,
|
|
selectedProviderId,
|
|
}) => {
|
|
const { t } = useTranslation()
|
|
const isInToolsPage = loc === LOC.tools
|
|
const isInDebugPage = !isInToolsPage
|
|
|
|
const [collectionList, setCollectionList] = useState<Collection[]>([])
|
|
const [currCollectionIndex, setCurrCollectionIndex] = useState<number | null>(null)
|
|
|
|
const [isDetailLoading, setIsDetailLoading] = useState(false)
|
|
|
|
const fetchCollectionList = async () => {
|
|
const list = await doFetchCollectionList() as Collection[]
|
|
setCollectionList(list)
|
|
if (list.length > 0 && currCollectionIndex === null) {
|
|
let index = 0
|
|
if (selectedProviderId)
|
|
index = list.findIndex(item => item.id === selectedProviderId)
|
|
|
|
setCurrCollectionIndex(index || 0)
|
|
}
|
|
}
|
|
useEffect(() => {
|
|
fetchCollectionList()
|
|
}, [])
|
|
|
|
const collectionTypeOptions = (() => {
|
|
const res = [
|
|
{ value: CollectionType.builtIn, text: t('tools.type.builtIn') },
|
|
{ value: CollectionType.custom, text: t('tools.type.custom') },
|
|
]
|
|
if (!isInToolsPage)
|
|
res.unshift({ value: CollectionType.all, text: t('tools.type.all') })
|
|
return res
|
|
})()
|
|
|
|
const [query, setQuery] = useState('')
|
|
const [collectionType, setCollectionType] = useState<CollectionType>(collectionTypeOptions[0].value)
|
|
|
|
const showCollectionList = (() => {
|
|
let typeFilteredList: Collection[] = []
|
|
if (collectionType === CollectionType.all)
|
|
typeFilteredList = collectionList
|
|
else
|
|
typeFilteredList = collectionList.filter(item => item.type === collectionType)
|
|
if (query)
|
|
return typeFilteredList.filter(item => item.name.includes(query))
|
|
|
|
return typeFilteredList
|
|
})()
|
|
|
|
const hasNoCustomCollection = !collectionList.find(item => item.type === CollectionType.custom)
|
|
|
|
useEffect(() => {
|
|
setCurrCollectionIndex(0)
|
|
}, [collectionType])
|
|
|
|
const currCollection = (() => {
|
|
if (currCollectionIndex === null)
|
|
return null
|
|
return showCollectionList[currCollectionIndex]
|
|
})()
|
|
|
|
const [currTools, setCurrentTools] = useState<Tool[]>([])
|
|
useEffect(() => {
|
|
if (!currCollection)
|
|
return
|
|
|
|
(async () => {
|
|
setIsDetailLoading(true)
|
|
try {
|
|
if (currCollection.type === CollectionType.builtIn) {
|
|
const list = await fetchBuiltInToolList(currCollection.name) as Tool[]
|
|
setCurrentTools(list)
|
|
}
|
|
else {
|
|
const list = await fetchCustomToolList(currCollection.name) as Tool[]
|
|
setCurrentTools(list)
|
|
}
|
|
}
|
|
catch (e) { }
|
|
setIsDetailLoading(false)
|
|
})()
|
|
}, [currCollection?.name])
|
|
|
|
const [isShowEditCollectionToolModal, setIsShowEditCollectionToolModal] = useState(false)
|
|
const handleCreateToolCollection = () => {
|
|
setIsShowEditCollectionToolModal(true)
|
|
}
|
|
|
|
const doCreateCustomToolCollection = async (data: CustomCollectionBackend) => {
|
|
await createCustomCollection(data)
|
|
Toast.notify({
|
|
type: 'success',
|
|
message: t('common.api.actionSuccess'),
|
|
})
|
|
await fetchCollectionList()
|
|
setIsShowEditCollectionToolModal(false)
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<div className='flex h-full'>
|
|
{/* sidebar */}
|
|
<div className={cn(isInToolsPage ? 'sm:w-[216px] px-4' : 'sm:w-[256px] px-3', 'flex flex-col w-16 shrink-0 pb-2')}>
|
|
{isInToolsPage && (
|
|
<Button className='mt-6 flex items-center !h-8 pl-4' type='primary' onClick={handleCreateToolCollection}>
|
|
<Plus className='w-4 h-4 mr-1' />
|
|
<div className='leading-[18px] text-[13px] font-medium truncate'>{t('tools.createCustomTool')}</div>
|
|
</Button>
|
|
)}
|
|
|
|
{isInDebugPage && (
|
|
<div className='mt-6 flex space-x-1 items-center'>
|
|
<Search
|
|
className='grow'
|
|
value={query}
|
|
onChange={setQuery}
|
|
/>
|
|
<Button className='flex items-center justify-center !w-8 !h-8 !p-0' type='primary'>
|
|
<Plus className='w-4 h-4' onClick={handleCreateToolCollection} />
|
|
</Button>
|
|
</div>
|
|
)}
|
|
|
|
<TabSlider
|
|
className='mt-3'
|
|
itemWidth={isInToolsPage ? 89 : 75}
|
|
value={collectionType}
|
|
onChange={v => setCollectionType(v as CollectionType)}
|
|
options={collectionTypeOptions}
|
|
/>
|
|
{isInToolsPage && (
|
|
<Search
|
|
className='mt-5'
|
|
value={query}
|
|
onChange={setQuery}
|
|
/>
|
|
)}
|
|
|
|
{(collectionType === CollectionType.custom && hasNoCustomCollection)
|
|
? (
|
|
<div className='grow h-0 p-2 pt-8'>
|
|
<NoCustomTool onCreateTool={handleCreateToolCollection} />
|
|
</div>
|
|
)
|
|
: (
|
|
(showCollectionList.length > 0 || !query)
|
|
? <ToolNavList
|
|
className='mt-2 grow height-0 overflow-y-auto'
|
|
currentName={currCollection?.name || ''}
|
|
list={showCollectionList}
|
|
onChosen={setCurrCollectionIndex}
|
|
/>
|
|
: (
|
|
<div className='grow h-0 p-2 pt-8'>
|
|
<NoSearchRes
|
|
onReset={() => { setQuery('') }}
|
|
/>
|
|
</div>
|
|
)
|
|
)}
|
|
|
|
{loc === LOC.tools && (
|
|
<Contribute />
|
|
)}
|
|
</div>
|
|
|
|
{/* tools */}
|
|
<div className={cn('grow h-full overflow-hidden p-2')}>
|
|
<div className='h-full bg-white rounded-2xl'>
|
|
{!(collectionType === CollectionType.custom && hasNoCustomCollection) && showCollectionList.length > 0 && (
|
|
<ToolList
|
|
collection={currCollection}
|
|
list={currTools}
|
|
loc={loc}
|
|
addedTools={addedTools}
|
|
onAddTool={onAddTool}
|
|
onRefreshData={fetchCollectionList}
|
|
onCollectionRemoved={() => {
|
|
setCurrCollectionIndex(0)
|
|
fetchCollectionList()
|
|
}}
|
|
isLoading={isDetailLoading}
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{isShowEditCollectionToolModal && (
|
|
<EditCustomToolModal
|
|
payload={null}
|
|
onHide={() => setIsShowEditCollectionToolModal(false)}
|
|
onAdd={doCreateCustomToolCollection}
|
|
/>
|
|
)}
|
|
</>
|
|
)
|
|
}
|
|
export default React.memo(Tools)
|