From 4a4312cd01f25c3ca4bc929d50ba0fd58b4e195d Mon Sep 17 00:00:00 2001 From: Anang Yusman Date: Fri, 21 Nov 2025 16:53:42 +0800 Subject: [PATCH] update-landing --- app/page.tsx | 21 +- components/landing-page/development.tsx | 136 ++++++++++ components/landing-page/header.tsx | 232 ++++++++++++++++++ components/landing-page/health.tsx | 186 ++++++++++++++ components/landing-page/latest-news.tsx | 126 ++++++++++ components/landing-page/navbar.tsx | 183 +++++++++----- components/landing-page/opinion-news.tsx | 126 ++++++++++ components/landing-page/youtube-selection.tsx | 151 ++++++++++++ 8 files changed, 1091 insertions(+), 70 deletions(-) create mode 100644 components/landing-page/development.tsx create mode 100644 components/landing-page/header.tsx create mode 100644 components/landing-page/health.tsx create mode 100644 components/landing-page/latest-news.tsx create mode 100644 components/landing-page/opinion-news.tsx create mode 100644 components/landing-page/youtube-selection.tsx diff --git a/app/page.tsx b/app/page.tsx index ab06942..59bd637 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,20 +1,25 @@ +import Development from "@/components/landing-page/development"; import Footer from "@/components/landing-page/footer"; -import Beranda from "@/components/landing-page/headers"; -import MoreNews from "@/components/landing-page/more-news"; +import Header from "@/components/landing-page/header"; +import NewsTerkini from "@/components/landing-page/health"; +import LatestNews from "@/components/landing-page/latest-news"; import Navbar from "@/components/landing-page/navbar"; -import News from "@/components/landing-page/news"; -import TravelNews from "@/components/landing-page/travel-news"; +import OpinionNews from "@/components/landing-page/opinion-news"; +import YouTubeSection from "@/components/landing-page/youtube-selection"; export default function Home() { return (
+
- +
- - - + + + + +
); diff --git a/components/landing-page/development.tsx b/components/landing-page/development.tsx new file mode 100644 index 0000000..d659243 --- /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} +
+ +
+

+ + {item.categories?.[0]?.title || "Kategori"} + +

+ +

+ {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/header.tsx b/components/landing-page/header.tsx new file mode 100644 index 0000000..4f78c7d --- /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) => ( + +
+ {item.title} +
+ + {/* dark overlay with text */} +
+

{item.title}

+
+ + {item.categoryName || + item.categories?.[0]?.title || + "Berita"} + + +
+
+ + {/* 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", + } + )} + +
+
+ +
+ ) : ( +

Loading...

+ )} + + {/* RIGHT SIDE – RECENT POSTS */} +
+

Recent Posts

+ +
+ {recentPosts.map((item) => ( + +
+ {item.title} +
+ +
+

+ {item.title} +

+ + {new Date(item.publishedAt).toLocaleDateString("id-ID", { + day: "2-digit", + month: "long", + year: "numeric", + })} + +
+ + ))} +
+
+
+ + {/* LOAD MORE */} +
+ +
+ + {/* 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..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)} +

+
+
+ {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..3a8f2a6 --- /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 LatestNews() { + 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.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", + })} + +
+
+ + ))} +
+ +
+ Kolom PPS +
+
+
+ ); +} diff --git a/components/landing-page/navbar.tsx b/components/landing-page/navbar.tsx index ddf14d4..2a3f9cc 100644 --- a/components/landing-page/navbar.tsx +++ b/components/landing-page/navbar.tsx @@ -1,71 +1,130 @@ "use client"; -import { useState } from "react"; -import { Search, ShoppingCart, Lock } from "lucide-react"; + +import { Search } from "lucide-react"; +import Image from "next/image"; import Link from "next/link"; export default function Navbar() { - const [date] = useState(() => { - const now = new Date(); - return new Intl.DateTimeFormat("id-ID", { - weekday: "long", - year: "numeric", - month: "long", - day: "numeric", - }).format(now); - }); - return ( -
- {/* Topbar */} -
- {date} -
- Shop - My Account - Contact Us - - Login - +
+
+ {/* Left: Logo */} +
+
+ + travel + news + +
+
+ {/* Social Icons */} +
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ {/* Middle Menu */} +
+ + +
+ + + {/* BURGER BUTTON (mobile menu) */} + + + + + +
- - {/* Main Navbar */} -
- {/* Logo */} - - travel - news - - - {/* Menu */} - - - {/* Icons */} -
- - -
-
-
+ ); } 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 */} +
+

BERITA OPINI

+
+
+ + {/* GRID 4 KOLOM */} +
+ {articles.slice(0, 4).map((item) => ( + +
+ {/* GAMBAR */} +
+ {item.title} + + {/* 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", + })} + +
+
+ + ))} +
+ +
+ Kolom PPS +
+
+
+ ); +} 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 */} +
+ 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 */} +
+ +
+
+ ); +}