diff --git a/app/category/citizen-news/page.tsx b/app/category/citizen-news/page.tsx index c3c8a28..ac51d6c 100644 --- a/app/category/citizen-news/page.tsx +++ b/app/category/citizen-news/page.tsx @@ -12,7 +12,7 @@ import Image from "next/image"; export default function Development() { return (
-
+ {/*
Background -
+
*/}
diff --git a/app/category/development/page.tsx b/app/category/development/page.tsx index 3b1b0fe..5fad8d4 100644 --- a/app/category/development/page.tsx +++ b/app/category/development/page.tsx @@ -8,7 +8,7 @@ import Image from "next/image"; export default function Development() { return (
-
+ {/*
Background -
+
*/}
diff --git a/app/category/health/page.tsx b/app/category/health/page.tsx index 2731f15..5dc86f4 100644 --- a/app/category/health/page.tsx +++ b/app/category/health/page.tsx @@ -13,7 +13,7 @@ import Image from "next/image"; export default function Development() { return (
-
+ {/*
Background -
+
*/}
diff --git a/app/category/popular/page.tsx b/app/category/popular/page.tsx index 60e05d8..3b38389 100644 --- a/app/category/popular/page.tsx +++ b/app/category/popular/page.tsx @@ -6,7 +6,7 @@ import Image from "next/image"; export default function Home() { return (
-
+ {/*
Background -
+
*/}
diff --git a/app/page.tsx b/app/page.tsx index c821a1a..e30ab04 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,39 +1,26 @@ -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 { id } from "date-fns/locale"; -import Image from "next/image"; +import LatestNews from "@/components/landing-page/latest-news"; +import Development from "@/components/landing-page/development"; +import OpinionNews from "@/components/landing-page/opinion-news"; +import NewsTerkini from "@/components/landing-page/health"; +import YouTubeSection from "@/components/landing-page/youtube-selection"; export default function Home() { return (
{/* Background fixed tidak ikut scroll */} -
- Background -
- -
- -
-
-
- - - - -
+ +
+
+ + + + + +
); } diff --git a/components/details/details-content.tsx b/components/details/details-content.tsx index 47020d3..41125df 100644 --- a/components/details/details-content.tsx +++ b/components/details/details-content.tsx @@ -397,9 +397,7 @@ export default function DetailContent() { - {new Date( - articleDetail?.publishedAt ?? articleDetail?.publishedAt, - ) + {new Date(articleDetail?.publishedAt ?? articleDetail?.createdAt) .toLocaleString("id-ID", { day: "numeric", month: "long", diff --git a/components/landing-page/development.tsx b/components/landing-page/development.tsx new file mode 100644 index 0000000..af8745b --- /dev/null +++ b/components/landing-page/development.tsx @@ -0,0 +1,136 @@ +"use client"; +import { useEffect, useState } from "react"; +import Image from "next/image"; +import { getListArticle } from "@/service/article"; +import Link from "next/link"; + +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 Development() { + const [articles, setArticles] = useState([]); + const [page, setPage] = useState(1); + const [totalPage, setTotalPage] = useState(1); + + useEffect(() => { + initState(); + }, [page]); + + async function initState() { + const req = { + limit: "10", + page, + search: "", + categorySlug: "", + 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); + } + } + + // Format tanggal ke gaya lokal + const formatDate = (dateString: string) => { + const date = new Date(dateString); + return date.toLocaleDateString("id-ID", { + day: "2-digit", + month: "long", + year: "numeric", + }); + }; + + // Mapping struktur seperti dummy sebelumnya + const leftMain = articles[0]; + const leftList = articles.slice(1, 4); + const centerMain = articles[4]; + const centerList = articles.slice(5, 8); + const rightMain = articles[8]; + const rightList = articles.slice(9, 12); + + return ( +
+
+
+

JAGA NEGERI

+ +
+
+ +
+ +
+ {articles.slice(0, 6).map((item) => ( + +
+ {item.title} +
+ +
+

+ + BERITA OPINI + +

+ +

+ {item.title} +

+ +

+ By{" "} + + {item.customCreatorName} + +

+ +

+ {new Date(item.publishedAt).toLocaleDateString("id-ID", { + day: "numeric", + month: "long", + year: "numeric", + })} +

+
+ + ))} +
+
+ Berita Utama +
+
+
+ ); +} diff --git a/components/landing-page/footer.tsx b/components/landing-page/footer.tsx index 9fff42e..0bf11b7 100644 --- a/components/landing-page/footer.tsx +++ b/components/landing-page/footer.tsx @@ -1,176 +1,66 @@ // components/Footer.tsx - import Image from "next/image"; -import Link from "next/link"; +import { Facebook, Twitter, Instagram, Youtube } from "lucide-react"; export default function Footer() { return ( -
-
- {/* Top Menu Links */} -
- {/*
-

- © 2025{" "} - JNews- - Premium WordPress news & magazine theme by{" "} - Jegtheme -

-
*/} -
- Background +
+ {/* Logo */} +
+ Logo +
+ + {/* Subscribe Box */} +
+
+

+ Subscribe us to get
+ the latest news! +

+ + + + -
-
-

-
- {[ - { label: "Beranda", href: "#" }, - { label: "Pembangunan", href: "/category/development" }, - { label: "Kesehatan", href: "/category/health" }, - { label: "Berita Warga", href: "/category/citizen-news" }, - ].map((item, idx, arr) => ( - - - {item.label} - - {idx !== arr.length - 1 && ( - / - )} - - ))} -
-
-
-
-

- Follow Us -

-

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
+
+ + + +
+ + + + +
+ +

+ © 2025 Mikul News - All Rights Reserved. +

); } diff --git a/components/landing-page/header.tsx b/components/landing-page/header.tsx index d6b713b..b2e49d4 100644 --- a/components/landing-page/header.tsx +++ b/components/landing-page/header.tsx @@ -1,236 +1,231 @@ "use client"; import { getListArticle } from "@/service/article"; -import { useEffect, useState } from "react"; import Image from "next/image"; import Link from "next/link"; -import { getAdvertise } from "@/service/advertisement"; +import { useEffect, useState } from "react"; type Article = { id: number; title: string; description: string; - publishedAt: string; categoryName: string; createdAt: string; - createdByName: string; slug: string; + createdByName: string; + publishedAt: string; customCreatorName: string; thumbnailUrl: string; - categories: { - title: string; - }[]; - files: { - fileUrl: string; - file_alt: 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 HeroNewsSection() { - const [page, setPage] = useState(1); - const [totalPage, setTotalPage] = useState(1); +export default function Header() { const [articles, setArticles] = useState([]); - const [showData, setShowData] = useState("5"); - const [search, setSearch] = useState(""); - const [selectedCategories, setSelectedCategories] = useState(""); - const [startDateValue, setStartDateValue] = useState({ - startDate: null, - endDate: null, - }); - - const [bannerAd, setBannerAd] = useState(null); useEffect(() => { - initStateAdver(); - }, []); + const fetchArticles = async () => { + const req = { + limit: "10", + page: 1, + search: "", + categorySlug: "", + sort: "desc", + isPublish: true, + sortBy: "created_at", + }; - 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 || []; - - // filter iklan dengan placement = "banner" - const banner = data.find((ad) => ad.placement === "banner"); - - if (banner) { - setBannerAd(banner); - } - } catch (err) { - console.error("Error fetching advertisement:", err); - } - } - - 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); - } finally { - // close(); - } - } + }; + + fetchArticles(); + }, []); + + const flashArticles = articles.slice(0, 8); + const mainArticle = articles[8] || articles[0]; + const recentPosts = articles.slice(1, 5); return ( -
-
- Background +
+ {/* FLASH STRIP */} +
+
+

Flash

+ +
+
LOAD MORE ➜
-
-
- {articles.length > 0 && ( -
- +
+
+ {flashArticles.map((item) => ( + +
{articles[0].title} -
- - {articles[0].categories?.[0]?.title || "TANPA KATEGORI"} +
+ + {/* dark overlay with text */} +
+

{item.title}

+
+ + {item.categoryName || + item.categories?.[0]?.title || + "Berita"} -

- {articles[0].title} -

-

- {articles[0]?.customCreatorName || - articles[0]?.createdByName}{" "} - -{" "} - - - - - - {" "} - {new Date(articles[0].publishedAt) - .toLocaleString("id-ID", { - day: "numeric", + +

+
+ + {/* play icon */} +
+ + + +
+ + ))} +
+
+ + {/* Main Layout */} +
+ {/* LEFT SIDE – MAIN ARTICLE */} + {mainArticle ? ( +
+ + {mainArticle.files?.[0]?.file_alt + + {/* 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", - hour: "2-digit", - minute: "2-digit", - hour12: false, - timeZone: "Asia/Jakarta", - }) - .replace("pukul ", "")}{" "} - WIB -

+ }, + )} +
- -
- )} +
+ +
+ ) : ( +

Loading...

+ )} -
- {articles.slice(1, 5).map((article, index) => ( -
- + {/* RIGHT SIDE – RECENT POSTS */} +
+

Recent Posts

+ +
+ {recentPosts.map((item) => ( + +
{article.title} -
- - {article?.categories?.[0]?.title || "TANPA KATEGORI"} - -

- {article.title} -

-
- -
+
+ +
+

+ {item.title} +

+ + {new Date(item.publishedAt).toLocaleDateString("id-ID", { + day: "2-digit", + month: "long", + year: "numeric", + })} + +
+ ))}
+
-
- {bannerAd ? ( - -
- {bannerAd.title -
-
- ) : ( - Berita Utama +
+ + +
+ + {/* KOLOM PPS BOTTOM BANNER */} +
+ Kolom PPS Bottom Banner
); diff --git a/components/landing-page/health.tsx b/components/landing-page/health.tsx new file mode 100644 index 0000000..7d72ef4 --- /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.categoryName || "Kategori"} +

+ + {/* JUDUL */} +

+ {item.title} +

+ + {/* DESKRIPSI */} +

+ {item.description} +

+ + {/* AUTHOR + DATE */} +

+ By {item.customCreatorName || item.createdByName} —{" "} + {formatDate(item.publishedAt)} +

+
+
+ {item.title} +
+
+ + ))} +
+ + {/* 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 */} +
+ {item.title} +
+ + ))} +
+ +
+
+ Kolom PPS +
+ +
+ Kolom PPS +
+
+
+
+ ); +} diff --git a/components/landing-page/latest-news.tsx b/components/landing-page/latest-news.tsx new file mode 100644 index 0000000..b01a2d1 --- /dev/null +++ b/components/landing-page/latest-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 News() { + 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 */} +
+

BERITA POPULER

+
+
+ + {/* GRID 4 KOLOM */} +
+ {articles.slice(0, 4).map((item) => ( + +
+ {/* GAMBAR */} +
+ {item.title} + + {/* BADGE CATEGORY DI DALAM GAMBAR */} +
+ + {item.categoryName || "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", + })} + +
+
+ + ))} +
+ +
+ Kolom PPS +
+
+
+ ); +} diff --git a/components/landing-page/navbar.tsx b/components/landing-page/navbar.tsx index 5865751..89d8c68 100644 --- a/components/landing-page/navbar.tsx +++ b/components/landing-page/navbar.tsx @@ -1,229 +1,170 @@ "use client"; -import { useState } from "react"; + +import { Search } from "lucide-react"; +import Image from "next/image"; import Link from "next/link"; -import { Menu, Lock, Search } from "lucide-react"; import { usePathname } from "next/navigation"; -const Navbar = () => { - const [isOpen, setIsOpen] = useState(false); - const toggleMenu = () => setIsOpen(!isOpen); - +export default function Navbar() { const pathname = usePathname(); - - const navLinks = [ - { href: "/", label: "BERANDA" }, - { href: "/category/development", label: "PEMBANGUNAN" }, - { href: "/category/health", label: "KESEHATAN" }, - { href: "/category/citizen-news", label: "BERITA WARGA" }, - ]; - - const isActive = (href: string) => { - if (href === "/") { - return pathname === "/"; - } - return pathname.startsWith(href); + const isActive = (href: any) => { + return pathname === href || pathname.startsWith(href + "/"); }; return ( - +
); -}; - -export default Navbar; +} diff --git a/components/landing-page/opinion-news.tsx b/components/landing-page/opinion-news.tsx new file mode 100644 index 0000000..90edd40 --- /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 */} +
+

BERITA OPINI

+
+
+ + {/* GRID 4 KOLOM */} +
+ {articles.slice(0, 4).map((item) => ( + +
+ {/* GAMBAR */} +
+ {item.title} + + {/* BADGE CATEGORY DI DALAM GAMBAR */} +
+ + {item.categoryName || "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", + })} + +
+
+ + ))} +
+ +
+ Kolom PPS +
+
+
+ ); +} diff --git a/components/landing-page/youtube-selection.tsx b/components/landing-page/youtube-selection.tsx new file mode 100644 index 0000000..2243f66 --- /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 */} +
+ Logo Channel +
+

YouTube • Channel Resmi

+

+ Lebih dari 3.5 juta pengikut • 12k video +

+
+ + + Subscribe + +
+ + {/* Grid Video */} +
+ {sampleVideos.map((v) => ( +
+
+ {v.title} + + + {v.duration} + +
+ +

+ {v.title} +

+ +

{v.publishedAt}

+ +
+ + {v.views} + + + {v.likes} + + + {v.comments} + +
+
+ ))} +
+ + {/* Pagination */} +
+ +
+
+ ); +} diff --git a/public/mikul-news-logo.png b/public/mikul-news-logo.png new file mode 100644 index 0000000..3a61b4e Binary files /dev/null and b/public/mikul-news-logo.png differ diff --git a/public/yt-logo.png b/public/yt-logo.png new file mode 100644 index 0000000..cbb41e7 Binary files /dev/null and b/public/yt-logo.png differ