style: update better visibility pagination

This commit is contained in:
Mahen 2026-02-22 09:03:54 +07:00
parent 67f2fccd33
commit 8204b15b4b
6 changed files with 114 additions and 16 deletions

View File

@ -20,7 +20,7 @@ export function BrandFilter() {
<button
onClick={() => handleSelect(null)}
className={cn(
"rounded-lg border px-4 py-2 text-sm font-medium transition-all",
"rounded-lg border px-4 py-2 text-sm font-medium transition-all cursor-pointer",
selectedBrand === null
? "border-primary bg-primary text-primary-foreground"
: "border-border bg-card text-muted-foreground hover:border-primary/50 hover:text-foreground",
@ -40,7 +40,7 @@ export function BrandFilter() {
key={brand.name}
onClick={() => handleSelect(brand.name)}
className={cn(
"rounded-lg border px-4 py-2 text-sm font-medium transition-all",
"rounded-lg border px-4 py-2 text-sm font-medium transition-all cursor-pointer",
isActive
? "border-primary bg-primary text-primary-foreground"
: "border-border bg-card text-muted-foreground hover:border-primary/50 hover:text-foreground",

View File

@ -9,24 +9,20 @@ import {
TableRow,
} from "../../components/ui/table";
import { Badge } from "../../components/ui/badge";
import { EllipsisVertical, Inbox, Loader2, Pencil, Trash } from "lucide-react";
import { Inbox, Loader2 } from "lucide-react";
import getSentimentBadge from "./SentimentBadge";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "../ui/dropdown-menu";
import { useReviewTable } from "@/src/hooks/useReviewTable";
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from "../ui/pagination";
import { useSearchParams } from "next/navigation";
import { getVisiblePages } from "@/src/utils/datas";
export function ReviewTable() {
const searchParams = useSearchParams();
@ -36,6 +32,7 @@ export function ReviewTable() {
selectedBrand,
);
const { currentPage, totalPages, nextPage, prevPage, goToPage } = pagination;
const visiblePage = getVisiblePages({ totalPages, currentPage });
if (isLoading) {
return (
@ -171,7 +168,7 @@ export function ReviewTable() {
</TableBody>
</Table>
{totalPages > 1 && (
{/* {totalPages > 1 && (
<div className="border-t bg-muted/20 px-6 py-4">
<Pagination className="justify-center sm:justify-end">
<PaginationContent>
@ -222,6 +219,63 @@ export function ReviewTable() {
</PaginationContent>
</Pagination>
</div>
)} */}
{totalPages > 1 && (
<div className="border-t bg-muted/20 px-6 py-4">
<Pagination className="justify-center sm:justify-end">
<PaginationContent>
<PaginationItem>
<PaginationPrevious
href="#"
onClick={(e) => {
e.preventDefault();
prevPage();
}}
className={
currentPage === 1
? "pointer-events-none opacity-50"
: "cursor-pointer hover:bg-[#F8FBFF] hover:text-primary"
}
/>
</PaginationItem>
{visiblePage.map((page, index) => (
<PaginationItem key={index}>
{page === "..." ? (
<PaginationEllipsis />
) : (
<PaginationLink
href="#"
onClick={(e) => {
e.preventDefault();
goToPage(page as number);
}}
isActive={currentPage === page}
>
{page}
</PaginationLink>
)}
</PaginationItem>
))}
<PaginationItem>
<PaginationNext
href="#"
onClick={(e) => {
e.preventDefault();
nextPage();
}}
className={
currentPage === totalPages
? "pointer-events-none opacity-50"
: "cursor-pointer hover:bg-primary hover:text-card"
}
/>
</PaginationItem>
</PaginationContent>
</Pagination>
</div>
)}
</div>
);

View File

@ -19,6 +19,7 @@ const buttonVariants = cva(
ghost:
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline",
primary: "bg-primary text-card"
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",

View File

@ -55,7 +55,7 @@ function PaginationLink({
data-active={isActive}
className={cn(
buttonVariants({
variant: isActive ? "outline" : "ghost",
variant: isActive ? "primary" : "ghost",
size,
}),
className,

View File

@ -285,3 +285,8 @@ export type AnalysisData = {
};
export type BodyData = (req: Request, body: any) => Promise<NextResponse>;
export interface VisiblePageProps {
totalPages: number;
currentPage: number;
}

View File

@ -1,4 +1,10 @@
import { ScrapeResult, WordCloudConfig, WordItem } from "../types";
import { useReviewTable } from "../hooks/useReviewTable";
import {
ScrapeResult,
VisiblePageProps,
WordCloudConfig,
WordItem,
} from "../types";
import { Brand } from "@prisma/client";
export const setWordCloud = ({ maxValue, minValue }: WordCloudConfig) => {
@ -24,7 +30,7 @@ export const setWordCloud = ({ maxValue, minValue }: WordCloudConfig) => {
return { getSize, getColor };
};
export function getFallbackData(url: string): ScrapeResult {
export const getFallbackData = (url: string): ScrapeResult => {
return {
name: "Produk (Data Sampel)",
url: url,
@ -39,7 +45,7 @@ export function getFallbackData(url: string): ScrapeResult {
"Pengiriman cepat dan packing kayu sangat aman.",
],
};
}
};
export const formatRupiah = (value: number | string) => {
if (!value) return "Rp 0";
@ -63,10 +69,42 @@ export const brandFormat = ({
return { brands };
};
export function toTitleCase(str: string) {
export const toTitleCase = (str: string) => {
return str
.toLowerCase()
.split(/[\s-_]+/)
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ");
}
};
export const getVisiblePages = (data: VisiblePageProps) => {
if (data.totalPages <= 6) {
return Array.from({ length: data.totalPages }, (_, i) => i + 1);
}
if (data.currentPage <= 3) {
return [1, 2, 3, 4, 5, "...", data.totalPages];
}
if (data.currentPage >= data.totalPages - 2) {
return [
1,
"...",
data.totalPages - 4,
data.totalPages - 3,
data.totalPages - 2,
data.totalPages - 1,
data.totalPages,
];
}
return [
1,
"...",
data.currentPage - 1,
data.currentPage,
data.currentPage + 1,
"...",
data.totalPages,
];
};