feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import React, { useLayoutEffect, useRef, useState } from 'react';
|
||||
|
||||
import {
|
||||
ConnectorPublishStatus,
|
||||
type PublishRecordDetail,
|
||||
PublishRecordStatus,
|
||||
ResourceType,
|
||||
} from '@coze-arch/idl/intelligence_api';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozPlugin, IconCozWorkflow } from '@coze-arch/coze-design/icons';
|
||||
import {
|
||||
type StepProps,
|
||||
Steps,
|
||||
TagGroup,
|
||||
Typography,
|
||||
} from '@coze-arch/coze-design';
|
||||
import { type DynamicParams } from '@coze-arch/bot-typings/teamspace';
|
||||
import { type UITagProps } from '@coze-arch/bot-semi';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { useWebSdkGuideModal } from '@/web-sdk-guide';
|
||||
import { WEB_SDK_CONNECTOR_ID } from '@/utils/constants';
|
||||
import { type ProjectPublishStore } from '@/store';
|
||||
|
||||
import { PublishStepTitle } from './components/publish-step-title';
|
||||
import { PublishStepIcon } from './components/publish-step-icon';
|
||||
import {
|
||||
ConnectorStatus,
|
||||
type ConnectorStatusProps,
|
||||
} from './components/connector-status';
|
||||
|
||||
function getDefaultStepProps(title: string): StepProps {
|
||||
return {
|
||||
icon: <PublishStepIcon status="wait" />,
|
||||
title: <PublishStepTitle title={title} />,
|
||||
};
|
||||
}
|
||||
|
||||
function toPackStepProps(
|
||||
record: PublishRecordDetail,
|
||||
tagGroupRef: React.RefObject<HTMLDivElement>,
|
||||
maxTagCount?: number,
|
||||
): StepProps {
|
||||
const title = I18n.t('project_release_package');
|
||||
if (typeof record.publish_status !== 'number') {
|
||||
return getDefaultStepProps(title);
|
||||
}
|
||||
switch (record.publish_status) {
|
||||
case PublishRecordStatus.Packing: {
|
||||
return {
|
||||
icon: <PublishStepIcon status="process" />,
|
||||
title: (
|
||||
<PublishStepTitle
|
||||
title={title}
|
||||
tag={I18n.t('project_release_in_progress')}
|
||||
color="brand"
|
||||
/>
|
||||
),
|
||||
};
|
||||
}
|
||||
case PublishRecordStatus.PackFailed: {
|
||||
const tags: UITagProps[] | undefined =
|
||||
record.publish_status_detail?.pack_failed_detail?.map(item => ({
|
||||
tagKey: item.entity_id,
|
||||
className: 'pack-status-tag',
|
||||
prefixIcon:
|
||||
item.entity_type === ResourceType.Workflow ? (
|
||||
<IconCozWorkflow />
|
||||
) : (
|
||||
<IconCozPlugin />
|
||||
),
|
||||
children: item.entity_name,
|
||||
}));
|
||||
return {
|
||||
icon: <PublishStepIcon status="error" />,
|
||||
title: (
|
||||
<PublishStepTitle
|
||||
title={title}
|
||||
tag={I18n.t('project_release_package_failed')}
|
||||
color="red"
|
||||
/>
|
||||
),
|
||||
description: tags ? (
|
||||
<div ref={tagGroupRef}>
|
||||
<Typography.Paragraph className="coz-fg-secondary mb-[4px]">
|
||||
{I18n.t('project_release_pack_fail_reason')}
|
||||
</Typography.Paragraph>
|
||||
<TagGroup
|
||||
tagList={tags}
|
||||
maxTagCount={maxTagCount}
|
||||
showPopover
|
||||
popoverProps={{
|
||||
position: 'top',
|
||||
style: {
|
||||
padding: 8,
|
||||
backgroundColor: 'rgb(var(--black-5))',
|
||||
borderColor: 'rgb(var(--black-5))',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : null,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return {
|
||||
icon: <PublishStepIcon status="finish" />,
|
||||
title: (
|
||||
<PublishStepTitle
|
||||
title={title}
|
||||
tag={I18n.t('project_release_finish')}
|
||||
color="green"
|
||||
/>
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function PackStep(props: { record: PublishRecordDetail }) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const [maxTagCount, setMaxTagCount] = useState<number | undefined>(undefined);
|
||||
const stepProps = toPackStepProps(props.record, ref, maxTagCount);
|
||||
|
||||
/**
|
||||
* TagGroup 仅支持设置 maxTagCount 控制展示 Tag 的数量,不支持设置展示行数。
|
||||
* 这里通过遍历所有 Tag 的 offsetTop 来判断其所在的行数,并将 maxTagCount
|
||||
* 设置为刚好能展示两行的数量。
|
||||
*/
|
||||
useLayoutEffect(() => {
|
||||
if (!ref.current) {
|
||||
return;
|
||||
}
|
||||
const tags = ref.current.getElementsByClassName(
|
||||
'pack-status-tag',
|
||||
) as HTMLCollectionOf<HTMLElement>;
|
||||
if (tags.length <= 0) {
|
||||
return;
|
||||
}
|
||||
let top = -1;
|
||||
let rowCount = 0;
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
const tagTop = tags[i].offsetTop;
|
||||
if (top !== tagTop) {
|
||||
top = tagTop;
|
||||
rowCount++;
|
||||
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- offsetTop 第三次变化,当前 Tag 处于第三行
|
||||
if (rowCount >= 3) {
|
||||
setMaxTagCount(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [props.record.publish_status_detail?.pack_failed_detail]);
|
||||
|
||||
return <Steps.Step {...stepProps} />;
|
||||
}
|
||||
|
||||
function toAuditStepProps(record: PublishRecordDetail): StepProps {
|
||||
const title = I18n.t('project_release_coze_audit');
|
||||
if (typeof record.publish_status !== 'number') {
|
||||
return getDefaultStepProps(title);
|
||||
}
|
||||
switch (record.publish_status) {
|
||||
case PublishRecordStatus.Packing:
|
||||
case PublishRecordStatus.PackFailed: {
|
||||
return getDefaultStepProps(title);
|
||||
}
|
||||
case PublishRecordStatus.Auditing: {
|
||||
return {
|
||||
status: 'process',
|
||||
icon: <PublishStepIcon status="process" />,
|
||||
title: (
|
||||
<PublishStepTitle
|
||||
title={title}
|
||||
tag={I18n.t('project_release_in_progress')}
|
||||
color="brand"
|
||||
/>
|
||||
),
|
||||
};
|
||||
}
|
||||
case PublishRecordStatus.AuditNotPass: {
|
||||
return {
|
||||
status: 'error',
|
||||
icon: <PublishStepIcon status="error" />,
|
||||
title: (
|
||||
<PublishStepTitle
|
||||
title={title}
|
||||
tag={I18n.t('project_release_not_pass')}
|
||||
color="red"
|
||||
/>
|
||||
),
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return {
|
||||
status: 'finish',
|
||||
icon: <PublishStepIcon status="finish" />,
|
||||
title: (
|
||||
<PublishStepTitle
|
||||
title={title}
|
||||
tag={I18n.t('project_release_pass')}
|
||||
color="green"
|
||||
/>
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getConnectorsPublishStatus(
|
||||
record: ProjectPublishStore['publishRecordDetail'],
|
||||
) {
|
||||
const connectorResults = record.connector_publish_result ?? [];
|
||||
if (connectorResults.length <= 0) {
|
||||
return 'wait';
|
||||
}
|
||||
const failedCount = connectorResults.filter(
|
||||
item => item.connector_publish_status === ConnectorPublishStatus.Failed,
|
||||
).length;
|
||||
if (failedCount > 0) {
|
||||
// 所有渠道均失败,显示红色叉号;部分渠道失败,显示黄色感叹号
|
||||
return failedCount === connectorResults.length ? 'error' : 'warn';
|
||||
}
|
||||
const publishingCount = connectorResults.filter(
|
||||
item =>
|
||||
item.connector_publish_status === ConnectorPublishStatus.Default ||
|
||||
item.connector_publish_status === ConnectorPublishStatus.Auditing,
|
||||
).length;
|
||||
if (publishingCount > 0) {
|
||||
// 部分渠道在发布中,显示时钟图标
|
||||
return 'process';
|
||||
}
|
||||
return 'finish';
|
||||
}
|
||||
|
||||
function toPublishStepProps(
|
||||
record: ProjectPublishStore['publishRecordDetail'],
|
||||
onShowWebSdkGuide: ConnectorStatusProps['onShowWebSdkGuide'],
|
||||
): StepProps {
|
||||
const title = I18n.t('project_release_channel');
|
||||
if (typeof record.publish_status !== 'number') {
|
||||
return getDefaultStepProps(title);
|
||||
}
|
||||
// 未到达“渠道审核与发布”步骤,显示默认的灰色时钟图标
|
||||
if (record.publish_status < PublishRecordStatus.ConnectorPublishing) {
|
||||
return {
|
||||
...getDefaultStepProps(title),
|
||||
description: record.connector_publish_result?.map(item => (
|
||||
<ConnectorStatus result={item} showTag={false} />
|
||||
)),
|
||||
};
|
||||
}
|
||||
return {
|
||||
icon: <PublishStepIcon status={getConnectorsPublishStatus(record)} />,
|
||||
title: (
|
||||
<PublishStepTitle
|
||||
title={title}
|
||||
{...(IS_OVERSEA &&
|
||||
// publish_monetization_result 为 nil 表示可能是接口失败,也不展示
|
||||
record.publish_monetization_result === false && {
|
||||
tag: I18n.t('monetization_publish_fail'),
|
||||
color: 'red',
|
||||
})}
|
||||
/>
|
||||
),
|
||||
description: record.connector_publish_result?.map(item => (
|
||||
<ConnectorStatus result={item} onShowWebSdkGuide={onShowWebSdkGuide} />
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
function PublishStep(props: { record: PublishRecordDetail }) {
|
||||
const { project_id = '' } = useParams<DynamicParams>();
|
||||
const { node, show: showWebSdkGuideModal } = useWebSdkGuideModal();
|
||||
const onShowWebSdkGuide = (workflowId: string) =>
|
||||
showWebSdkGuideModal({
|
||||
projectId: project_id,
|
||||
workflowId,
|
||||
version: props.record.connector_publish_result?.find(
|
||||
item => item.connector_id === WEB_SDK_CONNECTOR_ID,
|
||||
)?.connector_bind_info?.sdk_version,
|
||||
});
|
||||
const stepProps = toPublishStepProps(props.record, onShowWebSdkGuide);
|
||||
return (
|
||||
<>
|
||||
<Steps.Step {...stepProps} />
|
||||
{node}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export interface ProjectPublishProgressProps {
|
||||
record: ProjectPublishStore['publishRecordDetail'];
|
||||
}
|
||||
|
||||
export function ProjectPublishProgress({
|
||||
record,
|
||||
}: ProjectPublishProgressProps) {
|
||||
return (
|
||||
<Steps type="basic" direction="vertical" size="small">
|
||||
<PackStep record={record} />
|
||||
<Steps.Step {...toAuditStepProps(record)} />
|
||||
<PublishStep record={record} />
|
||||
</Steps>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user