diff --git a/ui/src/components/Empty.tsx b/ui/src/components/Empty.tsx index 83f79083..205a0704 100644 --- a/ui/src/components/Empty.tsx +++ b/ui/src/components/Empty.tsx @@ -38,11 +38,9 @@ const Empty = (props: EmptyProps) => {
- {isPrimitive(title) ? ( - {title} - ) : ( -

{title}

- )} +
+ {title} +
{isPrimitive(description) ? ( diff --git a/ui/src/components/access/AccessEditDrawer.tsx b/ui/src/components/access/AccessEditDrawer.tsx index 9eba19a9..470a1b86 100644 --- a/ui/src/components/access/AccessEditDrawer.tsx +++ b/ui/src/components/access/AccessEditDrawer.tsx @@ -1,7 +1,7 @@ import { useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { useControllableValue } from "ahooks"; -import { Button, Drawer, Space, notification } from "antd"; +import { App, Button, Drawer, Space } from "antd"; import { type AccessModel } from "@/domain/access"; import { useTriggerElement, useZustandShallowSelector } from "@/hooks"; @@ -24,7 +24,7 @@ export type AccessEditDrawerProps = { const AccessEditDrawer = ({ data, loading, trigger, scene, usage, afterSubmit, ...props }: AccessEditDrawerProps) => { const { t } = useTranslation(); - const [notificationApi, NotificationContextHolder] = notification.useNotification(); + const { notification } = App.useApp(); const { createAccess, updateAccess } = useAccessesStore(useZustandShallowSelector(["createAccess", "updateAccess"])); @@ -70,7 +70,7 @@ const AccessEditDrawer = ({ data, loading, trigger, scene, usage, afterSubmit, . afterSubmit?.(values); setOpen(false); } catch (err) { - notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); + notification.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); throw err; } finally { @@ -86,8 +86,6 @@ const AccessEditDrawer = ({ data, loading, trigger, scene, usage, afterSubmit, . return ( <> - {NotificationContextHolder} - {triggerEl} { const { t } = useTranslation(); - const [notificationApi, NotificationContextHolder] = notification.useNotification(); + const { notification } = App.useApp(); const { createAccess, updateAccess } = useAccessesStore(useZustandShallowSelector(["createAccess", "updateAccess"])); @@ -70,7 +70,7 @@ const AccessEditModal = ({ data, loading, trigger, scene, usage, afterSubmit, .. afterSubmit?.(values); setOpen(false); } catch (err) { - notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); + notification.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); throw err; } finally { @@ -86,8 +86,6 @@ const AccessEditModal = ({ data, loading, trigger, scene, usage, afterSubmit, .. return ( <> - {NotificationContextHolder} - {triggerEl} -
+
diff --git a/ui/src/components/workflow/WorkflowRunDetail.tsx b/ui/src/components/workflow/WorkflowRunDetail.tsx index 36a2daa7..17e122fd 100644 --- a/ui/src/components/workflow/WorkflowRunDetail.tsx +++ b/ui/src/components/workflow/WorkflowRunDetail.tsx @@ -2,7 +2,7 @@ import { useState } from "react"; import { useTranslation } from "react-i18next"; import { IconBrowserShare, IconCheck, IconChevronRight, IconDownload, IconSettings2 } from "@tabler/icons-react"; import { useRequest } from "ahooks"; -import { Button, Collapse, Divider, Dropdown, Empty, Flex, Skeleton, Spin, Table, type TableProps, Tooltip, Typography, notification, theme } from "antd"; +import { App, Button, Collapse, Divider, Dropdown, Empty, Flex, Skeleton, Spin, Table, type TableProps, Tooltip, Typography, theme } from "antd"; import dayjs from "dayjs"; import { ClientResponseError } from "pocketbase"; @@ -276,7 +276,7 @@ const WorkflowRunLogs = ({ runId, runStatus }: { runId: string; runStatus: strin const WorkflowRunArtifacts = ({ runId }: { runId: string }) => { const { t } = useTranslation(); - const [notificationApi, NotificationContextHolder] = notification.useNotification(); + const { notification } = App.useApp(); const tableColumns: TableProps["columns"] = [ { @@ -337,7 +337,7 @@ const WorkflowRunArtifacts = ({ runId }: { runId: string }) => { } console.error(err); - notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); + notification.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); throw err; }, @@ -346,8 +346,6 @@ const WorkflowRunArtifacts = ({ runId }: { runId: string }) => { return ( <> - {NotificationContextHolder} - {t("workflow_run.artifacts")} columns={tableColumns} diff --git a/ui/src/components/workflow/WorkflowRuns.tsx b/ui/src/components/workflow/WorkflowRuns.tsx index 49f06ba4..e9689a29 100644 --- a/ui/src/components/workflow/WorkflowRuns.tsx +++ b/ui/src/components/workflow/WorkflowRuns.tsx @@ -1,12 +1,13 @@ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { IconBrowserShare, IconPlayerPause, IconTrash } from "@tabler/icons-react"; +import { IconBrowserShare, IconHistory, IconPlayerPause, IconTrash } from "@tabler/icons-react"; import { useRequest } from "ahooks"; -import { Alert, Button, Empty, Modal, Table, type TableProps, Tooltip, notification } from "antd"; +import { Alert, App, Button, Skeleton, Table, type TableProps, Tooltip } from "antd"; import dayjs from "dayjs"; import { ClientResponseError } from "pocketbase"; import { cancelRun as cancelWorkflowRun } from "@/api/workflows"; +import Empty from "@/components/Empty"; import WorkflowStatusTag from "@/components/workflow/WorkflowStatusTag"; import { WORKFLOW_TRIGGERS } from "@/domain/workflow"; import { WORKFLOW_RUN_STATUSES, type WorkflowRunModel } from "@/domain/workflowRun"; @@ -28,8 +29,7 @@ export type WorkflowRunsProps = { const WorkflowRuns = ({ className, style, workflowId }: WorkflowRunsProps) => { const { t } = useTranslation(); - const [modalApi, ModelContextHolder] = Modal.useModal(); - const [notificationApi, NotificationContextHolder] = notification.useNotification(); + const { modal, notification } = App.useApp(); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(10); @@ -108,27 +108,29 @@ const WorkflowRuns = ({ className, style, workflowId }: WorkflowRunsProps) => { return (
- - - - - ) - } - loading={loading} - maskClosable={!pending} - open={open} - title={
{node.name}
} - width={720} - onClose={handleClose} - > - {children} - - + } type="text" />} + afterDelete={() => { + setOpen(false); + }} + /> + } + footer={ + !!footer && ( + + + + + ) + } + loading={loading} + maskClosable={!pending} + open={open} + title={
{node.name}
} + width={720} + onClose={handleClose} + > + {children} +
); }; // #endregion diff --git a/ui/src/i18n/locales/en/nls.settings.json b/ui/src/i18n/locales/en/nls.settings.json index d1967c12..2cc517de 100644 --- a/ui/src/i18n/locales/en/nls.settings.json +++ b/ui/src/i18n/locales/en/nls.settings.json @@ -28,7 +28,7 @@ "settings.sslprovider.tab": "Certificate authority", "settings.sslprovider.ca.title": "System-wide CA", - "settings.sslprovider.ca.tips": "The certificate validity lifetime, certificate algorithm, domain names count, and support for wildcard domain names are allowed may vary among different providers. After switching service providers, please check whether the configuration of the workflows needs to be adjusted.", + "settings.sslprovider.ca.tips": "You can set different CAs for each workflow as well.
The certificate validity lifetime, certificate algorithm, domain names count, and support for wildcard domain names are allowed may vary among different providers. After switching service providers, please check whether the configuration of the workflows needs to be adjusted.", "settings.sslprovider.form.provider.label": "ACME CA provider", "settings.sslprovider.form.letsencryptstaging_alert": "The staging environment can reduce the chance of your running up against rate limits.

Learn more:
https://letsencrypt.org/docs/staging-environment/", "settings.sslprovider.form.zerossl_eab_kid.label": "EAB KID", diff --git a/ui/src/i18n/locales/en/nls.workflow.json b/ui/src/i18n/locales/en/nls.workflow.json index c8baf506..3dd909c7 100644 --- a/ui/src/i18n/locales/en/nls.workflow.json +++ b/ui/src/i18n/locales/en/nls.workflow.json @@ -2,7 +2,9 @@ "workflow.page.title": "Workflows", "workflow.page.subtitle": "Workflows are collections of nodes that automate a process. Workflows begin execution when a trigger condition occurs and execute sequentially to achieve complex tasks.", - "workflow.nodata": "No workflows. Please create a workflow to generate certificates! 😀", + "workflow.nodata.title": "No workflows", + "workflow.nodata.description": "It looks like you don't have any workflows. Get started by adding one.", + "workflow.nodata.button": "Create workflow", "workflow.search.placeholder": "Search by workflow name ...", @@ -22,7 +24,8 @@ "workflow.props.trigger.auto": "Scheduled", "workflow.props.trigger.manual": "Manual", "workflow.props.last_run_at": "Last run at", - "workflow.props.state": "State", + "workflow.props.state": "Active", + "workflow.props.state.filter.all": "All", "workflow.props.state.filter.enabled": "Active", "workflow.props.state.filter.disabled": "Inactive", "workflow.props.created_at": "Created at", diff --git a/ui/src/i18n/locales/en/nls.workflow.runs.json b/ui/src/i18n/locales/en/nls.workflow.runs.json index 747b0dd2..85d6965e 100644 --- a/ui/src/i18n/locales/en/nls.workflow.runs.json +++ b/ui/src/i18n/locales/en/nls.workflow.runs.json @@ -5,7 +5,7 @@ "workflow_run.action.delete": "Delete run", "workflow_run.action.delete.confirm": "Are you sure want to delete this \"{{name}}\" workflow run?
This action cannot be undone.", - "workflow_run.table.alert": "Attention: The workflow run contains the execution results of each node. Deleting it may trigger re-application or re-deployment of certificates due to the inability to find the previous execution result. Please do not delete unless necessary. It is recommended to keep it for at least 180 days.", + "workflow_run.table.alert": "The workflow run contains the execution results of each node. Deleting it may trigger re-application or re-deployment of certificates due to the inability to find the previous execution result. Please do not delete unless necessary. It is recommended to keep it for at least 180 days.", "workflow_run.props.id": "ID", "workflow_run.props.status": "Status", diff --git a/ui/src/i18n/locales/zh/nls.settings.json b/ui/src/i18n/locales/zh/nls.settings.json index fc020a96..aa2b8ffd 100644 --- a/ui/src/i18n/locales/zh/nls.settings.json +++ b/ui/src/i18n/locales/zh/nls.settings.json @@ -28,7 +28,7 @@ "settings.sslprovider.tab": "证书颁发机构", "settings.sslprovider.ca.title": "全局证书颁发机构", - "settings.sslprovider.ca.tips": "不同服务商所支持的证书有效期、证书算法、多域名数量上限、是否允许泛域名等可能不同,切换服务商后请注意检查已有工作流的配置是否需要调整。", + "settings.sslprovider.ca.tips": "你也可以在每个工作流中设置不同的证书颁发机构。
不同服务商所支持的证书有效期、证书算法、多域名数量上限、是否允许泛域名等可能不同,切换服务商后请注意检查已有工作流的配置是否需要调整。", "settings.sslprovider.form.provider.label": "证书颁发机构提供商", "settings.sslprovider.form.letsencryptstaging_alert": "测试环境比生产环境有更宽松的速率限制,可进行测试性部署。

点击下方链接了解更多:
https://letsencrypt.org/zh-cn/docs/staging-environment/", "settings.sslprovider.form.zerossl_eab_kid.label": "EAB KID", diff --git a/ui/src/i18n/locales/zh/nls.workflow.json b/ui/src/i18n/locales/zh/nls.workflow.json index bcf823bb..392248c9 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.json +++ b/ui/src/i18n/locales/zh/nls.workflow.json @@ -1,8 +1,10 @@ { "workflow.page.title": "工作流", - "workflow.page.subtitle": "工作流是自动化流程的节点集合。当满足触发条件时,工作流开始顺序执行各节点以完成复杂的任务(如证书申请、部署等)。", + "workflow.page.subtitle": "工作流是自动化流程的节点集合。当满足触发条件时,工作流开始顺序执行各节点以完成复杂的任务。", - "workflow.nodata": "暂无工作流,请先新建工作流", + "workflow.nodata.title": "暂无工作流", + "workflow.nodata.description": "请先新建工作流以执行证书申请、部署等任务", + "workflow.nodata.button": "新建工作流", "workflow.search.placeholder": "按工作流名称搜索……", @@ -22,7 +24,8 @@ "workflow.props.trigger.auto": "定时", "workflow.props.trigger.manual": "手动", "workflow.props.last_run_at": "最近执行时间", - "workflow.props.state": "启用状态", + "workflow.props.state": "启用", + "workflow.props.state.filter.all": "全部", "workflow.props.state.filter.enabled": "启用", "workflow.props.state.filter.disabled": "未启用", "workflow.props.created_at": "创建时间", diff --git a/ui/src/pages/accesses/AccessList.tsx b/ui/src/pages/accesses/AccessList.tsx index b01b583e..5c1b09ee 100644 --- a/ui/src/pages/accesses/AccessList.tsx +++ b/ui/src/pages/accesses/AccessList.tsx @@ -52,7 +52,7 @@ const AccessList = () => { render: (_, record) => { return (
- +
{record.name} diff --git a/ui/src/pages/settings/SettingsSSLProvider.tsx b/ui/src/pages/settings/SettingsSSLProvider.tsx index 156a4a12..f9f14567 100644 --- a/ui/src/pages/settings/SettingsSSLProvider.tsx +++ b/ui/src/pages/settings/SettingsSSLProvider.tsx @@ -428,7 +428,9 @@ const SettingsSSLProvider = () => { }>
- {t("settings.sslprovider.ca.tips")} + + +
diff --git a/ui/src/pages/workflows/WorkflowDetail.tsx b/ui/src/pages/workflows/WorkflowDetail.tsx index 175fc8c4..2c04581b 100644 --- a/ui/src/pages/workflows/WorkflowDetail.tsx +++ b/ui/src/pages/workflows/WorkflowDetail.tsx @@ -1,9 +1,8 @@ import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate, useParams } from "react-router-dom"; -import { PageHeader } from "@ant-design/pro-components"; import { IconArrowBackUp, IconChevronDown, IconDots, IconHistory, IconPlayerPlay, IconRobot, IconTrash } from "@tabler/icons-react"; -import { Alert, Button, Card, Dropdown, Form, Input, Modal, Space, Tabs, Typography, message, notification } from "antd"; +import { Alert, App, Button, Card, Dropdown, Flex, Form, Input, Space, Tabs } from "antd"; import { createSchemaFieldRule } from "antd-zod"; import { isEqual } from "radash"; import { z } from "zod/v4"; @@ -25,9 +24,7 @@ const WorkflowDetail = () => { const { t } = useTranslation(); - const [messageApi, MessageContextHolder] = message.useMessage(); - const [modalApi, ModalContextHolder] = Modal.useModal(); - const [notificationApi, NotificationContextHolder] = notification.useNotification(); + const { message, modal, notification } = App.useApp(); const { id: workflowId } = useParams(); const { workflow, initialized, ...workflowState } = useWorkflowStore( @@ -79,7 +76,7 @@ const WorkflowDetail = () => { const handleEnableChange = async () => { if (!workflow.enabled && (!workflow.content || !isAllNodesValidated(workflow.content))) { - messageApi.warning(t("workflow.action.enable.failed.uncompleted")); + message.warning(t("workflow.action.enable.failed.uncompleted")); return; } @@ -87,12 +84,12 @@ const WorkflowDetail = () => { await workflowState.setEnabled(!workflow.enabled); } catch (err) { console.error(err); - notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); + notification.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); } }; const handleDeleteClick = () => { - modalApi.confirm({ + modal.confirm({ title: {t("workflow.action.delete")}, content: , icon: ( @@ -110,24 +107,24 @@ const WorkflowDetail = () => { } } catch (err) { console.error(err); - notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); + notification.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); } }, }); }; const handleDiscardClick = () => { - modalApi.confirm({ + modal.confirm({ title: t("workflow.detail.orchestration.action.discard"), content: t("workflow.detail.orchestration.action.discard.confirm"), onOk: async () => { try { await workflowState.discard(); - messageApi.success(t("common.text.operation_succeeded")); + message.success(t("common.text.operation_succeeded")); } catch (err) { console.error(err); - notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); + notification.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); } }, }); @@ -135,21 +132,21 @@ const WorkflowDetail = () => { const handleReleaseClick = () => { if (!isAllNodesValidated(workflow.draft!)) { - messageApi.warning(t("workflow.detail.orchestration.action.release.failed.uncompleted")); + message.warning(t("workflow.detail.orchestration.action.release.failed.uncompleted")); return; } - modalApi.confirm({ + modal.confirm({ title: t("workflow.detail.orchestration.action.release"), content: t("workflow.detail.orchestration.action.release.confirm"), onOk: async () => { try { await workflowState.release(); - messageApi.success(t("common.text.operation_succeeded")); + message.success(t("common.text.operation_succeeded")); } catch (err) { console.error(err); - notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); + notification.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); } }, }); @@ -158,7 +155,7 @@ const WorkflowDetail = () => { const handleRunClick = () => { const { promise, resolve, reject } = Promise.withResolvers(); if (workflow.hasDraft) { - modalApi.confirm({ + modal.confirm({ title: t("workflow.detail.orchestration.action.run"), content: t("workflow.detail.orchestration.action.run.confirm"), onOk: () => resolve(void 0), @@ -184,63 +181,62 @@ const WorkflowDetail = () => { await startWorkflowRun(workflowId!); - messageApi.info(t("workflow.detail.orchestration.action.run.prompt")); + message.info(t("workflow.detail.orchestration.action.run.prompt")); } catch (err) { setIsPendingOrRunning(false); unsubscribeFn?.(); console.error(err); - messageApi.warning(t("common.text.operation_failed")); + message.warning(t("common.text.operation_failed")); } }); }; return (
- {MessageContextHolder} - {ModalContextHolder} - {NotificationContextHolder} + +
+
+
+
+

{workflow.name || " "}

+

{workflow.description || " "}

+
+ + {initialized + ? [ + {t("common.button.edit")}} />, -
- - {t("common.button.edit")}} />, + , - , - - , - onClick: () => { - handleDeleteClick(); + , + onClick: () => { + handleDeleteClick(); + }, }, - }, - ], - }} - trigger={["click"]} - > - - , - ] - : [] - } - > - {workflow.description} + ], + }} + trigger={["click"]} + > + + , + ] + : []} + +
+ { tabBarStyle={{ border: "none" }} onChange={(key) => setTabValue(key as typeof tabValue)} /> - - -
+
+
+
@@ -285,7 +281,7 @@ const WorkflowDetail = () => { }} loading={!initialized} > -
+
{t("workflow.detail.orchestration.draft.alert")}
} type="warning" /> @@ -330,9 +326,9 @@ const WorkflowDetail = () => {
- +
- +
@@ -342,7 +338,7 @@ const WorkflowDetail = () => { const WorkflowBaseInfoModal = ({ trigger }: { trigger?: React.ReactNode }) => { const { t } = useTranslation(); - const [notificationApi, NotificationContextHolder] = notification.useNotification(); + const { notification } = App.useApp(); const { workflow, ...workflowState } = useWorkflowStore(useZustandShallowSelector(["workflow", "setBaseInfo"])); @@ -368,7 +364,7 @@ const WorkflowBaseInfoModal = ({ trigger }: { trigger?: React.ReactNode }) => { try { await workflowState.setBaseInfo(values.name!, values.description!); } catch (err) { - notificationApi.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); + notification.error({ message: t("common.text.request_error"), description: getErrMsg(err) }); throw err; } @@ -381,8 +377,6 @@ const WorkflowBaseInfoModal = ({ trigger }: { trigger?: React.ReactNode }) => { return ( <> - {NotificationContextHolder} - { const { t } = useTranslation(); const { message, modal, notification } = App.useApp(); - const { token: themeToken } = theme.useToken(); const [filters, setFilters] = useState>(() => { return { @@ -43,12 +43,12 @@ const WorkflowList = () => { title: t("workflow.props.name"), ellipsis: true, render: (_, record) => ( - +
{record.name} - - {record.description} + + {record.description || " "} - +
), }, { @@ -63,10 +63,10 @@ const WorkflowList = () => { return {t("workflow.props.trigger.manual")}; } else if (trigger === WORKFLOW_TRIGGERS.AUTO) { return ( - +
{t("workflow.props.trigger.auto")} {record.triggerCron ?? ""} - +
); } }, @@ -75,62 +75,21 @@ const WorkflowList = () => { key: "state", title: t("workflow.props.state"), defaultFilteredValue: searchParams.has("state") ? [searchParams.get("state") as string] : undefined, - filterDropdown: ({ setSelectedKeys, confirm, clearFilters }) => { - const items: Required["items"] = [ - ["enabled", "workflow.props.state.filter.enabled"], - ["disabled", "workflow.props.state.filter.disabled"], - ].map(([key, label]) => { - return { - key, - label: {t(label)}, - onClick: () => { - if (filters["state"] !== key) { - setPage(1); - setFilters((prev) => ({ ...prev, state: key })); - setSelectedKeys([key]); - } - - confirm({ closeDropdown: true }); - }, - }; - }); - - const handleResetClick = () => { - setPage(1); - setFilters((prev) => ({ ...prev, state: undefined })); - setSelectedKeys([]); - clearFilters?.(); - confirm(); - }; - - const handleConfirmClick = () => { - confirm(); - }; - - return ( -
- - - - - - -
- ); - }, render: (_, record) => { const enabled = record.enabled; return ( - { - handleRecordActiveChange(record); +
{ + e.stopPropagation(); }} - /> + > + { + handleRecordActiveChange(record); + }} + /> +
); }, }, @@ -139,10 +98,10 @@ const WorkflowList = () => { title: t("workflow.props.last_run_at"), render: (_, record) => { return ( - + {record.lastRunTime ? dayjs(record.lastRunTime!).format("YYYY-MM-DD HH:mm:ss") : ""} - + ); }, }, @@ -154,14 +113,6 @@ const WorkflowList = () => { return dayjs(record.created!).format("YYYY-MM-DD HH:mm:ss"); }, }, - { - key: "updatedAt", - title: t("workflow.props.updated_at"), - ellipsis: true, - render: (_, record) => { - return dayjs(record.updated!).format("YYYY-MM-DD HH:mm:ss"); - }, - }, { key: "$action", align: "end", @@ -174,8 +125,9 @@ const WorkflowList = () => { color="primary" icon={} variant="text" - onClick={() => { - navigate(`/workflows/${record.id}`); + onClick={(e) => { + e.stopPropagation(); + handleRecordDetailClick(record); }} /> @@ -186,6 +138,7 @@ const WorkflowList = () => { icon={} variant="text" onClick={() => { + e.stopPropagation(); handleRecordDuplicateClick(record); }} /> @@ -197,7 +150,8 @@ const WorkflowList = () => { danger icon={} variant="text" - onClick={() => { + onClick={(e) => { + e.stopPropagation(); handleRecordDeleteClick(record); }} /> @@ -256,6 +210,10 @@ const WorkflowList = () => { refreshData(); }; + const handleRecordDetailClick = (workflow: WorkflowModel) => { + navigate(`/workflows/${workflow.id}`); + }; + const handleRecordActiveChange = async (workflow: WorkflowModel) => { try { if (!workflow.enabled && (!workflow.content || !isAllNodesValidated(workflow.content))) { @@ -347,6 +305,22 @@ const WorkflowList = () => {
+
+ {t("workflow.props.state.filter.all")}, value: "" }, + { label: {t("workflow.props.state.filter.enabled")}, value: "enabled" }, + { label: {t("workflow.props.state.filter.disabled")}, value: "disabled" }, + ]} + size="large" + value={(filters["state"] as string) || ""} + onChange={(value) => { + setPage(1); + setFilters((prev) => ({ ...prev, state: value })); + }} + /> +
{ dataSource={tableData} loading={loading} locale={{ - emptyText: , + emptyText: loading ? ( + + ) : ( + } + extra={ + loadedError ? ( + + ) : ( + + ) + } + /> + ), }} pagination={{ current: page, @@ -390,8 +383,14 @@ const WorkflowList = () => { setPageSize(pageSize); }, }} + rowClassName="cursor-pointer" rowKey={(record) => record.id} scroll={{ x: "max(100%, 960px)" }} + onRow={(record) => ({ + onClick: () => { + handleRecordDetailClick(record); + }, + })} />
diff --git a/ui/src/pages/workflows/WorkflowNew.tsx b/ui/src/pages/workflows/WorkflowNew.tsx index 1afa72d1..8fb84630 100644 --- a/ui/src/pages/workflows/WorkflowNew.tsx +++ b/ui/src/pages/workflows/WorkflowNew.tsx @@ -123,7 +123,7 @@ const WorkflowNew = () => {
-
+
{t("workflow.new.templates.title")}