mirror of
https://github.com/usual2970/certimate.git
synced 2025-06-18 18:29:58 +00:00
feat: search by keyword on AccessList, CertificateList, WorkflowList
This commit is contained in:
parent
970fba90e0
commit
664bb692b6
@ -212,7 +212,6 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
|
|||||||
const handleProviderSelect = (value: string) => {
|
const handleProviderSelect = (value: string) => {
|
||||||
if (fieldProvider === value) return;
|
if (fieldProvider === value) return;
|
||||||
|
|
||||||
// TODO: 暂时不支持切换部署目标,需后端调整,否则之前若存在部署结果输出就不会再部署
|
|
||||||
// 切换部署目标时重置表单,避免其他部署目标的配置字段影响当前部署目标
|
// 切换部署目标时重置表单,避免其他部署目标的配置字段影响当前部署目标
|
||||||
if (initialValues?.provider === value) {
|
if (initialValues?.provider === value) {
|
||||||
formInst.resetFields();
|
formInst.resetFields();
|
||||||
@ -276,13 +275,7 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
|
|||||||
fallback={<DeployProviderPicker autoFocus placeholder={t("workflow_node.deploy.search.provider.placeholder")} onSelect={handleProviderPick} />}
|
fallback={<DeployProviderPicker autoFocus placeholder={t("workflow_node.deploy.search.provider.placeholder")} onSelect={handleProviderPick} />}
|
||||||
>
|
>
|
||||||
<Form.Item name="provider" label={t("workflow_node.deploy.form.provider.label")} rules={[formRule]}>
|
<Form.Item name="provider" label={t("workflow_node.deploy.form.provider.label")} rules={[formRule]}>
|
||||||
<DeployProviderSelect
|
<DeployProviderSelect allowClear placeholder={t("workflow_node.deploy.form.provider.placeholder")} showSearch onSelect={handleProviderSelect} />
|
||||||
allowClear
|
|
||||||
disabled={!!initialValues?.provider}
|
|
||||||
placeholder={t("workflow_node.deploy.form.provider.placeholder")}
|
|
||||||
showSearch
|
|
||||||
onSelect={handleProviderSelect}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item className="mb-0">
|
<Form.Item className="mb-0">
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
"access.nodata": "No accesses. Please create an authorization first.",
|
"access.nodata": "No accesses. Please create an authorization first.",
|
||||||
|
|
||||||
|
"access.search.placeholder": "Search by access name ...",
|
||||||
|
|
||||||
"access.action.add": "Create authorization",
|
"access.action.add": "Create authorization",
|
||||||
"access.action.edit": "Edit authorization",
|
"access.action.edit": "Edit authorization",
|
||||||
"access.action.duplicate": "Duplicate authorization",
|
"access.action.duplicate": "Duplicate authorization",
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
"certificate.nodata": "No certificates. Please create a workflow to generate certificates! 😀",
|
"certificate.nodata": "No certificates. Please create a workflow to generate certificates! 😀",
|
||||||
|
|
||||||
|
"certificate.search.placeholder": "Search by certificate name or serial number ...",
|
||||||
|
|
||||||
"certificate.action.view": "View certificate",
|
"certificate.action.view": "View certificate",
|
||||||
"certificate.action.delete": "Delete certificate",
|
"certificate.action.delete": "Delete certificate",
|
||||||
"certificate.action.delete.confirm": "Are you sure to delete this certificate?",
|
"certificate.action.delete.confirm": "Are you sure to delete this certificate?",
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
"workflow.nodata": "No workflows. Please create a workflow to generate certificates! 😀",
|
"workflow.nodata": "No workflows. Please create a workflow to generate certificates! 😀",
|
||||||
|
|
||||||
|
"workflow.search.placeholder": "Search by workflow name ...",
|
||||||
|
|
||||||
"workflow.action.create": "Create workflow",
|
"workflow.action.create": "Create workflow",
|
||||||
"workflow.action.edit": "Edit workflow",
|
"workflow.action.edit": "Edit workflow",
|
||||||
"workflow.action.delete": "Delete workflow",
|
"workflow.action.delete": "Delete workflow",
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
"access.nodata": "暂无授权信息,请先新建授权",
|
"access.nodata": "暂无授权信息,请先新建授权",
|
||||||
|
|
||||||
|
"access.search.placeholder": "按授权名称搜索……",
|
||||||
|
|
||||||
"access.action.add": "新建授权",
|
"access.action.add": "新建授权",
|
||||||
"access.action.edit": "编辑授权",
|
"access.action.edit": "编辑授权",
|
||||||
"access.action.duplicate": "复制授权",
|
"access.action.duplicate": "复制授权",
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
"certificate.nodata": "暂无证书,新建一个工作流去生成证书吧~ 😀",
|
"certificate.nodata": "暂无证书,新建一个工作流去生成证书吧~ 😀",
|
||||||
|
|
||||||
|
"certificate.search.placeholder": "按证书名称或序列号搜索……",
|
||||||
|
|
||||||
"certificate.action.view": "查看证书",
|
"certificate.action.view": "查看证书",
|
||||||
"certificate.action.delete": "删除证书",
|
"certificate.action.delete": "删除证书",
|
||||||
"certificate.action.delete.confirm": "确定要删除此证书吗?",
|
"certificate.action.delete.confirm": "确定要删除此证书吗?",
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
"workflow.nodata": "暂无工作流,请先新建工作流",
|
"workflow.nodata": "暂无工作流,请先新建工作流",
|
||||||
|
|
||||||
|
"workflow.search.placeholder": "按工作流名称搜索……",
|
||||||
|
|
||||||
"workflow.action.create": "新建工作流",
|
"workflow.action.create": "新建工作流",
|
||||||
"workflow.action.edit": "编辑工作流",
|
"workflow.action.edit": "编辑工作流",
|
||||||
"workflow.action.delete": "删除工作流",
|
"workflow.action.delete": "删除工作流",
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useSearchParams } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
DeleteOutlined as DeleteOutlinedIcon,
|
DeleteOutlined as DeleteOutlinedIcon,
|
||||||
EditOutlined as EditOutlinedIcon,
|
EditOutlined as EditOutlinedIcon,
|
||||||
PlusOutlined as PlusOutlinedIcon,
|
PlusOutlined as PlusOutlinedIcon,
|
||||||
|
ReloadOutlined as ReloadOutlinedIcon,
|
||||||
SnippetsOutlined as SnippetsOutlinedIcon,
|
SnippetsOutlined as SnippetsOutlinedIcon,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { PageHeader } from "@ant-design/pro-components";
|
import { PageHeader } from "@ant-design/pro-components";
|
||||||
import { useRequest } from "ahooks";
|
import { useRequest } from "ahooks";
|
||||||
import { Avatar, Button, Empty, Modal, Space, Table, type TableProps, Tooltip, Typography, notification } from "antd";
|
import { Avatar, Button, Card, Empty, Flex, Input, Modal, Space, Table, type TableProps, Tooltip, Typography, notification } from "antd";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { ClientResponseError } from "pocketbase";
|
import { ClientResponseError } from "pocketbase";
|
||||||
|
|
||||||
@ -20,6 +22,8 @@ import { useAccessesStore } from "@/stores/access";
|
|||||||
import { getErrMsg } from "@/utils/error";
|
import { getErrMsg } from "@/utils/error";
|
||||||
|
|
||||||
const AccessList = () => {
|
const AccessList = () => {
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [modalApi, ModelContextHolder] = Modal.useModal();
|
const [modalApi, ModelContextHolder] = Modal.useModal();
|
||||||
@ -116,6 +120,12 @@ const AccessList = () => {
|
|||||||
const [tableData, setTableData] = useState<AccessModel[]>([]);
|
const [tableData, setTableData] = useState<AccessModel[]>([]);
|
||||||
const [tableTotal, setTableTotal] = useState<number>(0);
|
const [tableTotal, setTableTotal] = useState<number>(0);
|
||||||
|
|
||||||
|
const [filters, setFilters] = useState<Record<string, unknown>>(() => {
|
||||||
|
return {
|
||||||
|
keyword: searchParams.get("keyword"),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const [page, setPage] = useState<number>(1);
|
const [page, setPage] = useState<number>(1);
|
||||||
const [pageSize, setPageSize] = useState<number>(10);
|
const [pageSize, setPageSize] = useState<number>(10);
|
||||||
|
|
||||||
@ -134,14 +144,21 @@ const AccessList = () => {
|
|||||||
() => {
|
() => {
|
||||||
const startIndex = (page - 1) * pageSize;
|
const startIndex = (page - 1) * pageSize;
|
||||||
const endIndex = startIndex + pageSize;
|
const endIndex = startIndex + pageSize;
|
||||||
const items = accesses.slice(startIndex, endIndex);
|
const list = accesses.filter((e) => {
|
||||||
|
const keyword = (filters["keyword"] as string | undefined)?.trim();
|
||||||
|
if (keyword) {
|
||||||
|
return e.name.includes(keyword);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
items,
|
items: list.slice(startIndex, endIndex),
|
||||||
totalItems: accesses.length,
|
totalItems: list.length,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
refreshDeps: [accesses, page, pageSize],
|
refreshDeps: [accesses, filters, page, pageSize],
|
||||||
onSuccess: (res) => {
|
onSuccess: (res) => {
|
||||||
setTableData(res.items);
|
setTableData(res.items);
|
||||||
setTableTotal(res.totalItems);
|
setTableTotal(res.totalItems);
|
||||||
@ -149,6 +166,16 @@ const AccessList = () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleSearch = (value: string) => {
|
||||||
|
setFilters((prev) => ({ ...prev, keyword: value }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReloadClick = () => {
|
||||||
|
if (loading) return;
|
||||||
|
|
||||||
|
fetchAccesses();
|
||||||
|
};
|
||||||
|
|
||||||
const handleDeleteClick = async (data: AccessModel) => {
|
const handleDeleteClick = async (data: AccessModel) => {
|
||||||
modalApi.confirm({
|
modalApi.confirm({
|
||||||
title: t("access.action.delete"),
|
title: t("access.action.delete"),
|
||||||
@ -186,30 +213,43 @@ const AccessList = () => {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Table<AccessModel>
|
<Card size="small">
|
||||||
columns={tableColumns}
|
<div className="mb-4">
|
||||||
dataSource={tableData}
|
<Flex gap="small">
|
||||||
loading={!loadedAtOnce || loading}
|
<div className="flex-1">
|
||||||
locale={{
|
<Input.Search allowClear defaultValue={filters["keyword"] as string} placeholder={t("access.search.placeholder")} onSearch={handleSearch} />
|
||||||
emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={t("access.nodata")} />,
|
</div>
|
||||||
}}
|
<div>
|
||||||
pagination={{
|
<Button icon={<ReloadOutlinedIcon spin={loading} />} onClick={handleReloadClick} />
|
||||||
current: page,
|
</div>
|
||||||
pageSize: pageSize,
|
</Flex>
|
||||||
total: tableTotal,
|
</div>
|
||||||
showSizeChanger: true,
|
|
||||||
onChange: (page: number, pageSize: number) => {
|
<Table<AccessModel>
|
||||||
setPage(page);
|
columns={tableColumns}
|
||||||
setPageSize(pageSize);
|
dataSource={tableData}
|
||||||
},
|
loading={!loadedAtOnce || loading}
|
||||||
onShowSizeChange: (page: number, pageSize: number) => {
|
locale={{
|
||||||
setPage(page);
|
emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={t("access.nodata")} />,
|
||||||
setPageSize(pageSize);
|
}}
|
||||||
},
|
pagination={{
|
||||||
}}
|
current: page,
|
||||||
rowKey={(record) => record.id}
|
pageSize: pageSize,
|
||||||
scroll={{ x: "max(100%, 960px)" }}
|
total: tableTotal,
|
||||||
/>
|
showSizeChanger: true,
|
||||||
|
onChange: (page: number, pageSize: number) => {
|
||||||
|
setPage(page);
|
||||||
|
setPageSize(pageSize);
|
||||||
|
},
|
||||||
|
onShowSizeChange: (page: number, pageSize: number) => {
|
||||||
|
setPage(page);
|
||||||
|
setPageSize(pageSize);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
rowKey={(record) => record.id}
|
||||||
|
scroll={{ x: "max(100%, 960px)" }}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,10 +1,28 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||||
import { DeleteOutlined as DeleteOutlinedIcon, SelectOutlined as SelectOutlinedIcon } from "@ant-design/icons";
|
import { DeleteOutlined as DeleteOutlinedIcon, ReloadOutlined as ReloadOutlinedIcon, SelectOutlined as SelectOutlinedIcon } from "@ant-design/icons";
|
||||||
import { PageHeader } from "@ant-design/pro-components";
|
import { PageHeader } from "@ant-design/pro-components";
|
||||||
import { useRequest } from "ahooks";
|
import { useRequest } from "ahooks";
|
||||||
import { Button, Divider, Empty, Menu, type MenuProps, Modal, Radio, Space, Table, type TableProps, Tooltip, Typography, notification, theme } from "antd";
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Divider,
|
||||||
|
Empty,
|
||||||
|
Flex,
|
||||||
|
Input,
|
||||||
|
Menu,
|
||||||
|
type MenuProps,
|
||||||
|
Modal,
|
||||||
|
Radio,
|
||||||
|
Space,
|
||||||
|
Table,
|
||||||
|
type TableProps,
|
||||||
|
Tooltip,
|
||||||
|
Typography,
|
||||||
|
notification,
|
||||||
|
theme,
|
||||||
|
} from "antd";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { ClientResponseError } from "pocketbase";
|
import { ClientResponseError } from "pocketbase";
|
||||||
|
|
||||||
@ -191,6 +209,7 @@ const CertificateList = () => {
|
|||||||
|
|
||||||
const [filters, setFilters] = useState<Record<string, unknown>>(() => {
|
const [filters, setFilters] = useState<Record<string, unknown>>(() => {
|
||||||
return {
|
return {
|
||||||
|
keyword: searchParams.get("keyword"),
|
||||||
state: searchParams.get("state"),
|
state: searchParams.get("state"),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -205,9 +224,10 @@ const CertificateList = () => {
|
|||||||
} = useRequest(
|
} = useRequest(
|
||||||
() => {
|
() => {
|
||||||
return listCertificate({
|
return listCertificate({
|
||||||
|
keyword: filters["keyword"] as string,
|
||||||
|
state: filters["state"] as ListCertificateRequest["state"],
|
||||||
page: page,
|
page: page,
|
||||||
perPage: pageSize,
|
perPage: pageSize,
|
||||||
state: filters["state"] as ListCertificateRequest["state"],
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -229,6 +249,16 @@ const CertificateList = () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleSearch = (value: string) => {
|
||||||
|
setFilters((prev) => ({ ...prev, keyword: value.trim() }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReloadClick = () => {
|
||||||
|
if (loading) return;
|
||||||
|
|
||||||
|
refreshData();
|
||||||
|
};
|
||||||
|
|
||||||
const handleDeleteClick = (certificate: CertificateModel) => {
|
const handleDeleteClick = (certificate: CertificateModel) => {
|
||||||
modalApi.confirm({
|
modalApi.confirm({
|
||||||
title: t("certificate.action.delete"),
|
title: t("certificate.action.delete"),
|
||||||
@ -255,30 +285,43 @@ const CertificateList = () => {
|
|||||||
|
|
||||||
<PageHeader title={t("certificate.page.title")} />
|
<PageHeader title={t("certificate.page.title")} />
|
||||||
|
|
||||||
<Table<CertificateModel>
|
<Card size="small">
|
||||||
columns={tableColumns}
|
<div className="mb-4">
|
||||||
dataSource={tableData}
|
<Flex gap="small">
|
||||||
loading={loading}
|
<div className="flex-1">
|
||||||
locale={{
|
<Input.Search allowClear defaultValue={filters["keyword"] as string} placeholder={t("certificate.search.placeholder")} onSearch={handleSearch} />
|
||||||
emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={getErrMsg(loadedError ?? t("certificate.nodata"))} />,
|
</div>
|
||||||
}}
|
<div>
|
||||||
pagination={{
|
<Button icon={<ReloadOutlinedIcon spin={loading} />} onClick={handleReloadClick} />
|
||||||
current: page,
|
</div>
|
||||||
pageSize: pageSize,
|
</Flex>
|
||||||
total: tableTotal,
|
</div>
|
||||||
showSizeChanger: true,
|
|
||||||
onChange: (page: number, pageSize: number) => {
|
<Table<CertificateModel>
|
||||||
setPage(page);
|
columns={tableColumns}
|
||||||
setPageSize(pageSize);
|
dataSource={tableData}
|
||||||
},
|
loading={loading}
|
||||||
onShowSizeChange: (page: number, pageSize: number) => {
|
locale={{
|
||||||
setPage(page);
|
emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={getErrMsg(loadedError ?? t("certificate.nodata"))} />,
|
||||||
setPageSize(pageSize);
|
}}
|
||||||
},
|
pagination={{
|
||||||
}}
|
current: page,
|
||||||
rowKey={(record) => record.id}
|
pageSize: pageSize,
|
||||||
scroll={{ x: "max(100%, 960px)" }}
|
total: tableTotal,
|
||||||
/>
|
showSizeChanger: true,
|
||||||
|
onChange: (page: number, pageSize: number) => {
|
||||||
|
setPage(page);
|
||||||
|
setPageSize(pageSize);
|
||||||
|
},
|
||||||
|
onShowSizeChange: (page: number, pageSize: number) => {
|
||||||
|
setPage(page);
|
||||||
|
setPageSize(pageSize);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
rowKey={(record) => record.id}
|
||||||
|
scroll={{ x: "max(100%, 960px)" }}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
DeleteOutlined as DeleteOutlinedIcon,
|
DeleteOutlined as DeleteOutlinedIcon,
|
||||||
EditOutlined as EditOutlinedIcon,
|
EditOutlined as EditOutlinedIcon,
|
||||||
PlusOutlined as PlusOutlinedIcon,
|
PlusOutlined as PlusOutlinedIcon,
|
||||||
|
ReloadOutlined as ReloadOutlinedIcon,
|
||||||
StopOutlined as StopOutlinedIcon,
|
StopOutlined as StopOutlinedIcon,
|
||||||
SyncOutlined as SyncOutlinedIcon,
|
SyncOutlined as SyncOutlinedIcon,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
@ -16,8 +17,11 @@ import { PageHeader } from "@ant-design/pro-components";
|
|||||||
import { useRequest } from "ahooks";
|
import { useRequest } from "ahooks";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
Card,
|
||||||
Divider,
|
Divider,
|
||||||
Empty,
|
Empty,
|
||||||
|
Flex,
|
||||||
|
Input,
|
||||||
Menu,
|
Menu,
|
||||||
type MenuProps,
|
type MenuProps,
|
||||||
Modal,
|
Modal,
|
||||||
@ -235,6 +239,7 @@ const WorkflowList = () => {
|
|||||||
|
|
||||||
const [filters, setFilters] = useState<Record<string, unknown>>(() => {
|
const [filters, setFilters] = useState<Record<string, unknown>>(() => {
|
||||||
return {
|
return {
|
||||||
|
keyword: searchParams.get("keyword"),
|
||||||
state: searchParams.get("state"),
|
state: searchParams.get("state"),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -249,9 +254,10 @@ const WorkflowList = () => {
|
|||||||
} = useRequest(
|
} = useRequest(
|
||||||
() => {
|
() => {
|
||||||
return listWorkflow({
|
return listWorkflow({
|
||||||
|
keyword: filters["keyword"] as string,
|
||||||
|
enabled: (filters["state"] as string) === "enabled" ? true : (filters["state"] as string) === "disabled" ? false : undefined,
|
||||||
page: page,
|
page: page,
|
||||||
perPage: pageSize,
|
perPage: pageSize,
|
||||||
enabled: (filters["state"] as string) === "enabled" ? true : (filters["state"] as string) === "disabled" ? false : undefined,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -273,10 +279,20 @@ const WorkflowList = () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleSearch = (value: string) => {
|
||||||
|
setFilters((prev) => ({ ...prev, keyword: value.trim() }));
|
||||||
|
};
|
||||||
|
|
||||||
const handleCreateClick = () => {
|
const handleCreateClick = () => {
|
||||||
navigate("/workflows/new");
|
navigate("/workflows/new");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleReloadClick = () => {
|
||||||
|
if (loading) return;
|
||||||
|
|
||||||
|
refreshData();
|
||||||
|
};
|
||||||
|
|
||||||
const handleEnabledChange = async (workflow: WorkflowModel) => {
|
const handleEnabledChange = async (workflow: WorkflowModel) => {
|
||||||
try {
|
try {
|
||||||
if (!workflow.enabled && (!workflow.content || !isAllNodesValidated(workflow.content))) {
|
if (!workflow.enabled && (!workflow.content || !isAllNodesValidated(workflow.content))) {
|
||||||
@ -345,30 +361,43 @@ const WorkflowList = () => {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Table<WorkflowModel>
|
<Card size="small">
|
||||||
columns={tableColumns}
|
<div className="mb-4">
|
||||||
dataSource={tableData}
|
<Flex gap="small">
|
||||||
loading={loading}
|
<div className="flex-1">
|
||||||
locale={{
|
<Input.Search allowClear defaultValue={filters["keyword"] as string} placeholder={t("workflow.search.placeholder")} onSearch={handleSearch} />
|
||||||
emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={getErrMsg(loadedError ?? t("workflow.nodata"))} />,
|
</div>
|
||||||
}}
|
<div>
|
||||||
pagination={{
|
<Button icon={<ReloadOutlinedIcon spin={loading} />} onClick={handleReloadClick} />
|
||||||
current: page,
|
</div>
|
||||||
pageSize: pageSize,
|
</Flex>
|
||||||
total: tableTotal,
|
</div>
|
||||||
showSizeChanger: true,
|
|
||||||
onChange: (page: number, pageSize: number) => {
|
<Table<WorkflowModel>
|
||||||
setPage(page);
|
columns={tableColumns}
|
||||||
setPageSize(pageSize);
|
dataSource={tableData}
|
||||||
},
|
loading={loading}
|
||||||
onShowSizeChange: (page: number, pageSize: number) => {
|
locale={{
|
||||||
setPage(page);
|
emptyText: <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={getErrMsg(loadedError ?? t("workflow.nodata"))} />,
|
||||||
setPageSize(pageSize);
|
}}
|
||||||
},
|
pagination={{
|
||||||
}}
|
current: page,
|
||||||
rowKey={(record) => record.id}
|
pageSize: pageSize,
|
||||||
scroll={{ x: "max(100%, 960px)" }}
|
total: tableTotal,
|
||||||
/>
|
showSizeChanger: true,
|
||||||
|
onChange: (page: number, pageSize: number) => {
|
||||||
|
setPage(page);
|
||||||
|
setPageSize(pageSize);
|
||||||
|
},
|
||||||
|
onShowSizeChange: (page: number, pageSize: number) => {
|
||||||
|
setPage(page);
|
||||||
|
setPageSize(pageSize);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
rowKey={(record) => record.id}
|
||||||
|
scroll={{ x: "max(100%, 960px)" }}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -4,11 +4,16 @@ import { type AccessModel } from "@/domain/access";
|
|||||||
import { COLLECTION_NAME_ACCESS, getPocketBase } from "./_pocketbase";
|
import { COLLECTION_NAME_ACCESS, getPocketBase } from "./_pocketbase";
|
||||||
|
|
||||||
export const list = async () => {
|
export const list = async () => {
|
||||||
return await getPocketBase().collection(COLLECTION_NAME_ACCESS).getFullList<AccessModel>({
|
const list = await getPocketBase().collection(COLLECTION_NAME_ACCESS).getFullList<AccessModel>({
|
||||||
|
batch: 65535,
|
||||||
filter: "deleted=null",
|
filter: "deleted=null",
|
||||||
sort: "-created",
|
sort: "-created",
|
||||||
requestKey: null,
|
requestKey: null,
|
||||||
});
|
});
|
||||||
|
return {
|
||||||
|
totalItems: list.length,
|
||||||
|
items: list,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const save = async (record: MaybeModelRecord<AccessModel>) => {
|
export const save = async (record: MaybeModelRecord<AccessModel>) => {
|
||||||
|
@ -1,55 +1,51 @@
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { type RecordListOptions } from "pocketbase";
|
|
||||||
|
|
||||||
import { type CertificateModel } from "@/domain/certificate";
|
import { type CertificateModel } from "@/domain/certificate";
|
||||||
import { COLLECTION_NAME_CERTIFICATE, getPocketBase } from "./_pocketbase";
|
import { COLLECTION_NAME_CERTIFICATE, getPocketBase } from "./_pocketbase";
|
||||||
|
|
||||||
export type ListCertificateRequest = {
|
export type ListCertificateRequest = {
|
||||||
|
keyword?: string;
|
||||||
|
state?: "expireSoon" | "expired";
|
||||||
page?: number;
|
page?: number;
|
||||||
perPage?: number;
|
perPage?: number;
|
||||||
state?: "expireSoon" | "expired";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const list = async (request: ListCertificateRequest) => {
|
export const list = async (request: ListCertificateRequest) => {
|
||||||
const pb = getPocketBase();
|
const pb = getPocketBase();
|
||||||
|
|
||||||
const page = request.page || 1;
|
const filters: string[] = ["deleted=null"];
|
||||||
const perPage = request.perPage || 10;
|
if (request.keyword) {
|
||||||
|
filters.push(pb.filter("(subjectAltNames~{:keyword} || serialNumber={:keyword})", { keyword: request.keyword }));
|
||||||
const options: RecordListOptions = {
|
}
|
||||||
expand: "workflowId",
|
|
||||||
filter: "deleted=null",
|
|
||||||
sort: "-created",
|
|
||||||
requestKey: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (request.state === "expireSoon") {
|
if (request.state === "expireSoon") {
|
||||||
options.filter = pb.filter("expireAt<{:expiredAt} && deleted=null", {
|
filters.push(pb.filter("expireAt<{:expiredAt}", { expiredAt: dayjs().add(20, "d").toDate() }));
|
||||||
expiredAt: dayjs().add(20, "d").toDate(),
|
|
||||||
});
|
|
||||||
} else if (request.state === "expired") {
|
} else if (request.state === "expired") {
|
||||||
options.filter = pb.filter("expireAt<={:expiredAt} && deleted=null", {
|
filters.push(pb.filter("expireAt<={:expiredAt}", { expiredAt: new Date() }));
|
||||||
expiredAt: new Date(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pb.collection(COLLECTION_NAME_CERTIFICATE).getList<CertificateModel>(page, perPage, options);
|
const page = request.page || 1;
|
||||||
|
const perPage = request.perPage || 10;
|
||||||
|
return pb.collection(COLLECTION_NAME_CERTIFICATE).getList<CertificateModel>(page, perPage, {
|
||||||
|
expand: "workflowId",
|
||||||
|
filter: filters.join(" && "),
|
||||||
|
sort: "-created",
|
||||||
|
requestKey: null,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const listByWorkflowRunId = async (workflowRunId: string) => {
|
export const listByWorkflowRunId = async (workflowRunId: string) => {
|
||||||
const pb = getPocketBase();
|
const pb = getPocketBase();
|
||||||
|
|
||||||
const options: RecordListOptions = {
|
const list = await pb.collection(COLLECTION_NAME_CERTIFICATE).getFullList<CertificateModel>({
|
||||||
filter: pb.filter("workflowRunId={:workflowRunId}", {
|
batch: 65535,
|
||||||
workflowRunId: workflowRunId,
|
filter: pb.filter("workflowRunId={:workflowRunId}", { workflowRunId: workflowRunId }),
|
||||||
}),
|
|
||||||
sort: "-created",
|
sort: "-created",
|
||||||
requestKey: null,
|
requestKey: null,
|
||||||
};
|
});
|
||||||
const items = await pb.collection(COLLECTION_NAME_CERTIFICATE).getFullList<CertificateModel>(options);
|
|
||||||
return {
|
return {
|
||||||
totalItems: items.length,
|
totalItems: list.length,
|
||||||
items: items,
|
items: list,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,30 +1,33 @@
|
|||||||
import { type RecordListOptions, type RecordSubscription } from "pocketbase";
|
import { type RecordSubscription } from "pocketbase";
|
||||||
|
|
||||||
import { type WorkflowModel } from "@/domain/workflow";
|
import { type WorkflowModel } from "@/domain/workflow";
|
||||||
import { COLLECTION_NAME_WORKFLOW, getPocketBase } from "./_pocketbase";
|
import { COLLECTION_NAME_WORKFLOW, getPocketBase } from "./_pocketbase";
|
||||||
|
|
||||||
export type ListWorkflowRequest = {
|
export type ListWorkflowRequest = {
|
||||||
|
keyword?: string;
|
||||||
|
enabled?: boolean;
|
||||||
page?: number;
|
page?: number;
|
||||||
perPage?: number;
|
perPage?: number;
|
||||||
enabled?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const list = async (request: ListWorkflowRequest) => {
|
export const list = async (request: ListWorkflowRequest) => {
|
||||||
const pb = getPocketBase();
|
const pb = getPocketBase();
|
||||||
|
|
||||||
const page = request.page || 1;
|
const filters: string[] = [];
|
||||||
const perPage = request.perPage || 10;
|
if (request.keyword) {
|
||||||
|
filters.push(pb.filter("name~{:keyword}", { keyword: request.keyword }));
|
||||||
const options: RecordListOptions = {
|
}
|
||||||
sort: "-created",
|
|
||||||
requestKey: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (request.enabled != null) {
|
if (request.enabled != null) {
|
||||||
options.filter = pb.filter("enabled={:enabled}", { enabled: request.enabled });
|
filters.push(pb.filter("enabled={:enabled}", { enabled: request.enabled }));
|
||||||
}
|
}
|
||||||
|
|
||||||
return await pb.collection(COLLECTION_NAME_WORKFLOW).getList<WorkflowModel>(page, perPage, options);
|
const page = request.page || 1;
|
||||||
|
const perPage = request.perPage || 10;
|
||||||
|
return await pb.collection(COLLECTION_NAME_WORKFLOW).getList<WorkflowModel>(page, perPage, {
|
||||||
|
filter: filters.join(" && "),
|
||||||
|
sort: "-created",
|
||||||
|
requestKey: null,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const get = async (id: string) => {
|
export const get = async (id: string) => {
|
||||||
|
@ -12,24 +12,21 @@ export type ListWorkflowRunsRequest = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const list = async (request: ListWorkflowRunsRequest) => {
|
export const list = async (request: ListWorkflowRunsRequest) => {
|
||||||
const page = request.page || 1;
|
const pb = getPocketBase();
|
||||||
const perPage = request.perPage || 10;
|
|
||||||
|
|
||||||
let filter = "";
|
const filters: string[] = [];
|
||||||
const params: Record<string, string> = {};
|
|
||||||
if (request.workflowId) {
|
if (request.workflowId) {
|
||||||
filter = `workflowId={:workflowId}`;
|
filters.push(pb.filter("workflowId={:workflowId}", { workflowId: request.workflowId }));
|
||||||
params.workflowId = request.workflowId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return await getPocketBase()
|
const page = request.page || 1;
|
||||||
.collection(COLLECTION_NAME_WORKFLOW_RUN)
|
const perPage = request.perPage || 10;
|
||||||
.getList<WorkflowRunModel>(page, perPage, {
|
return await pb.collection(COLLECTION_NAME_WORKFLOW_RUN).getList<WorkflowRunModel>(page, perPage, {
|
||||||
filter: getPocketBase().filter(filter, params),
|
filter: filters.join(" && "),
|
||||||
sort: "-created",
|
sort: "-created",
|
||||||
requestKey: null,
|
requestKey: null,
|
||||||
expand: request.expand ? "workflowId" : undefined,
|
expand: request.expand ? "workflowId" : undefined,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const remove = async (record: MaybeModelRecordWithId<WorkflowRunModel>) => {
|
export const remove = async (record: MaybeModelRecordWithId<WorkflowRunModel>) => {
|
||||||
|
@ -24,7 +24,7 @@ export const useAccessesStore = create<AccessesState>((set) => {
|
|||||||
loadedAtOnce: false,
|
loadedAtOnce: false,
|
||||||
|
|
||||||
fetchAccesses: async () => {
|
fetchAccesses: async () => {
|
||||||
fetcher ??= listAccess();
|
fetcher ??= listAccess().then((res) => res.items);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
set({ loading: true });
|
set({ loading: true });
|
||||||
|
Loading…
x
Reference in New Issue
Block a user