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