import { useState } from "react"; import { useTranslation } from "react-i18next"; import { RightOutlined as RightOutlinedIcon, SelectOutlined as SelectOutlinedIcon } from "@ant-design/icons"; import { useRequest } from "ahooks"; import { Alert, Button, Collapse, Divider, Empty, Skeleton, Space, Spin, Table, type TableProps, Tooltip, Typography, notification } from "antd"; import dayjs from "dayjs"; import { ClientResponseError } from "pocketbase"; import CertificateDetailDrawer from "@/components/certificate/CertificateDetailDrawer"; import Show from "@/components/Show"; import { type CertificateModel } from "@/domain/certificate"; import type { WorkflowLogModel } from "@/domain/workflowLog"; import { WORKFLOW_RUN_STATUSES, type WorkflowRunModel } from "@/domain/workflowRun"; import { listByWorkflowRunId as listCertificatesByWorkflowRunId } from "@/repository/certificate"; import { listByWorkflowRunId as listLogsByWorkflowRunId } from "@/repository/workflowLog"; import { mergeCls } from "@/utils/css"; import { getErrMsg } from "@/utils/error"; export type WorkflowRunDetailProps = { className?: string; style?: React.CSSProperties; data: WorkflowRunModel; }; const WorkflowRunDetail = ({ data, ...props }: WorkflowRunDetailProps) => { const { t } = useTranslation(); return (
{t("workflow_run.props.status.succeeded")}} /> {t("workflow_run.props.status.failed")}} />
); }; const WorkflowRunLogs = ({ runId, runStatus }: { runId: string; runStatus: string }) => { const { t } = useTranslation(); type Log = Pick; type LogGroup = { id: string; name: string; records: Log[] }; const [listData, setListData] = useState([]); const { loading } = useRequest( () => { return listLogsByWorkflowRunId(runId); }, { refreshDeps: [runId, runStatus], pollingInterval: runStatus === WORKFLOW_RUN_STATUSES.PENDING || runStatus === WORKFLOW_RUN_STATUSES.RUNNING ? 5000 : 0, pollingWhenHidden: false, throttleWait: 500, onBefore: () => { setListData([]); }, onSuccess: (res) => { setListData( res.items.reduce((acc, e) => { let group = acc.at(-1); if (!group || group.id !== e.nodeId) { group = { id: e.nodeId, name: e.nodeName, records: [] }; acc.push(group); } group.records.push({ level: e.level, message: e.message, data: e.data, created: e.created }); return acc; }, [] as LogGroup[]) ); }, onError: (err) => { if (err instanceof ClientResponseError && err.isAbort) { return; } console.error(err); throw err; }, } ); const renderLogRecord = (record: Log) => { let message = <>{record.message}; if (record.data != null && Object.keys(record.data).length > 0) { message = (
{record.message} {Object.entries(record.data).map(([key, value]) => (
{key}:
{JSON.stringify(value)}
))}
); } return (
[{dayjs(record.created).format("YYYY-MM-DD HH:mm:ss")}]
{message}
); }; return ( <> {t("workflow_run.logs")}
0} fallback={ } >
group.id)} expandIcon={({ isActive }) => } items={listData.map((group) => { return { key: group.id, classNames: { header: "text-sm text-stone-200", body: "text-stone-200", }, style: { color: "inherit", border: "none" }, styles: { header: { color: "inherit" }, }, label: group.name, children:
{group.records.map((record) => renderLogRecord(record))}
, }; })} />
); }; const WorkflowRunArtifacts = ({ runId }: { runId: string }) => { const { t } = useTranslation(); const [notificationApi, NotificationContextHolder] = notification.useNotification(); const tableColumns: TableProps["columns"] = [ { key: "$index", align: "center", fixed: "left", width: 50, render: (_, __, index) => index + 1, }, { key: "type", title: t("workflow_run_artifact.props.type"), render: () => t("workflow_run_artifact.props.type.certificate"), }, { key: "name", title: t("workflow_run_artifact.props.name"), ellipsis: true, render: (_, record) => { return ( {record.subjectAltNames} ); }, }, { key: "$action", align: "end", width: 120, render: (_, record) => (