first commit
This commit is contained in:
parent
074a3ce9c7
commit
b449cab9dd
|
|
@ -0,0 +1,30 @@
|
|||
import DetailContent from "@/components/details/details-content";
|
||||
import Footer from "@/components/landing-page/footer";
|
||||
import Navbar from "@/components/landing-page/navbar";
|
||||
import Image from "next/image";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="relative min-h-screen font-[family-name:var(--font-geist-sans)]">
|
||||
<div className="fixed top-0 left-0 w-full h-auto z-0">
|
||||
<Image
|
||||
src="/rumput.jpg"
|
||||
alt="Background"
|
||||
width={1450}
|
||||
height={600}
|
||||
className="w-full h-auto object-cover"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 bg-[#F2F4F3] max-w-7xl mx-auto">
|
||||
<Navbar />
|
||||
<div className="flex-1">
|
||||
<DetailContent />
|
||||
</div>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import Author from "@/components/landing-page/author";
|
||||
import Footer from "@/components/landing-page/footer";
|
||||
import Header from "@/components/landing-page/header";
|
||||
import Latest from "@/components/landing-page/latest";
|
||||
import LatestandPopular from "@/components/landing-page/latest-and-popular";
|
||||
import Navbar from "@/components/landing-page/navbar";
|
||||
import News from "@/components/landing-page/news";
|
||||
import Image from "next/image";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="relative min-h-screen font-[family-name:var(--font-geist-sans)]">
|
||||
<div className="fixed top-0 left-0 w-full h-auto z-0">
|
||||
<Image
|
||||
src="/rumput.jpg"
|
||||
alt="Background"
|
||||
width={1450}
|
||||
height={600}
|
||||
className="w-full h-auto object-cover"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 bg-[#F2F4F3] max-w-7xl mx-auto">
|
||||
<Navbar />
|
||||
<div className="flex-1">
|
||||
<Header />
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,622 @@
|
|||
"use client";
|
||||
import Image from "next/image";
|
||||
import Author from "../landing-page/author";
|
||||
import { useEffect, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { getListArticle } from "@/service/article";
|
||||
import { close } from "@/config/swal";
|
||||
|
||||
type TabKey = "trending" | "comments" | "latest";
|
||||
|
||||
type Article = {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
categoryName: string;
|
||||
createdAt: string;
|
||||
createdByName: string;
|
||||
thumbnailUrl: string;
|
||||
categories: {
|
||||
title: string;
|
||||
}[];
|
||||
files: {
|
||||
file_url: string;
|
||||
file_alt: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
export default function DetailContent() {
|
||||
const [page, setPage] = useState(1);
|
||||
const [totalPage, setTotalPage] = useState(1);
|
||||
const [articles, setArticles] = 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 [tabArticles, setTabArticles] = useState<Article[]>([]);
|
||||
|
||||
const [activeTab, setActiveTab] = useState<TabKey>("trending");
|
||||
|
||||
const tabs: { id: TabKey; label: string }[] = [
|
||||
{ id: "trending", label: "Trending" },
|
||||
{ id: "comments", label: "Comments" },
|
||||
{ id: "latest", label: "Latest" },
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
fetchTabArticles();
|
||||
}, [activeTab]);
|
||||
|
||||
async function fetchTabArticles() {
|
||||
const req = {
|
||||
limit: "5",
|
||||
page: 1,
|
||||
search: "",
|
||||
categorySlug: "",
|
||||
sort: "desc",
|
||||
sortBy: "created_at",
|
||||
};
|
||||
|
||||
try {
|
||||
if (activeTab === "trending") {
|
||||
req.sortBy = "view_count";
|
||||
} else if (activeTab === "comments") {
|
||||
req.sortBy = "comment_count";
|
||||
} else {
|
||||
req.sortBy = "created_at";
|
||||
}
|
||||
|
||||
const res = await getListArticle(req);
|
||||
setTabArticles(res?.data?.data || []);
|
||||
} catch (error) {
|
||||
console.error("Failed fetching tab articles:", error);
|
||||
}
|
||||
}
|
||||
|
||||
const content: Record<
|
||||
TabKey,
|
||||
{ image: string; title: string; date: string }[]
|
||||
> = {
|
||||
trending: [
|
||||
{
|
||||
image: "/thumb1.png",
|
||||
title:
|
||||
"#StopBullyDiSekolah: Peran Positif Media Sosial dalam Mengatasi Bullying",
|
||||
date: "22 FEBRUARI 2024",
|
||||
},
|
||||
{
|
||||
image: "/thumb2.png",
|
||||
title:
|
||||
"Polri Gelar Lomba Orasi Unjuk Rasa dalam Rangka Hari HAM Sedunia Berhadiah Total Lebih dari Rp 150 juta!",
|
||||
date: "29 NOVEMBER 2021",
|
||||
},
|
||||
{
|
||||
image: "/thumb3.png",
|
||||
title: "Tingkatkan Ibadah Sambut #RamadhanPenuhDamai",
|
||||
date: "7 MARET 2024",
|
||||
},
|
||||
{
|
||||
image: "/thumb4.png",
|
||||
title:
|
||||
"Exploring the Charm of Papua’s Traditional Clothing: A Captivating and Meaningful Cultural Heritage",
|
||||
date: "1 AGUSTUS 2024",
|
||||
},
|
||||
],
|
||||
comments: [
|
||||
{
|
||||
image: "/thumb-comment.png",
|
||||
title: "Pengunjung Komentar Positif tentang Fitur Baru",
|
||||
date: "3 JUNI 2024",
|
||||
},
|
||||
],
|
||||
latest: [
|
||||
{
|
||||
image: "/thumb-latest.png",
|
||||
title: "Update Terbaru dari Redaksi Hari Ini",
|
||||
date: "2 JULI 2025",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
initState();
|
||||
}, [page, showData, startDateValue, selectedCategories]);
|
||||
|
||||
async function initState() {
|
||||
// loading();
|
||||
const req = {
|
||||
limit: showData,
|
||||
page,
|
||||
search,
|
||||
categorySlug: Array.from(selectedCategories).join(","),
|
||||
sort: "desc",
|
||||
sortBy: "created_at",
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await getListArticle(req);
|
||||
setArticles(res?.data?.data || []);
|
||||
setTotalPage(res?.data?.meta?.totalPage || 1);
|
||||
} finally {
|
||||
// close();
|
||||
}
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center bg-[#F2F4F3] w-full overflow-hidden mb-4 py-6 px-8">
|
||||
<Image
|
||||
src="/mikul.png"
|
||||
alt="Background"
|
||||
width={272}
|
||||
height={90}
|
||||
className="w-full md:w-[272px] h-[90px] object-cover border"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
<div className="bg-white grid grid-cols-1 md:grid-cols-3 gap-6 px-8 py-8">
|
||||
<div className="md:col-span-2">
|
||||
<p className="text-sm text-gray-500 mb-2">Home > Internasional</p>
|
||||
<h1 className="text-3xl md:text-4xl font-bold text-[#1a1a1a] leading-tight mb-4">
|
||||
Bom Bunuh Diri Guncang Gereja di Damaskus, 20 Orang Tewas dan
|
||||
Puluhan Terluka
|
||||
</h1>
|
||||
<div className="flex items-center space-x-2 text-sm text-gray-500 mb-4">
|
||||
<Image
|
||||
src="/author.png"
|
||||
alt="author"
|
||||
width={30}
|
||||
height={30}
|
||||
className="rounded-full"
|
||||
/>
|
||||
<span className="text-[#3ba6ed] font-medium">
|
||||
christine natalia
|
||||
</span>
|
||||
<span>•</span>
|
||||
<span>23 Juni 2025</span>
|
||||
<span>•</span>
|
||||
<span>Internasional</span>
|
||||
</div>
|
||||
|
||||
<div className="w-full h-auto mb-6">
|
||||
<Image
|
||||
src="/bom.png"
|
||||
alt="Berita"
|
||||
width={800}
|
||||
height={400}
|
||||
className="rounded-lg w-full object-cover"
|
||||
/>
|
||||
<p className="text-sm text-gray-500 mt-2 text-end">
|
||||
Bom Bunuh Diri Guncang Gereja di Damaskus, 20 Orang Tewas dan
|
||||
Puluhan Terluka
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex relative">
|
||||
<div className=" flex flex-col w-fit rounded overflow-hidden mr-5">
|
||||
<Link
|
||||
href="#"
|
||||
aria-label="Facebook"
|
||||
className="bg-[#3b5998] p-4 flex justify-center items-center"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
fill="white"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path d="M14 13.5h2.5l1-4H14v-2c0-1.03 0-2 2-2h1.5V2.14c-.326-.043-1.557-.14-2.857-.14C11.928 2 10 3.657 10 6.7v2.8H7v4h3V22h4z" />
|
||||
</svg>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href="#"
|
||||
aria-label="Twitter"
|
||||
className="bg-[#55acee] p-4 flex justify-center items-center"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
fill="white"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path d="M7.91 20.889c8.302 0 12.845-6.885 12.845-12.845c0-.193 0-.387-.009-.58A9.2 9.2 0 0 0 23 5.121a9.2 9.2 0 0 1-2.597.713a4.54 4.54 0 0 0 1.99-2.5a9 9 0 0 1-2.87 1.091A4.5 4.5 0 0 0 16.23 3a4.52 4.52 0 0 0-4.516 4.516c0 .352.044.696.114 1.03a12.82 12.82 0 0 1-9.305-4.718a4.526 4.526 0 0 0 1.4 6.03a4.6 4.6 0 0 1-2.043-.563v.061a4.524 4.524 0 0 0 3.62 4.428a4.4 4.4 0 0 1-1.189.159q-.435 0-.845-.08a4.51 4.51 0 0 0 4.217 3.135a9.05 9.05 0 0 1-5.608 1.936A9 9 0 0 1 1 18.873a12.84 12.84 0 0 0 6.91 2.016" />
|
||||
</svg>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href="#"
|
||||
aria-label="Google"
|
||||
className="bg-[#fce9e7] p-4 flex justify-center items-center text-white"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path d="M7.796 14.333v-2.618h7.211c.066.382.12.763.12 1.265c0 4.364-2.923 7.462-7.33 7.462A7.63 7.63 0 0 1 .16 12.806a7.63 7.63 0 0 1 7.636-7.637c2.062 0 3.786.753 5.117 1.997L10.84 9.162c-.567-.546-1.56-1.178-3.044-1.178c-2.607 0-4.734 2.16-4.734 4.822s2.127 4.821 4.734 4.821c3.022 0 4.157-2.17 4.331-3.294zm13.27-2.6H23.2v2.134h-2.133V16h-2.134v-2.133H16.8v-2.134h2.133V9.6h2.134z" />
|
||||
</svg>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href="#"
|
||||
aria-label="Share"
|
||||
className="bg-[#cccccc] p-4 flex justify-center items-center text-white"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path d="m21 12l-7-7v4C7 10 4 15 3 20c2.5-3.5 6-5.1 11-5.1V19z" />
|
||||
</svg>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<p className="text-gray-700 leading-relaxed text-justify">
|
||||
Sebuah aksi bom bunuh diri terjadi di Gereja Saint Elias,
|
||||
kawasan Dwelaa, Damaskus, Suriah, pada Minggu (22/6), menewaskan
|
||||
sedikitnya 20 orang dan melukai 52 lainnya. Insiden tragis ini
|
||||
terjadi saat gereja tengah dipenuhi jemaat, termasuk anak-anak
|
||||
dan lansia, yang sedang mengikuti ibadah mingguan. Kementerian
|
||||
Dalam Negeri Suriah mengonfirmasi bahwa pelaku serangan
|
||||
merupakan anggota kelompok teroris ISIS. Dalam keterangan resmi,
|
||||
pihak kementerian menyebut pelaku memasuki gereja dengan membawa
|
||||
senjata api, melepaskan tembakan, dan kemudian meledakkan diri
|
||||
dengan sabuk peledak yang dibawanya. Pasca ledakan, aparat
|
||||
keamanan langsung mengamankan lokasi dan menutup akses ke area
|
||||
sekitar untuk mencegah kemungkinan serangan susulan. Pihak
|
||||
berwenang juga mengerahkan tim penyelamat dan tenaga medis untuk
|
||||
mengevakuasi korban dan memberikan perawatan darurat kepada yang
|
||||
terluka. Kementerian Kesehatan, melalui kantor berita SANA,
|
||||
menginformasikan bahwa sebagian besar korban luka mengalami
|
||||
cedera serius akibat serpihan ledakan. Sejumlah korban masih
|
||||
dalam kondisi kritis dan dirawat di beberapa rumah sakit rujukan
|
||||
di Damaskus. Seorang saksi mata bernama Lawrence Maamari
|
||||
menuturkan bahwa pelaku masuk ke dalam gereja dengan senapan di
|
||||
tangan. Jemaat yang panik sempat mencoba menghadang pelaku
|
||||
sebelum akhirnya terjadi ledakan. Saksi lainnya, Ziad (40),
|
||||
pemilik toko di dekat lokasi, mengatakan mendengar suara
|
||||
tembakan disusul ledakan besar. “Kami melihat kobaran api di
|
||||
dalam gereja dan bangku-bangku terlempar ke arah pintu masuk,”
|
||||
ujarnya. Suasana mencekam sempat terjadi selama beberapa jam.
|
||||
Beberapa warga kehilangan anggota keluarga dan berupaya
|
||||
mencarinya di antara puing-puing bangunan serta rumah sakit
|
||||
terdekat. Insiden ini menuai kecaman internasional. Utusan
|
||||
Khusus PBB untuk Suriah, Geir Pedersen, menyebut aksi tersebut
|
||||
sebagai kejahatan keji dan mendesak adanya penyelidikan
|
||||
menyeluruh. Sementara itu, Kementerian Luar Negeri Prancis
|
||||
menyatakan bahwa serangan ini merupakan bentuk terorisme brutal,
|
||||
serta kembali menyuarakan pentingnya transisi damai di Suriah
|
||||
agar seluruh warga dapat hidup dalam keamanan tanpa
|
||||
diskriminasi. Pemerintah Suriah menilai serangan ini sebagai
|
||||
langkah terdesak dari kelompok radikal untuk mengganggu
|
||||
stabilitas nasional dan menciptakan ketegangan sektarian.
|
||||
Menteri Dalam Negeri, Anas Khattab, menyampaikan belasungkawa
|
||||
kepada keluarga korban dan menyatakan bahwa penyelidikan sudah
|
||||
dimulai oleh tim khusus kementeriannya. Dalam keterangannya,
|
||||
Khattab menegaskan bahwa aksi teror serupa tidak akan
|
||||
menghentikan tekad pemerintah dalam menciptakan perdamaian. Ia
|
||||
juga mengungkapkan bahwa kelompok ISIS kini menargetkan
|
||||
komunitas-komunitas tertentu, termasuk umat Kristen dan Syiah,
|
||||
dengan pola serangan yang lebih terorganisir. Beberapa minggu
|
||||
sebelumnya, aparat keamanan berhasil menggagalkan sejumlah
|
||||
rencana serangan dan menangkap sel-sel ISIS di wilayah Damaskus.
|
||||
Operasi kontra-terorisme di Aleppo bahkan menewaskan tiga
|
||||
anggota kelompok tersebut dan satu petugas keamanan. Insiden bom
|
||||
bunuh diri di Damaskus menjadi pengingat bahwa ancaman
|
||||
ekstremisme belum sepenuhnya sirna di Suriah. Pemerintah pun
|
||||
terus berupaya meningkatkan keamanan dan memperkuat kerja sama
|
||||
dengan pihak internasional dalam memberantas terorisme.
|
||||
</p>
|
||||
<Author />
|
||||
<div className=" flex flex-row gap-2 items-center">
|
||||
<span className="font-semibold text-sm text-gray-700">
|
||||
Tags:
|
||||
</span>
|
||||
<div className="flex flex-wrap gap-2 mt-1">
|
||||
{[
|
||||
"Bom bunuh diri Suriah",
|
||||
"Bom Damaskus",
|
||||
"Gereja Saint Elias",
|
||||
"Serangan ISIS",
|
||||
].map((tag, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="bg-gray-100 text-gray-700 text-sm px-2 py-1 rounded"
|
||||
>
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative mb-2 h-[120px] overflow-hidden flex items-center border my-8">
|
||||
<Image
|
||||
src="/image-kolom.png"
|
||||
alt="Berita Utama"
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-10">
|
||||
<div className="flex items-center space-x-4 p-4 border rounded-lg mb-6">
|
||||
<Image
|
||||
src="/author.png"
|
||||
alt="Author"
|
||||
width={60}
|
||||
height={60}
|
||||
className="rounded-full"
|
||||
/>
|
||||
<div>
|
||||
<p className="text-green-600 font-bold text-lg">
|
||||
christine natalia
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-bold mb-2">Tinggalkan Balasan</h2>
|
||||
<p className="text-gray-600 mb-4 text-sm">
|
||||
Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib
|
||||
ditandai <span className="text-green-600">*</span>
|
||||
</p>
|
||||
|
||||
<form className="space-y-6 mt-6">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="komentar"
|
||||
className="block text-sm font-medium mb-1"
|
||||
>
|
||||
Komentar <span className="text-green-600">*</span>
|
||||
</label>
|
||||
<textarea
|
||||
id="komentar"
|
||||
className="w-full border border-gray-300 rounded-md p-3 h-40 focus:outline-none focus:ring-2 focus:ring-green-600"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label
|
||||
htmlFor="nama"
|
||||
className="block text-sm font-medium mb-1"
|
||||
>
|
||||
Nama <span className="text-green-600">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="nama"
|
||||
className="w-full border border-gray-300 rounded-md p-2"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-sm font-medium mb-1"
|
||||
>
|
||||
Email <span className="text-green-600">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
className="w-full border border-gray-300 rounded-md p-2"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="website"
|
||||
className="block text-sm font-medium mb-1"
|
||||
>
|
||||
Situs Web
|
||||
</label>
|
||||
<input
|
||||
type="url"
|
||||
id="website"
|
||||
className="w-full border border-gray-300 rounded-md p-2"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start space-x-2 mt-2">
|
||||
<input type="checkbox" id="saveInfo" className="mt-1" />
|
||||
<label htmlFor="saveInfo" className="text-sm text-gray-700">
|
||||
Simpan nama, email, dan situs web saya pada peramban ini untuk
|
||||
komentar saya berikutnya.
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<p className="text-red-600 text-sm">
|
||||
The reCAPTCHA verification period has expired. Please reload the
|
||||
page.
|
||||
</p>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-green-600 hover:bg-green-700 text-white font-semibold px-6 py-2 rounded-md transition mt-2"
|
||||
>
|
||||
KIRIM KOMENTAR
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="md:col-span-1 space-y-6">
|
||||
<div className="sticky top-0 space-y-6">
|
||||
<div className="bg-white shadow p-4 rounded-lg">
|
||||
<Image
|
||||
src="/kolom.png"
|
||||
alt="Iklan"
|
||||
width={345}
|
||||
height={345}
|
||||
className="rounded"
|
||||
/>
|
||||
<button className="mt-4 w-full bg-black text-white py-2 rounded hover:opacity-90">
|
||||
Learn More
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="bg-white shadow p-4 rounded-lg">
|
||||
<h2 className="text-lg font-semibold mb-2">Connect with us</h2>
|
||||
<div className="flex space-x-2">
|
||||
<div className="bg-[#0057ff] text-white px-3 py-2 rounded">
|
||||
<p className="text-sm font-bold">Bē</p>
|
||||
<p className="text-xs">139 Followers</p>
|
||||
</div>
|
||||
<div className="bg-[#ff0000] text-white px-3 py-2 rounded">
|
||||
<p className="text-sm font-bold">YouTube</p>
|
||||
<p className="text-xs">205k Subscribers</p>
|
||||
</div>
|
||||
<div className="bg-[#f9a825] text-white px-3 py-2 rounded">
|
||||
<p className="text-sm font-bold">RSS</p>
|
||||
<p className="text-xs">23.9k Followers</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white shadow p-4 rounded-lg">
|
||||
<div className="flex space-x-4 border-b mb-4">
|
||||
{tabs.map((tab) => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={`pb-2 text-sm font-medium ${
|
||||
activeTab === tab.id
|
||||
? "border-b-2 border-green-600 text-green-600"
|
||||
: "text-gray-600"
|
||||
}`}
|
||||
>
|
||||
{tab.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{tabArticles.map((item, idx) => (
|
||||
<div key={idx} className="flex space-x-3">
|
||||
<Image
|
||||
src={item.thumbnailUrl || "/default-thumb.png"}
|
||||
alt={item.title}
|
||||
width={70}
|
||||
height={70}
|
||||
className="rounded w-[70px] h-[70px] object-cover"
|
||||
/>
|
||||
<div>
|
||||
<p className="text-sm font-semibold leading-snug">
|
||||
{item.title}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
{new Date(item.createdAt).toLocaleDateString("id-ID", {
|
||||
day: "2-digit",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6">
|
||||
<h3 className="text-base font-semibold mb-2 text-gray-800 border-b pb-1 border-green-600 inline-block">
|
||||
Recommended
|
||||
</h3>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="relative">
|
||||
<Image
|
||||
src="/gaza.png"
|
||||
alt="Recommended Article"
|
||||
width={400}
|
||||
height={200}
|
||||
className="rounded-lg w-full h-auto object-cover"
|
||||
/>
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-black bg-opacity-60 text-white p-3 rounded-b-lg">
|
||||
<p className="text-sm font-semibold leading-tight">
|
||||
Bom Bunuh Diri Guncang Gereja di Damaskus, 20 Orang Tewas
|
||||
dan Puluhan Terluka
|
||||
</p>
|
||||
<p className="text-xs text-gray-300 mt-1">
|
||||
📅 23 JUNI 2025
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex space-x-3">
|
||||
<Image
|
||||
src="/perang.png"
|
||||
alt="OPM Serang Gereja"
|
||||
width={80}
|
||||
height={60}
|
||||
className="rounded object-cover w-[80px] h-[60px]"
|
||||
/>
|
||||
<div>
|
||||
<p className="text-sm font-semibold leading-snug">
|
||||
OPM Mulai Kehilangan Simpati dari Masyarakat Papua Usai
|
||||
Serang Gereja
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
📅 15 JUNI 2025
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex space-x-3">
|
||||
<Image
|
||||
src="/jateng.png"
|
||||
alt="Denda Merokok"
|
||||
width={80}
|
||||
height={60}
|
||||
className="rounded object-cover w-[80px] h-[60px]"
|
||||
/>
|
||||
<div>
|
||||
<p className="text-sm font-semibold leading-snug">
|
||||
Jakarta Terapkan Denda Rp 250.000 bagi Warga yang
|
||||
Merokok Sembarangan
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
📅 13 JUNI 2025
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex space-x-3">
|
||||
<Image
|
||||
src="/investasi.jpg"
|
||||
alt="Pengguna Internet"
|
||||
width={80}
|
||||
height={60}
|
||||
className="rounded object-cover w-[80px] h-[60px]"
|
||||
/>
|
||||
<div>
|
||||
<p className="text-sm font-semibold leading-snug">
|
||||
Warga Indonesia Jadi Pengguna Internet via Ponsel
|
||||
Terbanyak di Dunia
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
📅 26 MEI 2025
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
"use client";
|
||||
import { close, loading } from "@/config/swal";
|
||||
|
||||
import { getListArticle } from "@/service/article";
|
||||
import { useEffect, useState } from "react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
type Article = {
|
||||
id: number;
|
||||
|
|
@ -53,7 +54,7 @@ export default function HeroNewsSection() {
|
|||
setArticles(res?.data?.data || []);
|
||||
setTotalPage(res?.data?.meta?.totalPage || 1);
|
||||
} finally {
|
||||
close();
|
||||
// close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -65,75 +66,79 @@ export default function HeroNewsSection() {
|
|||
alt="Background"
|
||||
width={272}
|
||||
height={90}
|
||||
className="w-[272px] h-[90px] object-cover border"
|
||||
className="w-full md:w-[272px] h-[90px] object-cover border"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="pb-5">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-0.5 m-8 ">
|
||||
{/* Artikel Utama */}
|
||||
{articles.length > 0 && (
|
||||
<div className="md:col-span-2 lg:col-span-3 relative">
|
||||
<Image
|
||||
src={
|
||||
articles[0].thumbnailUrl ||
|
||||
articles[0]?.files?.[0]?.file_url ||
|
||||
"/default-image.jpg"
|
||||
}
|
||||
alt={articles[0].title}
|
||||
width={800}
|
||||
height={500}
|
||||
className="w-full h-full max-h-[460px] object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/50 to-transparent p-6 flex flex-col justify-end">
|
||||
<span className="text-xs bg-yellow-400 text-black px-2 py-0.5 inline-block mb-2 uppercase w-[130px]">
|
||||
{articles[0].categories?.[0]?.title || "TANPA KATEGORI"}
|
||||
</span>
|
||||
<h2 className="text-sm md:text-xl lg:text-2xl font-bold text-white leading-snug mb-2 w-full md:w-9/12">
|
||||
{articles[0].title}
|
||||
</h2>
|
||||
<p className="text-white text-xs">
|
||||
{articles[0].createdByName} -{" "}
|
||||
{new Date(articles[0].createdAt).toLocaleDateString("id-ID", {
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
<Link href={`/detail/${articles[0]?.id}`}>
|
||||
<Image
|
||||
src={
|
||||
articles[0].thumbnailUrl ||
|
||||
articles[0]?.files?.[0]?.file_url ||
|
||||
"/default-image.jpg"
|
||||
}
|
||||
alt={articles[0].title}
|
||||
width={800}
|
||||
height={500}
|
||||
className="w-full h-full max-h-[460px] object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/50 to-transparent p-6 flex flex-col justify-end">
|
||||
<span className="text-xs bg-yellow-400 text-black px-2 py-0.5 inline-block mb-2 uppercase w-[130px]">
|
||||
{articles[0].categories?.[0]?.title || "TANPA KATEGORI"}
|
||||
</span>
|
||||
<h2 className="text-sm md:text-xl lg:text-2xl font-bold text-white leading-snug mb-2 w-full md:w-9/12">
|
||||
{articles[0].title}
|
||||
</h2>
|
||||
<p className="text-white text-xs">
|
||||
{articles[0].createdByName} -{" "}
|
||||
{new Date(articles[0].createdAt).toLocaleDateString(
|
||||
"id-ID",
|
||||
{
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Artikel Tambahan */}
|
||||
<div className="md:col-span-1 lg:col-span-2 grid grid-cols-1 sm:grid-cols-2 gap-2">
|
||||
{articles.slice(1, 5).map((article, index) => (
|
||||
<div key={index} className="relative">
|
||||
<Image
|
||||
src={
|
||||
article.thumbnailUrl ||
|
||||
article?.files?.[0]?.file_url ||
|
||||
"/default-image.jpg"
|
||||
}
|
||||
alt={article.title}
|
||||
width={400}
|
||||
height={240}
|
||||
className="w-full h-56 object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/40 to-transparent p-4 flex flex-col justify-end">
|
||||
<span className="text-xs bg-yellow-400 text-black px-2 py-0.5 inline-block uppercase w-[130px]">
|
||||
{article.categoryName || "TANPA KATEGORI"}
|
||||
</span>
|
||||
<h3 className="text-sm font-semibold text-white leading-snug mb-1">
|
||||
{article.title}
|
||||
</h3>
|
||||
</div>
|
||||
<Link href={`/detail/${articles[0]?.id}`}>
|
||||
<Image
|
||||
src={
|
||||
article.thumbnailUrl ||
|
||||
article?.files?.[0]?.file_url ||
|
||||
"/default-image.jpg"
|
||||
}
|
||||
alt={article.title}
|
||||
width={400}
|
||||
height={240}
|
||||
className="w-full h-56 object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/40 to-transparent p-4 flex flex-col justify-end">
|
||||
<span className="text-xs bg-yellow-400 text-black px-2 py-0.5 inline-block uppercase w-[130px]">
|
||||
{article.categoryName || "TANPA KATEGORI"}
|
||||
</span>
|
||||
<h3 className="text-sm font-semibold text-white leading-snug mb-1">
|
||||
{article.title}
|
||||
</h3>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Iklan / Gambar Bawah */}
|
||||
<div className="relative mt-10 mb-2 h-[188px] overflow-hidden flex items-center mx-8 border my-8">
|
||||
<Image
|
||||
src="/image-kolom.png"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
"use client";
|
||||
import { getListArticle } from "@/service/article";
|
||||
import Image from "next/image";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const data1 = {
|
||||
main: {
|
||||
|
|
@ -152,7 +155,6 @@ const data1 = {
|
|||
],
|
||||
};
|
||||
|
||||
// Disusun ulang agar cocok dengan struktur komponen
|
||||
const data = {
|
||||
leftMain: data1.main,
|
||||
leftList: data1.left,
|
||||
|
|
@ -163,44 +165,119 @@ const data = {
|
|||
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: {
|
||||
file_url: 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",
|
||||
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">
|
||||
{/* Left: News Section */}
|
||||
<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 ">
|
||||
{/* === LEFT COLUMN === */}
|
||||
<div className=" w-full">
|
||||
{data.leftList.map((item, i) => (
|
||||
<div key={i}>
|
||||
<div className="relative w-full aspect-video mb-5">
|
||||
<Image
|
||||
src={item.image}
|
||||
alt={item.title}
|
||||
fill
|
||||
sizes="(max-width: 1024px) 100vw, 33vw"
|
||||
className="object-cover"
|
||||
/>
|
||||
<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">
|
||||
{item.category}
|
||||
</span>
|
||||
</div>
|
||||
<div className=" text-black">
|
||||
<h3 className=" font-semibold text-base mb-1">
|
||||
{item.title}
|
||||
</h3>
|
||||
|
||||
<p className="text-[#999999] text-sm font-serif">
|
||||
{item.excerpt}
|
||||
</p>
|
||||
<p className="text-xs text-[#999999] mb-2 flex items-center gap-2">
|
||||
by <span className="text-[#31942E]">{item?.author}</span>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
||||
{currentArticles.map((article, index) => (
|
||||
<div key={index}>
|
||||
<div className="relative w-full aspect-video mb-3">
|
||||
<Image
|
||||
src={article.thumbnailUrl}
|
||||
alt={"article.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"
|
||||
|
|
@ -215,7 +292,14 @@ export default function LatestandPopular() {
|
|||
/>
|
||||
</g>
|
||||
</svg>{" "}
|
||||
{item.date}{" "}
|
||||
{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"
|
||||
|
|
@ -230,99 +314,12 @@ export default function LatestandPopular() {
|
|||
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{" "}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 32 32"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M16 8C7.664 8 1.25 15.344 1.25 15.344L.656 16l.594.656s5.848 6.668 13.625 7.282c.371.046.742.062 1.125.062s.754-.016 1.125-.063c7.777-.613 13.625-7.28 13.625-7.28l.594-.657l-.594-.656S24.336 8 16 8m0 2c2.203 0 4.234.602 6 1.406A6.9 6.9 0 0 1 23 15a6.995 6.995 0 0 1-6.219 6.969c-.02.004-.043-.004-.062 0c-.239.011-.477.031-.719.031c-.266 0-.523-.016-.781-.031A6.995 6.995 0 0 1 9 15c0-1.305.352-2.52.969-3.563h-.031C11.717 10.617 13.773 10 16 10m0 2a3 3 0 1 0 .002 6.002A3 3 0 0 0 16 12m-8.75.938A9 9 0 0 0 7 15c0 1.754.5 3.395 1.375 4.781A23.2 23.2 0 0 1 3.531 16a24 24 0 0 1 3.719-3.063zm17.5 0A24 24 0 0 1 28.469 16a23.2 23.2 0 0 1-4.844 3.781A8.93 8.93 0 0 0 25 15c0-.715-.094-1.398-.25-2.063z"
|
||||
/>
|
||||
</svg>{" "}
|
||||
19
|
||||
</p>
|
||||
0
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* === RIGHT COLUMN === */}
|
||||
<div className=" w-full">
|
||||
{data.rightList.map((item, i) => (
|
||||
<div key={i}>
|
||||
<div className="relative w-full aspect-video mb-5">
|
||||
<Image
|
||||
src={item.image}
|
||||
alt={item.title}
|
||||
fill
|
||||
sizes="(max-width: 1024px) 100vw, 33vw"
|
||||
className="object-cover"
|
||||
/>
|
||||
<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">
|
||||
{item.category}
|
||||
</span>
|
||||
</div>
|
||||
<div className=" text-black">
|
||||
<h3 className=" font-semibold text-base mb-1">
|
||||
{item.title}
|
||||
</h3>
|
||||
|
||||
<p className="text-[#999999] text-sm font-serif">
|
||||
{item.excerpt}
|
||||
</p>
|
||||
<p className="text-xs text-[#999999] mb-2 flex items-center gap-2">
|
||||
by <span className="text-[#31942E]">{item?.author}</span>
|
||||
<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>{" "}
|
||||
{item.date}{" "}
|
||||
<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{" "}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 32 32"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M16 8C7.664 8 1.25 15.344 1.25 15.344L.656 16l.594.656s5.848 6.668 13.625 7.282c.371.046.742.062 1.125.062s.754-.016 1.125-.063c7.777-.613 13.625-7.28 13.625-7.28l.594-.657l-.594-.656S24.336 8 16 8m0 2c2.203 0 4.234.602 6 1.406A6.9 6.9 0 0 1 23 15a6.995 6.995 0 0 1-6.219 6.969c-.02.004-.043-.004-.062 0c-.239.011-.477.031-.719.031c-.266 0-.523-.016-.781-.031A6.995 6.995 0 0 1 9 15c0-1.305.352-2.52.969-3.563h-.031C11.717 10.617 13.773 10 16 10m0 2a3 3 0 1 0 .002 6.002A3 3 0 0 0 16 12m-8.75.938A9 9 0 0 0 7 15c0 1.754.5 3.395 1.375 4.781A23.2 23.2 0 0 1 3.531 16a24 24 0 0 1 3.719-3.063zm17.5 0A24 24 0 0 1 28.469 16a23.2 23.2 0 0 1-4.844 3.781A8.93 8.93 0 0 0 25 15c0-.715-.094-1.398-.25-2.063z"
|
||||
/>
|
||||
</svg>{" "}
|
||||
19
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="relative my-5 max-w-full h-[125px] overflow-hidden flex items-center mx-auto border">
|
||||
<Image
|
||||
|
|
@ -332,22 +329,25 @@ export default function LatestandPopular() {
|
|||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-center items-center space-x-2 pt-6">
|
||||
<button className="px-3 py-1 border text-white rounded bg-[#31942E]">
|
||||
1
|
||||
<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>
|
||||
<button className="px-3 py-1 border border-gray-300 text-black rounded hover:bg-gray-100">
|
||||
2
|
||||
</button>
|
||||
<button className="px-3 py-1 border border-gray-300 text-black rounded hover:bg-gray-100">
|
||||
3
|
||||
</button>
|
||||
<span className="text-gray-400">…</span>
|
||||
<button className="px-3 py-1 border border-gray-300 text-black rounded hover:bg-gray-100">
|
||||
19
|
||||
</button>
|
||||
<button className="px-3 py-1 border border-gray-300 text-black rounded hover:bg-gray-100">
|
||||
>
|
||||
<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>
|
||||
|
|
@ -355,13 +355,17 @@ export default function LatestandPopular() {
|
|||
<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
|
||||
Most Popularaa
|
||||
</h2>
|
||||
<div className=" w-full">
|
||||
<div className="relative w-full aspect-video mb-5">
|
||||
<Image
|
||||
src={data1.main.image}
|
||||
alt={data1.main.title}
|
||||
src={
|
||||
articles[0]?.thumbnailUrl ||
|
||||
articles[0]?.files?.[0]?.file_url ||
|
||||
"/default-image.jpg"
|
||||
}
|
||||
alt={"articles[0]?.title"}
|
||||
fill
|
||||
sizes="(max-width: 1024px) 100vw, 33vw"
|
||||
className="object-cover"
|
||||
|
|
@ -369,7 +373,7 @@ export default function LatestandPopular() {
|
|||
<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">
|
||||
{data1.main.title}
|
||||
{articles[0]?.title}
|
||||
</h3>
|
||||
<p className=" text-xs mb-2 flex items-center gap-2">
|
||||
<svg
|
||||
|
|
@ -386,25 +390,32 @@ export default function LatestandPopular() {
|
|||
/>
|
||||
</g>
|
||||
</svg>{" "}
|
||||
{data1.main.date}{" "}
|
||||
{new Date(articles[0]?.createdAt).toLocaleDateString(
|
||||
"id-ID",
|
||||
{
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-5">
|
||||
{data.bottomRightList.map((item, i) => (
|
||||
<div key={i} className="flex gap-3">
|
||||
{articles?.slice(1, 4).map((article, index) => (
|
||||
<div key={index} className="flex gap-3">
|
||||
<div className="relative w-[120px] h-[86px] shrink-0">
|
||||
<Image
|
||||
src={item.image}
|
||||
alt={item.title}
|
||||
src={article?.thumbnailUrl}
|
||||
alt={"article?.title"}
|
||||
fill
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-sm font-semibold mb-3">
|
||||
{item.title}
|
||||
{article?.title}
|
||||
</h4>
|
||||
<p className="text-xs text-gray-500 flex gap-2 items-center">
|
||||
<svg
|
||||
|
|
@ -421,7 +432,14 @@ export default function LatestandPopular() {
|
|||
/>
|
||||
</g>
|
||||
</svg>{" "}
|
||||
{item.date}
|
||||
{new Date(articles[0]?.createdAt).toLocaleDateString(
|
||||
"id-ID",
|
||||
{
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,8 @@
|
|||
import {
|
||||
Clock,
|
||||
Facebook,
|
||||
Instagram,
|
||||
RefreshCcwIcon,
|
||||
ThumbsUpIcon,
|
||||
Twitter,
|
||||
UserRoundPlus,
|
||||
Youtube,
|
||||
} from "lucide-react";
|
||||
"use client";
|
||||
|
||||
import { getListArticle } from "@/service/article";
|
||||
import Image from "next/image";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const data1 = {
|
||||
main: {
|
||||
|
|
@ -175,23 +169,76 @@ const popularPosts = [
|
|||
},
|
||||
];
|
||||
|
||||
type Article = {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
categoryName: string;
|
||||
createdAt: string;
|
||||
createdByName: string;
|
||||
thumbnailUrl: string;
|
||||
categories: {
|
||||
title: string;
|
||||
}[];
|
||||
files: {
|
||||
file_url: string;
|
||||
file_alt: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
export default function Latest() {
|
||||
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,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
initState();
|
||||
}, [page, showData, startDateValue, selectedCategories]);
|
||||
|
||||
async function initState() {
|
||||
// loading();
|
||||
const req = {
|
||||
limit: showData,
|
||||
page,
|
||||
search,
|
||||
categorySlug: Array.from(selectedCategories).join(","),
|
||||
sort: "desc",
|
||||
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();
|
||||
}
|
||||
}
|
||||
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">
|
||||
{/* Left: News Section */}
|
||||
<div className="w-full lg:w-[750px]">
|
||||
<h2 className="text-sm border-b-2 border-gray-300 font-bold mb-4">
|
||||
FEATURED
|
||||
</h2>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 ">
|
||||
{/* === LEFT COLUMN === */}
|
||||
<div className=" w-full">
|
||||
<div className="relative w-full aspect-video mb-5">
|
||||
<Image
|
||||
src={data.leftMain.image}
|
||||
alt={data.leftMain.title}
|
||||
src={
|
||||
articles[0]?.files?.[0]?.file_url || "/default-image.jpg"
|
||||
}
|
||||
alt={"articles[0]?.title"}
|
||||
fill
|
||||
sizes="(max-width: 1024px) 100vw, 33vw"
|
||||
className="object-cover"
|
||||
|
|
@ -199,7 +246,7 @@ export default function Latest() {
|
|||
<div className="absolute inset-0 bg-black/20" />
|
||||
<div className="absolute bottom-0.5 left-2 text-white">
|
||||
<h3 className=" font-semibold text-base mb-1">
|
||||
{data.leftMain.title}
|
||||
{articles[0]?.title}
|
||||
</h3>
|
||||
<p className=" text-xs mb-2 flex items-center gap-2">
|
||||
<svg
|
||||
|
|
@ -216,27 +263,34 @@ export default function Latest() {
|
|||
/>
|
||||
</g>
|
||||
</svg>{" "}
|
||||
{data.rightMain.date}{" "}
|
||||
{new Date(articles[0]?.createdAt).toLocaleDateString(
|
||||
"id-ID",
|
||||
{
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-5">
|
||||
{data.leftList.map((item, i) => (
|
||||
<div key={i} className="flex gap-3">
|
||||
{articles.slice(1, 5).map((article, index) => (
|
||||
<div key={index} className="flex gap-3">
|
||||
<div className="relative w-[120px] h-[86px] shrink-0">
|
||||
<Image
|
||||
src={item.image}
|
||||
alt={item.title}
|
||||
src={article?.thumbnailUrl}
|
||||
alt={"article?.title"}
|
||||
fill
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-sm font-semibold mb-3">
|
||||
{item.title}
|
||||
{article.title}
|
||||
</h4>
|
||||
<p className="text-xs text-gray-500 flex gap-2 items-center">
|
||||
<p className="text-xs text-gray-500 flex gap-2 articles-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
|
|
@ -251,7 +305,14 @@ export default function Latest() {
|
|||
/>
|
||||
</g>
|
||||
</svg>{" "}
|
||||
{item.date}
|
||||
{new Date(articles[0]?.createdAt).toLocaleDateString(
|
||||
"id-ID",
|
||||
{
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -259,12 +320,13 @@ export default function Latest() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* === RIGHT COLUMN === */}
|
||||
<div className="w-full">
|
||||
<div className="relative w-full aspect-video mb-5">
|
||||
<Image
|
||||
src={data.rightMain.image}
|
||||
alt={data.rightMain.title}
|
||||
src={
|
||||
articles[0]?.files?.[0]?.file_url || "/default-image.jpg"
|
||||
}
|
||||
alt={"articles[0]?.title"}
|
||||
fill
|
||||
sizes="(max-width: 1024px) 100vw, 33vw"
|
||||
className="object-cover "
|
||||
|
|
@ -272,7 +334,7 @@ export default function Latest() {
|
|||
<div className="absolute inset-0 bg-black/20" />
|
||||
<div className="absolute bottom-0.5 left-2 text-white">
|
||||
<h3 className=" font-semibold text-base mb-1">
|
||||
{data.leftMain.title}
|
||||
{articles[0]?.title}
|
||||
</h3>
|
||||
<p className=" text-xs mb-2 flex items-center gap-2">
|
||||
<svg
|
||||
|
|
@ -289,27 +351,34 @@ export default function Latest() {
|
|||
/>
|
||||
</g>
|
||||
</svg>{" "}
|
||||
{data.rightMain.date}{" "}
|
||||
{new Date(articles[0]?.createdAt).toLocaleDateString(
|
||||
"id-ID",
|
||||
{
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-5">
|
||||
{data.rightList.map((item, i) => (
|
||||
<div key={i} className="flex gap-3">
|
||||
{articles.slice(1, 5).map((article, index) => (
|
||||
<div key={index} className="flex gap-3">
|
||||
<div className="relative w-[120px] h-[86px] shrink-0">
|
||||
<Image
|
||||
src={item.image}
|
||||
alt={item.title}
|
||||
src={article?.thumbnailUrl}
|
||||
alt={"article?.title"}
|
||||
fill
|
||||
className="object-cover "
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-sm font-semibold mb-3">
|
||||
{item.title}
|
||||
{article?.title}
|
||||
</h4>
|
||||
<p className="text-xs text-gray-500 flex gap-2 items-center">
|
||||
<p className="text-xs text-gray-500 flex gap-2 articles-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
|
|
@ -324,7 +393,14 @@ export default function Latest() {
|
|||
/>
|
||||
</g>
|
||||
</svg>{" "}
|
||||
{item.date}
|
||||
{new Date(articles[0]?.createdAt).toLocaleDateString(
|
||||
"id-ID",
|
||||
{
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -369,21 +445,20 @@ export default function Latest() {
|
|||
</h2>
|
||||
|
||||
<div className="space-y-8">
|
||||
{popularPosts.map((post) => (
|
||||
<div key={post.id} className="space-y-3">
|
||||
{/* Title, Meta, Image in Flex */}
|
||||
{popularPosts.slice(1, 5).map((post, index) => (
|
||||
<div key={index} className="space-y-3">
|
||||
<div
|
||||
className={`flex gap-4 ${
|
||||
post.image ? "flex-col md:flex-row" : "flex-col"
|
||||
post.files?.[0]?.file_url
|
||||
? "flex-col md:flex-row"
|
||||
: "flex-col"
|
||||
}`}
|
||||
>
|
||||
{/* Text Content (Title + Meta) */}
|
||||
<div className="flex-1">
|
||||
<h3 className="text-base sm:text-lg md:text-sm font-bold leading-tight">
|
||||
{post.title}
|
||||
{post?.title}
|
||||
</h3>
|
||||
<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"
|
||||
|
|
@ -398,7 +473,14 @@ export default function Latest() {
|
|||
/>
|
||||
</g>
|
||||
</svg>{" "}
|
||||
{post.date}{" "}
|
||||
{new Date(articles[0]?.createdAt).toLocaleDateString(
|
||||
"id-ID",
|
||||
{
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
}
|
||||
)}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-4 w-4"
|
||||
|
|
@ -417,13 +499,12 @@ export default function Latest() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Image on the right (md+) or bottom (sm) */}
|
||||
{post.image && (
|
||||
{post.thumbnailUrl && (
|
||||
<div className="w-full md:w-1/3 relative">
|
||||
<div className="w-full aspect-[4/3] sm:w-[260px] sm:h-[157px] md:max-w-[320px] md:h-[220px] lg:max-w-[120px] lg:h-[87px] relative ml-auto">
|
||||
<Image
|
||||
src={post.image}
|
||||
alt={post.title}
|
||||
src={post?.thumbnailUrl}
|
||||
alt={"post?.title"}
|
||||
fill
|
||||
className="object-cover rounded"
|
||||
/>
|
||||
|
|
@ -432,9 +513,8 @@ export default function Latest() {
|
|||
)}
|
||||
</div>
|
||||
|
||||
{/* Excerpt - always below */}
|
||||
<p className="text-[13px] text-[#3D4248] line-clamp-2 mb-3">
|
||||
{post.excerpt}
|
||||
{post.description}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
"use client";
|
||||
import { close } from "@/config/swal";
|
||||
import { getListArticle } from "@/service/article";
|
||||
import { Clock } from "lucide-react";
|
||||
import { getPossibleInstrumentationHookFilenames } from "next/dist/build/utils";
|
||||
import Image from "next/image";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
|
|
@ -55,7 +53,7 @@ export default function Beranda() {
|
|||
setPosts(res?.data?.data || []);
|
||||
setTotalPage(res?.data?.meta?.totalPage || 1);
|
||||
} finally {
|
||||
close();
|
||||
// close();
|
||||
}
|
||||
}
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -2798,6 +2798,7 @@
|
|||
"version": "19.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz",
|
||||
"integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
|
|
@ -2806,7 +2807,7 @@
|
|||
"version": "19.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz",
|
||||
"integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.0.0"
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 864 KiB |
Loading…
Reference in New Issue