diff --git a/ui/package-lock.json b/ui/package-lock.json index 7e9e70630..1387e2be6 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -8,7 +8,6 @@ "name": "ui", "version": "0.0.0", "dependencies": { - "@ant-design/icons": "^5.6.1", "@ant-design/pro-components": "^2.8.9", "@codemirror/lang-json": "^6.0.2", "@codemirror/lang-yaml": "^6.1.2", diff --git a/ui/package.json b/ui/package.json index c65163ce5..33fb5d134 100644 --- a/ui/package.json +++ b/ui/package.json @@ -10,7 +10,6 @@ "preview": "vite preview" }, "dependencies": { - "@ant-design/icons": "^5.6.1", "@ant-design/pro-components": "^2.8.9", "@codemirror/lang-json": "^6.0.2", "@codemirror/lang-yaml": "^6.1.2", diff --git a/ui/src/App.tsx b/ui/src/App.tsx index d2b78bded..13e6532ea 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -66,6 +66,25 @@ const RootApp = () => { colorSuccess: browserTheme === "dark" ? "#57ab5a" : "#1a7f37", colorWarning: browserTheme === "dark" ? "#daaa3f" : "#eac54f", colorError: browserTheme === "dark" ? "#e5534b" : "#d1242f", + + /* @see https://tailwindcss.com/docs/responsive-design#overview */ + screenXS: 30 * 16, + screenXSMin: 30 * 16, + screenXSMax: 40 * 16 - 1, + screenSM: 40 * 16, + screenSMMin: 40 * 16, + screenSMMax: 48 * 16 - 1, + screenMD: 48 * 16, + screenMDMin: 48 * 16, + screenMDMax: 64 * 16 - 1, + screenLG: 64 * 16, + screenLGMin: 64 * 16, + screenLGMax: 80 * 16 - 1, + screenXL: 80 * 16, + screenXLMin: 80 * 16, + screenXLMax: 96 * 16 - 1, + screenXXL: 96 * 16, + screenXXLMin: 96 * 16, }, components: { Layout: { diff --git a/ui/src/components/AppTheme.tsx b/ui/src/components/AppTheme.tsx index 102d12fbd..4080187c0 100644 --- a/ui/src/components/AppTheme.tsx +++ b/ui/src/components/AppTheme.tsx @@ -12,15 +12,17 @@ export const useAppThemeMenuItems = () => { const { themeMode, setThemeMode } = useBrowserTheme(); - const items = [ - ["light", "common.theme.light", ], - ["dark", "common.theme.dark", ], - ["system", "common.theme.system", ], - ].map(([key, label, icon]) => { + const items = ( + [ + ["light", "common.theme.light", ], + ["dark", "common.theme.dark", ], + ["system", "common.theme.system", ], + ] satisfies Array<[string, string, React.ReactNode]> + ).map(([key, label, icon]) => { return { - key: key as string, - label: t(label as string), - icon: icon as React.ReactElement, + key: key, + label: t(label), + icon: icon, onClick: () => { if (key !== themeMode) { setThemeMode(key as Parameters[0]); diff --git a/ui/src/components/MultipleInput.tsx b/ui/src/components/MultipleInput.tsx index fb37bcc3a..4d87c889a 100644 --- a/ui/src/components/MultipleInput.tsx +++ b/ui/src/components/MultipleInput.tsx @@ -1,6 +1,6 @@ import { type ChangeEvent, forwardRef, useImperativeHandle, useMemo, useRef } from "react"; import { useTranslation } from "react-i18next"; -import { IconArrowDown, IconArrowUp, IconMinus, IconPlus } from "@tabler/icons-react"; +import { IconCircleArrowDown, IconCircleArrowUp, IconCircleMinus, IconCirclePlus } from "@tabler/icons-react"; import { useControllableValue } from "ahooks"; import { Button, Input, type InputProps, type InputRef, Space } from "antd"; import { produce } from "immer"; @@ -210,17 +210,17 @@ const MultipleInputItem = forwardRef { if (!showSortButton) return null; - return diff --git a/ui/src/components/workflow/WorkflowRunDetail.tsx b/ui/src/components/workflow/WorkflowRunDetail.tsx index 17e122fd7..86244c3a7 100644 --- a/ui/src/components/workflow/WorkflowRunDetail.tsx +++ b/ui/src/components/workflow/WorkflowRunDetail.tsx @@ -89,45 +89,31 @@ const WorkflowRunLogs = ({ runId, runStatus }: { runId: string; runStatus: strin const [showWhitespace, setShowWhitespace] = useState(true); const renderBadge = () => { + let color: string | undefined; + switch (runStatus) { case WORKFLOW_RUN_STATUSES.PENDING: - return ( - - - {t("workflow_run.props.status.pending")} - - ); + break; case WORKFLOW_RUN_STATUSES.RUNNING: - return ( - - - {t("workflow_run.props.status.running")} - - ); + color = themeToken.colorInfo; + break; case WORKFLOW_RUN_STATUSES.SUCCEEDED: - return ( - - - {t("workflow_run.props.status.succeeded")} - - ); + color = themeToken.colorSuccess; + break; case WORKFLOW_RUN_STATUSES.FAILED: - return ( - - - {t("workflow_run.props.status.failed")} - - ); + color = themeToken.colorError; + break; case WORKFLOW_RUN_STATUSES.CANCELED: - return ( - - - {t("workflow_run.props.status.canceled")} - - ); + color = themeToken.colorWarning; + break; } - return <>; + return ( + + + {t(`workflow_run.props.status.${runStatus}`)} + + ); }; const renderRecord = (record: Log) => { @@ -294,12 +280,13 @@ const WorkflowRunArtifacts = ({ runId }: { runId: string }) => { { key: "name", title: t("workflow_run_artifact.props.name"), - ellipsis: true, render: (_, record) => { return ( - - {record.subjectAltNames} - +
+ + {record.subjectAltNames} + +
); }, }, diff --git a/ui/src/components/workflow/WorkflowRuns.tsx b/ui/src/components/workflow/WorkflowRuns.tsx index e9689a290..3cf335ce9 100644 --- a/ui/src/components/workflow/WorkflowRuns.tsx +++ b/ui/src/components/workflow/WorkflowRuns.tsx @@ -45,13 +45,11 @@ const WorkflowRuns = ({ className, style, workflowId }: WorkflowRunsProps) => { { key: "id", title: t("workflow_run.props.id"), - ellipsis: true, render: (_, record) => {record.id}, }, { key: "status", title: t("workflow_run.props.status"), - ellipsis: true, render: (_, record) => { return ; }, @@ -243,7 +241,7 @@ const WorkflowRuns = ({ className, style, workflowId }: WorkflowRunsProps) => { /> ), icon: ( - + ), @@ -273,7 +271,11 @@ const WorkflowRuns = ({ className, style, workflowId }: WorkflowRunsProps) => { dataSource={tableData} loading={loading} locale={{ - emptyText: loading ? : } />, + emptyText: loading ? ( + + ) : ( + } /> + ), }} pagination={{ current: page, diff --git a/ui/src/components/workflow/WorkflowStatusIcon.tsx b/ui/src/components/workflow/WorkflowStatusIcon.tsx index caf807b1c..4ab965e5d 100644 --- a/ui/src/components/workflow/WorkflowStatusIcon.tsx +++ b/ui/src/components/workflow/WorkflowStatusIcon.tsx @@ -1,10 +1,4 @@ -import { - CheckCircleOutlined as CheckCircleOutlinedIcon, - ClockCircleOutlined as ClockCircleOutlinedIcon, - CloseCircleOutlined as CloseCircleOutlinedIcon, - StopOutlined as StopOutlinedIcon, - SyncOutlined as SyncOutlinedIcon, -} from "@ant-design/icons"; +import { IconCircleCheck, IconCircleDashed, IconCircleOff, IconCircleX, IconClock, IconLoader3 } from "@tabler/icons-react"; import { theme } from "antd"; import { WORKFLOW_RUN_STATUSES } from "@/domain/workflowRun"; @@ -18,7 +12,9 @@ export type WorkflowStatusIconProps = { status: string; }; -const WorkflowStatusIcon = ({ className, style, color, size, status }: WorkflowStatusIconProps) => { +const WorkflowStatusIcon = ({ className, style, color, status, ...props }: WorkflowStatusIconProps) => { + const { size = "1em" } = props; + const { token: themeToken } = theme.useToken(); switch (status) { @@ -27,74 +23,55 @@ const WorkflowStatusIcon = ({ className, style, color, size, status }: WorkflowS color = themeToken.colorTextSecondary; } return ( - + + + ); case WORKFLOW_RUN_STATUSES.RUNNING: if (color === true) { color = themeToken.colorInfo; } return ( - + + + ); case WORKFLOW_RUN_STATUSES.SUCCEEDED: if (color === true) { color = themeToken.colorSuccess; - console.log(color); } return ( - + + + ); case WORKFLOW_RUN_STATUSES.FAILED: if (color === true) { color = themeToken.colorError; } return ( - + + + ); case WORKFLOW_RUN_STATUSES.CANCELED: if (color === true) { color = themeToken.colorWarning; } return ( - + + + ); default: - return <>; + if (color === true) { + color = themeToken.colorTextSecondary; + } + return ( + + + + ); } }; diff --git a/ui/src/components/workflow/node/AddNode.tsx b/ui/src/components/workflow/node/AddNode.tsx index 1ef2f2a67..c9fe64968 100644 --- a/ui/src/components/workflow/node/AddNode.tsx +++ b/ui/src/components/workflow/node/AddNode.tsx @@ -1,14 +1,6 @@ import { memo, useMemo } from "react"; import { useTranslation } from "react-i18next"; -import { - CloudUploadOutlined as CloudUploadOutlinedIcon, - DeploymentUnitOutlined as DeploymentUnitOutlinedIcon, - MonitorOutlined as MonitorOutlinedIcon, - SendOutlined as SendOutlinedIcon, - SisternodeOutlined as SisternodeOutlinedIcon, - SolutionOutlined as SolutionOutlinedIcon, -} from "@ant-design/icons"; -import { IconPlus } from "@tabler/icons-react"; +import { IconCloudUpload, IconContract, IconDeviceDesktopSearch, IconPackage, IconPlus, IconSend, IconSitemap } from "@tabler/icons-react"; import { Dropdown } from "antd"; import { WorkflowNodeType, newNode } from "@/domain/workflow"; @@ -25,15 +17,17 @@ const AddNode = ({ node, disabled }: AddNodeProps) => { const { addNode } = useWorkflowStore(useZustandShallowSelector(["addNode"])); const dropdownMenus = useMemo(() => { - return [ - [WorkflowNodeType.Apply, "workflow_node.apply.label", ], - [WorkflowNodeType.Upload, "workflow_node.upload.label", ], - [WorkflowNodeType.Monitor, "workflow_node.monitor.label", ], - [WorkflowNodeType.Deploy, "workflow_node.deploy.label", ], - [WorkflowNodeType.Notify, "workflow_node.notify.label", ], - [WorkflowNodeType.Branch, "workflow_node.branch.label", ], - [WorkflowNodeType.ExecuteResultBranch, "workflow_node.execute_result_branch.label", ], - ] + return ( + [ + [WorkflowNodeType.Apply, "workflow_node.apply.label", ], + [WorkflowNodeType.Upload, "workflow_node.upload.label", ], + [WorkflowNodeType.Monitor, "workflow_node.monitor.label", ], + [WorkflowNodeType.Deploy, "workflow_node.deploy.label", ], + [WorkflowNodeType.Notify, "workflow_node.notify.label", ], + [WorkflowNodeType.Branch, "workflow_node.branch.label", ], + [WorkflowNodeType.ExecuteResultBranch, "workflow_node.execute_result_branch.label", ], + ] satisfies Array<[WorkflowNodeType, string, React.ReactNode]> + ) .filter(([type]) => { const hasExecuteResult = [ WorkflowNodeType.Apply, @@ -50,17 +44,21 @@ const AddNode = ({ node, disabled }: AddNodeProps) => { }) .map(([type, label, icon]) => { return { - key: type as string, + key: type, disabled: disabled, - label: t(label as string), - icon: icon, + label: t(label), + icon: ( + + {icon} + + ), onClick: () => { - const nextNode = newNode(type as WorkflowNodeType); + const nextNode = newNode(type); addNode(nextNode, node.id); }, }; }); - }, [node.id, disabled, node.type]); + }, [node.id, node.type, disabled]); return (
diff --git a/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx b/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx index ab05a5264..19ffb0f22 100644 --- a/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx +++ b/ui/src/components/workflow/node/ApplyNodeConfigForm.tsx @@ -1,7 +1,7 @@ import { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router"; -import { IconChevronRight, IconHelp, IconPlus, IconX } from "@tabler/icons-react"; +import { IconChevronRight, IconCircleMinus, IconHelp, IconPlus } from "@tabler/icons-react"; import { useControllableValue } from "ahooks"; import { AutoComplete, Button, Divider, Flex, Form, type FormInstance, Input, InputNumber, Select, Switch, Tooltip, Typography } from "antd"; import { createSchemaFieldRule } from "antd-zod"; @@ -607,7 +607,7 @@ const EmailInput = memo(
))} - diff --git a/ui/src/components/workflow/node/_SharedNode.tsx b/ui/src/components/workflow/node/_SharedNode.tsx index 0935708ad..cced54a3c 100644 --- a/ui/src/components/workflow/node/_SharedNode.tsx +++ b/ui/src/components/workflow/node/_SharedNode.tsx @@ -145,7 +145,7 @@ const SharedNodeMenu = ({ menus, trigger, node, disabled, branchId, branchIndex, disabled: disabled, label: isNodeBranchLike(node) ? t("workflow_node.action.rename_branch") : t("workflow_node.action.rename_node"), icon: ( - + ), @@ -180,7 +180,7 @@ const SharedNodeMenu = ({ menus, trigger, node, disabled, branchId, branchIndex, disabled: disabled || isNodeUnduplicatable(node), label: isNodeBranchLike(node) ? t("workflow_node.action.duplicate_branch") : t("workflow_node.action.duplicate_node"), icon: ( - + ), @@ -194,7 +194,7 @@ const SharedNodeMenu = ({ menus, trigger, node, disabled, branchId, branchIndex, disabled: disabled || isNodeUnremovable(node), label: isNodeBranchLike(node) ? t("workflow_node.action.remove_branch") : t("workflow_node.action.remove_node"), icon: ( - + ), diff --git a/ui/src/pages/ConsoleLayout.tsx b/ui/src/pages/ConsoleLayout.tsx index 7700ade05..ceff2e665 100644 --- a/ui/src/pages/ConsoleLayout.tsx +++ b/ui/src/pages/ConsoleLayout.tsx @@ -79,7 +79,7 @@ const ConsoleLayout = () => { { key: "theme", icon: ( - + ), @@ -89,7 +89,7 @@ const ConsoleLayout = () => { { key: "locale", icon: ( - + ), @@ -99,7 +99,7 @@ const ConsoleLayout = () => { { key: "document", icon: ( - + ), @@ -110,7 +110,7 @@ const ConsoleLayout = () => { key: "logout", danger: true, icon: ( - + ), @@ -166,20 +166,26 @@ const SiderMenu = memo(({ onSelect }: { onSelect?: (key: string) => void }) => { const MENU_KEY_CERTIFICATES = "/certificates"; const MENU_KEY_ACCESSES = "/accesses"; const MENU_KEY_SETTINGS = "/settings"; - const menuItems: Required["items"] = [ - [MENU_KEY_HOME, , t("dashboard.page.title")], - [MENU_KEY_WORKFLOWS, , t("workflow.page.title")], - [MENU_KEY_CERTIFICATES, , t("certificate.page.title")], - [MENU_KEY_ACCESSES, , t("access.page.title")], - [MENU_KEY_SETTINGS, , t("settings.page.title")], - ].map(([key, icon, label]) => { + const menuItems: Required["items"] = ( + [ + [MENU_KEY_HOME, "dashboard.page.title", ], + [MENU_KEY_WORKFLOWS, "workflow.page.title", ], + [MENU_KEY_CERTIFICATES, "certificate.page.title", ], + [MENU_KEY_ACCESSES, "access.page.title", ], + [MENU_KEY_SETTINGS, "settings.page.title", ], + ] satisfies Array<[string, string, React.ReactNode]> + ).map(([key, label, icon]) => { return { - key: key as string, - icon: {icon}, - label: label, + key: key, + icon: ( + + {icon} + + ), + label: t(label), onClick: () => { - navigate(key as string); - onSelect?.(key as string); + navigate(key); + onSelect?.(key); }, }; }); diff --git a/ui/src/pages/accesses/AccessList.tsx b/ui/src/pages/accesses/AccessList.tsx index 2e760ccb7..22329a407 100644 --- a/ui/src/pages/accesses/AccessList.tsx +++ b/ui/src/pages/accesses/AccessList.tsx @@ -48,12 +48,11 @@ const AccessList = () => { { key: "name", title: t("access.props.name"), - ellipsis: true, render: (_, record) => { return ( -
+
-
+
{record.name || "\u00A0"} {t(accessProvidersMap.get(record.provider)?.name ?? "") || "\u00A0"} @@ -204,7 +203,7 @@ const AccessList = () => { title: {t("access.action.delete")}, content: , icon: ( - + ), diff --git a/ui/src/pages/certificates/CertificateList.tsx b/ui/src/pages/certificates/CertificateList.tsx index 49c0b4a3b..567770ffc 100644 --- a/ui/src/pages/certificates/CertificateList.tsx +++ b/ui/src/pages/certificates/CertificateList.tsx @@ -49,36 +49,41 @@ const CertificateList = () => { { key: "expiry", title: t("certificate.props.validity"), - ellipsis: true, sorter: true, sortOrder: sorter.columnKey === "expiry" ? sorter.order : undefined, render: (_, record) => { const total = dayjs(record.expireAt).diff(dayjs(record.created), "d") + 1; - const expired = dayjs().isAfter(dayjs(record.expireAt)); - const leftDays = dayjs(record.expireAt).diff(dayjs(), "d"); + const isExpired = dayjs().isAfter(dayjs(record.expireAt)); + const leftHours = dayjs(record.expireAt).diff(dayjs(), "h"); + const leftDays = Math.round(leftHours / 24); return ( -
- {!expired ? ( - leftDays >= 1 ? ( - -   +
+ {!isExpired ? ( + leftDays >= 20 ? ( + +   + {t("certificate.props.validity.left_days", { left: leftDays, total })} + + ) : leftDays >= 1 ? ( + +   {t("certificate.props.validity.left_days", { left: leftDays, total })} ) : ( - -   + +   {t("certificate.props.validity.less_than_a_day")} ) ) : ( - -   + +   {t("certificate.props.validity.expired")} )} - + {t("certificate.props.validity.expiration", { date: dayjs(record.expireAt).format("YYYY-MM-DD") })}
@@ -89,24 +94,23 @@ const CertificateList = () => { key: "brand", title: t("certificate.props.brand"), render: (_, record) => ( -
- {record.issuerOrg || "\u00A0"} - {record.keyAlgorithm || "\u00A0"} +
+ {record.issuerOrg || "\u00A0"} + {record.keyAlgorithm || "\u00A0"}
), }, { key: "source", title: t("certificate.props.source"), - ellipsis: true, render: (_, record) => { const workflowId = record.workflowId; return ( -
- {t(`certificate.props.source.${record.source}`)} +
+ {t(`certificate.props.source.${record.source}`)} { e.stopPropagation(); if (workflowId) { @@ -226,7 +230,7 @@ const CertificateList = () => { title: {t("certificate.action.delete")}, content: , icon: ( - + ), diff --git a/ui/src/pages/dashboard/Dashboard.tsx b/ui/src/pages/dashboard/Dashboard.tsx index 9f3a35e04..e3e232734 100644 --- a/ui/src/pages/dashboard/Dashboard.tsx +++ b/ui/src/pages/dashboard/Dashboard.tsx @@ -189,8 +189,12 @@ const StatisticCards = () => { }; const WorkflowRunHistoryTable = () => { + const navigate = useNavigate(); + const { t } = useTranslation(); + const { notification } = App.useApp(); + const tableColumns: TableProps["columns"] = [ { key: "$index", @@ -202,27 +206,27 @@ const WorkflowRunHistoryTable = () => { { key: "name", title: t("workflow.props.name"), - ellipsis: true, render: (_, record) => { const workflow = record.expand?.workflowId; return ( - { - if (workflow) { - navigate(`/workflows/${workflow.id}`); - } - }} - > - {workflow?.name ?? {t(`#${record.workflowId}`)}} - +
+ { + if (workflow) { + navigate(`/workflows/${workflow.id}`); + } + }} + > + {workflow?.name ?? {t(`#${record.workflowId}`)}} + +
); }, }, { key: "status", title: t("workflow_run.props.status"), - ellipsis: true, render: (_, record) => { return ; }, diff --git a/ui/src/pages/settings/Settings.tsx b/ui/src/pages/settings/Settings.tsx index 05f7f186a..0c22af811 100644 --- a/ui/src/pages/settings/Settings.tsx +++ b/ui/src/pages/settings/Settings.tsx @@ -40,7 +40,11 @@ const Settings = () => { items={menus.map(([key, label, icon]) => ({ key, label: t(label), - icon: {icon}, + icon: ( + + {icon} + + ), }))} onClick={({ key }) => { setMenuValue(key); @@ -58,7 +62,11 @@ const Settings = () => { items={menus.map(([key, label, icon]) => ({ key, label: t(label), - icon: {icon}, + icon: ( + + {icon} + + ), }))} onClick={({ key }) => { setMenuValue(key); diff --git a/ui/src/pages/workflows/WorkflowDetail.tsx b/ui/src/pages/workflows/WorkflowDetail.tsx index 531ef7e8d..380c87d77 100644 --- a/ui/src/pages/workflows/WorkflowDetail.tsx +++ b/ui/src/pages/workflows/WorkflowDetail.tsx @@ -93,7 +93,7 @@ const WorkflowDetail = () => { title: {t("workflow.action.delete")}, content: , icon: ( - + ), @@ -246,7 +246,7 @@ const WorkflowDetail = () => { key: "orchestration", label: t("workflow.detail.orchestration.tab"), icon: ( - + ), @@ -255,7 +255,7 @@ const WorkflowDetail = () => { key: "runs", label: t("workflow.detail.runs.tab"), icon: ( - + ), diff --git a/ui/src/pages/workflows/WorkflowList.tsx b/ui/src/pages/workflows/WorkflowList.tsx index 7e1e8e749..e4a3d6cc2 100644 --- a/ui/src/pages/workflows/WorkflowList.tsx +++ b/ui/src/pages/workflows/WorkflowList.tsx @@ -41,9 +41,8 @@ const WorkflowList = () => { { key: "name", title: t("workflow.props.name"), - ellipsis: true, render: (_, record) => ( -
+
{record.name || "\u00A0"} {record.description || "\u00A0"} @@ -54,7 +53,6 @@ const WorkflowList = () => { { key: "trigger", title: t("workflow.props.trigger"), - ellipsis: true, render: (_, record) => { const trigger = record.trigger; if (!trigger) { @@ -99,7 +97,7 @@ const WorkflowList = () => { render: (_, record) => { return ( - + {record.lastRunTime ? dayjs(record.lastRunTime!).format("YYYY-MM-DD HH:mm:ss") : ""} ); @@ -137,7 +135,7 @@ const WorkflowList = () => { color="primary" icon={} variant="text" - onClick={() => { + onClick={(e) => { e.stopPropagation(); handleRecordDuplicateClick(record); }} @@ -276,7 +274,7 @@ const WorkflowList = () => { title: {t("workflow.action.delete")}, content: , icon: ( - + ), diff --git a/ui/src/pages/workflows/WorkflowNew.tsx b/ui/src/pages/workflows/WorkflowNew.tsx index 8fb846302..8c9d86360 100644 --- a/ui/src/pages/workflows/WorkflowNew.tsx +++ b/ui/src/pages/workflows/WorkflowNew.tsx @@ -25,7 +25,7 @@ const WorkflowNew = () => { const templateGridSpans = { xs: { flex: "100%" }, - md: { flex: "100%" }, + md: { flex: "50%" }, lg: { flex: "50%" }, xl: { flex: "33.3333%" }, xxl: { flex: "33.3333%" }, @@ -112,102 +112,96 @@ const WorkflowNew = () => { }; return ( -
- -
-
-

{t("workflow.new.title")}

-

{t("workflow.new.subtitle")}

-
-
-
- -
-
- -
{t("workflow.new.templates.title")}
-
- - - - } - hoverable - onClick={() => handleTemplateClick(TEMPLATE_KEY_STANDARD)} - > -
- - -
-
- - - - } - hoverable - onClick={() => handleTemplateClick(TEMPLATE_KEY_CERTTEST)} - > -
- - -
-
- - - - } - hoverable - onClick={() => handleTemplateClick(TEMPLATE_KEY_EMPTY)} - > -
- - -
-
- -
-
- - - - - - - - - - +
+
+

{t("workflow.new.title")}

+

{t("workflow.new.subtitle")}

+ +
+ +
{t("workflow.new.templates.title")}
+
+ + + + } + hoverable + onClick={() => handleTemplateClick(TEMPLATE_KEY_STANDARD)} + > +
+ + +
+
+ + + + } + hoverable + onClick={() => handleTemplateClick(TEMPLATE_KEY_CERTTEST)} + > +
+ + +
+
+ + + + } + hoverable + onClick={() => handleTemplateClick(TEMPLATE_KEY_EMPTY)} + > +
+ + +
+
+ +
+
+ + + + + + + + + +
); };