+ {/* Logo */}
+
-
- KabarHarapan.com adalah media online yang berkomitmen untuk
- menyebarkan berita yang menginspirasi dan mendorong perubahan positif
- di masyarakat. Kami percaya bahwa di balik setiap cerita, ada potensi
- untuk memberikan harapan dan memberdayakan individu untuk menciptakan
- perbedaan dalam dunia ini.
-
+ {/* Subscribe Box */}
+
+
+
+
+
+
+
+
+
+
+
+
+ © 2025 Milenial Bersuara - All Rights Reserved.
+
);
}
diff --git a/components/landing-page/header.tsx b/components/landing-page/header.tsx
new file mode 100644
index 0000000..43de37d
--- /dev/null
+++ b/components/landing-page/header.tsx
@@ -0,0 +1,232 @@
+"use client";
+
+import { getListArticle } from "@/service/article";
+import Image from "next/image";
+import Link from "next/link";
+import { useEffect, useState } from "react";
+
+type Article = {
+ id: number;
+ title: string;
+ description: string;
+ categoryName: string;
+ createdAt: string;
+ slug: string;
+ createdByName: string;
+ publishedAt: string;
+ customCreatorName: string;
+ thumbnailUrl: string;
+ categories: { title: string }[];
+ files: { fileUrl: string; file_alt: string }[];
+};
+
+export default function Header() {
+ const [articles, setArticles] = useState
([]);
+
+ useEffect(() => {
+ const fetchArticles = async () => {
+ const req = {
+ limit: "10",
+ page: 1,
+ search: "",
+ categorySlug: "",
+ sort: "desc",
+ isPublish: true,
+ sortBy: "created_at",
+ };
+
+ const res = await getListArticle(req);
+ setArticles(res?.data?.data || []);
+ };
+
+ fetchArticles();
+ }, []);
+
+ const flashArticles = articles.slice(0, 8);
+ const mainArticle = articles[8] || articles[0];
+ const recentPosts = articles.slice(1, 5);
+
+ return (
+
+ {/* FLASH STRIP */}
+
+
+
Flash
+ ⚡
+
+
LOAD MORE ➜
+
+
+
+
+ {flashArticles.map((item) => (
+
+
+
+
+
+ {/* dark overlay with text */}
+
+
{item.title}
+
+
+ {item.categoryName ||
+ item.categories?.[0]?.title ||
+ "Berita"}
+
+ ●
+
+
+
+ {/* play icon */}
+
+
+ ))}
+
+
+
+ {/* Main Layout */}
+
+ {/* LEFT SIDE – MAIN ARTICLE */}
+ {mainArticle ? (
+
+
+
+
+ {/* White Card Overlay */}
+
+
+ {mainArticle.categoryName ||
+ mainArticle.categories?.[0]?.title ||
+ "Berita"}
+
+
+
+ {mainArticle.title}
+
+
+
+
+ By{" "}
+ {mainArticle.customCreatorName ||
+ mainArticle.createdByName ||
+ "Admin"}
+
+ •
+
+ {new Date(mainArticle.publishedAt).toLocaleDateString(
+ "id-ID",
+ {
+ day: "2-digit",
+ month: "long",
+ year: "numeric",
+ }
+ )}
+
+
+
+
+
+ ) : (
+
Loading...
+ )}
+
+ {/* RIGHT SIDE – RECENT POSTS */}
+
+
Recent Posts
+
+
+ {recentPosts.map((item) => (
+
+
+
+
+
+
+
+ {item.title}
+
+
+ {new Date(item.publishedAt).toLocaleDateString("id-ID", {
+ day: "2-digit",
+ month: "long",
+ year: "numeric",
+ })}
+
+
+
+ ))}
+
+
+
+
+ {/* LOAD MORE */}
+
+
+ {/* KOLOM PPS BOTTOM BANNER */}
+
+
+
+
+ );
+}
diff --git a/components/landing-page/health.tsx b/components/landing-page/health.tsx
new file mode 100644
index 0000000..c8b2a6d
--- /dev/null
+++ b/components/landing-page/health.tsx
@@ -0,0 +1,186 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import Image from "next/image";
+import Link from "next/link";
+import { getListArticle } from "@/service/article";
+
+type Article = {
+ id: number;
+ title: string;
+ description: string;
+ categoryName: string;
+ createdAt: string;
+ publishedAt: string;
+ slug: string;
+ createdByName: string;
+ customCreatorName?: string;
+ thumbnailUrl?: string;
+ categories?: { title: string }[];
+};
+
+export default function NewsTerkini() {
+ const [articles, setArticles] = useState([]);
+ const [popular, setPopular] = useState([]);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ loadData();
+ }, []);
+
+ async function loadData() {
+ setLoading(true);
+
+ try {
+ const res = await getListArticle({
+ limit: "20",
+ page: 1,
+ search: "",
+ isPublish: true,
+ sort: "desc",
+ sortBy: "created_at",
+ });
+
+ const data = res?.data?.data || [];
+ setArticles(data.slice(0, 5));
+ setPopular(data.slice(0, 5));
+ } catch (err) {
+ console.log(err);
+ }
+
+ setLoading(false);
+ }
+
+ const formatDate = (d: string) =>
+ new Date(d).toLocaleDateString("id-ID", {
+ day: "numeric",
+ month: "long",
+ year: "numeric",
+ });
+
+ if (loading)
+ return (
+
+ Memuat berita terbaru...
+
+ );
+
+ return (
+
+
+
BERITA TERKINI
+
+
+
+ {articles.map((item) => (
+
+
+
+ {/* CATEGORY */}
+
+ {item.categories?.[0]?.title || "Kategori"}
+
+
+ {/* JUDUL */}
+
+ {item.title}
+
+
+ {/* DESKRIPSI */}
+
+ {item.description}
+
+
+ {/* AUTHOR + DATE */}
+
+ By {item.customCreatorName || item.createdByName} —{" "}
+ {formatDate(item.publishedAt)}
+
+
+
+
+
+
+
+ ))}
+
+
+ {/* LOAD MORE */}
+
+ LOAD MORE ↓
+
+
+
+
+
TERBANYAK DIBAGIKAN
+
+
+
+ {popular.map((item, index) => (
+
+ {/* NOMOR */}
+
+ {(index + 1).toString().padStart(2, "0")}
+
+
+
+
+ {item.categories?.[0]?.title || "Kategori"}
+
+
+ {item.title}
+
+
+ {formatDate(item.createdAt)}
+
+
+
+ {/* THUMBNAIL KECIL */}
+
+
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/components/landing-page/latest-news.tsx b/components/landing-page/latest-news.tsx
index 9b96f04..3a8f2a6 100644
--- a/components/landing-page/latest-news.tsx
+++ b/components/landing-page/latest-news.tsx
@@ -1,193 +1,126 @@
"use client";
-import { useState, useEffect } from "react";
-import Image from "next/image";
+
import { getListArticle } from "@/service/article";
+import { ChevronDown } from "lucide-react";
+import Image from "next/image";
import Link from "next/link";
-import { usePathname } from "next/navigation";
-import { getAdvertise } from "@/service/advertisement";
+import { useEffect, useState } from "react";
type Article = {
id: number;
title: string;
description: string;
categoryName: string;
- createdAt: string;
slug: string;
+ createdAt: string;
+ publishedAt: string;
createdByName: 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 LatestNews() {
+ const [page, setPage] = useState(1);
+ const [totalPage, setTotalPage] = useState(1);
const [articles, setArticles] = useState([]);
- const [showData, setShowData] = useState("5");
- const pathname = usePathname();
- const currentSlug = pathname.split("/")[2] || "";
- const [bannerAd, setBannerAd] = useState(null);
+ const [showData, setShowData] = useState("6");
+ const [search] = useState("");
+ const [selectedCategories] = useState("");
+ const [startDateValue] = useState({
+ startDate: null,
+ endDate: null,
+ });
useEffect(() => {
- initStateAdver();
- }, []);
+ initState();
+ }, [page, showData, startDateValue, selectedCategories]);
- async function initStateAdver() {
+ async function initState() {
const req = {
- limit: 100,
- page: 1,
+ limit: showData,
+ page,
+ search,
+ categorySlug: Array.from(selectedCategories).join(","),
sort: "desc",
- sortBy: "created_at",
isPublish: true,
+ sortBy: "created_at",
};
try {
- const res = await getAdvertise(req);
- const data: Advertise[] = res?.data?.data || [1];
-
- // filter iklan dengan placement = "banner"
- const banner = data.find((ad) => ad.placement === "jumbotron");
-
- if (banner) {
- setBannerAd(banner);
- }
- } catch (err) {
- console.error("Error fetching advertisement:", err);
- }
- }
- useEffect(() => {
- fetchArticles();
- }, []);
-
- async function fetchArticles() {
- try {
- const req = {
- limit: showData,
- page: 1,
- search: "",
- categorySlug: "",
- sort: "desc",
- isPublish: true,
- sortBy: "created_at",
- };
-
const res = await getListArticle(req);
setArticles(res?.data?.data || []);
- } catch (error) {
- console.error("Gagal memuat artikel:", error);
+ setTotalPage(res?.data?.meta?.totalPage || 1);
+ } catch (err) {
+ console.error("Error fetching articles:", err);
}
}
- const mainArticle = articles[0];
- const secondArticle = articles[1];
-
return (
-
-
-
- {" "}
- {currentSlug ? ` ${currentSlug}` : "Berita Terkini"}
-
-
- Kolom ini berisi berita-berita yang saat ini sedang menjadi sorotan
- atau terkait dengan peristiwa terbaru.
-
+
+
+ {/* TITLE */}
+
- {articles.slice(0, 5).map((article, index) => (
-
- {/* Gambar */}
-
-
-
-
- {/* Konten */}
-
-
- BERITA TERKINI
-
-
- {article.title}
-
-
- by {article.createdByName} • {formatDate(article.createdAt)} •
- 💬 0
-
-
- {article.description}
-
-
-
-
- ))}
-
-
- {/* KANAN: Advertisement */}
-
-
-
- Smart & Responsive
-
-
ADVERTISEMENT
-
-
+
);
}
-
-function formatDate(dateStr: string): string {
- const options: Intl.DateTimeFormatOptions = {
- year: "numeric",
- month: "long",
- day: "numeric",
- };
- return new Date(dateStr).toLocaleDateString("id-ID", options);
-}
diff --git a/components/landing-page/navbar.tsx b/components/landing-page/navbar.tsx
index 4f55070..aa9a4da 100644
--- a/components/landing-page/navbar.tsx
+++ b/components/landing-page/navbar.tsx
@@ -1,56 +1,126 @@
-// components/landing-page/navbar.tsx
-import Image from "next/image";
+"use client";
+
import { Search } from "lucide-react";
+import Image from "next/image";
import Link from "next/link";
-import { Button } from "../ui/button";
export default function Navbar() {
return (
-
- {/* Top section dengan logo */}
-
-
-
+
+
+ {/* Left: Logo */}
+
+
+
+
+
+ {/* Social Icons */}
+
+
+
+
- {/* Navbar menu */}
-
+ {/* Middle Menu */}
+
+
+
+
+
+
+ {/* BURGER BUTTON (mobile menu) */}
+
+
-
+
-
-
+
+
);
}
diff --git a/components/landing-page/opinion-news.tsx b/components/landing-page/opinion-news.tsx
new file mode 100644
index 0000000..e1dd909
--- /dev/null
+++ b/components/landing-page/opinion-news.tsx
@@ -0,0 +1,126 @@
+"use client";
+
+import { getListArticle } from "@/service/article";
+import { ChevronDown } from "lucide-react";
+import Image from "next/image";
+import Link from "next/link";
+import { useEffect, useState } from "react";
+
+type Article = {
+ id: number;
+ title: string;
+ description: string;
+ categoryName: string;
+ slug: string;
+ createdAt: string;
+ publishedAt: string;
+ createdByName: string;
+ customCreatorName: string;
+ thumbnailUrl: string;
+ categories: { title: string }[];
+ files: { fileUrl: string; file_alt: string }[];
+};
+
+export default function OpinionNews() {
+ const [page, setPage] = useState(1);
+ const [totalPage, setTotalPage] = useState(1);
+ const [articles, setArticles] = useState
([]);
+ const [showData, setShowData] = useState("6");
+ const [search] = useState("");
+ const [selectedCategories] = useState("");
+ const [startDateValue] = useState({
+ startDate: null,
+ endDate: null,
+ });
+
+ 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 (
+
+
+ {/* TITLE */}
+
+
+ {/* GRID 4 KOLOM */}
+
+ {articles.slice(0, 4).map((item) => (
+
+
+ {/* GAMBAR */}
+
+
+
+ {/* BADGE CATEGORY DI DALAM GAMBAR */}
+
+
+ {item.categories?.[0]?.title || "Kategori"}
+
+
+
+
+ {/* JUDUL */}
+
+ {item.title}
+
+
+ {/* AUTHOR + DATE */}
+
+
+ By {item.customCreatorName || item.createdByName || "Admin"}
+
+ -
+
+ {new Date(item.publishedAt).toLocaleDateString("id-ID", {
+ day: "numeric",
+ month: "long",
+ year: "numeric",
+ })}
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+ );
+}
diff --git a/components/landing-page/youtube-selection.tsx b/components/landing-page/youtube-selection.tsx
new file mode 100644
index 0000000..88e6f80
--- /dev/null
+++ b/components/landing-page/youtube-selection.tsx
@@ -0,0 +1,151 @@
+import { Eye, Heart, MessageCircle } from "lucide-react";
+import Image from "next/image";
+
+interface VideoItem {
+ id: number;
+ title: string;
+ thumbnail: string;
+ duration: string;
+ publishedAt: string;
+ views: string;
+ likes: string;
+ comments: string;
+}
+
+const sampleVideos: VideoItem[] = [
+ {
+ id: 1,
+ title: "Cuplikan Kegiatan Presiden di IKN",
+ thumbnail: "/yt/thumb1.jpg",
+ duration: "12:30",
+ publishedAt: "2 jam yang lalu",
+ views: "12K",
+ likes: "230",
+ comments: "45",
+ },
+ {
+ id: 2,
+ title: "Pembangunan MRT Fase Berikutnya Resmi Dimulai",
+ thumbnail: "/yt/thumb2.jpg",
+ duration: "08:12",
+ publishedAt: "5 jam yang lalu",
+ views: "9.4K",
+ likes: "180",
+ comments: "30",
+ },
+ {
+ id: 3,
+ title: "Wilayah Indonesia Siap Hadapi Cuaca Ekstrem",
+ thumbnail: "/yt/thumb3.jpg",
+ duration: "05:50",
+ publishedAt: "1 hari lalu",
+ views: "21K",
+ likes: "540",
+ comments: "121",
+ },
+ {
+ id: 4,
+ title: "Peningkatan Ekonomi Regional Terus Dijaga",
+ thumbnail: "/yt/thumb4.jpg",
+ duration: "10:44",
+ publishedAt: "2 hari lalu",
+ views: "18K",
+ likes: "420",
+ comments: "88",
+ },
+ {
+ id: 5,
+ title: "Laporan Khusus Perkembangan Industri Kreatif",
+ thumbnail: "/yt/thumb5.jpg",
+ duration: "14:02",
+ publishedAt: "3 hari lalu",
+ views: "30K",
+ likes: "830",
+ comments: "200",
+ },
+ {
+ id: 6,
+ title: "Paparan Menteri Perhubungan Soal Transportasi",
+ thumbnail: "/yt/thumb6.jpg",
+ duration: "07:21",
+ publishedAt: "4 hari lalu",
+ views: "9K",
+ likes: "114",
+ comments: "26",
+ },
+];
+
+export default function YouTubeSection() {
+ return (
+
+ {/* Header Channel */}
+
+
+
+
YouTube • Channel Resmi
+
+ Lebih dari 3.5 juta pengikut • 12k video
+
+
+
+
+ Subscribe
+
+
+
+ {/* Grid Video */}
+
+ {sampleVideos.map((v) => (
+
+
+
+
+
+ {v.duration}
+
+
+
+
+ {v.title}
+
+
+
{v.publishedAt}
+
+
+
+ {v.views}
+
+
+ {v.likes}
+
+
+ {v.comments}
+
+
+
+ ))}
+
+
+ {/* Pagination */}
+
+
+
+
+ );
+}