|
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) |
|
|