TIFNGK_E41222719/src/components/dashboards/ReviewTable.tsx

217 lines
8.1 KiB
TypeScript

"use client";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "../../components/ui/table";
import { Badge } from "../../components/ui/badge";
import { Inbox, Loader2 } from "lucide-react";
import getSentimentBadge from "./SentimentBadge";
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();
const selectedBrand = searchParams.get("brand");
const { currentData, isLoading, pagination } = useReviewTable(
10,
selectedBrand,
);
const { currentPage, totalPages } = pagination;
const visiblePage = getVisiblePages({ totalPages, currentPage });
if (isLoading) {
return (
<div className="flex h-75 w-full flex-col items-center justify-center gap-2 rounded-xl border bg-card text-muted-foreground">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
<p className="text-sm">Memuat data ulasan...</p>
</div>
);
}
return (
<div className="space-y-4">
<div className="rounded-xl border bg-card">
<Table>
<TableHeader>
<TableRow className="hover:bg-transparent">
<TableHead className="w-62.5">Produk</TableHead>
<TableHead className="w-auto min-w-75 text-center">
Ulasan & Kata Kunci
</TableHead>
<TableHead className="w-30 whitespace-nowrap">Tanggal</TableHead>
<TableHead className="w-30 text-center">Sentimen</TableHead>
<TableHead className="w-50 text-center">
Confidence Score
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{currentData.length === 0 ? (
<TableRow>
<TableCell colSpan={5} className="h-75 text-center">
<div className="flex flex-col items-center justify-center gap-2 text-muted-foreground">
<div className="rounded-full bg-muted">
<Inbox className="h-8 w-8" />
</div>
<p className="text-lg font-medium text-foreground">
Belum ada data
</p>
<p className="text-sm">
Belum ada ulasan yang dianalisis oleh sistem.
</p>
</div>
</TableCell>
</TableRow>
) : (
currentData.map((review, index) => (
<TableRow
key={review.id || index}
className="group animate-in fade-in transition-colors hover:bg-muted/40"
style={{
animationDelay: `${index * 50}ms`,
animationFillMode: "backwards",
}}
>
<TableCell className="align-top">
<div className="flex flex-col gap-1.5">
<div className="flex items-center gap-2">
<span className="inline-flex items-center rounded-md bg-primary/10 px-2 py-0.5 text-xs font-medium text-primary ring-1 ring-inset ring-primary/20">
{review.product?.brand?.name || "Generic"}
</span>
</div>
<span className="text-sm font-medium leading-tight text-foreground line-clamp-2">
{review.product?.name || "Unknown Product"}
</span>
</div>
</TableCell>
<TableCell className="align-top">
<div className="flex flex-col gap-3">
<p className="text-sm leading-relaxed text-muted-foreground line-clamp-3 group-hover:text-foreground transition-colors">
{review.content}
</p>
{review.keywords && review.keywords.length > 0 && (
<div className="flex flex-wrap gap-1.5">
{review.keywords.slice(0, 5).map((k, i) => (
<Badge
key={i}
variant="secondary"
className="h-5 px-1.5 text-[10px] font-normal text-muted-foreground border-border bg-muted group-hover:bg-background transition-all"
>
{k}
</Badge>
))}
</div>
)}
</div>
</TableCell>
<TableCell className="align-top whitespace-nowrap">
<span className="text-xs text-muted-foreground font-medium">
{review.createdAt
? new Date(review.createdAt).toLocaleDateString(
"id-ID",
{
day: "numeric",
month: "short",
year: "numeric",
},
)
: "-"}
</span>
</TableCell>
<TableCell className="align-top text-center">
{getSentimentBadge(review.sentiment ?? null)}
</TableCell>
<TableCell className="align-top text-center">
<span className="font-mono text-sm font-semibold text-foreground">
{review.confidenceScore
? `${(review.confidenceScore * 100).toFixed(1)}%`
: "-"}
</span>
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
{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();
pagination.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 className="hover:cursor-not-allowed" />
) : (
<PaginationLink
href="#"
onClick={(e) => {
e.preventDefault();
pagination.goToPage(page as number);
}}
isActive={currentPage === page}
>
{page}
</PaginationLink>
)}
</PaginationItem>
))}
<PaginationItem>
<PaginationNext
href="#"
onClick={(e) => {
e.preventDefault();
pagination.nextPage();
}}
className={
currentPage === totalPages
? "pointer-events-none opacity-50"
: "cursor-pointer hover:bg-primary hover:text-card"
}
/>
</PaginationItem>
</PaginationContent>
</Pagination>
</div>
)}
</div>
</div>
);
}