Spaces:
Build error
Build error
'use client' | |
import React, { useCallback, useEffect, useMemo, useState } from 'react' | |
import { useTranslation } from 'react-i18next' | |
import { useRouter } from 'next/navigation' | |
import cn from '@/utils/classnames' | |
import Button from '@/app/components/base/button' | |
import { ArrowUpRight } from '@/app/components/base/icons/src/vender/line/arrows' | |
import { Tools } from '@/app/components/base/icons/src/vender/line/others' | |
import Indicator from '@/app/components/header/indicator' | |
import WorkflowToolModal from '@/app/components/tools/workflow-tool' | |
import Loading from '@/app/components/base/loading' | |
import Toast from '@/app/components/base/toast' | |
import { createWorkflowToolProvider, fetchWorkflowToolDetailByAppID, saveWorkflowToolProvider } from '@/service/tools' | |
import type { Emoji, WorkflowToolProviderParameter, WorkflowToolProviderRequest, WorkflowToolProviderResponse } from '@/app/components/tools/types' | |
import type { InputVar } from '@/app/components/workflow/types' | |
import { useAppContext } from '@/context/app-context' | |
type Props = { | |
disabled: boolean | |
published: boolean | |
detailNeedUpdate: boolean | |
workflowAppId: string | |
icon: Emoji | |
name: string | |
description: string | |
inputs?: InputVar[] | |
handlePublish: () => void | |
onRefreshData?: () => void | |
} | |
const WorkflowToolConfigureButton = ({ | |
disabled, | |
published, | |
detailNeedUpdate, | |
workflowAppId, | |
icon, | |
name, | |
description, | |
inputs, | |
handlePublish, | |
onRefreshData, | |
}: Props) => { | |
const { t } = useTranslation() | |
const router = useRouter() | |
const [showModal, setShowModal] = useState(false) | |
const [isLoading, setIsLoading] = useState(false) | |
const [detail, setDetail] = useState<WorkflowToolProviderResponse>() | |
const { isCurrentWorkspaceManager } = useAppContext() | |
const outdated = useMemo(() => { | |
if (!detail) | |
return false | |
if (detail.tool.parameters.length !== inputs?.length) { | |
return true | |
} | |
else { | |
for (const item of inputs || []) { | |
const param = detail.tool.parameters.find(toolParam => toolParam.name === item.variable) | |
if (!param) { | |
return true | |
} | |
else if (param.required !== item.required) { | |
return true | |
} | |
else { | |
if (item.type === 'paragraph' && param.type !== 'string') | |
return true | |
if (item.type === 'text-input' && param.type !== 'string') | |
return true | |
} | |
} | |
} | |
return false | |
}, [detail, inputs]) | |
const payload = useMemo(() => { | |
let parameters: WorkflowToolProviderParameter[] = [] | |
if (!published) { | |
parameters = (inputs || []).map((item) => { | |
return { | |
name: item.variable, | |
description: '', | |
form: 'llm', | |
required: item.required, | |
type: item.type, | |
} | |
}) | |
} | |
else if (detail && detail.tool) { | |
parameters = (inputs || []).map((item) => { | |
return { | |
name: item.variable, | |
required: item.required, | |
type: item.type === 'paragraph' ? 'string' : item.type, | |
description: detail.tool.parameters.find(param => param.name === item.variable)?.llm_description || '', | |
form: detail.tool.parameters.find(param => param.name === item.variable)?.form || 'llm', | |
} | |
}) | |
} | |
return { | |
icon: detail?.icon || icon, | |
label: detail?.label || name, | |
name: detail?.name || '', | |
description: detail?.description || description, | |
parameters, | |
labels: detail?.tool?.labels || [], | |
privacy_policy: detail?.privacy_policy || '', | |
...(published | |
? { | |
workflow_tool_id: detail?.workflow_tool_id, | |
} | |
: { | |
workflow_app_id: workflowAppId, | |
}), | |
} | |
}, [detail, published, workflowAppId, icon, name, description, inputs]) | |
const getDetail = useCallback(async (workflowAppId: string) => { | |
setIsLoading(true) | |
const res = await fetchWorkflowToolDetailByAppID(workflowAppId) | |
setDetail(res) | |
setIsLoading(false) | |
}, []) | |
useEffect(() => { | |
if (published) | |
getDetail(workflowAppId) | |
}, [getDetail, published, workflowAppId]) | |
useEffect(() => { | |
if (detailNeedUpdate) | |
getDetail(workflowAppId) | |
}, [detailNeedUpdate, getDetail, workflowAppId]) | |
const createHandle = async (data: WorkflowToolProviderRequest & { workflow_app_id: string }) => { | |
try { | |
await createWorkflowToolProvider(data) | |
onRefreshData?.() | |
getDetail(workflowAppId) | |
Toast.notify({ | |
type: 'success', | |
message: t('common.api.actionSuccess'), | |
}) | |
setShowModal(false) | |
} | |
catch (e) { | |
Toast.notify({ type: 'error', message: (e as Error).message }) | |
} | |
} | |
const updateWorkflowToolProvider = async (data: WorkflowToolProviderRequest & Partial<{ | |
workflow_app_id: string | |
workflow_tool_id: string | |
}>) => { | |
try { | |
await handlePublish() | |
await saveWorkflowToolProvider(data) | |
onRefreshData?.() | |
getDetail(workflowAppId) | |
Toast.notify({ | |
type: 'success', | |
message: t('common.api.actionSuccess'), | |
}) | |
setShowModal(false) | |
} | |
catch (e) { | |
Toast.notify({ type: 'error', message: (e as Error).message }) | |
} | |
} | |
return ( | |
<> | |
<div className='mt-2 pt-2 border-t-[0.5px] border-t-black/5'> | |
{(!published || !isLoading) && ( | |
<div className={cn( | |
'group bg-gray-100 rounded-lg transition-colors', | |
disabled ? 'shadow-xs opacity-30 cursor-not-allowed' : 'cursor-pointer', | |
!published && 'hover:bg-primary-50', | |
)}> | |
{isCurrentWorkspaceManager | |
? ( | |
<div | |
className='flex justify-start items-center gap-2 px-2.5 py-2' | |
onClick={() => !published && setShowModal(true)} | |
> | |
<Tools className={cn('relative w-4 h-4', !published && 'group-hover:text-primary-600')} /> | |
<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> | |
{!published && ( | |
<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> | |
)} | |
</div>) | |
: ( | |
<div | |
className='flex justify-start items-center gap-2 px-2.5 py-2' | |
> | |
<Tools className='w-4 h-4 text-gray-500' /> | |
<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> | |
</div> | |
)} | |
{published && ( | |
<div className='px-2.5 py-2 border-t-[0.5px] border-black/5'> | |
<div className='flex justify-between'> | |
<Button | |
size='small' | |
className='w-[140px]' | |
onClick={() => setShowModal(true)} | |
disabled={!isCurrentWorkspaceManager} | |
> | |
{t('workflow.common.configure')} | |
{outdated && <Indicator className='ml-1' color={'yellow'} />} | |
</Button> | |
<Button | |
size='small' | |
className='w-[140px]' | |
onClick={() => router.push('/tools?category=workflow')} | |
> | |
{t('workflow.common.manageInTools')} | |
<ArrowUpRight className='ml-1' /> | |
</Button> | |
</div> | |
{outdated && <div className='mt-1 text-xs leading-[18px] text-[#dc6803]'>{t('workflow.common.workflowAsToolTip')}</div>} | |
</div> | |
)} | |
</div> | |
)} | |
{published && isLoading && <div className='pt-2'><Loading type='app' /></div>} | |
</div> | |
{showModal && ( | |
<WorkflowToolModal | |
isAdd={!published} | |
payload={payload} | |
onHide={() => setShowModal(false)} | |
onCreate={createHandle} | |
onSave={updateWorkflowToolProvider} | |
/> | |
)} | |
</> | |
) | |
} | |
export default WorkflowToolConfigureButton | |