mirror of
https://github.com/certimate-go/certimate.git
synced 2026-09-24 23:10:13 +08:00
feat(ui): new AccessList page
This commit is contained in:
@@ -27,7 +27,7 @@ const Empty = (props: EmptyProps) => {
|
||||
<div className={className} style={style}>
|
||||
<div className="relative w-full overflow-hidden">
|
||||
<div className="relative top-0 left-0 z-1 flex h-full w-full py-4">
|
||||
<div className="relative w-full max-w-lg">
|
||||
<div className="relative mx-auto w-full max-w-lg">
|
||||
<div className="flex flex-col gap-2 text-center">
|
||||
<Show when={!!icon}>
|
||||
<div className="mx-auto">
|
||||
|
||||
@@ -51,7 +51,7 @@ const AccessEditDrawer = ({ data, loading, trigger, scene, usage, afterSubmit, .
|
||||
try {
|
||||
let values: AccessModel = formRef.current!.getFieldsValue();
|
||||
|
||||
if (scene === "add") {
|
||||
if (scene === "create") {
|
||||
if (data?.id) {
|
||||
throw "Invalid props: `data`";
|
||||
}
|
||||
@@ -109,7 +109,7 @@ const AccessEditDrawer = ({ data, loading, trigger, scene, usage, afterSubmit, .
|
||||
width={720}
|
||||
onClose={() => setOpen(false)}
|
||||
>
|
||||
<AccessForm ref={formRef} initialValues={data} scene={scene === "add" ? "add" : "edit"} usage={usage} />
|
||||
<AccessForm ref={formRef} initialValues={data} scene={scene === "create" ? "create" : "edit"} usage={usage} />
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -51,7 +51,7 @@ const AccessEditModal = ({ data, loading, trigger, scene, usage, afterSubmit, ..
|
||||
try {
|
||||
let values: AccessModel = formRef.current!.getFieldsValue();
|
||||
|
||||
if (scene === "add") {
|
||||
if (scene === "create") {
|
||||
if (data?.id) {
|
||||
throw "Invalid props: `data`";
|
||||
}
|
||||
@@ -113,7 +113,7 @@ const AccessEditModal = ({ data, loading, trigger, scene, usage, afterSubmit, ..
|
||||
onCancel={handleCancelClick}
|
||||
>
|
||||
<div className="pb-2 pt-4">
|
||||
<AccessForm ref={formRef} initialValues={data} scene={scene === "add" ? "add" : "edit"} usage={usage} />
|
||||
<AccessForm ref={formRef} initialValues={data} scene={scene === "create" ? "create" : "edit"} usage={usage} />
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
|
||||
@@ -86,7 +86,7 @@ import AccessFormWestcnConfig from "./AccessFormWestcnConfig";
|
||||
import AccessFormZeroSSLConfig from "./AccessFormZeroSSLConfig";
|
||||
|
||||
type AccessFormFieldValues = Partial<MaybeModelRecord<AccessModel>>;
|
||||
type AccessFormScenes = "add" | "edit";
|
||||
type AccessFormScenes = "create" | "edit";
|
||||
type AccessFormUsages = "both-dns-hosting" | "ca-only" | "notification-only";
|
||||
|
||||
export type AccessFormProps = {
|
||||
@@ -406,7 +406,7 @@ const AccessForm = forwardRef<AccessFormInstance, AccessFormProps>(({ className,
|
||||
<Form.Item name="provider" label={providerLabel} rules={[formRule]} tooltip={providerTooltip}>
|
||||
<AccessProviderSelect
|
||||
filter={providerFilter}
|
||||
disabled={scene !== "add"}
|
||||
disabled={scene !== "create"}
|
||||
placeholder={providerPlaceholder}
|
||||
showOptionTags={usage == null || (usage === "both-dns-hosting" ? { [ACCESS_USAGES.DNS]: true, [ACCESS_USAGES.HOSTING]: true } : false)}
|
||||
showSearch={!disabled}
|
||||
|
||||
@@ -326,7 +326,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<AccessEditModal
|
||||
scene="add"
|
||||
scene="create"
|
||||
trigger={
|
||||
<Button size="small" type="link">
|
||||
{t("workflow_node.apply.form.provider_access.button")}
|
||||
@@ -408,7 +408,7 @@ const ApplyNodeConfigForm = forwardRef<ApplyNodeConfigFormInstance, ApplyNodeCon
|
||||
<div className="text-right">
|
||||
<AccessEditModal
|
||||
data={{ provider: caProvidersMap.get(fieldCAProvider!)?.provider }}
|
||||
scene="add"
|
||||
scene="create"
|
||||
trigger={
|
||||
<Button size="small" type="link">
|
||||
{t("workflow_node.apply.form.ca_provider_access.button")}
|
||||
|
||||
@@ -489,7 +489,7 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
|
||||
<div className="text-right">
|
||||
<AccessEditModal
|
||||
data={{ provider: deploymentProvidersMap.get(fieldProvider!)?.provider }}
|
||||
scene="add"
|
||||
scene="create"
|
||||
trigger={
|
||||
<Button size="small" type="link">
|
||||
{t("workflow_node.deploy.form.provider_access.button")}
|
||||
|
||||
@@ -199,7 +199,7 @@ const NotifyNodeConfigForm = forwardRef<NotifyNodeConfigFormInstance, NotifyNode
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<AccessEditModal
|
||||
scene="add"
|
||||
scene="create"
|
||||
trigger={
|
||||
<Button size="small" type="link">
|
||||
{t("workflow_node.notify.form.provider_access.button")}
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
"access.page.title": "Credentials",
|
||||
"access.page.subtitle": "Credentials store authentication information (username and password, API key, OAuth tokens, etc.) to connect with specific third-party apps and services.",
|
||||
|
||||
"access.nodata": "No credentials. Please create an credential first.",
|
||||
"access.nodata": "No credentials",
|
||||
"access.nodata.title": "No credentials",
|
||||
"access.nodata.description": "Add a credential to store authentication information.",
|
||||
"access.nodata.button": "Create credential",
|
||||
|
||||
"access.search.placeholder": "Search by credential name ...",
|
||||
|
||||
@@ -13,7 +16,6 @@
|
||||
"access.action.delete.confirm": "Are you sure want to delete this \"{{name}}\" credential?<br>This action cannot be undone.",
|
||||
|
||||
"access.props.name": "Name",
|
||||
"access.props.provider": "Provider",
|
||||
"access.props.provider.usage.dns": "DNS provider",
|
||||
"access.props.provider.usage.hosting": "Hosting provider",
|
||||
"access.props.provider.usage.ca": "Certificate authority",
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"certificate.page.subtitle": "SSL certificates contain the website's public key and the website's identity, along with related information. They are generated from the execution output of workflows.",
|
||||
|
||||
"certificate.nodata.title": "No Certificates",
|
||||
"certificate.nodata.description": "Create a workflow to generate certificates!",
|
||||
"certificate.nodata.button": "Create workflow",
|
||||
"certificate.nodata.description": "Add a workflow to generate certificates!",
|
||||
"certificate.nodata.button": "Go to workflows",
|
||||
|
||||
"certificate.search.placeholder": "Search by certificate name or serial number ...",
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
"certificate.props.validity.less_than_a_day": "Less than 1 day",
|
||||
"certificate.props.validity.expired": "Expired",
|
||||
"certificate.props.validity.expiration": "Expire on {{date}}",
|
||||
"certificate.props.validity.filters.all": "All",
|
||||
"certificate.props.validity.filters.expire_soon": "Expire soon",
|
||||
"certificate.props.validity.filters.expired": "Expired",
|
||||
"certificate.props.validity.filter.all": "All",
|
||||
"certificate.props.validity.filter.expire_soon": "Expire soon",
|
||||
"certificate.props.validity.filter.expired": "Expired",
|
||||
"certificate.props.brand": "Brand",
|
||||
"certificate.props.source": "Source",
|
||||
"certificate.props.source.workflow": "Workflow",
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
"workflow.props.trigger.manual": "Manual",
|
||||
"workflow.props.last_run_at": "Last run at",
|
||||
"workflow.props.state": "State",
|
||||
"workflow.props.state.filters.enabled": "Active",
|
||||
"workflow.props.state.filters.disabled": "Inactive",
|
||||
"workflow.props.state.filter.enabled": "Active",
|
||||
"workflow.props.state.filter.disabled": "Inactive",
|
||||
"workflow.props.created_at": "Created at",
|
||||
"workflow.props.updated_at": "Updated at",
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
"access.page.title": "授权管理",
|
||||
"access.page.subtitle": "授权中存储有用于访问特定第三方应用程序和服务的身份验证信息(如账号密码、API 密钥、OAuth 令牌等)。",
|
||||
|
||||
"access.nodata": "暂无授权信息,请先新建授权",
|
||||
"access.nodata.title": "暂无授权",
|
||||
"access.nodata.description": "新建一个授权并存储身份验证信息吧~",
|
||||
"access.nodata.button": "新建授权",
|
||||
|
||||
"access.search.placeholder": "按授权名称搜索……",
|
||||
|
||||
@@ -13,7 +15,6 @@
|
||||
"access.action.delete.confirm": "确定要删除授权「{{name}}」吗?<br>注意此操作不可撤销,请谨慎操作。",
|
||||
|
||||
"access.props.name": "名称",
|
||||
"access.props.provider": "提供商",
|
||||
"access.props.provider.usage.dns": "DNS 提供商",
|
||||
"access.props.provider.usage.hosting": "主机提供商",
|
||||
"access.props.provider.usage.ca": "证书颁发机构",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
"certificate.nodata.title": "暂无证书",
|
||||
"certificate.nodata.description": "新建一个工作流去生成证书吧~",
|
||||
"certificate.nodata.button": "新建工作流",
|
||||
"certificate.nodata.button": "前往工作流",
|
||||
|
||||
"certificate.search.placeholder": "按证书名称或序列号搜索……",
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
"certificate.props.validity.less_than_a_day": "不足 1 天",
|
||||
"certificate.props.validity.expired": "已过期",
|
||||
"certificate.props.validity.expiration": "{{date}} 过期",
|
||||
"certificate.props.validity.filters.all": "全部",
|
||||
"certificate.props.validity.filters.expire_soon": "即将过期",
|
||||
"certificate.props.validity.filters.expired": "已过期",
|
||||
"certificate.props.validity.filter.all": "全部",
|
||||
"certificate.props.validity.filter.expire_soon": "即将过期",
|
||||
"certificate.props.validity.filter.expired": "已过期",
|
||||
"certificate.props.brand": "证书品牌",
|
||||
"certificate.props.source": "来源",
|
||||
"certificate.props.source.workflow": "工作流",
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
"workflow.props.trigger.manual": "手动",
|
||||
"workflow.props.last_run_at": "最近执行时间",
|
||||
"workflow.props.state": "启用状态",
|
||||
"workflow.props.state.filters.enabled": "启用",
|
||||
"workflow.props.state.filters.disabled": "未启用",
|
||||
"workflow.props.state.filter.enabled": "启用",
|
||||
"workflow.props.state.filter.disabled": "未启用",
|
||||
"workflow.props.created_at": "创建时间",
|
||||
"workflow.props.updated_at": "更新时间",
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
import { IconCopy, IconEdit, IconPlus, IconReload, IconTrash } from "@tabler/icons-react";
|
||||
import { IconCirclePlus, IconCopy, IconEdit, IconFingerprint, IconPlus, IconReload, IconTrash } from "@tabler/icons-react";
|
||||
import { useRequest } from "ahooks";
|
||||
import { App, Avatar, Button, Empty, Input, Table, type TableProps, Tabs, Tooltip, Typography } from "antd";
|
||||
import { App, Avatar, Button, Input, Skeleton, Table, type TableProps, Tabs, Tooltip, Typography } from "antd";
|
||||
import dayjs from "dayjs";
|
||||
import { ClientResponseError } from "pocketbase";
|
||||
|
||||
import AccessEditDrawer, { type AccessEditDrawerProps } from "@/components/access/AccessEditDrawer";
|
||||
import Empty from "@/components/Empty";
|
||||
import { type AccessModel } from "@/domain/access";
|
||||
import { ACCESS_USAGES, accessProvidersMap } from "@/domain/provider";
|
||||
import { useZustandShallowSelector } from "@/hooks";
|
||||
@@ -48,17 +49,16 @@ const AccessList = () => {
|
||||
key: "name",
|
||||
title: t("access.props.name"),
|
||||
ellipsis: true,
|
||||
render: (_, record) => <>{record.name}</>,
|
||||
},
|
||||
{
|
||||
key: "provider",
|
||||
title: t("access.props.provider"),
|
||||
ellipsis: true,
|
||||
render: (_, record) => {
|
||||
return (
|
||||
<div className="flex max-w-full items-center gap-2 truncate overflow-hidden">
|
||||
<Avatar shape="square" src={accessProvidersMap.get(record.provider)?.icon} size="small" />
|
||||
<Typography.Text ellipsis>{t(accessProvidersMap.get(record.provider)?.name ?? "")}</Typography.Text>
|
||||
<div className="flex max-w-full items-center gap-4 truncate overflow-hidden">
|
||||
<Avatar shape="square" src={accessProvidersMap.get(record.provider)?.icon} size="large" />
|
||||
<div className="flex max-w-full flex-col gap-1">
|
||||
<Typography.Text ellipsis>{record.name}</Typography.Text>
|
||||
<Typography.Text ellipsis type="secondary">
|
||||
{t(accessProvidersMap.get(record.provider)?.name ?? "")}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
@@ -71,14 +71,6 @@ const AccessList = () => {
|
||||
return dayjs(record.created!).format("YYYY-MM-DD HH:mm:ss");
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "updatedAt",
|
||||
title: t("access.props.updated_at"),
|
||||
ellipsis: true,
|
||||
render: (_, record) => {
|
||||
return dayjs(record.updated!).format("YYYY-MM-DD HH:mm:ss");
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "$action",
|
||||
align: "end",
|
||||
@@ -86,35 +78,36 @@ const AccessList = () => {
|
||||
width: 120,
|
||||
render: (_, record) => (
|
||||
<div className="flex items-center justify-end">
|
||||
<AccessEditDrawer
|
||||
data={record}
|
||||
usage={filters["usage"] as AccessUsageProp}
|
||||
scene="edit"
|
||||
trigger={
|
||||
<Tooltip title={t("access.action.edit")}>
|
||||
<Button color="primary" icon={<IconEdit size="1.25em" />} variant="text" />
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
|
||||
<AccessEditDrawer
|
||||
data={{ ...record, id: undefined, name: `${record.name}-copy` }}
|
||||
usage={filters["usage"] as AccessUsageProp}
|
||||
scene="add"
|
||||
trigger={
|
||||
<Tooltip title={t("access.action.duplicate")}>
|
||||
<Button color="primary" icon={<IconCopy size="1.25em" />} variant="text" />
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
|
||||
<Tooltip title={t("access.action.edit")}>
|
||||
<Button
|
||||
color="primary"
|
||||
icon={<IconEdit size="1.25em" />}
|
||||
variant="text"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleRecordDetailClick(record);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title={t("access.action.duplicate")}>
|
||||
<Button
|
||||
color="primary"
|
||||
icon={<IconCopy size="1.25em" />}
|
||||
variant="text"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleRecordDuplicateClick(record);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title={t("access.action.delete")}>
|
||||
<Button
|
||||
color="danger"
|
||||
icon={<IconTrash size="1.25em" />}
|
||||
variant="text"
|
||||
onClick={() => {
|
||||
handleDeleteClick(record);
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleRecordDeleteClick(record);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
@@ -190,7 +183,23 @@ const AccessList = () => {
|
||||
fetchAccesses();
|
||||
};
|
||||
|
||||
const handleDeleteClick = async (access: AccessModel) => {
|
||||
const [detailRecord, setDetailRecord] = useState<MaybeModelRecord<AccessModel>>();
|
||||
const [detailScene, setDetailScene] = useState<AccessEditDrawerProps["scene"]>("create");
|
||||
const [detailOpen, setDetailOpen] = useState<boolean>(false);
|
||||
|
||||
const handleRecordDetailClick = (access: AccessModel) => {
|
||||
setDetailRecord(access);
|
||||
setDetailScene("edit");
|
||||
setDetailOpen(true);
|
||||
};
|
||||
|
||||
const handleRecordDuplicateClick = (access: AccessModel) => {
|
||||
setDetailRecord({ ...access, id: undefined, name: `${access.name}-copy` });
|
||||
setDetailScene("create");
|
||||
setDetailOpen(true);
|
||||
};
|
||||
|
||||
const handleRecordDeleteClick = async (access: AccessModel) => {
|
||||
modal.confirm({
|
||||
title: <span className="text-error">{t("access.action.delete")}</span>,
|
||||
content: <span dangerouslySetInnerHTML={{ __html: t("access.action.delete.confirm", { name: access.name }) }} />,
|
||||
@@ -238,8 +247,8 @@ const AccessList = () => {
|
||||
</div>
|
||||
<div>
|
||||
<AccessEditDrawer
|
||||
scene="create"
|
||||
usage={filters["usage"] as AccessUsageProp}
|
||||
scene="add"
|
||||
trigger={
|
||||
<Button className="text-sm" icon={<IconPlus size="1.25em" />} size="large" type="primary">
|
||||
{t("access.action.create")}
|
||||
@@ -269,12 +278,32 @@ const AccessList = () => {
|
||||
size="large"
|
||||
onChange={(key) => handleTabChange(key)}
|
||||
/>
|
||||
|
||||
<Table<AccessModel>
|
||||
columns={tableColumns}
|
||||
dataSource={tableData}
|
||||
loading={!loadedAtOnce || loading}
|
||||
locale={{
|
||||
emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={t("access.nodata")} />,
|
||||
emptyText: loading ? (
|
||||
<Skeleton />
|
||||
) : (
|
||||
<Empty
|
||||
title={t("access.nodata.title")}
|
||||
description={t("access.nodata.description")}
|
||||
icon={<IconFingerprint size={24} />}
|
||||
extra={
|
||||
<AccessEditDrawer
|
||||
scene="create"
|
||||
usage={filters["usage"] as AccessUsageProp}
|
||||
trigger={
|
||||
<Button icon={<IconCirclePlus size="1.25em" />} type="primary">
|
||||
{t("access.action.create")}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
pagination={{
|
||||
current: page,
|
||||
@@ -290,9 +319,17 @@ const AccessList = () => {
|
||||
setPageSize(pageSize);
|
||||
},
|
||||
}}
|
||||
rowClassName="cursor-pointer"
|
||||
rowKey={(record) => record.id}
|
||||
scroll={{ x: "max(100%, 960px)" }}
|
||||
onRow={(record) => ({
|
||||
onClick: () => {
|
||||
handleRecordDetailClick(record);
|
||||
},
|
||||
})}
|
||||
/>
|
||||
|
||||
<AccessEditDrawer data={detailRecord} open={detailOpen} scene={detailScene} usage={filters["usage"] as AccessUsageProp} onOpenChange={setDetailOpen} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { IconBrowserShare, IconCirclePlus, IconReload, IconShieldCheckeredFilled, IconTrash } from "@tabler/icons-react";
|
||||
import { IconBrowserShare, IconExternalLink, IconReload, IconShieldCheckeredFilled, IconTrash } from "@tabler/icons-react";
|
||||
import { useRequest } from "ahooks";
|
||||
import { App, Button, Input, Segmented, Skeleton, Table, type TableProps, Tooltip, Typography } from "antd";
|
||||
import dayjs from "dayjs";
|
||||
@@ -34,6 +34,13 @@ const CertificateList = () => {
|
||||
const [pageSize, setPageSize] = useState<number>(() => parseInt(+searchParams.get("perPage")! + "") || 15);
|
||||
|
||||
const tableColumns: TableProps<CertificateModel>["columns"] = [
|
||||
{
|
||||
key: "$index",
|
||||
align: "center",
|
||||
fixed: "left",
|
||||
width: 50,
|
||||
render: (_, __, index) => (page - 1) * pageSize + index + 1,
|
||||
},
|
||||
{
|
||||
key: "name",
|
||||
title: t("certificate.props.subject_alt_names"),
|
||||
@@ -209,8 +216,8 @@ const CertificateList = () => {
|
||||
const [detailRecord, setDetailRecord] = useState<CertificateModel>();
|
||||
const [detailOpen, setDetailOpen] = useState<boolean>(false);
|
||||
|
||||
const handleRecordDetailClick = (record: CertificateModel) => {
|
||||
setDetailRecord(record);
|
||||
const handleRecordDetailClick = (certificate: CertificateModel) => {
|
||||
setDetailRecord(certificate);
|
||||
setDetailOpen(true);
|
||||
};
|
||||
|
||||
@@ -252,9 +259,9 @@ const CertificateList = () => {
|
||||
<Segmented
|
||||
className="shadow-xs"
|
||||
options={[
|
||||
{ label: <span className="text-sm">{t("certificate.props.validity.filters.all")}</span>, value: "" },
|
||||
{ label: <span className="text-sm">{t("certificate.props.validity.filters.expire_soon")}</span>, value: "expireSoon" },
|
||||
{ label: <span className="text-sm">{t("certificate.props.validity.filters.expired")}</span>, value: "expired" },
|
||||
{ label: <span className="text-sm">{t("certificate.props.validity.filter.all")}</span>, value: "" },
|
||||
{ label: <span className="text-sm">{t("certificate.props.validity.filter.expire_soon")}</span>, value: "expireSoon" },
|
||||
{ label: <span className="text-sm">{t("certificate.props.validity.filter.expired")}</span>, value: "expired" },
|
||||
]}
|
||||
size="large"
|
||||
value={(filters["state"] as string) || ""}
|
||||
@@ -300,8 +307,8 @@ const CertificateList = () => {
|
||||
{t("common.button.reload")}
|
||||
</Button>
|
||||
) : (
|
||||
<Button icon={<IconCirclePlus size="1.25em" />} type="primary" onClick={() => navigate("/workflows/new")}>
|
||||
{t("workflow.action.create")}
|
||||
<Button icon={<IconExternalLink size="1.25em" />} type="primary" onClick={() => navigate("/workflows")}>
|
||||
{t("certificate.nodata.button")}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -77,8 +77,8 @@ const WorkflowList = () => {
|
||||
defaultFilteredValue: searchParams.has("state") ? [searchParams.get("state") as string] : undefined,
|
||||
filterDropdown: ({ setSelectedKeys, confirm, clearFilters }) => {
|
||||
const items: Required<MenuProps>["items"] = [
|
||||
["enabled", "workflow.props.state.filters.enabled"],
|
||||
["disabled", "workflow.props.state.filters.disabled"],
|
||||
["enabled", "workflow.props.state.filter.enabled"],
|
||||
["disabled", "workflow.props.state.filter.disabled"],
|
||||
].map(([key, label]) => {
|
||||
return {
|
||||
key,
|
||||
|
||||
Reference in New Issue
Block a user