feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,661 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-lines */
|
||||
/* eslint-disable @coze-arch/max-line-per-function */
|
||||
|
||||
/* eslint-disable max-lines-per-function, complexity -- PluginItem 的 onApiToggle 后续可以提取优化 */
|
||||
/* eslint-disable import/order */
|
||||
import {
|
||||
type MutableRefObject,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { type WorkflowNodeJSON } from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { useInViewport } from 'ahooks';
|
||||
import groupBy from 'lodash-es/groupBy';
|
||||
|
||||
import {
|
||||
Collapse,
|
||||
Divider,
|
||||
Highlight,
|
||||
Image,
|
||||
Space,
|
||||
Toast,
|
||||
Typography,
|
||||
UIButton,
|
||||
} from '@coze-arch/bot-semi';
|
||||
import { IconViewinchatOutlined } from '@coze-arch/bot-icons';
|
||||
import { ConnectorList, OfficialLabel } from '@coze-community/components';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import {
|
||||
emitEvent,
|
||||
OpenBlockEvent,
|
||||
formatDate,
|
||||
formatNumber,
|
||||
} from '@coze-arch/bot-utils';
|
||||
|
||||
import { EVENT_NAMES, sendTeaEvent } from '@coze-arch/bot-tea';
|
||||
import { PluginDevelopApi } from '@coze-arch/bot-api';
|
||||
import { useBotInfoStore } from '@coze-studio/bot-detail-store/bot-info';
|
||||
|
||||
import { useSpaceStore } from '@coze-arch/bot-studio-store';
|
||||
import { OpenModeType } from '@coze-arch/bot-hooks';
|
||||
import {
|
||||
From,
|
||||
type PluginModalModeProps,
|
||||
} from '../../types/plugin-modal-types';
|
||||
import { getPluginApiKey } from '../../utils';
|
||||
import {
|
||||
type PluginApi,
|
||||
type PluginInfoForPlayground,
|
||||
PluginType,
|
||||
} from '@coze-arch/bot-api/plugin_develop';
|
||||
import { type Int64 } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import s from './index.module.less';
|
||||
import { type SimplifyProductInfo } from '../../service/fetch-plugin';
|
||||
import { PluginItem } from './item';
|
||||
import { extractApiParams } from './helper';
|
||||
import { AvatarName } from '@coze-studio/components';
|
||||
|
||||
import { PluginPerfStatics } from './plugin-perf-statics';
|
||||
import { withSlardarIdButton } from '@coze-studio/bot-utils';
|
||||
import { Tag, Tooltip } from '@coze-arch/coze-design';
|
||||
|
||||
import {
|
||||
IconCozDesktop,
|
||||
IconCozInfoCircle,
|
||||
} from '@coze-arch/coze-design/icons';
|
||||
|
||||
import { isBoolean, isUndefined } from 'lodash-es';
|
||||
import {
|
||||
type CommercialSetting,
|
||||
PluginType as ProductPluginType,
|
||||
} from '@coze-arch/bot-api/product_api';
|
||||
import { PluginAuthMode } from '../../types/auth-mode';
|
||||
export interface PluginPanelProps extends PluginModalModeProps {
|
||||
info: PluginInfoForPlayground & {
|
||||
listed_at?: Int64;
|
||||
version_name?: string;
|
||||
version_ts?: string;
|
||||
};
|
||||
highlightWords?: string[];
|
||||
showButton?: boolean;
|
||||
showCreator?: boolean;
|
||||
showCreateTime?: boolean;
|
||||
showMarketLink?: boolean;
|
||||
showProjectPluginLink?: boolean;
|
||||
showPublishTime?: boolean;
|
||||
className?: string;
|
||||
pluginApiList: PluginApi[];
|
||||
onPluginApiListChange: (list: PluginApi[]) => void;
|
||||
productInfo?: SimplifyProductInfo;
|
||||
commercialSetting?: CommercialSetting;
|
||||
isFromMarket?: boolean;
|
||||
type?: string;
|
||||
scrollContainerRef?: MutableRefObject<HTMLDivElement | null>;
|
||||
activeKey?: string | string[] | undefined;
|
||||
agentId?: string;
|
||||
workflowNodes?: WorkflowNodeJSON[];
|
||||
index?: number;
|
||||
addonBefore?: React.ReactNode;
|
||||
addonAfter?: React.ReactNode;
|
||||
}
|
||||
|
||||
const CopyPlugin = ({ onApiToggle }: { onApiToggle: () => void }) => (
|
||||
<UIButton
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onApiToggle?.();
|
||||
}}
|
||||
>
|
||||
{I18n.t('add_resource_modal_copy_to_project')}
|
||||
</UIButton>
|
||||
);
|
||||
|
||||
export const PluginPanel: React.FC<PluginPanelProps> = ({
|
||||
info,
|
||||
highlightWords,
|
||||
showCreator = false,
|
||||
showCreateTime = true,
|
||||
showPublishTime = false,
|
||||
showButton = true,
|
||||
showCopyPlugin = false,
|
||||
className = '',
|
||||
pluginApiList,
|
||||
productInfo,
|
||||
showMarketLink,
|
||||
showProjectPluginLink,
|
||||
clickProjectPluginCallback,
|
||||
isFromMarket,
|
||||
onPluginApiListChange,
|
||||
openMode,
|
||||
from,
|
||||
workflowNodes,
|
||||
openModeCallback,
|
||||
onCopyPluginCallback,
|
||||
scrollContainerRef,
|
||||
type,
|
||||
activeKey,
|
||||
agentId,
|
||||
index = 0,
|
||||
addonBefore,
|
||||
addonAfter,
|
||||
}) => {
|
||||
const {
|
||||
name,
|
||||
plugin_apis,
|
||||
id,
|
||||
plugin_icon,
|
||||
project_id,
|
||||
desc_for_human,
|
||||
create_time = 0,
|
||||
update_time = 0,
|
||||
statistic_data,
|
||||
listed_at,
|
||||
plugin_product_status,
|
||||
plugin_type,
|
||||
is_official,
|
||||
version_name,
|
||||
version_ts,
|
||||
} = info;
|
||||
const botId = useBotInfoStore(state => state.botId);
|
||||
const { id: productId, status: marketStatus, auth_mode } = productInfo || {};
|
||||
const refTarget = useRef(null);
|
||||
const refHasReport = useRef(false);
|
||||
|
||||
// 记录当前点击插件的 apiId
|
||||
const currentApiId = useRef('');
|
||||
|
||||
// 记录当前是否在拉取 store 的最新插件数据
|
||||
const [isFetching, setIsFetching] = useState(false);
|
||||
|
||||
// 例如 { 'pluginID_apiID': [node1, node2, node3, ...] }
|
||||
const pluginApiNodesMap = useMemo(
|
||||
() =>
|
||||
groupBy(workflowNodes || [], item => {
|
||||
const apiParams = item.data?.inputs?.apiParam ?? [];
|
||||
const pluginID = extractApiParams('pluginID', apiParams);
|
||||
const apiID = extractApiParams('apiID', apiParams);
|
||||
return `${pluginID}_${apiID}`;
|
||||
}),
|
||||
[workflowNodes],
|
||||
);
|
||||
|
||||
const [isInView] = useInViewport(refTarget, {
|
||||
root: () => scrollContainerRef?.current,
|
||||
});
|
||||
const isCheckNow = useMemo(() => {
|
||||
if (!id) {
|
||||
return false;
|
||||
}
|
||||
if (activeKey === id || activeKey?.includes(id)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}, [activeKey, id]);
|
||||
useEffect(() => {
|
||||
if (isCheckNow) {
|
||||
sendTeaEvent(EVENT_NAMES.product_click_front, {
|
||||
plugin_id: info.id,
|
||||
product_id: `${productInfo?.id}`,
|
||||
product_name: info?.name || '',
|
||||
entity_type: 'plugin',
|
||||
source: 'add_plugin_menu',
|
||||
from: 'add_plugin_menu',
|
||||
filter_tag: type || '',
|
||||
action: 'expand_tools',
|
||||
c_position: index,
|
||||
});
|
||||
}
|
||||
}, [isCheckNow, productInfo?.id]);
|
||||
useEffect(() => {
|
||||
if (!isFromMarket || refHasReport.current) {
|
||||
return;
|
||||
}
|
||||
if (isInView) {
|
||||
refHasReport.current = true;
|
||||
sendTeaEvent(EVENT_NAMES.product_show_front, {
|
||||
plugin_id: info.id,
|
||||
product_id: `${productInfo?.id}`,
|
||||
product_name: info?.name || '',
|
||||
entity_type: 'plugin',
|
||||
source: 'add_plugin_menu',
|
||||
from: 'add_plugin_menu',
|
||||
filter_tag: type || '',
|
||||
c_position: index,
|
||||
});
|
||||
}
|
||||
}, [isInView, productInfo, info, type]);
|
||||
|
||||
const timePrefixText = showPublishTime
|
||||
? I18n.t('mkl_plugin_publish')
|
||||
: showCreateTime
|
||||
? I18n.t('mkl_plugin_created')
|
||||
: I18n.t('mkl_plugin_updated');
|
||||
const timeToShow =
|
||||
(showPublishTime
|
||||
? Number(listed_at)
|
||||
: showCreateTime
|
||||
? Number(create_time)
|
||||
: Number(update_time)) || 0;
|
||||
|
||||
const renderAuthStatus = () => {
|
||||
if (isUndefined(auth_mode) || auth_mode === PluginAuthMode.NoAuth) {
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
auth_mode === PluginAuthMode.Required ||
|
||||
auth_mode === PluginAuthMode.Supported
|
||||
) {
|
||||
return (
|
||||
<Tag color="yellow" className="font-medium !py-2px !px-4px !h-20px">
|
||||
{I18n.t('plugin_tool_config_status_unauthorized')}
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Tag color="brand" className="font-medium !py-2px !px-4px !h-20px">
|
||||
{I18n.t('plugin_tool_config_status_authorized')}
|
||||
</Tag>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Collapse.Panel
|
||||
data-testid="plugin-collapse-panel"
|
||||
className={classNames(s['plugin-panel'], className)}
|
||||
disabled={!plugin_apis?.length}
|
||||
header={
|
||||
<div className={s['plugin-panel-header']} ref={refTarget}>
|
||||
<OfficialLabel
|
||||
size="small"
|
||||
visible={productInfo?.is_official ?? false}
|
||||
>
|
||||
<Image
|
||||
className={s['header-icon']}
|
||||
src={plugin_icon}
|
||||
preview={false}
|
||||
/>
|
||||
</OfficialLabel>
|
||||
|
||||
<div className={s['header-main']}>
|
||||
<div className={s['header-name']}>
|
||||
<Space spacing={8} className="flex-1">
|
||||
{/* 插件名称最长30字符,无需ellipsis */}
|
||||
<Typography.Text>
|
||||
<Highlight
|
||||
sourceString={name}
|
||||
searchWords={highlightWords}
|
||||
component="strong"
|
||||
/>
|
||||
</Typography.Text>
|
||||
{renderAuthStatus()}
|
||||
{showProjectPluginLink && clickProjectPluginCallback ? (
|
||||
<IconViewinchatOutlined
|
||||
className={s['market-link-icon']}
|
||||
onClick={event => {
|
||||
event.stopPropagation();
|
||||
clickProjectPluginCallback?.({
|
||||
...info,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
{showMarketLink && Number(productId) > 0 ? (
|
||||
<IconViewinchatOutlined
|
||||
className={s['market-link-icon']}
|
||||
onClick={event => {
|
||||
event.stopPropagation();
|
||||
sendTeaEvent(EVENT_NAMES.product_click_front, {
|
||||
plugin_id: info.id,
|
||||
product_id: `${productInfo?.id}`,
|
||||
product_name: info?.name || '',
|
||||
entity_type: 'plugin',
|
||||
source: 'add_plugin_menu',
|
||||
from: 'add_plugin_menu',
|
||||
filter_tag: type || '',
|
||||
action: 'enter_detailpage',
|
||||
c_position: index,
|
||||
});
|
||||
window.open(
|
||||
`/store/plugin/${productId}?from=add_plugin_menu`,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</Space>
|
||||
</div>
|
||||
<div className={classNames(s['header-desc'], 'flex items-center')}>
|
||||
{plugin_type === PluginType.LOCAL ? (
|
||||
<>
|
||||
<Tag color="cyan" size="mini">
|
||||
{I18n.t('local_plugin_label')}
|
||||
</Tag>
|
||||
<Divider layout="vertical" margin="4px" className="h-[9px]" />
|
||||
</>
|
||||
) : null}
|
||||
<Typography.Text
|
||||
ellipsis={{
|
||||
showTooltip: {
|
||||
opts: {
|
||||
content: desc_for_human,
|
||||
style: { wordWrap: 'break-word', maxWidth: '560px' },
|
||||
},
|
||||
},
|
||||
rows: 1,
|
||||
}}
|
||||
>
|
||||
{desc_for_human}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
|
||||
<div className="my-[8px] leading-[16px]">
|
||||
<Space spacing={4}>
|
||||
{/* 条件 */}
|
||||
|
||||
{addonBefore}
|
||||
{productInfo?.plugin_type === ProductPluginType.LocalPlugin ? (
|
||||
<Tag
|
||||
color="cyan"
|
||||
prefixIcon={
|
||||
<IconCozDesktop className="coz-fg-color-cyan text-[10px]" />
|
||||
}
|
||||
size="mini"
|
||||
>
|
||||
{I18n.t('store_service_plugin')}
|
||||
</Tag>
|
||||
) : null}
|
||||
<Tag color="primary" size="mini">
|
||||
{I18n.t('bot_edit_page_plugin_list_plugin_has_n_tools', {
|
||||
n: plugin_apis?.length,
|
||||
})}
|
||||
</Tag>
|
||||
{!!statistic_data?.bot_quote && (
|
||||
<Tag color="primary" size="mini">
|
||||
{I18n.t('bot_edit_page_plugin_list_plugin_n_bots_using', {
|
||||
n: formatNumber(statistic_data?.bot_quote),
|
||||
})}
|
||||
</Tag>
|
||||
)}
|
||||
{productInfo?.connectors?.length ? (
|
||||
<>
|
||||
<Divider
|
||||
layout="vertical"
|
||||
margin={0}
|
||||
className="coz-stroke-primary"
|
||||
/>
|
||||
<div className="ml-auto coz-fg-secondary text-base flex items-center gap-6px">
|
||||
{I18n.t('store_service_plugin_connector')}
|
||||
<ConnectorList connectors={productInfo?.connectors} />
|
||||
<Tooltip
|
||||
content={I18n.t('store_add_connector_tootip')}
|
||||
theme="dark"
|
||||
>
|
||||
<IconCozInfoCircle className="coz-fg-secondary text-lg" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<div className={'flex justify-between'}>
|
||||
<Space className={s['header-info']}>
|
||||
{showCreator ? (
|
||||
<span className={'max-w-[260px]'}>
|
||||
<AvatarName
|
||||
avatar={productInfo?.user_info?.avatar_url}
|
||||
username={productInfo?.user_info?.user_name}
|
||||
name={productInfo?.user_info?.name}
|
||||
label={{
|
||||
name: productInfo?.user_info?.user_label?.label_name,
|
||||
icon: productInfo?.user_info?.user_label?.icon_url,
|
||||
href: productInfo?.user_info?.user_label?.jump_link,
|
||||
}}
|
||||
nameMaxWidth={150}
|
||||
/>
|
||||
</span>
|
||||
) : null}
|
||||
{showCreator ? <Divider layout="vertical" /> : null}
|
||||
<div className={s['creator-time']}>
|
||||
{`${timePrefixText} `}
|
||||
{formatDate(timeToShow, 'YYYY-MM-DD HH:mm')}
|
||||
</div>
|
||||
{addonAfter}
|
||||
</Space>
|
||||
<PluginPerfStatics
|
||||
className={s['plugin-total']}
|
||||
successRate={productInfo?.success_rate}
|
||||
callAmount={productInfo?.call_amount}
|
||||
avgExecTime={productInfo?.avg_exec_time}
|
||||
botsUseCount={productInfo?.bots_use_count}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{showCopyPlugin ? (
|
||||
<div>
|
||||
<CopyPlugin
|
||||
onApiToggle={() => {
|
||||
onCopyPluginCallback?.({
|
||||
pluginID: id,
|
||||
name,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
}
|
||||
itemKey={`${id}`}
|
||||
>
|
||||
{plugin_apis?.map(api => {
|
||||
const isAdded = pluginApiList.some(
|
||||
addedApi =>
|
||||
(addedApi.api_id && addedApi.api_id === api.api_id) ||
|
||||
(addedApi.plugin_id?.toString() ?? '0') + (addedApi.name ?? '') ===
|
||||
(api.plugin_id?.toString() ?? '0') + (api.name ?? ''),
|
||||
);
|
||||
return (
|
||||
<PluginItem
|
||||
data-testid="plugin-panel-item-pluginapi"
|
||||
isAdded={isAdded}
|
||||
pluginApi={api}
|
||||
from={from}
|
||||
workflowNodes={
|
||||
pluginApiNodesMap[`${api?.plugin_id}_${api?.api_id}`] ?? []
|
||||
}
|
||||
marketPluginInfo={
|
||||
isFromMarket
|
||||
? productInfo?.tools?.find(item => item.id === api.api_id)
|
||||
: undefined
|
||||
}
|
||||
isLocalPlugin={
|
||||
productInfo?.plugin_type === ProductPluginType.LocalPlugin
|
||||
}
|
||||
connectors={productInfo?.connectors?.map(item => item.name ?? '')}
|
||||
marketStatus={isFromMarket ? marketStatus : undefined}
|
||||
onApiToggle={async () => {
|
||||
let isSuccess = true;
|
||||
emitEvent(OpenBlockEvent.PLUGIN_API_BLOCK_OPEN);
|
||||
if (isAdded) {
|
||||
onPluginApiListChange(
|
||||
pluginApiList.filter(
|
||||
item =>
|
||||
getPluginApiKey(item) !== getPluginApiKey(api) &&
|
||||
(!item?.api_id || item?.api_id !== api?.api_id),
|
||||
),
|
||||
);
|
||||
Toast.success({
|
||||
content: I18n.t('bot_edit_tool_removed_toast', {
|
||||
api_name: api.name,
|
||||
}),
|
||||
showClose: false,
|
||||
});
|
||||
sendTeaEvent(EVENT_NAMES.click_tool_select, {
|
||||
operation: 'remove',
|
||||
bot_id: botId,
|
||||
operation_type: 'single',
|
||||
tool_id: api?.api_id || '',
|
||||
tool_name: api?.name || '',
|
||||
product_id: `${productInfo?.id}`,
|
||||
product_name: info?.name || '',
|
||||
source: 'add_plugin_list',
|
||||
from: 'bot_develop',
|
||||
});
|
||||
} else {
|
||||
let apiToSend: PluginApi | undefined;
|
||||
// api所在的plugin的信息,添加后存入store,避免GetPlaygroundPluginList多次请求
|
||||
const pluginInfo = {
|
||||
plugin_icon,
|
||||
plugin_type,
|
||||
is_official,
|
||||
project_id,
|
||||
version_name,
|
||||
version_ts,
|
||||
};
|
||||
// 检测当前要添加的 Plugins名称在已添加列表里是否有重名的情况(模型不支持,所以加此处理)
|
||||
if (
|
||||
pluginApiList
|
||||
.map(i => (i.plugin_name ?? '') + i.name)
|
||||
.includes((api.plugin_name ?? '') + (api.name ?? ''))
|
||||
) {
|
||||
Toast.error({
|
||||
content: I18n.t('plugin_name_conflict_error'),
|
||||
showClose: false,
|
||||
});
|
||||
isSuccess = false;
|
||||
} else {
|
||||
if (isFromMarket) {
|
||||
if (api.plugin_id && api?.api_id && api?.api_id !== '0') {
|
||||
setIsFetching(true);
|
||||
currentApiId.current = api.api_id;
|
||||
|
||||
// 从market 过来的数据,需要重新拉取最新数据。
|
||||
const result =
|
||||
await PluginDevelopApi.GetPlaygroundPluginList({
|
||||
page: 1,
|
||||
size: 1,
|
||||
plugin_ids: [api.plugin_id],
|
||||
plugin_types: [
|
||||
PluginType.PLUGIN,
|
||||
PluginType.APP,
|
||||
PluginType.LOCAL,
|
||||
],
|
||||
space_id: useSpaceStore.getState().getSpaceId(),
|
||||
});
|
||||
|
||||
setIsFetching(false);
|
||||
const targetApi =
|
||||
result?.data?.plugin_list?.[0]?.plugin_apis?.find(
|
||||
item => item?.api_id === api?.api_id,
|
||||
);
|
||||
if (targetApi) {
|
||||
Object.assign(targetApi, {
|
||||
plugin_product_status:
|
||||
result?.data?.plugin_list?.[0]
|
||||
.plugin_product_status,
|
||||
...pluginInfo,
|
||||
});
|
||||
}
|
||||
apiToSend = targetApi;
|
||||
}
|
||||
} else {
|
||||
apiToSend = Object.assign(
|
||||
{ ...api },
|
||||
{ plugin_product_status, ...pluginInfo },
|
||||
);
|
||||
}
|
||||
if (apiToSend) {
|
||||
// 如果仅可添加一次,则添加后调用callback,并且关闭弹窗
|
||||
if (
|
||||
openMode === OpenModeType.OnlyOnceAdd ||
|
||||
(from &&
|
||||
[
|
||||
From.WorkflowAddNode,
|
||||
From.ProjectIde,
|
||||
From.ProjectWorkflow,
|
||||
].includes(from))
|
||||
) {
|
||||
const cbResult = await openModeCallback?.({
|
||||
...apiToSend,
|
||||
...pluginInfo,
|
||||
});
|
||||
/** 允许添加失败的场景 */
|
||||
if (isBoolean(cbResult)) {
|
||||
return cbResult;
|
||||
}
|
||||
return isSuccess;
|
||||
}
|
||||
if (!IS_OPEN_SOURCE) {
|
||||
// 添加plugin成功后,快速绑定预设卡片信息
|
||||
await PluginDevelopApi.QuickBindPluginPresetCard({
|
||||
plugin_id: apiToSend.plugin_id,
|
||||
api_name: apiToSend.name,
|
||||
bot_id: botId,
|
||||
agent_id: agentId,
|
||||
space_id: useSpaceStore.getState().getSpaceId(),
|
||||
});
|
||||
}
|
||||
onPluginApiListChange([...pluginApiList, apiToSend]);
|
||||
Toast.success({
|
||||
content: I18n.t('bot_edit_tool_added_toast', {
|
||||
api_name: api.name,
|
||||
}),
|
||||
showClose: false,
|
||||
});
|
||||
} else {
|
||||
Toast.error({
|
||||
content: withSlardarIdButton(
|
||||
I18n.t('bot_edit_tool_added_toast_error', {
|
||||
api_name: api.name,
|
||||
}),
|
||||
),
|
||||
showClose: false,
|
||||
});
|
||||
isSuccess = false;
|
||||
}
|
||||
}
|
||||
|
||||
sendTeaEvent(EVENT_NAMES.click_tool_select, {
|
||||
operation: 'add',
|
||||
bot_id: botId,
|
||||
operation_type: 'single',
|
||||
tool_id: api?.api_id || '',
|
||||
tool_name: api?.name || '',
|
||||
product_id: `${productInfo?.id}`,
|
||||
product_name: info?.name || '',
|
||||
source: 'add_plugin_list',
|
||||
from: 'bot_develop',
|
||||
});
|
||||
}
|
||||
|
||||
return isSuccess;
|
||||
}}
|
||||
key={(api.plugin_id?.toString() ?? '') + (api.name ?? '')}
|
||||
showButton={showButton}
|
||||
loading={isFetching && api?.api_id === currentApiId.current}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Collapse.Panel>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user