mirror of
https://github.com/usual2970/certimate.git
synced 2025-10-04 21:44:54 +00:00
feat: a new status for canceled workflow run
This commit is contained in:
@@ -41,11 +41,11 @@ const WorkflowRunDetailDrawer = ({ data, loading, trigger, ...props }: WorkflowR
|
||||
</Show>
|
||||
|
||||
<div className="mt-4 rounded-md bg-black p-4 text-stone-200">
|
||||
<div className="flex flex-col space-y-3">
|
||||
<div className="flex flex-col space-y-4">
|
||||
{data!.logs?.map((item, i) => {
|
||||
return (
|
||||
<div key={i} className="flex flex-col space-y-2">
|
||||
<div>{item.nodeName}</div>
|
||||
<div className="font-semibold">{item.nodeName}</div>
|
||||
<div className="flex flex-col space-y-1">
|
||||
{item.outputs?.map((output, j) => {
|
||||
return (
|
||||
|
@@ -5,6 +5,7 @@ import {
|
||||
ClockCircleOutlined as ClockCircleOutlinedIcon,
|
||||
CloseCircleOutlined as CloseCircleOutlinedIcon,
|
||||
DeleteOutlined as DeleteOutlinedIcon,
|
||||
PauseCircleOutlined as PauseCircleOutlinedIcon,
|
||||
SelectOutlined as SelectOutlinedIcon,
|
||||
SyncOutlined as SyncOutlinedIcon,
|
||||
} from "@ant-design/icons";
|
||||
@@ -70,6 +71,12 @@ const WorkflowRuns = ({ className, style, workflowId }: WorkflowRunsProps) => {
|
||||
{t("workflow_run.props.status.failed")}
|
||||
</Tag>
|
||||
);
|
||||
} else if (record.status === WORKFLOW_RUN_STATUSES.CANCELED) {
|
||||
return (
|
||||
<Tag icon={<PauseCircleOutlinedIcon />} color="warning">
|
||||
{t("workflow_run.props.status.canceled")}
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
|
||||
return <></>;
|
||||
@@ -133,7 +140,11 @@ const WorkflowRuns = ({ className, style, workflowId }: WorkflowRunsProps) => {
|
||||
<Button
|
||||
color="danger"
|
||||
danger
|
||||
disabled={record.status !== WORKFLOW_RUN_STATUSES.SUCCEEDED && record.status !== WORKFLOW_RUN_STATUSES.FAILED}
|
||||
disabled={
|
||||
record.status !== WORKFLOW_RUN_STATUSES.SUCCEEDED &&
|
||||
record.status !== WORKFLOW_RUN_STATUSES.FAILED &&
|
||||
record.status !== WORKFLOW_RUN_STATUSES.CANCELED
|
||||
}
|
||||
icon={<DeleteOutlinedIcon />}
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { PlusOutlined as PlusOutlinedIcon, QuestionCircleOutlined as QuestionCircleOutlinedIcon } from "@ant-design/icons";
|
||||
import { Button, Divider, Flex, Form, type FormInstance, Select, Switch, Tooltip, Typography } from "antd";
|
||||
import { Alert, Button, Divider, Flex, Form, type FormInstance, Select, Switch, Tooltip, Typography } from "antd";
|
||||
import { createSchemaFieldRule } from "antd-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -310,6 +310,15 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
|
||||
<Show when={fieldProvider === DEPLOY_PROVIDERS.LOCAL}>
|
||||
<Form.Item>
|
||||
<Alert
|
||||
type="info"
|
||||
message={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.provider_access.guide_for_local") }}></span>}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Show>
|
||||
|
||||
<Form.Item
|
||||
name="certificate"
|
||||
label={t("workflow_node.deploy.form.certificate.label")}
|
||||
|
@@ -136,7 +136,7 @@ const StartNodeConfigForm = forwardRef<StartNodeConfigFormInstance, StartNodeCon
|
||||
|
||||
<Show when={fieldTrigger === WORKFLOW_TRIGGERS.AUTO}>
|
||||
<Form.Item>
|
||||
<Alert type="info" message={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.start.form.trigger_cron_alert.content") }}></span>} />
|
||||
<Alert type="info" message={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.start.form.trigger_cron.guide") }}></span>} />
|
||||
</Form.Item>
|
||||
</Show>
|
||||
</Form>
|
||||
|
@@ -32,6 +32,7 @@ export const WORKFLOW_RUN_STATUSES = Object.freeze({
|
||||
RUNNING: "running",
|
||||
SUCCEEDED: "succeeded",
|
||||
FAILED: "failed",
|
||||
CANCELED: "canceled",
|
||||
} as const);
|
||||
|
||||
export type WorkflorRunStatusType = (typeof WORKFLOW_RUN_STATUSES)[keyof typeof WORKFLOW_RUN_STATUSES];
|
||||
|
@@ -20,7 +20,7 @@
|
||||
"workflow_node.start.form.trigger_cron.errmsg.invalid": "Please enter a valid cron expression",
|
||||
"workflow_node.start.form.trigger_cron.tooltip": "Time zone is based on the server.",
|
||||
"workflow_node.start.form.trigger_cron.extra": "Expected execution time for the last 5 times:",
|
||||
"workflow_node.start.form.trigger_cron_alert.content": "Tips: If you have multiple workflows, it is recommended to set them to run at multiple times of the day instead of always running at specific times.<br><br>Reference links:<br>1. <a href=\"https://letsencrypt.org/docs/rate-limits/\" target=\"_blank\">Let’s Encrypt rate limits</a><br>2. <a href=\"https://letsencrypt.org/docs/faq/#why-should-my-let-s-encrypt-acme-client-run-at-a-random-time\" target=\"_blank\">Why should my Let’s Encrypt (ACME) client run at a random time?</a>",
|
||||
"workflow_node.start.form.trigger_cron.guide": "Tips: If you have multiple workflows, it is recommended to set them to run at multiple times of the day instead of always running at specific times.<br><br>Reference links:<br>1. <a href=\"https://letsencrypt.org/docs/rate-limits/\" target=\"_blank\">Let’s Encrypt rate limits</a><br>2. <a href=\"https://letsencrypt.org/docs/faq/#why-should-my-let-s-encrypt-acme-client-run-at-a-random-time\" target=\"_blank\">Why should my Let’s Encrypt (ACME) client run at a random time?</a>",
|
||||
|
||||
"workflow_node.apply.label": "Application",
|
||||
"workflow_node.apply.form.domains.label": "Domains",
|
||||
@@ -82,6 +82,7 @@
|
||||
"workflow_node.deploy.form.provider_access.placeholder": "Please select an authorization of host provider",
|
||||
"workflow_node.deploy.form.provider_access.tooltip": "Used to deploy certificates.",
|
||||
"workflow_node.deploy.form.provider_access.button": "Create",
|
||||
"workflow_node.deploy.form.provider_access.guide_for_local": "Tips: Due to the form validations, youe need to select an authorization for local deployment also, even if it means nothing.",
|
||||
"workflow_node.deploy.form.certificate.label": "Certificate",
|
||||
"workflow_node.deploy.form.certificate.placeholder": "Please select certificate",
|
||||
"workflow_node.deploy.form.certificate.tooltip": "The certificate to be deployed comes from the previous application stage node.",
|
||||
|
@@ -9,6 +9,7 @@
|
||||
"workflow_run.props.status.running": "Running",
|
||||
"workflow_run.props.status.succeeded": "Succeeded",
|
||||
"workflow_run.props.status.failed": "Failed",
|
||||
"workflow_run.props.status.canceled": "Canceled",
|
||||
"workflow_run.props.trigger": "Trigger",
|
||||
"workflow_run.props.trigger.auto": "Timing",
|
||||
"workflow_run.props.trigger.manual": "Manual",
|
||||
|
@@ -20,7 +20,7 @@
|
||||
"workflow_node.start.form.trigger_cron.errmsg.invalid": "请输入正确的 Cron 表达式",
|
||||
"workflow_node.start.form.trigger_cron.tooltip": "支持使用任意值(即 <strong>*</strong>)、值列表分隔符(即 <strong>,</strong>)、值的范围(即 <strong>-</strong>)、步骤值(即 <strong>/</strong>)等四种表达式,时区以服务器设置为准。",
|
||||
"workflow_node.start.form.trigger_cron.extra": "预计最近 5 次执行时间:",
|
||||
"workflow_node.start.form.trigger_cron_alert.content": "小贴士:如果你有多个工作流,建议将它们设置为在一天中的多个时间段运行,而非总是在相同的特定时间。<br><br>参考链接:<br>1. <a href=\"https://letsencrypt.org/zh-cn/docs/rate-limits/\" target=\"_blank\">Let’s Encrypt 速率限制</a><br>2. <a href=\"https://letsencrypt.org/zh-cn/docs/faq/#%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E7%9A%84-let-s-encrypt-acme-%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%90%AF%E5%8A%A8%E6%97%B6%E9%97%B4%E5%BA%94%E5%BD%93%E9%9A%8F%E6%9C%BA\" target=\"_blank\">为什么我的 Let’s Encrypt (ACME) 客户端启动时间应当随机?</a>",
|
||||
"workflow_node.start.form.trigger_cron.guide": "小贴士:如果你有多个工作流,建议将它们设置为在一天中的多个时间段运行,而非总是在相同的特定时间。<br><br>参考链接:<br>1. <a href=\"https://letsencrypt.org/zh-cn/docs/rate-limits/\" target=\"_blank\">Let’s Encrypt 速率限制</a><br>2. <a href=\"https://letsencrypt.org/zh-cn/docs/faq/#%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E7%9A%84-let-s-encrypt-acme-%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%90%AF%E5%8A%A8%E6%97%B6%E9%97%B4%E5%BA%94%E5%BD%93%E9%9A%8F%E6%9C%BA\" target=\"_blank\">为什么我的 Let’s Encrypt (ACME) 客户端启动时间应当随机?</a>",
|
||||
|
||||
"workflow_node.apply.label": "申请",
|
||||
"workflow_node.apply.form.domains.label": "域名",
|
||||
@@ -37,6 +37,7 @@
|
||||
"workflow_node.apply.form.provider_access.placeholder": "请选择 DNS 提供商授权",
|
||||
"workflow_node.apply.form.provider_access.tooltip": "用于 ACME DNS-01 认证时操作域名解析记录,注意与部署阶段所需的主机提供商相区分。",
|
||||
"workflow_node.apply.form.provider_access.button": "新建",
|
||||
"workflow_node.deploy.form.provider_access.guide_for_local": "小贴士:由于表单限制,你同样需要为本地部署选择一个授权 —— 即使它是空白的。",
|
||||
"workflow_node.apply.form.aws_route53_region.label": "AWS Route53 区域",
|
||||
"workflow_node.apply.form.aws_route53_region.placeholder": "请输入 AWS Route53 区域(例如:us-east-1)",
|
||||
"workflow_node.apply.form.aws_route53_region.tooltip": "这是什么?请参阅 <a href=\"https://docs.aws.amazon.com/zh_cn/general/latest/gr/rande.html#regional-endpoints\" tworkflow_node.applyank\">https://docs.aws.amazon.com/zh_cn/general/latest/gr/rande.html#regional-endpoints</a>",
|
||||
@@ -385,7 +386,7 @@
|
||||
|
||||
"workflow_node.execute_result_branch.label": "执行结果分支",
|
||||
|
||||
"workflow_node.execute_success.label": "若前一节点执行成功…",
|
||||
"workflow_node.execute_success.label": "若前序节点执行成功…",
|
||||
|
||||
"workflow_node.execute_failure.label": "若前一节点执行失败…"
|
||||
"workflow_node.execute_failure.label": "若前序节点执行失败…"
|
||||
}
|
||||
|
@@ -7,8 +7,9 @@
|
||||
"workflow_run.props.status": "状态",
|
||||
"workflow_run.props.status.pending": "等待执行",
|
||||
"workflow_run.props.status.running": "执行中",
|
||||
"workflow_run.props.status.succeeded": "成功",
|
||||
"workflow_run.props.status.failed": "失败",
|
||||
"workflow_run.props.status.succeeded": "已成功",
|
||||
"workflow_run.props.status.failed": "已失败",
|
||||
"workflow_run.props.status.canceled": "已取消",
|
||||
"workflow_run.props.trigger": "执行方式",
|
||||
"workflow_run.props.trigger.auto": "定时执行",
|
||||
"workflow_run.props.trigger.manual": "手动执行",
|
||||
|
@@ -2,15 +2,16 @@ import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import {
|
||||
ApiOutlined,
|
||||
CheckCircleOutlined,
|
||||
ClockCircleOutlined,
|
||||
CloseCircleOutlined,
|
||||
LockOutlined,
|
||||
PlusOutlined,
|
||||
SelectOutlined,
|
||||
SendOutlined,
|
||||
SyncOutlined,
|
||||
ApiOutlined as ApiOutlinedIcon,
|
||||
CheckCircleOutlined as CheckCircleOutlinedIcon,
|
||||
ClockCircleOutlined as ClockCircleOutlinedIcon,
|
||||
CloseCircleOutlined as CloseCircleOutlinedIcon,
|
||||
LockOutlined as LockOutlinedIcon,
|
||||
PauseCircleOutlined as PauseCircleOutlinedIcon,
|
||||
PlusOutlined as PlusOutlinedIcon,
|
||||
SelectOutlined as SelectOutlinedIcon,
|
||||
SendOutlined as SendOutlinedIcon,
|
||||
SyncOutlined as SyncOutlinedIcon,
|
||||
} from "@ant-design/icons";
|
||||
import { PageHeader } from "@ant-design/pro-components";
|
||||
import { useRequest } from "ahooks";
|
||||
@@ -99,25 +100,31 @@ const Dashboard = () => {
|
||||
ellipsis: true,
|
||||
render: (_, record) => {
|
||||
if (record.status === WORKFLOW_RUN_STATUSES.PENDING) {
|
||||
return <Tag icon={<ClockCircleOutlined />}>{t("workflow_run.props.status.pending")}</Tag>;
|
||||
return <Tag icon={<ClockCircleOutlinedIcon />}>{t("workflow_run.props.status.pending")}</Tag>;
|
||||
} else if (record.status === WORKFLOW_RUN_STATUSES.RUNNING) {
|
||||
return (
|
||||
<Tag icon={<SyncOutlined spin />} color="processing">
|
||||
<Tag icon={<SyncOutlinedIcon spin />} color="processing">
|
||||
{t("workflow_run.props.status.running")}
|
||||
</Tag>
|
||||
);
|
||||
} else if (record.status === WORKFLOW_RUN_STATUSES.SUCCEEDED) {
|
||||
return (
|
||||
<Tag icon={<CheckCircleOutlined />} color="success">
|
||||
<Tag icon={<CheckCircleOutlinedIcon />} color="success">
|
||||
{t("workflow_run.props.status.succeeded")}
|
||||
</Tag>
|
||||
);
|
||||
} else if (record.status === WORKFLOW_RUN_STATUSES.FAILED) {
|
||||
return (
|
||||
<Tag icon={<CloseCircleOutlined />} color="error">
|
||||
<Tag icon={<CloseCircleOutlinedIcon />} color="error">
|
||||
{t("workflow_run.props.status.failed")}
|
||||
</Tag>
|
||||
);
|
||||
} else if (record.status === WORKFLOW_RUN_STATUSES.CANCELED) {
|
||||
return (
|
||||
<Tag icon={<PauseCircleOutlinedIcon />} color="warning">
|
||||
{t("workflow_run.props.status.canceled")}
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
|
||||
return <></>;
|
||||
@@ -153,7 +160,7 @@ const Dashboard = () => {
|
||||
width: 120,
|
||||
render: (_, record) => (
|
||||
<Button.Group>
|
||||
<WorkflowRunDetailDrawer data={record} trigger={<Button color="primary" icon={<SelectOutlined />} variant="text" />} />
|
||||
<WorkflowRunDetailDrawer data={record} trigger={<Button color="primary" icon={<SelectOutlinedIcon />} variant="text" />} />
|
||||
</Button.Group>
|
||||
),
|
||||
},
|
||||
@@ -248,16 +255,16 @@ const Dashboard = () => {
|
||||
<Flex justify="stretch" vertical={!breakpoints.lg} gap={16}>
|
||||
<Card className="max-lg:flex-1 lg:w-[360px]" title={t("dashboard.quick_actions")}>
|
||||
<Space className="w-full" direction="vertical" size="large">
|
||||
<Button block type="primary" size="large" icon={<PlusOutlined />} onClick={() => navigate("/workflows/new")}>
|
||||
<Button block type="primary" size="large" icon={<PlusOutlinedIcon />} onClick={() => navigate("/workflows/new")}>
|
||||
{t("dashboard.quick_actions.create_workflow")}
|
||||
</Button>
|
||||
<Button block size="large" icon={<LockOutlined />} onClick={() => navigate("/settings/password")}>
|
||||
<Button block size="large" icon={<LockOutlinedIcon />} onClick={() => navigate("/settings/password")}>
|
||||
{t("dashboard.quick_actions.change_login_password")}
|
||||
</Button>
|
||||
<Button block size="large" icon={<SendOutlined />} onClick={() => navigate("/settings/notification")}>
|
||||
<Button block size="large" icon={<SendOutlinedIcon />} onClick={() => navigate("/settings/notification")}>
|
||||
{t("dashboard.quick_actions.cofigure_notification")}
|
||||
</Button>
|
||||
<Button block size="large" icon={<ApiOutlined />} onClick={() => navigate("/settings/ssl-provider")}>
|
||||
<Button block size="large" icon={<ApiOutlinedIcon />} onClick={() => navigate("/settings/ssl-provider")}>
|
||||
{t("dashboard.quick_actions.configure_ca")}
|
||||
</Button>
|
||||
</Space>
|
||||
|
@@ -7,6 +7,7 @@ import {
|
||||
CloseCircleOutlined as CloseCircleOutlinedIcon,
|
||||
DeleteOutlined as DeleteOutlinedIcon,
|
||||
EditOutlined as EditOutlinedIcon,
|
||||
PauseCircleOutlined as PauseCircleOutlinedIcon,
|
||||
PlusOutlined as PlusOutlinedIcon,
|
||||
SyncOutlined as SyncOutlinedIcon,
|
||||
} from "@ant-design/icons";
|
||||
@@ -168,6 +169,8 @@ const WorkflowList = () => {
|
||||
icon = <CheckCircleOutlinedIcon style={{ color: themeToken.colorSuccess }} />;
|
||||
} else if (record.lastRunStatus === WORKFLOW_RUN_STATUSES.FAILED) {
|
||||
icon = <CloseCircleOutlinedIcon style={{ color: themeToken.colorError }} />;
|
||||
} else if (record.lastRunStatus === WORKFLOW_RUN_STATUSES.CANCELED) {
|
||||
icon = <PauseCircleOutlinedIcon style={{ color: themeToken.colorWarning }} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
Reference in New Issue
Block a user