470 lines
16 KiB
TypeScript
470 lines
16 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from "@/components/ui/table"; // pastikan path sesuai
|
|
import { format } from "date-fns";
|
|
import { id } from "date-fns/locale";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { listDataImage } from "@/service/content";
|
|
import { Button } from "../ui/button";
|
|
import { getCookiesDecrypt } from "@/lib/utils";
|
|
import Swal from "sweetalert2";
|
|
import withReactContent from "sweetalert2-react-content";
|
|
import { useParams, useRouter, useSearchParams } from "next/navigation";
|
|
import { ChevronDown, Edit, Eye, Search, Trash, View } from "lucide-react";
|
|
import { InputGroup, InputGroupText } from "../ui/input-group";
|
|
import { Input } from "../ui/input";
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuRadioGroup,
|
|
DropdownMenuRadioItem,
|
|
DropdownMenuTrigger,
|
|
} from "../ui/dropdown-menu";
|
|
import { Label } from "../ui/label";
|
|
import TablePagination from "./table-pagination";
|
|
import { table } from "console";
|
|
|
|
export default function ImageTable() {
|
|
const [dataTable, setDataTable] = useState([]);
|
|
const [loading, setLoading] = useState(false);
|
|
const router = useRouter();
|
|
const searchParams = useSearchParams();
|
|
|
|
const params = useParams();
|
|
const locale = params?.locale;
|
|
const MySwal = withReactContent(Swal);
|
|
const [totalData, setTotalData] = useState<number>(1);
|
|
|
|
const [showData, setShowData] = useState("10");
|
|
|
|
const [page, setPage] = useState(1);
|
|
const [totalPage, setTotalPage] = useState(1);
|
|
const [search, setSearch] = useState("");
|
|
const userId = getCookiesDecrypt("uie");
|
|
const userLevelId = getCookiesDecrypt("ulie");
|
|
|
|
const [categories, setCategories] = useState<any[]>([]);
|
|
const [selectedCategories, setSelectedCategories] = useState<number[]>([]);
|
|
|
|
const [categoryFilter, setCategoryFilter] = useState<string>("");
|
|
const [statusFilter, setStatusFilter] = useState<any[]>([]);
|
|
const [startDate, setStartDate] = useState("");
|
|
const [endDate, setEndDate] = useState("");
|
|
const [filterByCreator, setFilterByCreator] = useState("");
|
|
const [filterBySource, setFilterBySource] = useState("");
|
|
const [filterByCreatorGroup, setFilterByCreatorGroup] = useState("");
|
|
|
|
const roleId = getCookiesDecrypt("urie");
|
|
|
|
useEffect(() => {
|
|
fetchData();
|
|
}, [page, showData, search, categoryFilter, statusFilter]);
|
|
|
|
async function fetchData() {
|
|
const formattedStartDate = startDate
|
|
? format(new Date(startDate), "yyyy-MM-dd")
|
|
: "";
|
|
const formattedEndDate = endDate
|
|
? format(new Date(endDate), "yyyy-MM-dd")
|
|
: "";
|
|
try {
|
|
const isForSelf = Number(roleId) === 4;
|
|
setLoading(true);
|
|
const res = await listDataImage(
|
|
showData,
|
|
page - 1,
|
|
isForSelf,
|
|
!isForSelf,
|
|
categoryFilter,
|
|
statusFilter,
|
|
statusFilter?.sort().join(",").includes("1") ? userLevelId : "",
|
|
filterByCreator,
|
|
filterBySource,
|
|
formattedStartDate,
|
|
formattedEndDate,
|
|
search,
|
|
filterByCreatorGroup,
|
|
locale == "en"
|
|
);
|
|
const contentData = res?.data?.data?.content || [];
|
|
|
|
const formattedData = contentData.map((item: any, index: number) => ({
|
|
no: index + 1,
|
|
...item,
|
|
}));
|
|
|
|
setDataTable(formattedData);
|
|
} catch (error) {
|
|
console.error("Error fetching data:", error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
fetchData();
|
|
}, []);
|
|
|
|
const getStatusBadge = (status: number) => {
|
|
switch (status) {
|
|
case 1:
|
|
return <Badge className="bg-green-500">Done</Badge>;
|
|
case 2:
|
|
return <Badge className="bg-blue-500">Queue</Badge>;
|
|
case 3:
|
|
return <Badge className="bg-red-500">Trigger Error</Badge>;
|
|
default:
|
|
return <Badge>Unknown</Badge>;
|
|
}
|
|
};
|
|
|
|
// const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
// setSearch(e.target.value);
|
|
// dataTable.getColumn("judul")?.setFilterValue(e.target.value);
|
|
// };
|
|
const handleSearchFilterBySource = (
|
|
e: React.ChangeEvent<HTMLInputElement>
|
|
) => {
|
|
const value = e.target.value;
|
|
setFilterBySource(value);
|
|
fetchData();
|
|
};
|
|
|
|
function handleStatusCheckboxChange(value: any) {
|
|
setStatusFilter((prev: any) =>
|
|
prev.includes(value)
|
|
? prev.filter((status: any) => status !== value)
|
|
: [...prev, value]
|
|
);
|
|
}
|
|
|
|
const handleSearchFilterByCreator = (
|
|
e: React.ChangeEvent<HTMLInputElement>
|
|
) => {
|
|
const value = e.target.value;
|
|
setFilterByCreator(value);
|
|
fetchData();
|
|
};
|
|
|
|
const handleCheckboxChange = (categoryId: number) => {
|
|
setSelectedCategories((prev: any) =>
|
|
prev.includes(categoryId)
|
|
? prev.filter((id: any) => id !== categoryId)
|
|
: [...prev, categoryId]
|
|
);
|
|
|
|
// Perbarui filter kategori
|
|
setCategoryFilter((prev) => {
|
|
const updatedCategories = prev.split(",").filter(Boolean).map(Number);
|
|
|
|
const newCategories = updatedCategories.includes(categoryId)
|
|
? updatedCategories.filter((id) => id !== categoryId)
|
|
: [...updatedCategories, categoryId];
|
|
|
|
return newCategories.join(",");
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className="rounded-md border">
|
|
<div className="flex flex-col md:flex-row lg:flex-row md:justify-between lg:justify-between items-center md:px-5 lg:px-5">
|
|
<div className="w-full md:w-[200px] lg:w-[200px] ">
|
|
<div className="relative w-full">
|
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-500 dark:text-white" />
|
|
<Input
|
|
type="text"
|
|
placeholder="Search Judul..."
|
|
className="pl-10 bg-transparent dark:border-secondary dark:placeholder-white/80 dark:focus:border-secondary dark:text-white"
|
|
defaultValue={search}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="flex flex-row items-center gap-3">
|
|
<div className="flex items-center py-4">
|
|
<div className="mx-3">
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button size="md" variant="outline">
|
|
{showData} Data
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent className="w-56 text-sm">
|
|
<DropdownMenuRadioGroup
|
|
value={showData}
|
|
onValueChange={setShowData}
|
|
>
|
|
<DropdownMenuRadioItem value="10">
|
|
10 Data
|
|
</DropdownMenuRadioItem>
|
|
<DropdownMenuRadioItem value="50">
|
|
50 Data
|
|
</DropdownMenuRadioItem>
|
|
<DropdownMenuRadioItem value="100">
|
|
100 Data
|
|
</DropdownMenuRadioItem>
|
|
<DropdownMenuRadioItem value="250">
|
|
250 Data
|
|
</DropdownMenuRadioItem>
|
|
</DropdownMenuRadioGroup>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="outline" className="ml-auto" size="md">
|
|
Filter <ChevronDown />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent
|
|
align="end"
|
|
className="w-64 h-[200px] overflow-y-auto"
|
|
>
|
|
<div className="flex flex-row justify-between my-1 mx-1">
|
|
<p>Filter</p>
|
|
{/* <p
|
|
className="text-blue-600 cursor-pointer"
|
|
onClick={fetchData}
|
|
>
|
|
Simpan
|
|
</p> */}
|
|
</div>
|
|
<Label className="ml-2">Kategori</Label>
|
|
{categories.length > 0 ? (
|
|
categories.map((category) => (
|
|
<div
|
|
key={category.id}
|
|
className="flex items-center px-4 py-1"
|
|
>
|
|
<input
|
|
type="checkbox"
|
|
id={`category-${category.id}`}
|
|
className="mr-2"
|
|
checked={selectedCategories.includes(category.id)}
|
|
onChange={() => handleCheckboxChange(category.id)}
|
|
/>
|
|
<label
|
|
htmlFor={`category-${category.id}`}
|
|
className="text-sm"
|
|
>
|
|
{category.name}
|
|
</label>
|
|
</div>
|
|
))
|
|
) : (
|
|
<p className="text-sm text-gray-500 px-4 py-2">
|
|
No categories found.
|
|
</p>
|
|
)}
|
|
<div className="mx-2 my-1">
|
|
<Label>Tanggal Awal</Label>
|
|
<Input
|
|
type="date"
|
|
value={startDate}
|
|
onChange={(e) => setStartDate(e.target.value)}
|
|
className="max-w-sm"
|
|
/>
|
|
</div>
|
|
<div className="mx-2 my-1">
|
|
<Label>Tanggal Akhir</Label>
|
|
<Input
|
|
type="date"
|
|
value={endDate}
|
|
onChange={(e) => setEndDate(e.target.value)}
|
|
className="max-w-sm"
|
|
/>
|
|
</div>
|
|
<div className="mx-2 my-1">
|
|
<Label>Kreator</Label>
|
|
<Input
|
|
placeholder="Filter Status..."
|
|
value={filterByCreator}
|
|
onChange={handleSearchFilterByCreator}
|
|
className="max-w-sm"
|
|
/>
|
|
</div>
|
|
<div className="mx-2 my-1">
|
|
<Label>Sumber</Label>
|
|
<Input
|
|
placeholder="Filter Status..."
|
|
value={filterBySource}
|
|
onChange={handleSearchFilterBySource}
|
|
className="max-w-sm"
|
|
/>
|
|
</div>
|
|
|
|
<Label className="ml-2 mt-2">Status</Label>
|
|
<div className="flex items-center px-4 py-1">
|
|
<input
|
|
type="checkbox"
|
|
id="status-2"
|
|
className="mr-2"
|
|
checked={statusFilter.includes(1)}
|
|
onChange={() => handleStatusCheckboxChange(1)}
|
|
/>
|
|
<label htmlFor="status-2" className="text-sm">
|
|
Menunggu Review
|
|
</label>
|
|
</div>
|
|
<div className="flex items-center px-4 py-1">
|
|
<input
|
|
type="checkbox"
|
|
id="status-2"
|
|
className="mr-2"
|
|
checked={statusFilter.includes(2)}
|
|
onChange={() => handleStatusCheckboxChange(2)}
|
|
/>
|
|
<label htmlFor="status-2" className="text-sm">
|
|
Diterima
|
|
</label>
|
|
</div>
|
|
<div className="flex items-center px-4 py-1">
|
|
<input
|
|
type="checkbox"
|
|
id="status-3"
|
|
className="mr-2"
|
|
checked={statusFilter.includes(3)}
|
|
onChange={() => handleStatusCheckboxChange(3)}
|
|
/>
|
|
<label htmlFor="status-3" className="text-sm">
|
|
Minta Update
|
|
</label>
|
|
</div>
|
|
<div className="flex items-center px-4 py-1">
|
|
<input
|
|
type="checkbox"
|
|
id="status-4"
|
|
className="mr-2"
|
|
checked={statusFilter.includes(4)}
|
|
onChange={() => handleStatusCheckboxChange(4)}
|
|
/>
|
|
<label htmlFor="status-4" className="text-sm">
|
|
Ditolak
|
|
</label>
|
|
</div>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead className="text-center w-[70px]">No</TableHead>
|
|
<TableHead>Title</TableHead>
|
|
<TableHead>Category</TableHead>
|
|
<TableHead>Created At</TableHead>
|
|
<TableHead>Creator Name</TableHead>
|
|
<TableHead>Creator Group</TableHead>
|
|
<TableHead>Status</TableHead>
|
|
<TableHead className="text-center">Action</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{loading ? (
|
|
<TableRow>
|
|
<TableCell colSpan={8} className="text-center">
|
|
Loading...
|
|
</TableCell>
|
|
</TableRow>
|
|
) : dataTable.length > 0 ? (
|
|
dataTable.map((item: any) => (
|
|
<TableRow key={item.id}>
|
|
<TableCell className="text-center">{item.no}</TableCell>
|
|
<TableCell>{item.title}</TableCell>
|
|
<TableCell>{item.categoryName}</TableCell>
|
|
<TableCell>
|
|
{item.createdAt
|
|
? format(new Date(item.createdAt), "dd-MM-yyyy HH:mm", {
|
|
locale: id,
|
|
})
|
|
: "-"}
|
|
</TableCell>
|
|
<TableCell>{item.creatorName}</TableCell>
|
|
<TableCell>{item.creatorGroup}</TableCell>
|
|
<TableCell>{getStatusBadge(item.status)}</TableCell>
|
|
<TableCell className="flex flex-row text-center space-x-2">
|
|
<Button
|
|
size="sm"
|
|
variant="outline"
|
|
className="flex items-center gap-2"
|
|
>
|
|
<Eye size={15} />
|
|
Detail
|
|
</Button>
|
|
<Button
|
|
size="sm"
|
|
variant="outline"
|
|
className="flex items-center gap-2"
|
|
>
|
|
<Edit size={15} />
|
|
Edit
|
|
</Button>
|
|
<Button
|
|
size="sm"
|
|
className="flex items-center gap-2 bg-red-500 text-white"
|
|
>
|
|
<Trash size={15} />
|
|
Delete
|
|
</Button>
|
|
</TableCell>
|
|
</TableRow>
|
|
))
|
|
) : (
|
|
<TableRow>
|
|
<TableCell colSpan={8} className="text-center">
|
|
No data found
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
{/* Pagination */}
|
|
<div className="flex justify-between items-center mt-4">
|
|
{/* Info jumlah data */}
|
|
<span className="text-sm text-gray-500">
|
|
Page {page} of {totalPage}
|
|
</span>
|
|
|
|
{/* Navigasi halaman */}
|
|
<div className="flex items-center space-x-2">
|
|
<button
|
|
onClick={() => setPage((prev) => Math.max(prev - 1, 1))}
|
|
disabled={page === 1}
|
|
className="px-3 py-1 border rounded disabled:opacity-50"
|
|
>
|
|
Prev
|
|
</button>
|
|
|
|
{Array.from({ length: totalPage }, (_, i) => (
|
|
<button
|
|
key={i}
|
|
onClick={() => setPage(i + 1)}
|
|
className={`px-3 py-1 border rounded ${
|
|
page === i + 1 ? "bg-blue-500 text-white" : "hover:bg-gray-100"
|
|
}`}
|
|
>
|
|
{i + 1}
|
|
</button>
|
|
))}
|
|
|
|
<button
|
|
onClick={() => setPage((prev) => Math.min(prev + 1, totalPage))}
|
|
disabled={page === totalPage}
|
|
className="px-3 py-1 border rounded disabled:opacity-50"
|
|
>
|
|
Next
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|