/* * 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: , title: , }; } function toPackStepProps( record: PublishRecordDetail, tagGroupRef: React.RefObject, 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: , title: ( ), }; } 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 ? ( ) : ( ), children: item.entity_name, })); return { icon: , title: ( ), description: tags ? (
{I18n.t('project_release_pack_fail_reason')}
) : null, }; } default: { return { icon: , title: ( ), }; } } } function PackStep(props: { record: PublishRecordDetail }) { const ref = useRef(null); const [maxTagCount, setMaxTagCount] = useState(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; 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 ; } 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: , title: ( ), }; } case PublishRecordStatus.AuditNotPass: { return { status: 'error', icon: , title: ( ), }; } default: { return { status: 'finish', icon: , title: ( ), }; } } } 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 => ( )), }; } return { icon: , title: ( ), description: record.connector_publish_result?.map(item => ( )), }; } function PublishStep(props: { record: PublishRecordDetail }) { const { project_id = '' } = useParams(); 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 ( <> {node} ); } export interface ProjectPublishProgressProps { record: ProjectPublishStore['publishRecordDetail']; } export function ProjectPublishProgress({ record, }: ProjectPublishProgressProps) { return ( ); }