web-milenial-bersuara/components/landing-page/news.tsx

910 lines
30 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import Image from "next/image";
import { CalendarDays, MessageCircle, Timer } from "lucide-react";
import Link from "next/link";
import { useEffect, useState } from "react";
import { getListArticle } from "@/service/article";
import { getAdvertise } from "@/service/advertisement";
const featured = [
{
id: 1,
title: "Tak Perpanjang Kontrak, Megawati Hengkang dari Red Sparks",
date: "11 APRIL 2025",
image: "/megawati.png",
},
{
id: 2,
title: "Polri Dinilai Sukses Kelola Arus Mudik dan Balik Lebaran 2025",
date: "11 APRIL 2025",
image: "/mudik.png",
},
];
const smallNews = [
{
id: 3,
title:
"Arus Mudik dan Balik Libur Lebaran 2025 Lancar, Kerja Keras Polri Panen Apresiasi",
date: "8 APRIL 2025",
image: "/mudik.png",
},
{
id: 4,
title:
"1 Juta Penonton dalam Seminggu, Jumbo Pecahkan Rekor Animasi Indonesia",
date: "8 APRIL 2025",
image: "/jumbo.png",
},
{
id: 5,
title:
"Jalur Fungsional Tol Jakarta-Cikampek II Selatan Siap Layani Pemudik",
date: "8 APRIL 2025",
image: "/tol.jpg",
},
{
id: 6,
title:
"Arus Balik Diprediksi 5-6 April, Polri Imbau Pemudik Siapkan Perjalanan",
date: "8 APRIL 2025",
image: "/mudik.png",
},
];
const featured1 = [
{
id: 1,
title: "Explore the Beauty of Bali: Beaches, Culture, and More",
category: "TRAVEL IDEAS",
date: "August 30, 2025",
image: "/summer.jpg",
},
];
const smallNews1 = [
{
id: 2,
title: "Top 5 Destinations for Your Summer Getaway",
category: "DESTINATION",
date: "August 28, 2025",
image: "/blogger.jpg",
},
{
id: 3,
title: "How to Travel Safely During the Holiday Season",
category: "NEWS",
date: "August 25, 2025",
image: "/trip.jpg",
},
];
const posts = [
{
id: 1,
title: "Your Guide to Canggus Hottest Street: The Essential Batu Bolong",
author: "Admin",
date: "27 November 2024",
comments: 0,
excerpt:
"Dropcap the popularization of the “ideal measure” has led to advice such as “Increase font size for large screens and...",
image: "/guide.jpg",
},
{
id: 2,
title: "6 Perfect places to watch the sunrise in Bali while you honeymoon",
author: "Admin",
date: "26 November 2024",
comments: 0,
excerpt:
"Intro text we refine our methods of responsive web design, weve increasingly focused on measure and its relationship to how...",
image: "/car.jpg",
},
];
const posts1 = [
{
id: 1,
title: "Great Surf Spots in Bali: From Beginner to Pro Surfers",
author: "Admin",
date: "27 November 2024",
comments: 0,
excerpt:
"Intro text we refine our methods of responsive web design, weve increasingly focused on measure and its relationship to how...",
image: "/tea.jpg",
},
{
id: 2,
title: "Important things you should know for Mount Agung hiking",
author: "Admin",
date: "26 November 2024",
comments: 0,
excerpt:
"Dropcap the popularization of the “ideal measure” has led to advice such as “Increase font size for large screens and...",
image: "/play.png",
},
];
const popularPosts = [
{
id: 1,
title:
"Kisah Cinta IU-Park Bo Gum di 1950-an Hadir di When Life Gives You Tangerines, Tayang 7 Maret 2025",
image: "/park-go.jpg",
shares: 0,
},
{
id: 2,
title:
"Temani Perjalanan Mudik Anda dengan Musik AI dari Qudo Buana Nawakara",
shares: 0,
},
{
id: 3,
title: "Bali Nightlife Guide : The most popular clubs in Kuta",
shares: 0,
},
{
id: 4,
title:
"Dirumorkan Hubungan dengan Kim Ji Won, Kim Soo Hyun Langsung Klarifikasi Begini",
shares: 0,
},
{
id: 5,
title:
"Carmen Idol K-Pop Asal Bali yang Resmi Debut dengan Girl Grup Baru SM Entertainment",
shares: 0,
},
];
const categories = [
{ id: 1, name: "DESTINATION", count: 22 },
{ id: 2, name: "FOOD & DRINK", count: 8 },
{ id: 3, name: "NEWS", count: 21 },
{ id: 4, name: "PHOTO", count: 17 },
{ id: 5, name: "TAK BERKATEGORI", count: 34 },
{ id: 6, name: "TRAVEL IDEAS", count: 27 },
{ id: 7, name: "VIDEO", count: 7 },
];
type Article = {
id: number;
title: string;
description: string;
categoryName: string;
createdAt: string;
publishedAt: string;
createdByName: string;
slug: string;
customCreatorName: string;
thumbnailUrl: string;
categories: {
title: string;
}[];
files: {
fileUrl: string;
file_alt: string;
}[];
};
type Advertise = {
id: number;
title: string;
description: string;
placement: string;
contentFileUrl: string;
redirectLink: string;
};
export default function News() {
const [page, setPage] = useState(1);
const [totalPage, setTotalPage] = useState(1);
const [articles, setArticles] = useState<Article[]>([]);
const [showData, setShowData] = useState("4"); // 👈 ambil 4 artikel
const [search, setSearch] = useState("");
const [selectedCategories, setSelectedCategories] = useState<any>([]);
const [startDateValue, setStartDateValue] = useState({
startDate: null,
endDate: null,
});
const [bannerAd, setBannerAd] = useState<Advertise | null>(null);
const [bannerImageAd, setBannerImageAd] = useState<Advertise | null>(null);
useEffect(() => {
initStateAdver();
}, []);
async function initStateAdver() {
const req = {
limit: 100,
page: 1,
sort: "desc",
sortBy: "created_at",
isPublish: true,
};
try {
const res = await getAdvertise(req);
const data: Advertise[] = res?.data?.data || [];
// ambil iklan placement jumbotron
const jumbotron = data.find((ad) => ad.placement === "jumbotron");
if (jumbotron) setBannerAd(jumbotron);
// ambil iklan placement banner
const banner = data.find((ad) => ad.placement === "banner");
if (banner) setBannerImageAd(banner);
} catch (err) {
console.error("Error fetching advertisement:", err);
}
}
useEffect(() => {
initState();
}, [page, showData, startDateValue, selectedCategories]);
async function initState() {
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);
} catch (err) {
console.error("Error fetching articles:", err);
}
}
return (
<section className="max-w-7xl mx-auto py-12 grid grid-cols-1 lg:grid-cols-3 gap-10">
{/* Left Column */}
<div className="lg:col-span-2">
{/* Section Title */}
<div className="flex items-center justify-between border-b border-gray-200 pb-2 mb-6">
<h2 className="text-xl font-semibold relative">
Travel Ideas
<span className="absolute bottom-[-2px] left-0 w-16 h-[2px] bg-teal-400"></span>
</h2>
<div className="flex gap-4 text-sm text-gray-500 font-medium">
<Link href="#" className="hover:text-teal-500">
ALL
</Link>
<Link href="#" className="hover:text-teal-500">
NEWS
</Link>
<Link href="#" className="hover:text-teal-500">
TRAVEL IDEAS
</Link>
<Link href="#" className="hover:text-teal-500">
DESTINATION
</Link>
<Link href="#" className="hover:text-teal-500">
PHOTO
</Link>
</div>
</div>
{/* Featured News */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
{articles.slice(0, 2).map((item) => (
<div key={item.id}>
<Link
className="relative rounded overflow-hidden group cursor-pointer"
href={`/details/${item?.slug}`}
>
<Image
src={item.thumbnailUrl || "/placeholder.jpg"}
alt={item.title}
width={500}
height={300}
className="w-full h-[250px] object-cover group-hover:scale-105 transition-transform duration-500"
/>
<div className="absolute inset-0 bg-black/50 flex flex-col justify-end p-4">
<h3 className="text-white font-bold text-lg leading-snug">
{item.title}
</h3>
<div className="flex items-center text-white text-xs gap-2 mt-2">
<CalendarDays size={14} />
<span>
{new Date(item.publishedAt).toLocaleDateString("id-ID", {
day: "2-digit",
month: "long",
year: "numeric",
})}
</span>
</div>
</div>
</Link>
</div>
))}
</div>
{/* Small News */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{articles.map((item) => (
<div key={item.id}>
<Link className="flex gap-4" href={`/details/${item?.slug}`}>
<Image
src={item.thumbnailUrl}
alt={item.title}
width={120}
height={80}
className="rounded object-cover flex-shrink-0"
/>
<div>
<h4 className="text-sm font-semibold text-gray-800 hover:text-teal-500 leading-snug">
{item.title}
</h4>
<div className="flex items-center text-xs text-gray-500 gap-2 mt-1">
<CalendarDays size={12} />
<span>
{" "}
{new Date(item.publishedAt).toLocaleDateString("id-ID", {
day: "2-digit",
month: "long",
year: "numeric",
})}
</span>
</div>
</div>
</Link>
</div>
))}
</div>
<div className="relative my-5 max-w-full h-[125px] overflow-hidden flex items-center mx-auto border">
{bannerImageAd ? (
<a
href={bannerImageAd.redirectLink}
target="_blank"
rel="noopener noreferrer"
className="w-full h-full relative"
>
<Image
src={bannerImageAd.contentFileUrl}
alt={bannerImageAd.title || "Iklan Banner"}
fill
className="object-cover"
/>
</a>
) : (
<Image
src="/image-kolom.png"
alt="Banner Default"
fill
className="object-cover"
/>
)}
</div>
{/* Header */}
<div className="flex items-center justify-between border-b border-gray-200 pb-2 mb-6">
<h2 className="text-xl font-semibold relative">
Travel Destination
<span className="absolute bottom-[-2px] left-0 w-16 h-[2px] bg-teal-400"></span>
</h2>
<div className="flex gap-4 text-sm text-gray-500 font-medium">
<Link href="#" className="hover:text-teal-500">
ALL
</Link>
<Link href="#" className="hover:text-teal-500">
NEWS
</Link>
<Link href="#" className="hover:text-teal-500">
TRAVEL IDEAS
</Link>
<Link href="#" className="hover:text-teal-500">
DESTINATION
</Link>
<Link href="#" className="hover:text-teal-500">
VIDEO
</Link>
</div>
</div>
{/* Featured News */}
<div>
{/* Featured News Besar */}
<div className="mb-6">
<div className="relative rounded overflow-hidden group cursor-pointer">
{articles.length > 0 && (
<>
<Link
className="flex gap-4"
href={`/details/${articles[0]?.slug}`}
>
<Image
src={articles[0]?.thumbnailUrl || "/placeholder.jpg"}
alt={articles[0]?.title || "No title"}
width={800}
height={400}
className="w-full h-[400px] object-cover group-hover:scale-105 transition-transform duration-500"
/>
<div className="absolute inset-0 bg-black/40 flex flex-col justify-end p-6">
<span className="bg-red-500 text-white text-xs px-2 py-1 rounded mb-2 w-fit uppercase">
{articles[0]?.categories?.[0]?.title ||
"TAK BERKATEGORI"}
</span>
<h3 className="text-white font-bold text-2xl leading-snug">
{articles[0]?.title}
</h3>
<div className="flex items-center text-white text-xs gap-2 mt-2">
<span className="font-semibold">
BY {articles[0]?.createdByName || "ADMIN"}
</span>
<CalendarDays size={14} />
<span>
{new Date(
articles[0]?.publishedAt || ""
).toLocaleDateString("id-ID", {
day: "2-digit",
month: "long",
year: "numeric",
})}
</span>
</div>
</div>
</Link>
</>
)}
</div>
</div>
{/* Small News */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{articles.slice(1, 3).map((item) => (
<div key={item.id}>
<Link
className="relative rounded overflow-hidden group cursor-pointer"
href={`/details/${item?.slug}`}
>
<Image
src={item.thumbnailUrl}
alt={item.title}
width={400}
height={200}
className="w-full h-[250px] object-cover group-hover:scale-105 transition-transform duration-500"
/>
{/* Badge kategori di atas gambar */}
<div className="absolute top-3 left-3">
<span className="bg-red-500 text-white text-xs px-2 py-1 rounded w-fit uppercase">
{item.categories?.[0]?.title}
</span>
</div>
{/* Content di bawah gambar */}
<div className="mt-3">
<h4 className="text-black font-semibold text-base leading-snug">
{item.title}
</h4>
<div className="flex items-center text-xs text-gray-500 gap-2 mt-1">
<span className="font-semibold text-gray-700">
BY <span className="text-[#49C5C4]">ADMIN</span>
</span>
<Timer className="text-[#49C5C4]" size={12} />
<span>
{new Date(item?.publishedAt || "").toLocaleDateString(
"id-ID",
{
day: "2-digit",
month: "long",
year: "numeric",
}
)}
</span>
</div>
</div>
</Link>
</div>
))}
</div>
</div>
<div className="relative my-5 max-w-full h-[125px] overflow-hidden flex items-center mx-auto border">
{bannerImageAd ? (
<a
href={bannerImageAd.redirectLink}
target="_blank"
rel="noopener noreferrer"
className="w-full h-full relative"
>
<Image
src={bannerImageAd.contentFileUrl}
alt={bannerImageAd.title || "Iklan Banner"}
fill
className="object-cover"
/>
</a>
) : (
<Image
src="/image-kolom.png"
alt="Banner Default"
fill
className="object-cover"
/>
)}
</div>
<div className="flex items-center justify-between border-b border-gray-200 pb-2 mb-6">
<h2 className="text-xl font-semibold relative">
Travel Destination
<span className="absolute bottom-[-2px] left-0 w-16 h-[2px] bg-teal-400"></span>
</h2>
<div className="flex gap-4 text-sm text-gray-500 font-medium">
<Link href="#" className="hover:text-teal-500">
ALL
</Link>
<Link href="#" className="hover:text-teal-500">
NEWS
</Link>
<Link href="#" className="hover:text-teal-500">
TRAVEL IDEAS
</Link>
<Link href="#" className="hover:text-teal-500">
DESTINATION
</Link>
<Link href="#" className="hover:text-teal-500">
VIDEO
</Link>
</div>
</div>
{/* Featured News */}
<div>
{/* Featured News Besar */}
<div className="space-y-8">
{articles.slice(2, 4).map((post) => (
<div
key={post.id}
className="flex flex-col md:flex-row border-b border-gray-200 pb-6"
>
<Link href={`/details/${post?.slug}`}>
{/* Left Content */}
<div className="flex-1 pr-0 md:pr-6">
<h2 className="text-xl md:text-2xl font-bold mb-2">
{post.title}
</h2>
<div className="flex items-center text-sm text-gray-500 gap-2 mb-3">
<span>
BY{" "}
<span className="text-[#49C5C4] font-semibold uppercase">
{post.createdByName}
</span>
</span>
<span></span>
<span>
{new Date(post?.publishedAt || "").toLocaleDateString(
"id-ID",
{
day: "2-digit",
month: "long",
year: "numeric",
}
)}
</span>
<span></span>
<MessageCircle size={14} className="text-[#49C5C4]" />
<span>{/* {post.comments} */}0</span>
</div>
<p className="text-gray-600 mb-4">
{post.description.split(" ").slice(0, 50).join(" ")}
{post.description.split(" ").length > 50 && "..."}
</p>
<Link href={`/details/${post?.slug}`}>
<button className="px-4 py-2 border border-gray-300 text-gray-700 text-sm rounded hover:bg-gray-100 transition">
READ MORE
</button>
</Link>
</div>
</Link>
{/* Right Thumbnail */}
<div className="w-full md:w-[280px] flex-shrink-0 mt-4 md:mt-0">
<Image
src={post.thumbnailUrl}
alt={post.title}
width={400}
height={250}
className="w-full h-[180px] md:h-[150px] object-cover rounded"
/>
</div>
</div>
))}
</div>
</div>
<div className="relative my-5 max-w-full h-[125px] overflow-hidden flex items-center mx-auto border">
{bannerImageAd ? (
<a
href={bannerImageAd.redirectLink}
target="_blank"
rel="noopener noreferrer"
className="w-full h-full relative"
>
<Image
src={bannerImageAd.contentFileUrl}
alt={bannerImageAd.title || "Iklan Banner"}
fill
className="object-cover"
/>
</a>
) : (
<Image
src="/image-kolom.png"
alt="Banner Default"
fill
className="object-cover"
/>
)}
</div>
<div>
{/* Featured News Besar */}
<div className="space-y-8">
{articles.slice(1, 3).map((post) => (
<div
key={post.id}
className="flex flex-col md:flex-row border-b border-gray-200 pb-6"
>
{/* Left Content */}
<div className="flex-1 pr-0 md:pr-6">
<h2 className="text-xl md:text-2xl font-bold mb-2">
{post.title}
</h2>
<div className="flex items-center text-sm text-gray-500 gap-2 mb-3">
<span>
BY{" "}
<span className="text-[#49C5C4] font-semibold uppercase">
{post.createdByName}
</span>
</span>
<span></span>
<span>
{new Date(post?.publishedAt || "").toLocaleDateString(
"id-ID",
{
day: "2-digit",
month: "long",
year: "numeric",
}
)}
</span>
<span></span>
<MessageCircle size={14} className="text-[#49C5C4]" />
<span>{/* {post.comments} */}0</span>
</div>
<p className="text-gray-600 mb-4">
{post.description.split(" ").slice(0, 50).join(" ")}
{post.description.split(" ").length > 50 && "..."}
</p>
<Link href={`/details/${post?.slug}`}>
<button className="px-4 py-2 border border-gray-300 text-gray-700 text-sm rounded hover:bg-gray-100 transition">
READ MORE
</button>
</Link>
</div>
{/* Right Thumbnail */}
<div className="w-full md:w-[280px] flex-shrink-0 mt-4 md:mt-0">
<Image
src={post.thumbnailUrl}
alt={post.title}
width={400}
height={250}
className="w-full h-[180px] md:h-[150px] object-cover rounded"
/>
</div>
</div>
))}
</div>
</div>
<div className="relative my-5 max-w-full h-[125px] overflow-hidden flex items-center mx-auto border">
{bannerImageAd ? (
<a
href={bannerImageAd.redirectLink}
target="_blank"
rel="noopener noreferrer"
className="w-full h-full relative"
>
<Image
src={bannerImageAd.contentFileUrl}
alt={bannerImageAd.title || "Iklan Banner"}
fill
className="object-cover"
/>
</a>
) : (
<Image
src="/image-kolom.png"
alt="Banner Default"
fill
className="object-cover"
/>
)}
</div>
</div>
{/* Sidebar */}
<aside className="space-y-6">
{/* Connect with us */}
<div>
<h3 className="font-semibold border-b border-gray-200 pb-2 mb-4">
Connect with us
</h3>
<div className="space-y-3">
<div className="flex justify-between items-center border px-4 py-3 rounded">
<span className="font-medium"></span>
<span>140 Followers</span>
</div>
<div className="flex justify-between items-center border px-4 py-3 rounded">
<span className="text-red-500 font-medium">YouTube</span>
<span>204k Subscribers</span>
</div>
</div>
</div>
{/* Advertisement */}
<div>
{bannerAd ? (
<a
href={bannerAd.redirectLink}
target="_blank"
rel="noopener noreferrer"
className="block w-full"
>
<div className="relative w-full h-[350px] flex justify-center">
<Image
src={bannerAd.contentFileUrl}
alt={bannerAd.title || "Iklan Banner"}
width={1200} // ukuran dasar untuk responsive
height={350}
className="object-cover w-full h-full"
/>
</div>
</a>
) : (
<Image
src="/kolom.png"
alt="Berita Utama"
width={1200}
height={188}
className="object-contain w-full h-[188px]"
/>
)}
</div>
{/* Popular (Placeholder) */}
<div>
<h3 className="font-semibold border-b border-gray-200 pb-2 mb-4 relative">
Popular
<span className="absolute bottom-[-2px] left-0 w-16 h-[2px] bg-teal-400"></span>
</h3>
{popularPosts.map((post, index) =>
index === 0 ? (
// Post pertama tampil besar
<div key={post.id} className="mb-6 relative">
<Image
src={post.image || "/images/placeholder.jpg"}
alt={post.title}
width={400}
height={250}
className="w-full h-[246px] object-cover rounded"
/>
<h2 className="mt-3 text-base font-semibold">{post.title}</h2>
<div className="flex items-center text-xs text-gray-500 mt-1">
<span>{post.shares} SHARES</span>
</div>
<span className="absolute bottom-2 right-2 text-3xl font-bold text-gray-200">
{String(index + 1).padStart(2, "0")}
</span>
</div>
) : (
// Post berikutnya tampil list
<div
key={post.id}
className="flex items-start gap-3 border-t border-gray-200 py-3"
>
<span className="text-lg font-bold text-gray-400">
{String(index + 1).padStart(2, "0")}
</span>
<div>
<h3 className="text-sm font-medium leading-snug">
{post.title}
</h3>
<span className="text-xs text-gray-500">
{post.shares} SHARES
</span>
</div>
</div>
)
)}
</div>
<h3 className="font-semibold border-b border-gray-200 pb-2 mb-4 mt-10 relative">
Categories
<span className="absolute bottom-[-2px] left-0 w-16 h-[2px] bg-teal-400"></span>
</h3>
<ul className="space-y-2">
{categories.map((cat) => (
<li
key={cat.id}
className="flex justify-between items-center border-b border-gray-100 pb-2"
>
<span className="text-sm hover:text-teal-600 cursor-pointer font-medium">
{cat.name}
</span>
<span className="text-sm text-gray-500">({cat.count})</span>
</li>
))}
</ul>
<h3 className="font-semibold border-b border-gray-200 pb-2 mb-4 mt-10 relative">
Instagram
<span className="absolute bottom-[-2px] left-0 w-16 h-[2px] bg-teal-400"></span>
</h3>
{/* <div className="relative w-[1111px] max-w-full h-[300px] overflow-hidden flex items-center mx-auto border my-6 rounded">
{bannerAd ? (
<a
href={bannerAd.redirectLink}
target="_blank"
rel="noopener noreferrer"
className="block w-full"
>
<div className="relative w-full h-[350px] flex justify-center">
<Image
src={bannerAd.contentFileUrl}
alt={bannerAd.title || "Iklan Banner"}
width={1200} // ukuran dasar untuk responsive
height={350}
className="object-cover w-full h-full"
/>
</div>
</a>
) : (
<Image
src="/kolom.png"
alt="Berita Utama"
width={1200}
height={188}
className="object-contain w-full h-[188px]"
/>
)}
</div> */}
<div className="relative w-[1111px] max-w-full h-[300px] overflow-hidden flex items-center mx-auto border my-6 rounded">
{bannerAd ? (
<a
href={bannerAd.redirectLink}
target="_blank"
rel="noopener noreferrer"
className="block w-full"
>
<div className="relative w-full h-[350px] flex justify-center">
<Image
src={bannerAd.contentFileUrl}
alt={bannerAd.title || "Iklan Banner"}
width={1200} // ukuran dasar untuk responsive
height={350}
className="object-cover w-full h-full"
/>
</div>
</a>
) : (
<Image
src="/kolom.png"
alt="Berita Utama"
width={1200}
height={188}
className="object-contain w-full h-[188px]"
/>
)}
</div>
</aside>
</section>
);
}