web-mikul-news/components/landing-page/latest-and-popular.tsx

481 lines
19 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { getListArticle } from "@/service/article";
import Image from "next/image";
import Link from "next/link";
import { useEffect, useState } from "react";
const data1 = {
main: {
image: "/jumbo.png",
category: "BERANDA",
title:
"Industri Animasi Indonesia Melesat: “Jumbo” Masuk Daftar Film Terlaris Sepanjang Masa",
author: "christine natalia",
date: "10 MARET 2025",
comments: 0,
excerpt:
"Mikulnews.com - Industri film animasi Indonesia menunjukkan kemajuan signifikan dalam beberapa tahun terakhir. Tak hanya sukses dari sisi kreativitas dan produksi,...",
},
left: [
{
image: "/jumbo.png",
category: "BERANDA",
title:
"Industri Animasi Indonesia Melesat: “Jumbo” Masuk Daftar Film Terlaris Sepanjang Masa",
author: "christine natalia",
date: "10 MARET 2025",
comments: 0,
excerpt:
"Mikulnews.com - Industri film animasi Indonesia menunjukkan kemajuan signifikan dalam beberapa tahun terakhir. Tak hanya sukses dari sisi kreativitas dan produksi,...",
},
],
topRight: [
{
image: "/bimantoro.png",
category: "BERANDA",
title:
"Bimantoro Wiyono Apresiasi Keberhasilan Atur Mudik dan Arus Balik Lebaran 2025",
author: "SALMA HN",
date: "10 MARET 2025",
comments: 0,
excerpt:
"Jakarta - Rekrutmen Bersama BUMN (RBB BUMN) 2025 kini resmi dibuka mulai hari ini, Senin, 10 Maret 2025. Pelamar yang...",
},
],
topRightMain: [
{
image: "/bimantoro.png",
category: "BERANDA",
title:
"Bimantoro Wiyono Apresiasi Keberhasilan Atur Mudik dan Arus Balik Lebaran 2025",
author: "SALMA HN",
date: "10 MARET 2025",
comments: 0,
excerpt:
"Jakarta - Rekrutmen Bersama BUMN (RBB BUMN) 2025 kini resmi dibuka mulai hari ini, Senin, 10 Maret 2025. Pelamar yang...",
},
{
image: "/cpns.jpg",
category: "BERANDA",
title:
"Polri Dapat Meningkatkan Transparansi: Paparan Fathul Ulum tentang Keterbukaan Informasi",
author: "SALMA HN",
date: "7 MARET 2025",
comments: 0,
excerpt:
"JAKARTA - Program Rekrutmen Bersama BUMN (RBB) 2025 resmi dibuka pada Jumat...",
},
{
image: "/bpnt.jpg",
category: "BERANDA",
title: "Tutorial Cek Status Penerima BPNT 2025 Melalui HP",
author: "SALMA HN",
date: "7 MARET 2025",
comments: 0,
excerpt:
"JAKARTA - Program Rekrutmen Bersama BUMN (RBB) 2025 resmi dibuka pada Jumat...",
},
],
bottomMid: [
{
image: "/vvip.jpg",
title:
"Pembangunan Bandara VVIP IKN Berjalan Lancar, Ditargetkan Rampung Maret 2025",
date: "6 MARET 2025",
},
{
image: "/pmk.png",
title: "PMK 11/2025 Ubah Ketentuan PPN Besaran Tertentu, Ini Rinciannya",
date: "11 FEBRUARI 2025",
},
{
image: "/cpns.jpg",
title:
"Hasil Kelulusan CPNS 2024 Diumumkan, Simak Arti Kode dan Cara Mengecek Hasilnya",
date: "17 FEBRUARI 2025",
},
],
bottomRightData: [
{
image: "/iums.png",
category: "INTERNASIONAL",
title:
"IUMS Serukan Boikot Produk Pendukung Israel, PMII Rilis Daftar Merek Terkait",
author: "christine natalia",
date: "10 MARET 2025",
comments: 0,
excerpt:
"Mikulnews.com - Persatuan Cendekiawan Muslim Internasional atau International Union of Muslim Scholars (IUMS) yang berbasis di Qatar mengeluarkan fatwa terkait konflik...",
},
{
image: "/jateng.png",
category: "INTERNASIONAL",
title:
"Tiga Polisi Jaga Tahanan di Polda Jateng Ditahan Akibat Dugaan Pungli",
author: "christine natalia",
date: "10 MARET 2025",
comments: 0,
excerpt:
"Mikulnews.com - Persatuan Cendekiawan Muslim Internasional atau International Union of Muslim Scholars (IUMS) yang berbasis di Qatar mengeluarkan fatwa terkait konflik...",
},
{
image: "/perang.png",
category: "INTERNASIONAL",
title:
"Perang Dagang AS-China Meningkat, Indonesia Terancam Tekanan Ekonomi Ganda",
author: "christine natalia",
date: "10 MARET 2025",
comments: 0,
excerpt:
"Mikulnews.com - Persatuan Cendekiawan Muslim Internasional atau International Union of Muslim Scholars (IUMS) yang berbasis di Qatar mengeluarkan fatwa terkait konflik...",
},
{
image: "/gaza.png",
category: "INTERNASIONAL",
title:
"Fatwa Ulama Dunia Serukan Blokade Total Terhadap Israel Demi Bela Gaza",
author: "christine natalia",
date: "10 MARET 2025",
comments: 0,
excerpt:
"Mikulnews.com - Persatuan Cendekiawan Muslim Internasional atau International Union of Muslim Scholars (IUMS) yang berbasis di Qatar mengeluarkan fatwa terkait konflik...",
},
],
bottomRight: [
{
image: "/iums.png",
category: "INTERNASIONAL",
title:
"IUMS Serukan Boikot Produk Pendukung Israel, PMII Rilis Daftar Merek Terkait",
author: "christine natalia",
date: "10 MARET 2025",
comments: 0,
excerpt:
"Mikulnews.com - Persatuan Cendekiawan Muslim Internasional atau International Union of Muslim Scholars (IUMS) yang berbasis di Qatar mengeluarkan fatwa terkait konflik...",
},
],
};
const data = {
leftMain: data1.main,
leftList: data1.left,
bottomRightList: data1.bottomRightData,
centerMain: data1.topRight[0],
centerList: data1.bottomMid.slice(0, 3),
rightMain: data1.topRightMain[0],
rightList: data1.bottomRight.slice(0, 3),
};
type Article = {
id: number;
title: string;
description: string;
categoryName: string;
createdAt: string;
createdByName: string;
thumbnailUrl: string;
categories: {
title: string;
}[];
files: {
fileUrl: string;
file_alt: string;
}[];
};
const ITEMS_PER_PAGE = 2;
export default function LatestandPopular() {
const [page, setPage] = useState(1);
const [totalPage, setTotalPage] = useState(1);
const [articles, setArticles] = useState<Article[]>([]);
const [popularPosts, setPopularPosts] = useState<Article[]>([]);
const [showData, setShowData] = useState("5");
const [search, setSearch] = useState("");
const [selectedCategories, setSelectedCategories] = useState<any>("");
const [startDateValue, setStartDateValue] = useState({
startDate: null,
endDate: null,
});
const [currentPage, setCurrentPage] = useState(1);
// useEffect(() => {
// const fetchData = async () => {
// const res = await getListArticle(); // ganti dengan fungsi fetch-mu
// setArticles(res.data || []);
// };
// fetchData();
// }, []);
useEffect(() => {
initState();
}, [page, showData, startDateValue, selectedCategories]);
async function initState() {
// loading();
const req = {
limit: showData,
page,
search,
categorySlug: Array.from(selectedCategories).join(","),
sort: "desc",
isPublish: true,
sortBy: "created_at",
};
try {
const res = await getListArticle(req);
setArticles(res?.data?.data || []);
setTotalPage(res?.data?.meta?.totalPage || 1);
setPopularPosts(res?.data?.data || []);
} finally {
// close();
}
}
const totalPages = Math.ceil(articles.length / ITEMS_PER_PAGE);
const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;
const currentArticles = articles.slice(
startIndex,
startIndex + ITEMS_PER_PAGE
);
return (
<section className="bg-white py-10 px-4 md:px-10 w-full">
<div className="max-w-screen-6xl mx-auto flex flex-col lg:flex-row lg:justify-between gap-5">
<div className="w-full lg:w-[750px]">
<h2 className="text-sm border-b-2 border-gray-300 font-bold mb-4">
Latest Post
</h2>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
{currentArticles.map((article, index) => (
<div key={index}>
<Link href={`/detail/${article?.id}`}>
<div className="relative w-full aspect-video mb-3">
<Image
src={
article?.thumbnailUrl ||
article?.files?.[0]?.fileUrl ||
"/default-image.jpg"
}
alt={article?.title || "No title"}
fill
sizes="(max-width: 1024px) 100vw, 33vw"
className="object-cover rounded"
/>
<div className="absolute inset-0 bg-black/20" />
<span className="absolute bottom-1 left-1 bg-[#FFC600] text-black text-[11px] px-2 py-1 uppercase">
{article?.categories?.map((cat) => cat.title).join(", ")}
</span>
</div>
<div className="text-black">
<h3 className="font-semibold text-base mb-1">
{article.title}
</h3>
<p className="text-[#999999] text-sm font-serif">
{article.description.length > 100
? `${article.description.slice(0, 100)}...`
: article.description}
</p>
<div className="text-xs text-[#999999] mb-2 flex items-center gap-2">
by{" "}
<span className="text-[#31942E]">
{article.createdByName}
</span>{" "}
|{" "}
<div className="text-xs mt-1.5 text-[#A0A0A0] space-x-2 flex items-center">
{/* Clock Icon + Date */}
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
>
<g fill="none">
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
<path
fill="currentColor"
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1"
/>
</g>
</svg>{" "}
{new Date(article?.createdAt).toLocaleDateString(
"id-ID",
{
day: "numeric",
month: "long",
year: "numeric",
}
)}
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
/>
</svg>{" "}
0
</div>
</div>
</div>
</Link>
</div>
))}
</div>
<div className="relative my-5 max-w-full h-[125px] overflow-hidden flex items-center mx-auto border">
<Image
src="/image-kolom.png"
alt="Berita Utama"
fill
className="object-cover"
/>
</div>
<div className="flex justify-center gap-2">
<button
onClick={() => setCurrentPage((prev) => Math.max(prev - 1, 1))}
disabled={currentPage === 1}
className="px-3 py-1 bg-gray-200 rounded disabled:opacity-50"
>
Previous
</button>
<span className="px-2 py-1">
{currentPage} / {totalPages}
</span>
<button
onClick={() =>
setCurrentPage((prev) => Math.min(prev + 1, totalPages))
}
disabled={currentPage === totalPages}
className="px-3 py-1 bg-gray-200 rounded disabled:opacity-50"
>
Next
</button>
</div>
</div>
<aside className="w-full lg:w-[345px]">
<div className="">
<h2 className="text-sm border-b-2 border-gray-300 font-bold mb-4">
Most Popular
</h2>
<div className=" w-full">
<div className="relative w-full aspect-video mb-5">
<Link href={`/detail/${articles[0]?.id}`}>
<Image
src={
articles[0]?.thumbnailUrl ||
articles[0]?.files?.[0]?.fileUrl ||
"/default-image.jpg"
}
alt={"articles[0]?.title"}
fill
sizes="(max-width: 1024px) 100vw, 33vw"
className="object-cover"
/>
<div className="absolute inset-0 bg-black/30" />
<div className="absolute bottom-0.5 left-2 text-white">
<h3 className=" font-semibold text-base mb-1">
{articles[0]?.title}
</h3>
<p className=" text-xs mb-2 flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
>
<g fill="none">
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
<path
fill="currentColor"
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1"
/>
</g>
</svg>{" "}
{new Date(articles[0]?.createdAt).toLocaleDateString(
"id-ID",
{
day: "numeric",
month: "long",
year: "numeric",
}
)}
</p>
</div>
</Link>
</div>
<div className="space-y-5">
{articles?.slice(1, 4).map((article, index) => (
<div key={index} className="flex gap-3">
<Link
className="flex gap-3"
href={`/detail/${article?.id}`}
>
<div className="relative w-[120px] h-[86px] shrink-0">
<Image
src={
article?.thumbnailUrl ||
article?.files?.[0]?.fileUrl ||
"/default-image.jpg"
}
alt={"article?.title"}
fill
className="object-cover"
/>
</div>
<div>
<h4 className="text-sm font-semibold mb-3">
{article?.title}
</h4>
<p className="text-xs text-gray-500 flex gap-2 items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
>
<g fill="none">
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
<path
fill="currentColor"
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2m0 2a8 8 0 1 0 0 16a8 8 0 0 0 0-16m0 2a1 1 0 0 1 .993.883L13 7v4.586l2.707 2.707a1 1 0 0 1-1.32 1.497l-.094-.083l-3-3a1 1 0 0 1-.284-.576L11 12V7a1 1 0 0 1 1-1"
/>
</g>
</svg>{" "}
{new Date(articles[0]?.createdAt).toLocaleDateString(
"id-ID",
{
day: "numeric",
month: "long",
year: "numeric",
}
)}
</p>
</div>
</Link>
</div>
))}
</div>
</div>
</div>
<div className="flex items-center gap-4 my-6">
<div className="flex-1 h-px bg-gray-300" />
<p className="text-center border px-2 py-2 w-[110px] text-xs font-medium cursor-pointer">
LOAD MORE
</p>
<div className="flex-1 h-px bg-gray-300" />
</div>
</aside>
</div>
</section>
);
}