Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

tools.tsx 6.5KB

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