Certificate displaying and monitoring

This commit is contained in:
yoan
2024-11-22 10:59:57 +08:00
parent 09e4b24445
commit 86761bd3a0
19 changed files with 498 additions and 381 deletions

View File

@@ -0,0 +1,162 @@
import CertificateDetail from "@/components/certificate/CertificateDetail";
import { Button } from "@/components/ui/button";
import { DataTable } from "@/components/workflow/DataTable";
import { Certificate as CertificateType } from "@/domain/certificate";
import { diffDays, getLeftDays } from "@/lib/time";
import { list } from "@/repository/certificate";
import { ColumnDef } from "@tanstack/react-table";
import { useState } from "react";
import { useNavigate } from "react-router-dom";
type CertificateListProps = {
withPagination?: boolean;
};
const CertificateList = ({ withPagination }: CertificateListProps) => {
const [data, setData] = useState<CertificateType[]>([]);
const [pageCount, setPageCount] = useState<number>(0);
const [open, setOpen] = useState(false);
const [selectedCertificate, setSelectedCertificate] = useState<CertificateType>();
const fetchData = async (page: number, pageSize?: number) => {
const resp = await list({ page: page, perPage: pageSize });
setData(resp.items);
setPageCount(resp.totalPages);
};
const navigate = useNavigate();
const columns: ColumnDef<CertificateType>[] = [
{
accessorKey: "san",
header: "域名",
cell: ({ row }) => {
let san: string = row.getValue("san");
if (!san) {
san = "";
}
return (
<div>
{san.split(";").map((item, i) => {
return (
<div key={i} className="max-w-[250px] truncate">
{item}
</div>
);
})}
</div>
);
},
},
{
accessorKey: "expireAt",
header: "有效期限",
cell: ({ row }) => {
const expireAt: string = row.getValue("expireAt");
const data = row.original;
const leftDays = getLeftDays(expireAt);
const allDays = diffDays(data.expireAt, data.created);
return (
<div className="">
{leftDays > 0 ? (
<div className="text-green-500">
{leftDays} / {allDays}
</div>
) : (
<div className="text-red-500"></div>
)}
<div>{new Date(expireAt).toLocaleString().split(" ")[0]} </div>
</div>
);
},
},
{
accessorKey: "workflow",
header: "所属工作流",
cell: ({ row }) => {
const name = row.original.expand.workflow?.name;
const workflowId: string = row.getValue("workflow");
return (
<div className="max-w-[200px] truncate">
<Button
size={"sm"}
variant={"link"}
onClick={() => {
handleWorkflowClick(workflowId);
}}
>
{name}
</Button>
</div>
);
},
},
{
accessorKey: "created",
header: "颁发时间",
cell: ({ row }) => {
const date: string = row.getValue("created");
return new Date(date).toLocaleString();
},
},
{
id: "actions",
cell: ({ row }) => {
return (
<div className="flex items-center space-x-2">
<Button
size="sm"
variant={"link"}
onClick={() => {
handleView(row.original.id);
}}
>
</Button>
</div>
);
},
},
];
const handleWorkflowClick = (id: string) => {
navigate(`/workflow/detail?id=${id}`);
};
const handleView = (id: string) => {
setOpen(true);
const certificate = data.find((item) => item.id === id);
setSelectedCertificate(certificate);
};
return (
<>
<DataTable
columns={columns}
onPageChange={fetchData}
data={data}
pageCount={pageCount}
withPagination={withPagination}
fallback={
<div className="flex flex-col">
<div className="text-muted-foreground">😀</div>
<Button
size={"sm"}
onClick={() => {
navigate("/workflow/detail");
}}
>
</Button>
</div>
}
/>
<CertificateDetail open={open} onOpenChange={setOpen} certificate={selectedCertificate} />
</>
);
};
export default CertificateList;

View File

@@ -3,6 +3,7 @@ import { ColumnDef, flexRender, getCoreRowModel, getPaginationRowModel, Paginati
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Button } from "../ui/button";
import { useEffect, useState } from "react";
import Show from "../Show";
interface DataTableProps<TData extends { id: string }, TValue> {
columns: ColumnDef<TData, TValue>[];
@@ -10,9 +11,19 @@ interface DataTableProps<TData extends { id: string }, TValue> {
pageCount: number;
onPageChange?: (pageIndex: number, pageSize?: number) => Promise<void>;
onRowClick?: (id: string) => void;
withPagination?: boolean;
fallback?: React.ReactNode;
}
export function DataTable<TData extends { id: string }, TValue>({ columns, data, onPageChange, pageCount, onRowClick }: DataTableProps<TData, TValue>) {
export function DataTable<TData extends { id: string }, TValue>({
columns,
data,
onPageChange,
pageCount,
onRowClick,
withPagination,
fallback,
}: DataTableProps<TData, TValue>) {
const [{ pageIndex, pageSize }, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
@@ -77,28 +88,30 @@ export function DataTable<TData extends { id: string }, TValue>({ columns, data,
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
{fallback ? fallback : "暂无数据"}
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<div className="flex items-center justify-end mt-5">
<div className="flex items-center space-x-2 dark:text-stone-200">
{table.getCanPreviousPage() && (
<Button variant="outline" size="sm" onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()}>
</Button>
)}
<Show when={!!withPagination}>
<div className="flex items-center justify-end mt-5">
<div className="flex items-center space-x-2 dark:text-stone-200">
{table.getCanPreviousPage() && (
<Button variant="outline" size="sm" onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()}>
</Button>
)}
{table.getCanNextPage && (
<Button variant="outline" size="sm" onClick={() => table.nextPage()} disabled={!table.getCanNextPage()}>
</Button>
)}
{table.getCanNextPage && (
<Button variant="outline" size="sm" onClick={() => table.nextPage()} disabled={!table.getCanNextPage()}>
</Button>
)}
</div>
</div>
</div>
</Show>
</div>
);
}