123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- '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 NoCustomToolPlaceholder from './no-custom-tool-placeholder'
- 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}
- />
- )}
- {collectionType === CollectionType.custom && hasNoCustomCollection && (
- <NoCustomToolPlaceholder />
- )}
- </div>
- </div>
- </div>
- {isShowEditCollectionToolModal && (
- <EditCustomToolModal
- payload={null}
- onHide={() => setIsShowEditCollectionToolModal(false)}
- onAdd={doCreateCustomToolCollection}
- />
- )}
- </>
- )
- }
- export default React.memo(Tools)
|