fix:category landing

This commit is contained in:
Rama Priyanto 2025-06-02 12:18:56 +07:00
parent af5b8000cd
commit 70ac77dba4
3 changed files with 191 additions and 167 deletions

View File

@ -29,7 +29,7 @@ import {
UserIcon,
} from "../../icons";
import Link from "next/link";
import { useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import {
getArticleByCategoryLanding,
getListArticle,
@ -49,6 +49,7 @@ import {
import { close, loading } from "@/config/swal";
import { format } from "date-fns";
import { getCategoryById } from "@/services/master-categories";
import AsyncSelect from "react-select/async";
const months = [
"Jan",
@ -81,12 +82,7 @@ export default function ListNews() {
searchParams.get("search") || ""
);
const [categorySearch, setCategorySearch] = useState("");
const [debouncedValue, setDebouncedValue] = useState("");
const [selectedCategoryId, setSelectedCategoryId] = useState<any>(
categoryIds ? categoryIds : ""
);
const [count, setCount] = useState(0);
const [selectedCategoryId, setSelectedCategoryId] = useState<any>([]);
const today = new Date();
const [year, setYear] = useState(today.getFullYear());
@ -101,42 +97,43 @@ export default function ListNews() {
setSelectedDate(new Date(year, monthIndex, 1));
};
const getCategoryId = async () => {
if (categoryIds) {
const res = await getCategoryById(Number(selectedCategoryId));
setCategorySearch(res?.data?.data.title);
setCategories([res?.data?.data]);
setCount(1);
}
};
useEffect(() => {
getCategory();
if (categoryIds && count == 0) {
getCategoryId();
const search = searchParams.get("search");
const category = searchParams.get("category_id");
if (searchParams.get("search")) {
setSearchValue(String(searchParams.get("search")));
getArticle({ title: String(search) });
}
}, [debouncedValue]);
if (category && category !== "") {
getCategoryFromQueries(category.split(","));
}
}, [searchParams]);
const getCategoryFromQueries = async (category: string[]) => {
const temp = [];
for (const element of category) {
const res = await getCategoryById(Number(element));
if (res?.data?.data) {
temp.push(res?.data?.data);
}
}
const setup = setupCategory(temp);
setSelectedCategoryId(setup);
getArticle({ category: setup });
};
useEffect(() => {
getArticle();
}, [page]);
}, [page, searchParams]);
const getCategory = async () => {
const res = await getArticleByCategoryLanding({
limit: debouncedValue === "" ? "5" : "",
title: debouncedValue,
});
if (res?.data?.data) {
setCategories(res?.data?.data);
}
};
async function getArticle() {
async function getArticle(props?: { title?: string; category?: any }) {
loading();
// topRef.current?.scrollIntoView({ behavior: "smooth" });
const req = {
page: page,
search: searchValue || "",
search: props?.title || searchValue || "",
limit: "9",
isPublish: true,
sort: "desc",
@ -144,9 +141,12 @@ export default function ListNews() {
pathname.includes("polda") || pathname.includes("satker")
? String(category)
: "",
categoryIds: selectedCategoryId,
// categoryIds:
// selectedCategoryId && categorySearch !== "" ? selectedCategoryId : "",
categoryIds: props?.category
? props.category.map((val: any) => val.id).join(",")
: selectedCategoryId.length > 0
? selectedCategoryId.map((val: any) => val.id).join(",")
: "",
startDate:
selectedDate && selectedMonth !== null
? convertDateFormatNoTimeV2(new Date(year, selectedMonth, 1))
@ -162,21 +162,52 @@ export default function ListNews() {
close();
}
useEffect(() => {
const timeout = setTimeout(() => {
setDebouncedValue(categorySearch);
}, 1500);
const debounceTimeout = useRef<NodeJS.Timeout | null>(null);
return () => clearTimeout(timeout);
}, [categorySearch]);
const onInputChange = (value: string) => {
setCategorySearch(value);
if (value === "") {
setSelectedCategoryId("");
const getCategory = async (search?: string) => {
const res = await getArticleByCategoryLanding({
// limit: debouncedValue === "" ? "5" : "",
// title: debouncedValue,
limit: !search || search === "" ? "5" : "",
title: search ? search : "",
});
if (res?.data?.data) {
setCategories(res?.data?.data);
return res?.data?.data;
}
return [];
};
const setupCategory = (data: any) => {
const temp = [];
for (const element of data) {
temp.push({
id: element.id,
label: element.title,
value: element.id,
});
}
return temp;
};
const loadOptions = useCallback(
(inputValue: string, callback: (options: any) => void) => {
if (debounceTimeout.current) {
clearTimeout(debounceTimeout.current);
}
debounceTimeout.current = setTimeout(async () => {
try {
const data = await getCategory(inputValue);
callback(setupCategory(data));
} catch (error) {
callback([]);
}
}, 1500);
},
[]
);
return (
<div className="bg-white border-b-1" ref={topRef}>
<div className="text-black py-5 px-3 lg:w-[75vw] mx-auto bg-white">
@ -187,118 +218,104 @@ export default function ListNews() {
<ChevronRightIcon />
<p className="text-black">Berita</p>
</div>
<div className="py-5 lg:py-10 lg:px-10 flex flex-col lg:flex-row gap-2 items-end">
<Input
aria-label="Judul"
className="w-full"
classNames={{
inputWrapper: "bg-white hover:!bg-gray-100 border-1",
input: "text-sm !text-black",
}}
// onKeyDown={(event) => {
// if (event.key === "Enter") {
// router.push(pathname + `?search=${searchValue}`);
// getArticle();
// }
// }}
labelPlacement="outside"
placeholder="Judul..."
value={searchValue}
onValueChange={setSearchValue}
type="search"
/>
<div className="flex w-full lg:w-fit gap-4">
<Autocomplete
className="w-full lg:w-[240px] mt-0"
label=""
<div className="w-full flex justify-center">
<div className="py-5 lg:py-10 lg:mx-auto flex flex-col lg:flex-row gap-2 items-end grow-0 mx-auto">
<Input
aria-label="Judul"
className="w-full lg:w-[300px]"
classNames={{
inputWrapper: "bg-white hover:!bg-gray-100 border-1 rounded-md",
input: "text-sm !text-black",
}}
// onKeyDown={(event) => {
// if (event.key === "Enter") {
// router.push(pathname + `?search=${searchValue}`);
// getArticle();
// }
// }}
labelPlacement="outside"
variant="bordered"
placeholder="Judul..."
value={searchValue}
onValueChange={setSearchValue}
type="search"
/>
<AsyncSelect
isMulti
loadOptions={loadOptions}
defaultOptions
placeholder="Kategori"
inputValue={categorySearch}
// selectedKey={selectedCategoryId}
onInputChange={onInputChange}
onSelectionChange={(e) => setSelectedCategoryId(e)}
inputProps={{ classNames: { inputWrapper: "border-1" } }}
defaultItems={categories}
>
{/* {categories.length > 0 &&
categories.map((category: any) => (
<AutocompleteItem key={`${category.id}`}>
{category.title}
</AutocompleteItem>
))} */}
{(item: any) => (
<AutocompleteItem key={item.id}>{item.title}</AutocompleteItem>
)}
</Autocomplete>
</div>
<div className="flex flex-row items-center border-1 h-[40px] w-full lg:w-[240px] rounded-xl px-2">
<Popover placement="bottom" showArrow={true} className="w-full">
<PopoverTrigger>
<a className="px-2 py-1 text-sm w-[220px]">
{" "}
{selectedDate
? format(selectedDate, "MMMM yyyy")
: "Pilih Bulan"}
</a>
</PopoverTrigger>
<PopoverContent className="p-4 w-[220px]">
<div className="flex items-center justify-between mb-2 px-1 w-full">
<button
className="text-gray-500 hover:text-black"
onClick={() => setYear((prev) => prev - 1)}
>
<ChevronLeftIcon />
</button>
<span className="font-semibold text-center">{year}</span>
<button
className="text-gray-500 hover:text-black"
onClick={() => setYear((prev) => prev + 1)}
>
<ChevronRightIcon />
</button>
</div>
<div className="grid grid-cols-3 gap-2 w-full">
{months.map((month, idx) => (
className="z-50 min-w-[300px] max-w-[600px]"
classNames={{
control: () =>
"border border-gray-300 border-1 rounded-xl min-w-[300px] max-w-[600px]",
menu: () => "z-50",
}}
value={selectedCategoryId}
onChange={setSelectedCategoryId}
/>
<div className="flex flex-row items-center border-1 h-[40px] w-full lg:w-[200px] rounded-md px-2">
<Popover placement="bottom" showArrow={true} className="w-full">
<PopoverTrigger>
<a className="px-2 py-1 text-sm w-[220px]">
{" "}
{selectedDate
? format(selectedDate, "MMMM yyyy")
: "Pilih Bulan"}
</a>
</PopoverTrigger>
<PopoverContent className="p-4 w-[200px]">
<div className="flex items-center justify-between mb-2 px-1 w-full">
<button
key={idx}
onClick={() => handleMonthClick(idx)}
className={`py-1 rounded-md text-sm transition-colors ${
selectedDate &&
selectedDate.getMonth() === idx &&
selectedDate.getFullYear() === year
? "bg-blue-500 text-white"
: "hover:bg-gray-200 text-gray-700"
}`}
className="text-gray-500 hover:text-black"
onClick={() => setYear((prev) => prev - 1)}
>
{month}
<ChevronLeftIcon />
</button>
))}
</div>
</PopoverContent>
</Popover>{" "}
{selectedDate && (
<a
className="cursor-pointer w-[20px]"
onClick={() => setSelectedDate(null)}
>
<TimesIcon size={20} />
</a>
)}
<span className="font-semibold text-center">{year}</span>
<button
className="text-gray-500 hover:text-black"
onClick={() => setYear((prev) => prev + 1)}
>
<ChevronRightIcon />
</button>
</div>
<div className="grid grid-cols-3 gap-2 w-full">
{months.map((month, idx) => (
<button
key={idx}
onClick={() => handleMonthClick(idx)}
className={`py-1 rounded-md text-sm transition-colors ${
selectedDate &&
selectedDate.getMonth() === idx &&
selectedDate.getFullYear() === year
? "bg-blue-500 text-white"
: "hover:bg-gray-200 text-gray-700"
}`}
>
{month}
</button>
))}
</div>
</PopoverContent>
</Popover>{" "}
{selectedDate && (
<a
className="cursor-pointer w-[20px]"
onClick={() => setSelectedDate(null)}
>
<TimesIcon size={20} />
</a>
)}
</div>
<Button
onPress={() => getArticle()}
className="bg-red-600 text-white w-[80px] rounded-md"
>
Cari
</Button>
{/* </Link> */}
</div>
{/* <Link
href={`/news/all?search=${searchValue}&category_id=${
selectedCategoryId || ""
}&month=${selectedMonth && selectedDate ? selectedMonth + 1 : ""}`}
> */}
<Button
onPress={getArticle}
className="bg-red-600 text-white w-[80px]"
>
Cari
</Button>
{/* </Link> */}
</div>
{article?.length < 1 || !article ? (
<div className="flex justify-center items-center">Tidak ada Data</div>

View File

@ -260,7 +260,11 @@ export default function DetailNews(props: { data: any; listArticle: any }) {
<p className="text-lg border-b-3 border-red-600 font-semibold">TAGS</p>
<div className="flex flex-wrap gap-2">
{data?.categories?.map((category: any) => (
<Link href={""} key={category?.id} className="text-sm ">
<Link
href={`/news/all?category_id=${category?.id}`}
key={category?.id}
className="text-sm "
>
<Button className="bg-[#BE0106] text-white px-2 py-2 rounded-md ">
{category?.title}
</Button>
@ -268,17 +272,20 @@ export default function DetailNews(props: { data: any; listArticle: any }) {
))}
</div>
<div className="flex flex-wrap gap-2">
{data?.tags?.split(",").map((tag: any) => (
<Link href={""} key={tag} className="text-xs">
<Button
className="border-[#BE0106] px-2 py-2 rounded-md "
variant="bordered"
size="sm"
>
{tag}
</Button>
</Link>
))}
{data?.tags?.split(",").map(
(tag: any) =>
tag !== "" && (
<Link href={``} key={tag} className="text-xs">
<Button
className="border-[#BE0106] px-2 py-2 rounded-md "
variant="bordered"
size="sm"
>
{tag}
</Button>
</Link>
)
)}
</div>
</div>
<div className="grid grid-cols-2 md:flex lg:justify-between gap-2 lg:gap-10 my-8">

View File

@ -72,8 +72,8 @@ export default function RelatedNews(props: { categories: any }) {
);
}}
>
{article?.map((newsItem: any) => (
<SwiperSlide key={newsItem.id}>
{article?.map((newsItem: any, index: number) => (
<SwiperSlide key={`${newsItem.id}-${index}`}>
<Card isFooterBlurred radius="lg" className="border-none">
<Image
width={480}