Merge pull request #6 from usual2970/feat/pagination

Feat/pagination
This commit is contained in:
usual2970 2024-09-02 22:56:04 +08:00 committed by GitHub
commit 69671075fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 553 additions and 263 deletions

BIN
certimate Executable file

Binary file not shown.

1
ui/dist/assets/index-BPSHHpDP.css vendored Normal file

File diff suppressed because one or more lines are too long

254
ui/dist/assets/index-CglXs5Ou.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
ui/dist/index.html vendored
View File

@ -5,8 +5,8 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Certimate - Your Trusted SSL Automation Partner</title>
<script type="module" crossorigin src="/assets/index-nGGiqZOp.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DHLrqoj1.css">
<script type="module" crossorigin src="/assets/index-CglXs5Ou.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BPSHHpDP.css">
</head>
<body class="bg-background">
<div id="root"></div>

View File

@ -0,0 +1,102 @@
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
} from "../ui/pagination";
type PaginationProps = {
totalPages: number;
currentPage: number;
onPageChange: (page: number) => void;
};
type PageNumber = number | string;
const XPagination = ({
totalPages,
currentPage,
onPageChange,
}: PaginationProps) => {
const pageNeighbours = 1; // Number of page numbers to show on either side of the current page
const getPageNumbers = () => {
const totalNumbers = pageNeighbours * 2 + 3; // total pages to display (left + right neighbours + current + 2 for start and end)
const totalBlocks = totalNumbers + 2; // adding 2 for the start and end page numbers
if (totalPages > totalBlocks) {
let pages: PageNumber[] = [];
const leftBound = Math.max(2, currentPage - pageNeighbours);
const rightBound = Math.min(totalPages - 1, currentPage + pageNeighbours);
const beforeLastPage = totalPages - 1;
pages = range(leftBound, rightBound);
if (currentPage > pageNeighbours + 2) {
pages.unshift("...");
}
if (currentPage < beforeLastPage - pageNeighbours) {
pages.push("...");
}
pages.unshift(1);
pages.push(totalPages);
return pages;
}
return range(1, totalPages);
};
const range = (from: number, to: number, step = 1) => {
let i = from;
const range = [];
while (i <= to) {
range.push(i);
i += step;
}
return range;
};
const pages = getPageNumbers();
return (
<>
<Pagination className="dark:text-stone-200 justify-end mt-3">
<PaginationContent>
{pages.map((page, index) => {
if (page === "...") {
return (
<PaginationItem key={index}>
<PaginationEllipsis />
</PaginationItem>
);
}
return (
<PaginationItem key={index}>
<PaginationLink
href="#"
isActive={currentPage == page}
onClick={(e) => {
e.preventDefault();
onPageChange(page as number);
}}
>
{page}
</PaginationLink>
</PaginationItem>
);
})}
</PaginationContent>
</Pagination>
</>
);
};
export default XPagination;

View File

@ -0,0 +1,117 @@
import * as React from "react";
import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react";
import { cn } from "@/lib/utils";
import { ButtonProps, buttonVariants } from "@/components/ui/button";
const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
<nav
role="navigation"
aria-label="pagination"
className={cn("mx-auto flex w-full justify-center", className)}
{...props}
/>
);
Pagination.displayName = "Pagination";
const PaginationContent = React.forwardRef<
HTMLUListElement,
React.ComponentProps<"ul">
>(({ className, ...props }, ref) => (
<ul
ref={ref}
className={cn("flex flex-row items-center gap-1", className)}
{...props}
/>
));
PaginationContent.displayName = "PaginationContent";
const PaginationItem = React.forwardRef<
HTMLLIElement,
React.ComponentProps<"li">
>(({ className, ...props }, ref) => (
<li ref={ref} className={cn("", className)} {...props} />
));
PaginationItem.displayName = "PaginationItem";
type PaginationLinkProps = {
isActive?: boolean;
} & Pick<ButtonProps, "size"> &
React.ComponentProps<"a">;
const PaginationLink = ({
className,
isActive,
size = "icon",
...props
}: PaginationLinkProps) => (
<a
aria-current={isActive ? "page" : undefined}
className={cn(
buttonVariants({
variant: isActive ? "outline" : "ghost",
size,
}),
className
)}
{...props}
/>
);
PaginationLink.displayName = "PaginationLink";
const PaginationPrevious = ({
className,
...props
}: React.ComponentProps<typeof PaginationLink>) => (
<PaginationLink
aria-label="Go to previous page"
size="default"
className={cn("gap-1 pl-2.5", className)}
{...props}
>
<ChevronLeft className="h-4 w-4" />
<span></span>
</PaginationLink>
);
PaginationPrevious.displayName = "PaginationPrevious";
const PaginationNext = ({
className,
...props
}: React.ComponentProps<typeof PaginationLink>) => (
<PaginationLink
aria-label="Go to next page"
size="default"
className={cn("gap-1 pr-2.5", className)}
{...props}
>
<span></span>
<ChevronRight className="h-4 w-4" />
</PaginationLink>
);
PaginationNext.displayName = "PaginationNext";
const PaginationEllipsis = ({
className,
...props
}: React.ComponentProps<"span">) => (
<span
aria-hidden
className={cn("flex h-9 w-9 items-center justify-center", className)}
{...props}
>
<MoreHorizontal className="h-4 w-4" />
<span className="sr-only">More pages</span>
</span>
);
PaginationEllipsis.displayName = "PaginationEllipsis";
export {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
};

View File

@ -194,7 +194,7 @@ export default function Dashboard() {
href="https://github.com/usual2970/certimate/releases"
target="_blank"
>
Certimate v0.0.13
Certimate v0.0.14
</a>
</div>
</div>

View File

@ -1,4 +1,5 @@
import { AccessEdit } from "@/components/certimate/AccessEdit";
import XPagination from "@/components/certimate/XPagination";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import { Access as AccessType, accessTypeMap } from "@/domain/access";
@ -6,11 +7,25 @@ import { convertZulu2Beijing } from "@/lib/time";
import { useConfig } from "@/providers/config";
import { remove } from "@/repository/access";
import { Key } from "lucide-react";
import { useLocation, useNavigate } from "react-router-dom";
const Access = () => {
const { config, deleteAccess } = useConfig();
const { accesses } = config;
const perPage = 10;
const totalPages = Math.ceil(accesses.length / perPage);
const navigate = useNavigate();
const location = useLocation();
const query = new URLSearchParams(location.search);
const page = query.get("page");
const pageNumber = page ? Number(page) : 1;
const startIndex = (pageNumber - 1) * perPage;
const endIndex = startIndex + perPage;
const handleDelete = async (data: AccessType) => {
const rs = await remove(data);
deleteAccess(rs.id);
@ -50,7 +65,7 @@ const Access = () => {
<div className="sm:hidden flex text-sm text-muted-foreground">
</div>
{accesses.map((access) => (
{accesses.slice(startIndex, endIndex).map((access) => (
<div
className="flex flex-col sm:flex-row text-secondary-foreground border-b dark:border-stone-500 sm:p-2 hover:bg-muted/50 text-sm"
key={access.id}
@ -95,6 +110,14 @@ const Access = () => {
</div>
</div>
))}
<XPagination
totalPages={totalPages}
currentPage={pageNumber}
onPageChange={(page) => {
query.set("page", page.toString());
navigate({ search: query.toString() });
}}
/>
</>
)}
</div>

View File

@ -1,4 +1,5 @@
import DeployProgress from "@/components/certimate/DeployProgress";
import XPagination from "@/components/certimate/XPagination";
import Show from "@/components/Show";
import {
AlertDialogAction,
@ -32,16 +33,28 @@ import {
import { TooltipContent, TooltipProvider } from "@radix-ui/react-tooltip";
import { CircleCheck, CircleX, Earth } from "lucide-react";
import { useEffect, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import { Link, useLocation, useNavigate } from "react-router-dom";
const Home = () => {
const toast = useToast();
const navigate = useNavigate();
const location = useLocation();
const query = new URLSearchParams(location.search);
const page = query.get("page");
const [totalPage, setTotalPage] = useState(0);
const handleCreateClick = () => {
navigate("/edit");
};
const setPage = (newPage: number) => {
query.set("page", newPage.toString());
navigate(`?${query.toString()}`);
};
const handleEditClick = (id: string) => {
navigate(`/edit?id=${id}`);
};
@ -63,11 +76,16 @@ const Home = () => {
useEffect(() => {
const fetchData = async () => {
const data = await list();
setDomains(data);
const data = await list({
page: page ? Number(page) : 1,
perPage: 10,
});
setDomains(data.items);
setTotalPage(data.totalPages);
};
fetchData();
}, []);
}, [page]);
const handelCheckedChange = async (id: string) => {
const checkedDomains = domains.filter((domain) => domain.id === id);
@ -323,6 +341,14 @@ const Home = () => {
</div>
</div>
))}
<XPagination
totalPages={totalPage}
currentPage={page ? Number(page) : 1}
onPageChange={(page) => {
setPage(page);
}}
/>
</>
)}
</div>

View File

@ -1,11 +1,28 @@
import { Domain } from "@/domain/domain";
import { getPb } from "./api";
export const list = async () => {
const response = getPb().collection("domains").getFullList<Domain>({
sort: "-created",
expand: "lastDeployment",
});
type DomainListReq = {
domain?: string;
page?: number;
perPage?: number;
};
export const list = async (req: DomainListReq) => {
let page = 1;
if (req.page) {
page = req.page;
}
let perPage = 2;
if (req.perPage) {
perPage = req.perPage;
}
const response = getPb()
.collection("domains")
.getList<Domain>(page, perPage, {
sort: "-created",
expand: "lastDeployment",
});
return response;
};