configure-button.tsx 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. 'use client'
  2. import React, { useCallback, useEffect, useMemo, useState } from 'react'
  3. import { useTranslation } from 'react-i18next'
  4. import { useRouter } from 'next/navigation'
  5. import cn from '@/utils/classnames'
  6. import Button from '@/app/components/base/button'
  7. import { ArrowUpRight } from '@/app/components/base/icons/src/vender/line/arrows'
  8. import { Tools } from '@/app/components/base/icons/src/vender/line/others'
  9. import Indicator from '@/app/components/header/indicator'
  10. import WorkflowToolModal from '@/app/components/tools/workflow-tool'
  11. import Loading from '@/app/components/base/loading'
  12. import Toast from '@/app/components/base/toast'
  13. import { createWorkflowToolProvider, fetchWorkflowToolDetailByAppID, saveWorkflowToolProvider } from '@/service/tools'
  14. import type { Emoji, WorkflowToolProviderParameter, WorkflowToolProviderRequest, WorkflowToolProviderResponse } from '@/app/components/tools/types'
  15. import type { InputVar } from '@/app/components/workflow/types'
  16. import { useAppContext } from '@/context/app-context'
  17. type Props = {
  18. disabled: boolean
  19. published: boolean
  20. detailNeedUpdate: boolean
  21. workflowAppId: string
  22. icon: Emoji
  23. name: string
  24. description: string
  25. inputs?: InputVar[]
  26. handlePublish: () => void
  27. onRefreshData?: () => void
  28. }
  29. const WorkflowToolConfigureButton = ({
  30. disabled,
  31. published,
  32. detailNeedUpdate,
  33. workflowAppId,
  34. icon,
  35. name,
  36. description,
  37. inputs,
  38. handlePublish,
  39. onRefreshData,
  40. }: Props) => {
  41. const { t } = useTranslation()
  42. const router = useRouter()
  43. const [showModal, setShowModal] = useState(false)
  44. const [isLoading, setIsLoading] = useState(false)
  45. const [detail, setDetail] = useState<WorkflowToolProviderResponse>()
  46. const { isCurrentWorkspaceManager } = useAppContext()
  47. const outdated = useMemo(() => {
  48. if (!detail)
  49. return false
  50. if (detail.tool.parameters.length !== inputs?.length) {
  51. return true
  52. }
  53. else {
  54. for (const item of inputs || []) {
  55. const param = detail.tool.parameters.find(toolParam => toolParam.name === item.variable)
  56. if (!param) {
  57. return true
  58. }
  59. else if (param.required !== item.required) {
  60. return true
  61. }
  62. else {
  63. if (item.type === 'paragraph' && param.type !== 'string')
  64. return true
  65. if (item.type === 'text-input' && param.type !== 'string')
  66. return true
  67. }
  68. }
  69. }
  70. return false
  71. }, [detail, inputs])
  72. const payload = useMemo(() => {
  73. let parameters: WorkflowToolProviderParameter[] = []
  74. if (!published) {
  75. parameters = (inputs || []).map((item) => {
  76. return {
  77. name: item.variable,
  78. description: '',
  79. form: 'llm',
  80. required: item.required,
  81. type: item.type,
  82. }
  83. })
  84. }
  85. else if (detail && detail.tool) {
  86. parameters = (inputs || []).map((item) => {
  87. return {
  88. name: item.variable,
  89. required: item.required,
  90. type: item.type === 'paragraph' ? 'string' : item.type,
  91. description: detail.tool.parameters.find(param => param.name === item.variable)?.llm_description || '',
  92. form: detail.tool.parameters.find(param => param.name === item.variable)?.form || 'llm',
  93. }
  94. })
  95. }
  96. return {
  97. icon: detail?.icon || icon,
  98. label: detail?.label || name,
  99. name: detail?.name || '',
  100. description: detail?.description || description,
  101. parameters,
  102. labels: detail?.tool?.labels || [],
  103. privacy_policy: detail?.privacy_policy || '',
  104. ...(published
  105. ? {
  106. workflow_tool_id: detail?.workflow_tool_id,
  107. }
  108. : {
  109. workflow_app_id: workflowAppId,
  110. }),
  111. }
  112. }, [detail, published, workflowAppId, icon, name, description, inputs])
  113. const getDetail = useCallback(async (workflowAppId: string) => {
  114. setIsLoading(true)
  115. const res = await fetchWorkflowToolDetailByAppID(workflowAppId)
  116. setDetail(res)
  117. setIsLoading(false)
  118. }, [])
  119. useEffect(() => {
  120. if (published)
  121. getDetail(workflowAppId)
  122. }, [getDetail, published, workflowAppId])
  123. useEffect(() => {
  124. if (detailNeedUpdate)
  125. getDetail(workflowAppId)
  126. }, [detailNeedUpdate, getDetail, workflowAppId])
  127. const createHandle = async (data: WorkflowToolProviderRequest & { workflow_app_id: string }) => {
  128. try {
  129. await createWorkflowToolProvider(data)
  130. onRefreshData?.()
  131. getDetail(workflowAppId)
  132. Toast.notify({
  133. type: 'success',
  134. message: t('common.api.actionSuccess'),
  135. })
  136. setShowModal(false)
  137. }
  138. catch (e) {
  139. Toast.notify({ type: 'error', message: (e as Error).message })
  140. }
  141. }
  142. const updateWorkflowToolProvider = async (data: WorkflowToolProviderRequest & Partial<{
  143. workflow_app_id: string
  144. workflow_tool_id: string
  145. }>) => {
  146. try {
  147. await handlePublish()
  148. await saveWorkflowToolProvider(data)
  149. onRefreshData?.()
  150. getDetail(workflowAppId)
  151. Toast.notify({
  152. type: 'success',
  153. message: t('common.api.actionSuccess'),
  154. })
  155. setShowModal(false)
  156. }
  157. catch (e) {
  158. Toast.notify({ type: 'error', message: (e as Error).message })
  159. }
  160. }
  161. return (
  162. <>
  163. <div className='mt-2 pt-2 border-t-[0.5px] border-t-black/5'>
  164. {(!published || !isLoading) && (
  165. <div className={cn(
  166. 'group bg-gray-100 rounded-lg transition-colors',
  167. disabled ? 'shadow-xs opacity-30 cursor-not-allowed' : 'cursor-pointer',
  168. !published && 'hover:bg-primary-50',
  169. )}>
  170. {isCurrentWorkspaceManager
  171. ? (
  172. <div
  173. className='flex justify-start items-center gap-2 px-2.5 py-2'
  174. onClick={() => !published && setShowModal(true)}
  175. >
  176. <Tools className={cn('relative w-4 h-4', !published && 'group-hover:text-primary-600')} />
  177. <div title={t('workflow.common.workflowAsTool') || ''} className={cn('grow shrink basis-0 text-[13px] font-medium leading-[18px] truncate', !published && 'group-hover:text-primary-600')}>{t('workflow.common.workflowAsTool')}</div>
  178. {!published && (
  179. <span className='shrink-0 px-1 border border-black/8 rounded-[5px] bg-white text-[10px] font-medium leading-[18px] text-gray-500'>{t('workflow.common.configureRequired').toLocaleUpperCase()}</span>
  180. )}
  181. </div>)
  182. : (
  183. <div
  184. className='flex justify-start items-center gap-2 px-2.5 py-2'
  185. >
  186. <Tools className='w-4 h-4 text-gray-500' />
  187. <div title={t('workflow.common.workflowAsTool') || ''} className='grow shrink basis-0 text-[13px] font-medium leading-[18px] truncate text-gray-500'>{t('workflow.common.workflowAsTool')}</div>
  188. </div>
  189. )}
  190. {published && (
  191. <div className='px-2.5 py-2 border-t-[0.5px] border-black/5'>
  192. <div className='flex justify-between'>
  193. <Button
  194. size='small'
  195. className='w-[140px]'
  196. onClick={() => setShowModal(true)}
  197. disabled={!isCurrentWorkspaceManager}
  198. >
  199. {t('workflow.common.configure')}
  200. {outdated && <Indicator className='ml-1' color={'yellow'} />}
  201. </Button>
  202. <Button
  203. size='small'
  204. className='w-[140px]'
  205. onClick={() => router.push('/tools?category=workflow')}
  206. >
  207. {t('workflow.common.manageInTools')}
  208. <ArrowUpRight className='ml-1' />
  209. </Button>
  210. </div>
  211. {outdated && <div className='mt-1 text-xs leading-[18px] text-[#dc6803]'>{t('workflow.common.workflowAsToolTip')}</div>}
  212. </div>
  213. )}
  214. </div>
  215. )}
  216. {published && isLoading && <div className='pt-2'><Loading type='app' /></div>}
  217. </div>
  218. {showModal && (
  219. <WorkflowToolModal
  220. isAdd={!published}
  221. payload={payload}
  222. onHide={() => setShowModal(false)}
  223. onCreate={createHandle}
  224. onSave={updateWorkflowToolProvider}
  225. />
  226. )}
  227. </>
  228. )
  229. }
  230. export default WorkflowToolConfigureButton