tools.tsx 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import {
  2. memo,
  3. useCallback,
  4. } from 'react'
  5. import cn from 'classnames'
  6. import { useTranslation } from 'react-i18next'
  7. import { ArrowUpRight } from '@/app/components/base/icons/src/vender/line/arrows'
  8. import { Check, Plus } from '@/app/components/base/icons/src/vender/line/general'
  9. import { Tag01 } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
  10. import type { ToolWithProvider } from '@/app/components/workflow/types'
  11. import { BlockEnum } from '@/app/components/workflow/types'
  12. import BlockIcon from '@/app/components/workflow/block-icon'
  13. import Tooltip from '@/app/components/base/tooltip'
  14. import Button from '@/app/components/base/button'
  15. import { useGetLanguage } from '@/context/i18n'
  16. import { useStore as useLabelStore } from '@/app/components/tools/labels/store'
  17. import Empty from '@/app/components/tools/add-tool-modal/empty'
  18. import type { Tool } from '@/app/components/tools/types'
  19. import { CollectionType } from '@/app/components/tools/types'
  20. import type { AgentTool } from '@/types/app'
  21. import { MAX_TOOLS_NUM } from '@/config'
  22. type ToolsProps = {
  23. showWorkflowEmpty: boolean
  24. tools: ToolWithProvider[]
  25. addedTools: AgentTool[]
  26. onSelect: (provider: ToolWithProvider, tool: Tool) => void
  27. onAuthSetup: (provider: ToolWithProvider) => void
  28. }
  29. const Blocks = ({
  30. showWorkflowEmpty,
  31. tools,
  32. addedTools,
  33. onSelect,
  34. onAuthSetup,
  35. }: ToolsProps) => {
  36. const { t } = useTranslation()
  37. const language = useGetLanguage()
  38. const labelList = useLabelStore(s => s.labelList)
  39. const addable = addedTools.length < MAX_TOOLS_NUM
  40. const renderGroup = useCallback((toolWithProvider: ToolWithProvider) => {
  41. const list = toolWithProvider.tools
  42. const needAuth = toolWithProvider.allow_delete && !toolWithProvider.is_team_authorization && toolWithProvider.type === CollectionType.builtIn
  43. return (
  44. <div
  45. key={toolWithProvider.id}
  46. className='group mb-1 last-of-type:mb-0'
  47. >
  48. <div className='flex items-center justify-between w-full pl-3 pr-1 h-[22px] text-xs font-medium text-gray-500'>
  49. {toolWithProvider.label[language]}
  50. <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>
  51. </div>
  52. {list.map((tool) => {
  53. const labelContent = (() => {
  54. if (!tool.labels)
  55. return ''
  56. return tool.labels.map((name) => {
  57. const label = labelList.find(item => item.name === name)
  58. return label?.label[language]
  59. }).filter(Boolean).join(', ')
  60. })()
  61. const added = !!addedTools?.find(v => v.provider_id === toolWithProvider.id && v.provider_type === toolWithProvider.type && v.tool_name === tool.name)
  62. return (
  63. <Tooltip
  64. key={tool.name}
  65. selector={`workflow-block-tool-${tool.name}`}
  66. position='bottom'
  67. className='!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]'
  68. htmlContent={(
  69. <div>
  70. <BlockIcon
  71. size='md'
  72. className='mb-2'
  73. type={BlockEnum.Tool}
  74. toolIcon={toolWithProvider.icon}
  75. />
  76. <div className='mb-1 text-sm leading-5 text-gray-900'>{tool.label[language]}</div>
  77. <div className='text-xs text-gray-700 leading-[18px]'>{tool.description[language]}</div>
  78. {tool.labels?.length > 0 && (
  79. <div className='flex items-center shrink-0 mt-1'>
  80. <div className='relative w-full flex items-center gap-1 py-1 rounded-md text-gray-500' title={labelContent}>
  81. <Tag01 className='shrink-0 w-3 h-3 text-gray-500' />
  82. <div className='grow text-xs text-start leading-[18px] font-normal truncate'>{labelContent}</div>
  83. </div>
  84. </div>
  85. )}
  86. </div>
  87. )}
  88. noArrow
  89. >
  90. <div className='group/item flex items-center w-full pl-3 pr-1 h-8 rounded-lg hover:bg-gray-50 cursor-pointer'>
  91. <BlockIcon
  92. className={cn('mr-2 shrink-0', needAuth && 'opacity-30')}
  93. type={BlockEnum.Tool}
  94. toolIcon={toolWithProvider.icon}
  95. />
  96. <div className={cn('grow text-sm text-gray-900 truncate', needAuth && 'opacity-30')}>{tool.label[language]}</div>
  97. {!needAuth && added && (
  98. <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]'>
  99. <Check className='w-3 h-3'/>
  100. {t('tools.addToolModal.added').toLocaleUpperCase()}
  101. </div>
  102. )}
  103. {!needAuth && !added && addable && (
  104. <Button
  105. type='default'
  106. className={cn('hidden shrink-0 items-center !h-6 px-2 py-1 bg-white text-xs font-medium leading-[18px] text-primary-600 group-hover/item:flex')}
  107. onClick={() => onSelect(toolWithProvider, tool)}
  108. >
  109. <Plus className='w-3 h-3'/>
  110. {t('tools.addToolModal.add').toLocaleUpperCase()}
  111. </Button>
  112. )}
  113. {needAuth && (
  114. <Button
  115. type='default'
  116. className={cn('hidden shrink-0 items-center !h-6 px-2 py-1 bg-white text-xs font-medium leading-[18px] text-primary-600 group-hover/item:flex')}
  117. onClick={() => onAuthSetup(toolWithProvider)}
  118. >{t('tools.auth.setup')}</Button>
  119. )}
  120. </div>
  121. </Tooltip>
  122. )
  123. })}
  124. </div>
  125. )
  126. }, [addable, language, t, labelList, addedTools, onAuthSetup, onSelect])
  127. return (
  128. <div className='p-1 pb-6 max-w-[440px]'>
  129. {!tools.length && !showWorkflowEmpty && (
  130. <div className='flex items-center px-3 h-[22px] text-xs font-medium text-gray-500'>{t('workflow.tabs.noResult')}</div>
  131. )}
  132. {!tools.length && showWorkflowEmpty && (
  133. <div className='pt-[280px]'>
  134. <Empty/>
  135. </div>
  136. )}
  137. {!!tools.length && tools.map(renderGroup)}
  138. </div>
  139. )
  140. }
  141. export default memo(Blocks)