configure-button.tsx 7.7 KB

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