mirror of
http://112.124.100.131/huang.ze/ebiz-dify-ai.git
synced 2025-12-24 10:13:01 +08:00
feat: support assistant frontend (#2139)
Co-authored-by: StyleZhang <jasonapring2015@outlook.com>
This commit is contained in:
232
web/app/components/tools/index.tsx
Normal file
232
web/app/components/tools/index.tsx
Normal file
@@ -0,0 +1,232 @@
|
||||
'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'>{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)
|
||||
Reference in New Issue
Block a user