index.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useEffect, useState } from 'react'
  4. import { useTranslation } from 'react-i18next'
  5. import cn from 'classnames'
  6. import { CollectionType, LOC } from '../types'
  7. import type { Collection, CustomCollectionBackend, Tool } from '../types'
  8. import Loading from '../../base/loading'
  9. import { ArrowNarrowRight } from '../../base/icons/src/vender/line/arrows'
  10. import Toast from '../../base/toast'
  11. import Header from './header'
  12. import Item from './item'
  13. import AppIcon from '@/app/components/base/app-icon'
  14. import ConfigCredential from '@/app/components/tools/setting/build-in/config-credentials'
  15. import { fetchCustomCollection, removeBuiltInToolCredential, removeCustomCollection, updateBuiltInToolCredential, updateCustomCollection } from '@/service/tools'
  16. import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal'
  17. import type { AgentTool } from '@/types/app'
  18. import { MAX_TOOLS_NUM } from '@/config'
  19. type Props = {
  20. collection: Collection | null
  21. list: Tool[]
  22. // onToolListChange: () => void // custom tools change
  23. loc: LOC
  24. addedTools?: AgentTool[]
  25. onAddTool?: (collection: Collection, payload: Tool) => void
  26. onRefreshData: () => void
  27. onCollectionRemoved: () => void
  28. isLoading: boolean
  29. }
  30. const ToolList: FC<Props> = ({
  31. collection,
  32. list,
  33. loc,
  34. addedTools,
  35. onAddTool,
  36. onRefreshData,
  37. onCollectionRemoved,
  38. isLoading,
  39. }) => {
  40. const { t } = useTranslation()
  41. const isInToolsPage = loc === LOC.tools
  42. const isBuiltIn = collection?.type === CollectionType.builtIn
  43. const needAuth = collection?.allow_delete
  44. const [showSettingAuth, setShowSettingAuth] = useState(false)
  45. const [customCollection, setCustomCollection] = useState<CustomCollectionBackend | null>(null)
  46. useEffect(() => {
  47. if (!collection)
  48. return
  49. (async () => {
  50. if (collection.type === CollectionType.custom) {
  51. const res = await fetchCustomCollection(collection.name) as any
  52. setCustomCollection({
  53. ...res,
  54. provider: collection.name,
  55. } as CustomCollectionBackend)
  56. }
  57. })()
  58. }, [collection])
  59. const [isShowEditCollectionToolModal, setIsShowEditCustomCollectionModal] = useState(false)
  60. const doUpdateCustomToolCollection = async (data: CustomCollectionBackend) => {
  61. await updateCustomCollection(data)
  62. onRefreshData()
  63. Toast.notify({
  64. type: 'success',
  65. message: t('common.api.actionSuccess'),
  66. })
  67. setIsShowEditCustomCollectionModal(false)
  68. }
  69. const doRemoveCustomToolCollection = async () => {
  70. await removeCustomCollection(collection?.name as string)
  71. onCollectionRemoved()
  72. Toast.notify({
  73. type: 'success',
  74. message: t('common.api.actionSuccess'),
  75. })
  76. setIsShowEditCustomCollectionModal(false)
  77. }
  78. if (!collection || isLoading)
  79. return <Loading type='app' />
  80. const icon = <>{typeof collection.icon === 'string'
  81. ? (
  82. <div
  83. className='p-2 bg-cover bg-center border border-gray-100 rounded-lg'
  84. >
  85. <div className='w-6 h-6 bg-center bg-contain rounded-md'
  86. style={{
  87. backgroundImage: `url(${collection.icon})`,
  88. }}
  89. ></div>
  90. </div>
  91. )
  92. : (
  93. <AppIcon
  94. size='large'
  95. icon={collection.icon.content}
  96. background={collection.icon.background}
  97. />
  98. )}
  99. </>
  100. return (
  101. <div className='flex flex-col h-full pb-4'>
  102. <Header
  103. icon={icon}
  104. collection={collection}
  105. loc={loc}
  106. onShowAuth={() => setShowSettingAuth(true)}
  107. onShowEditCustomCollection={() => setIsShowEditCustomCollectionModal(true)}
  108. />
  109. <div className={cn(isInToolsPage ? 'px-6 pt-4' : 'px-4 pt-3')}>
  110. <div className='flex items-center h-[4.5] space-x-2 text-xs font-medium text-gray-500'>
  111. <div className=''>{t('tools.includeToolNum', {
  112. num: list.length,
  113. })}</div>
  114. {needAuth && isBuiltIn && !collection.is_team_authorization && (
  115. <>
  116. <div>·</div>
  117. <div
  118. className='flex items-center text-[#155EEF] cursor-pointer'
  119. onClick={() => setShowSettingAuth(true)}
  120. >
  121. <div>{t('tools.auth.setup')}</div>
  122. <ArrowNarrowRight className='ml-0.5 w-3 h-3' />
  123. </div>
  124. </>
  125. )}
  126. </div>
  127. </div>
  128. <div className={cn(isInToolsPage ? 'px-6' : 'px-4', 'grow h-0 pt-2 overflow-y-auto')}>
  129. {/* list */}
  130. <div className={cn(isInToolsPage ? 'grid-cols-3 gap-4' : 'grid-cols-1 gap-2', 'grid')}>
  131. {list.map(item => (
  132. <Item
  133. key={item.name}
  134. icon={icon}
  135. payload={item}
  136. collection={collection}
  137. isInToolsPage={isInToolsPage}
  138. isToolNumMax={(addedTools?.length || 0) >= MAX_TOOLS_NUM}
  139. added={!!addedTools?.find(v => v.provider_id === collection.id && v.tool_name === item.name)}
  140. onAdd={!isInToolsPage ? tool => onAddTool?.(collection as Collection, tool) : undefined}
  141. />
  142. ))}
  143. </div>
  144. </div>
  145. {showSettingAuth && (
  146. <ConfigCredential
  147. collection={collection}
  148. onCancel={() => setShowSettingAuth(false)}
  149. onSaved={async (value) => {
  150. await updateBuiltInToolCredential(collection.name, value)
  151. Toast.notify({
  152. type: 'success',
  153. message: t('common.api.actionSuccess'),
  154. })
  155. await onRefreshData()
  156. setShowSettingAuth(false)
  157. }}
  158. onRemove={async () => {
  159. await removeBuiltInToolCredential(collection.name)
  160. Toast.notify({
  161. type: 'success',
  162. message: t('common.api.actionSuccess'),
  163. })
  164. await onRefreshData()
  165. setShowSettingAuth(false)
  166. }}
  167. />
  168. )}
  169. {isShowEditCollectionToolModal && (
  170. <EditCustomToolModal
  171. payload={customCollection}
  172. onHide={() => setIsShowEditCustomCollectionModal(false)}
  173. onEdit={doUpdateCustomToolCollection}
  174. onRemove={doRemoveCustomToolCollection}
  175. />
  176. )}
  177. </div>
  178. )
  179. }
  180. export default React.memo(ToolList)