| import { |
| memo, |
| useCallback, |
| } from 'react' |
| import { useTranslation } from 'react-i18next' |
| import { |
| RiAddLine, |
| } from '@remixicon/react' |
| import cn from '@/utils/classnames' |
| import { ArrowUpRight } from '@/app/components/base/icons/src/vender/line/arrows' |
| import { Check } from '@/app/components/base/icons/src/vender/line/general' |
| import { Tag01 } from '@/app/components/base/icons/src/vender/line/financeAndECommerce' |
| import type { ToolWithProvider } from '@/app/components/workflow/types' |
| import { BlockEnum } from '@/app/components/workflow/types' |
| import BlockIcon from '@/app/components/workflow/block-icon' |
| import Tooltip from '@/app/components/base/tooltip' |
| import Button from '@/app/components/base/button' |
| import { useGetLanguage } from '@/context/i18n' |
| import { useStore as useLabelStore } from '@/app/components/tools/labels/store' |
| import Empty from '@/app/components/tools/add-tool-modal/empty' |
| import type { Tool } from '@/app/components/tools/types' |
| import { CollectionType } from '@/app/components/tools/types' |
| import type { AgentTool } from '@/types/app' |
| import { MAX_TOOLS_NUM } from '@/config' |
|
|
| type ToolsProps = { |
| showWorkflowEmpty: boolean |
| tools: ToolWithProvider[] |
| addedTools: AgentTool[] |
| onSelect: (provider: ToolWithProvider, tool: Tool) => void |
| onAuthSetup: (provider: ToolWithProvider) => void |
| } |
| const Blocks = ({ |
| showWorkflowEmpty, |
| tools, |
| addedTools, |
| onSelect, |
| onAuthSetup, |
| }: ToolsProps) => { |
| const { t } = useTranslation() |
| const language = useGetLanguage() |
| const labelList = useLabelStore(s => s.labelList) |
| const addable = addedTools.length < MAX_TOOLS_NUM |
|
|
| const renderGroup = useCallback((toolWithProvider: ToolWithProvider) => { |
| const list = toolWithProvider.tools |
| const needAuth = toolWithProvider.allow_delete && !toolWithProvider.is_team_authorization && toolWithProvider.type === CollectionType.builtIn |
|
|
| return ( |
| <div |
| key={toolWithProvider.id} |
| className='group mb-1 last-of-type:mb-0' |
| > |
| <div className='flex items-center justify-between w-full pl-3 pr-1 h-[22px] text-xs font-medium text-gray-500'> |
| {toolWithProvider.label[language]} |
| <a className='hidden cursor-pointer items-center group-hover:flex' href={`/tools?category=${toolWithProvider.type}`} target='_blank'>{t('tools.addToolModal.manageInTools')}<ArrowUpRight className='ml-0.5 w-3 h-3' /></a> |
| </div> |
| {list.map((tool) => { |
| const labelContent = (() => { |
| if (!tool.labels) |
| return '' |
| return tool.labels.map((name) => { |
| const label = labelList.find(item => item.name === name) |
| return label?.label[language] |
| }).filter(Boolean).join(', ') |
| })() |
| const added = !!addedTools?.find(v => v.provider_id === toolWithProvider.id && v.provider_type === toolWithProvider.type && v.tool_name === tool.name) |
| return ( |
| <Tooltip |
| key={tool.name} |
| position='bottom' |
| popupClassName='!p-0 !px-3 !py-2.5 !w-[210px] !leading-[18px] !text-xs !text-gray-700 !border-[0.5px] !border-black/5 !bg-transparent !rounded-xl !shadow-lg translate-x-[108px]' |
| popupContent={( |
| <div> |
| <BlockIcon |
| size='md' |
| className='mb-2' |
| type={BlockEnum.Tool} |
| toolIcon={toolWithProvider.icon} |
| /> |
| <div className='mb-1 text-sm leading-5 text-gray-900'>{tool.label[language]}</div> |
| <div className='text-xs text-gray-700 leading-[18px]'>{tool.description[language]}</div> |
| {tool.labels?.length > 0 && ( |
| <div className='flex items-center shrink-0 mt-1'> |
| <div className='relative w-full flex items-center gap-1 py-1 rounded-md text-gray-500' title={labelContent}> |
| <Tag01 className='shrink-0 w-3 h-3 text-gray-500' /> |
| <div className='grow text-xs text-start leading-[18px] font-normal truncate'>{labelContent}</div> |
| </div> |
| </div> |
| )} |
| </div> |
| )} |
| > |
| <div className='group/item flex items-center w-full pl-3 pr-1 h-8 rounded-lg hover:bg-gray-50 cursor-pointer'> |
| <BlockIcon |
| className={cn('mr-2 shrink-0', needAuth && 'opacity-30')} |
| type={BlockEnum.Tool} |
| toolIcon={toolWithProvider.icon} |
| /> |
| <div className={cn('grow text-sm text-gray-900 truncate', needAuth && 'opacity-30')}>{tool.label[language]}</div> |
| {!needAuth && added && ( |
| <div className='flex items-center gap-1 rounded-[6px] border border-gray-100 px-2 py-[3px] bg-white text-gray-300 text-xs font-medium leading-[18px]'> |
| <Check className='w-3 h-3' /> |
| {t('tools.addToolModal.added').toLocaleUpperCase()} |
| </div> |
| )} |
| {!needAuth && !added && addable && ( |
| <Button |
| variant='secondary-accent' |
| size='small' |
| className={cn('hidden shrink-0 items-center group-hover/item:flex')} |
| onClick={() => onSelect(toolWithProvider, tool)} |
| > |
| <RiAddLine className='w-3 h-3' /> |
| {t('tools.addToolModal.add').toLocaleUpperCase()} |
| </Button> |
| )} |
| {needAuth && ( |
| <Button |
| variant='secondary-accent' |
| size='small' |
| className={cn('hidden shrink-0 group-hover/item:flex')} |
| onClick={() => onAuthSetup(toolWithProvider)} |
| >{t('tools.auth.setup')}</Button> |
| )} |
| </div> |
| </Tooltip> |
| ) |
| })} |
| </div> |
| ) |
| }, [addable, language, t, labelList, addedTools, onAuthSetup, onSelect]) |
|
|
| return ( |
| <div className='p-1 pb-6 max-w-[440px]'> |
| {!tools.length && !showWorkflowEmpty && ( |
| <div className='flex items-center px-3 h-[22px] text-xs font-medium text-gray-500'>{t('workflow.tabs.noResult')}</div> |
| )} |
| {!tools.length && showWorkflowEmpty && ( |
| <div className='pt-[280px]'> |
| <Empty /> |
| </div> |
| )} |
| {!!tools.length && tools.map(renderGroup)} |
| </div> |
| ) |
| } |
|
|
| export default memo(Blocks) |
|
|