This commit is contained in:
Anang Yusman 2025-09-26 18:26:42 +08:00
parent 8782ac0e99
commit ce5c494743
12 changed files with 409 additions and 307 deletions

View File

@ -0,0 +1,22 @@
import BreakingNews from "@/components/landing-page/breaking-news";
import Footer from "@/components/landing-page/footer";
import Header from "@/components/landing-page/header";
import HeaderLatest from "@/components/landing-page/header-latest";
import Navbar from "@/components/landing-page/navbar";
import News from "@/components/landing-page/news";
import Opini from "@/components/landing-page/opini";
import PopularNews from "@/components/landing-page/popular-news";
export default function Home() {
return (
<div className="flex min-h-screen flex-col font-[family-name:var(--font-geist-sans)] bg-white">
<Navbar />
<div className="flex-1">
<HeaderLatest />
</div>
<BreakingNews />
<Footer />
</div>
);
}

View File

@ -0,0 +1,22 @@
import BreakingNews from "@/components/landing-page/breaking-news";
import Footer from "@/components/landing-page/footer";
import Header from "@/components/landing-page/header";
import HeaderLatest from "@/components/landing-page/header-latest";
import Navbar from "@/components/landing-page/navbar";
import News from "@/components/landing-page/news";
import Opini from "@/components/landing-page/opini";
import PopularNews from "@/components/landing-page/popular-news";
export default function Home() {
return (
<div className="flex min-h-screen flex-col font-[family-name:var(--font-geist-sans)] bg-white">
<Navbar />
<div className="flex-1">
<HeaderLatest />
</div>
<BreakingNews />
<Footer />
</div>
);
}

View File

@ -0,0 +1,22 @@
import BreakingNews from "@/components/landing-page/breaking-news";
import Footer from "@/components/landing-page/footer";
import Header from "@/components/landing-page/header";
import HeaderLatest from "@/components/landing-page/header-latest";
import Navbar from "@/components/landing-page/navbar";
import News from "@/components/landing-page/news";
import Opini from "@/components/landing-page/opini";
import PopularNews from "@/components/landing-page/popular-news";
export default function Home() {
return (
<div className="flex min-h-screen flex-col font-[family-name:var(--font-geist-sans)] bg-white">
<Navbar />
<div className="flex-1">
<HeaderLatest />
</div>
<BreakingNews />
<Footer />
</div>
);
}

19
app/detail/[id]/page.tsx Normal file
View File

@ -0,0 +1,19 @@
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="relative z-10 bg-[#F2F4F3] max-w-7xl mx-auto">
<Navbar />
<div className="flex-1">
<DetailContent />
</div>
<Footer />
</div>
</div>
);
}

View File

@ -21,7 +21,7 @@ type Article = {
title: string; title: string;
}[]; }[];
files: { files: {
file_url: string; fileUrl: string;
file_alt: string; file_alt: string;
}[]; }[];
}; };
@ -56,6 +56,8 @@ export default function DetailContent() {
null null
); );
const [selectedIndex, setSelectedIndex] = useState(0);
const [tabArticles, setTabArticles] = useState<Article[]>([]); const [tabArticles, setTabArticles] = useState<Article[]>([]);
const [activeTab, setActiveTab] = useState<TabKey>("trending"); const [activeTab, setActiveTab] = useState<TabKey>("trending");
@ -154,16 +156,17 @@ export default function DetailContent() {
useEffect(() => { useEffect(() => {
initState(); initState();
}, [page, showData, startDateValue, selectedCategories]); }, [page, showData]);
async function initState() { async function initState() {
// loading(); // loading();
const req = { const req = {
limit: showData, limit: showData,
page, page: 1,
search, search: "",
categorySlug: Array.from(selectedCategories).join(","), categorySlug: "",
sort: "desc", sort: "desc",
isPublish: true,
sortBy: "created_at", sortBy: "created_at",
}; };
@ -188,22 +191,20 @@ export default function DetailContent() {
setThumbnail(data?.thumbnailUrl); setThumbnail(data?.thumbnailUrl);
setDiseId(data?.aiArticleId); setDiseId(data?.aiArticleId);
setDetailFiles(data?.files); setDetailFiles(data?.files);
setArticleDetail(data); // <-- Add this setArticleDetail(data);
close(); close();
} }
if (!articleDetail?.files || articleDetail.files.length === 0) {
return (
<div className="w-full h-[400px] bg-gray-100 flex items-center justify-center rounded-lg">
<p className="text-gray-400 text-sm">Gambar tidak tersedia</p>
</div>
);
}
return ( 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="bg-white grid grid-cols-1 md:grid-cols-3 gap-6 px-8 py-8">
<div className="md:col-span-2"> <div className="md:col-span-2">
<p className="text-sm text-gray-500 mb-2">Home {">"}Detail</p> <p className="text-sm text-gray-500 mb-2">Home {">"}Detail</p>
@ -211,7 +212,7 @@ export default function DetailContent() {
{articleDetail?.title} {articleDetail?.title}
</h1> </h1>
<div className="flex items-center space-x-2 text-sm text-gray-500 mb-4"> <div className="flex items-center space-x-2 text-sm text-gray-500 mb-4">
<div className="text-[#31942E]"> <div className="text-blue-400">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="24" width="24"
@ -231,7 +232,7 @@ export default function DetailContent() {
</svg> </svg>
</div> </div>
<span className="text-[#31942E] font-medium"> <span className="text-blue-400 font-medium">
{articleDetail?.createdByName} {articleDetail?.createdByName}
</span> </span>
<span></span> <span></span>
@ -252,19 +253,41 @@ export default function DetailContent() {
</div> </div>
<div className="w-full h-auto mb-6"> <div className="w-full h-auto mb-6">
{articleDetail?.files?.[0]?.file_url ? ( {/* Gambar utama */}
<div className="w-full">
<Image <Image
src={articleDetail.files[0].file_url} src={articleDetail.files[selectedIndex].fileUrl}
alt="Berita" alt={articleDetail.files[selectedIndex].fileAlt || "Berita"}
width={800} width={800}
height={400} height={400}
className="rounded-lg w-full object-cover" className="rounded-lg w-full object-cover"
/> />
) : ( </div>
<div className="w-full h-[400px] bg-gray-100 flex items-center justify-center rounded-lg">
<p className="text-gray-400 text-sm">Gambar tidak tersedia</p> {/* Thumbnail */}
</div> <div className="flex gap-2 mt-3 overflow-x-auto">
)} {articleDetail.files.map((file: any, index: number) => (
<button
key={file.id || index}
onClick={() => setSelectedIndex(index)}
className={`border-2 rounded-lg overflow-hidden ${
selectedIndex === index
? "border-red-500"
: "border-transparent"
}`}
>
<Image
src={file.fileUrl}
alt={file.fileAlt || "Thumbnail"}
width={100}
height={80}
className="object-cover"
/>
</button>
))}
</div>
{/* Slug */}
<p className="text-sm text-gray-500 mt-2 text-end"> <p className="text-sm text-gray-500 mt-2 text-end">
{articleDetail?.slug} {articleDetail?.slug}
</p> </p>
@ -336,13 +359,13 @@ export default function DetailContent() {
</Link> </Link>
</div> </div>
<div className="flex-1 overflow-y-auto"> <div className="flex-1 overflow-y-auto">
<p className="text-gray-700 leading-relaxed text-justify"> <div className="text-gray-700 leading-relaxed text-justify">
<span className="text-black font-bold text-md"> <div
Mikulnews.com - dangerouslySetInnerHTML={{
</span> __html: articleDetail?.htmlDescription || "",
}}
{articleDetail?.description} />
</p> </div>
<Author /> <Author />
<div className="flex flex-row gap-2 items-center"> <div className="flex flex-row gap-2 items-center">
<span className="font-semibold text-sm text-gray-700"> <span className="font-semibold text-sm text-gray-700">
@ -360,34 +383,12 @@ export default function DetailContent() {
</div> </div>
</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>
<div className="mt-10">
<h2 className="text-2xl font-bold mb-2">Tinggalkan Balasan</h2> <h2 className="text-2xl font-bold mb-2">Tinggalkan Balasan</h2>
<p className="text-gray-600 mb-4 text-sm"> <p className="text-gray-600 mb-4 text-sm">
Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib
ditandai <span className="text-green-600">*</span> ditandai <span className="text-blue-600">*</span>
</p> </p>
<form className="space-y-6 mt-6"> <form className="space-y-6 mt-6">
@ -396,7 +397,7 @@ export default function DetailContent() {
htmlFor="komentar" htmlFor="komentar"
className="block text-sm font-medium mb-1" className="block text-sm font-medium mb-1"
> >
Komentar <span className="text-green-600">*</span> Komentar <span className="text-blue-600">*</span>
</label> </label>
<textarea <textarea
id="komentar" id="komentar"
@ -410,7 +411,7 @@ export default function DetailContent() {
htmlFor="nama" htmlFor="nama"
className="block text-sm font-medium mb-1" className="block text-sm font-medium mb-1"
> >
Nama <span className="text-green-600">*</span> Nama <span className="text-blue-600">*</span>
</label> </label>
<input <input
type="text" type="text"
@ -426,7 +427,7 @@ export default function DetailContent() {
htmlFor="email" htmlFor="email"
className="block text-sm font-medium mb-1" className="block text-sm font-medium mb-1"
> >
Email <span className="text-green-600">*</span> Email <span className="text-blue-600">*</span>
</label> </label>
<input <input
type="email" type="email"
@ -465,7 +466,7 @@ export default function DetailContent() {
<button <button
type="submit" type="submit"
className="bg-green-600 hover:bg-green-700 text-white font-semibold px-6 py-2 rounded-md transition mt-2" className="bg-blue-600 hover:bg-blue-700 text-white font-semibold px-6 py-2 rounded-md transition mt-2"
> >
KIRIM KOMENTAR KIRIM KOMENTAR
</button> </button>
@ -584,82 +585,64 @@ export default function DetailContent() {
</h3> </h3>
<div className="space-y-4"> <div className="space-y-4">
<div className="relative"> {/* Artikel utama (featured) */}
<Image {articles.length > 0 && (
src={"/gaza.png"} <div className="relative">
alt="Recommended Article" <Image
width={400} src={articles[0]?.thumbnailUrl || "/default.jpg"}
height={200} alt={articles[0]?.title || "Recommended Article"}
className="rounded-lg w-full h-auto object-cover" width={400}
/> height={200}
<div className="absolute bottom-0 left-0 right-0 bg-black bg-opacity-60 text-white p-3 rounded-b-lg"> className="rounded-lg w-full h-auto object-cover"
<p className="text-sm font-semibold leading-tight"> />
Bom Bunuh Diri Guncang Gereja di Damaskus, 20 Orang Tewas <div className="absolute bottom-0 left-0 right-0 bg-black bg-opacity-60 text-white p-3 rounded-b-lg">
dan Puluhan Terluka <p className="text-sm font-semibold leading-tight">
</p> {articles[0]?.title}
<p className="text-xs text-gray-300 mt-1"> </p>
📅 23 JUNI 2025 <p className="text-xs text-gray-300 mt-1">
</p> 📅{" "}
{new Date(articles[0]?.createdAt).toLocaleDateString(
"id-ID",
{
day: "2-digit",
month: "long",
year: "numeric",
}
)}
</p>
</div>
</div> </div>
</div> )}
{/* List artikel lain */}
<div className="space-y-3"> <div className="space-y-3">
<div className="flex space-x-3"> {articles.slice(1, 4).map((item) => (
<Image <div key={item.id} className="flex space-x-3">
src={"/perang.png"} <Image
alt="OPM Serang Gereja" src={item.thumbnailUrl || "/default.jpg"}
width={80} alt={item.title}
height={60} width={80}
className="rounded object-cover w-[80px] h-[60px]" height={60}
/> className="rounded object-cover w-[80px] h-[60px]"
<div> />
<p className="text-sm font-semibold leading-snug"> <div>
OPM Mulai Kehilangan Simpati dari Masyarakat Papua Usai <p className="text-sm font-semibold leading-snug">
Serang Gereja {item.title}
</p> </p>
<p className="text-xs text-gray-500 mt-1"> <p className="text-xs text-gray-500 mt-1">
📅 15 JUNI 2025 📅{" "}
</p> {new Date(item.createdAt).toLocaleDateString(
"id-ID",
{
day: "2-digit",
month: "long",
year: "numeric",
}
)}
</p>
</div>
</div> </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>

View File

@ -3,6 +3,7 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import Image from "next/image"; import Image from "next/image";
import { getListArticle } from "@/service/article"; import { getListArticle } from "@/service/article";
import Link from "next/link";
type Article = { type Article = {
id: number; id: number;
@ -16,7 +17,7 @@ type Article = {
title: string; title: string;
}[]; }[];
files: { files: {
file_url: string; fileUrl: string;
file_alt: string; file_alt: string;
}[]; }[];
}; };
@ -72,29 +73,31 @@ export default function BreakingNews() {
<div className="md:col-span-2 space-y-6"> <div className="md:col-span-2 space-y-6">
{articles.map((item) => ( {articles.map((item) => (
<div key={item.id} className="flex gap-4 border-b pb-4"> <div key={item.id} className="flex gap-4 border-b pb-4">
<Image <Link className="flex gap-4" href={`/detail/${item?.id}`}>
src={item.thumbnailUrl || "/dummy.jpg"} <Image
alt={item.title} src={item.thumbnailUrl || "/dummy.jpg"}
width={160} alt={item.title}
height={100} width={160}
className="object-cover rounded-md w-40 h-28" height={100}
/> className="object-cover rounded-md w-40 h-28"
<div className="flex-1"> />
<h3 className="text-base font-semibold hover:text-blue-600 cursor-pointer"> <div className="flex-1">
{item.title} <h3 className="text-base font-semibold hover:text-blue-600 cursor-pointer">
</h3> {item.title}
<p className="text-xs text-gray-500 mt-1"> </h3>
By {item.createdByName} {" "} <p className="text-xs text-gray-500 mt-1">
{new Date(item.createdAt).toLocaleDateString("id-ID", { By {item.createdByName} {" "}
day: "numeric", {new Date(item.createdAt).toLocaleDateString("id-ID", {
month: "long", day: "numeric",
year: "numeric", month: "long",
})} year: "numeric",
</p> })}
<p className="text-sm text-gray-600 mt-2 line-clamp-2"> </p>
{item.description} <p className="text-sm text-gray-600 mt-2 line-clamp-2">
</p> {item.description}
</div> </p>
</div>
</Link>
</div> </div>
))} ))}
</div> </div>
@ -148,25 +151,30 @@ export default function BreakingNews() {
<div className="space-y-5"> <div className="space-y-5">
{/* Item pertama tampil besar */} {/* Item pertama tampil besar */}
<div className="relative"> <div className="relative">
<Image <Link
src={popular[0]?.files?.[0]?.file_url || "/dummy.jpg"} className="flex flex-col gap-4"
alt={ href={`/detail/${popular[0]?.id}`}
popular[0]?.files?.[0]?.file_alt || >
popular[0]?.title || <Image
"No Title" src={popular[0]?.files?.[0]?.fileUrl || "/dummy.jpg"}
} alt={
width={400} popular[0]?.files?.[0]?.file_alt ||
height={200} popular[0]?.title ||
className="w-full h-48 object-cover rounded-md" "No Title"
/> }
<div className="mt-2"> width={400}
<h5 className="text-sm font-semibold hover:text-blue-600 cursor-pointer"> height={200}
{popular[0]?.title} className="w-full h-48 object-cover rounded-md"
</h5> />
<span className="absolute top-2 right-2 text-4xl font-bold text-gray-300/80"> <div className="mt-2">
01 <h5 className="text-sm font-semibold hover:text-blue-600 cursor-pointer">
</span> {popular[0]?.title}
</div> </h5>
<span className="absolute top-2 right-2 text-4xl font-bold text-gray-300/80">
01
</span>
</div>
</Link>
</div> </div>
{/* Item sisanya */} {/* Item sisanya */}
@ -176,15 +184,17 @@ export default function BreakingNews() {
key={item.id} key={item.id}
className="flex gap-3 items-start border-b pb-2 last:border-b-0" className="flex gap-3 items-start border-b pb-2 last:border-b-0"
> >
<span className="text-lg font-bold text-gray-400"> <Link className="flex gap-4" href={`/detail/${item?.id}`}>
0{i + 2} <span className="text-lg font-bold text-gray-400">
</span> 0{i + 2}
<div> </span>
<h5 className="text-sm font-medium hover:text-blue-600 cursor-pointer"> <div>
{item.title} <h5 className="text-sm font-medium hover:text-blue-600 cursor-pointer">
</h5> {item.title}
<p className="text-xs text-gray-400">0 Shares</p> </h5>
</div> <p className="text-xs text-gray-400">0 Shares</p>
</div>
</Link>
</div> </div>
))} ))}
</div> </div>

View File

@ -3,6 +3,7 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import Image from "next/image"; import Image from "next/image";
import { getListArticle } from "@/service/article"; import { getListArticle } from "@/service/article";
import Link from "next/link";
type Article = { type Article = {
id: number; id: number;
@ -50,26 +51,28 @@ export default function HeaderLatest() {
return ( return (
<header className="w-full pt-3 flex justify-center"> <header className="w-full pt-3 flex justify-center">
<div className="relative max-w-screen-xl w-full h-[320px] md:h-[480px] mx-3 md:mx-5 overflow-hidden group"> <div className="relative max-w-screen-xl w-full h-[320px] md:h-[480px] mx-3 md:mx-5 overflow-hidden group">
{/* Gambar utama */} <Link className="flex" href={`/detail/${article?.id}`}>
<Image {/* Gambar utama */}
src={article?.thumbnailUrl || "/dummy.jpg"} <Image
alt={article?.title || "No Title"} src={article?.thumbnailUrl || "/dummy.jpg"}
fill alt={article?.title || "No Title"}
className="object-cover transition-transform duration-500 group-hover:scale-110" fill
/> className="object-cover transition-transform duration-500 group-hover:scale-110"
/>
{/* Overlay gradient */} {/* Overlay gradient */}
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent" /> <div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent" />
{/* Konten teks */} {/* Konten teks */}
<div className="absolute bottom-5 left-5 text-white max-w-[80%]"> <div className="absolute bottom-5 left-5 text-white max-w-[80%]">
<span className="bg-orange-600 text-white text-[10px] md:text-xs px-2 py-1 mb-3 inline-block font-semibold uppercase tracking-wide"> <span className="bg-orange-600 text-white text-[10px] md:text-xs px-2 py-1 mb-3 inline-block font-semibold uppercase tracking-wide">
{article?.categoryName || "Berita Terkini"} {article?.categoryName || "Berita Terkini"}
</span> </span>
<h1 className="text-lg md:text-2xl font-semibold leading-snug md:leading-tight"> <h1 className="text-lg md:text-2xl font-semibold leading-snug md:leading-tight">
{article?.title || "Memuat..."} {article?.title || "Memuat..."}
</h1> </h1>
</div> </div>
</Link>
</div> </div>
</header> </header>
); );

View File

@ -3,6 +3,7 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Image from "next/image"; import Image from "next/image";
import { getListArticle } from "@/service/article"; import { getListArticle } from "@/service/article";
import Link from "next/link";
type Article = { type Article = {
id: number; id: number;
@ -48,31 +49,33 @@ export default function HeaderNews() {
<div className="py-6 grid grid-cols-1 md:grid-cols-2 gap-4 h-[600px]"> <div className="py-6 grid grid-cols-1 md:grid-cols-2 gap-4 h-[600px]">
{/* Kiri - berita utama */} {/* Kiri - berita utama */}
<div className="relative overflow-hidden h-full"> <div className="relative overflow-hidden h-full">
<Image <Link className="flex" href={`/detail/${articles[0]?.id}`}>
src={articles[0]?.thumbnailUrl || "/dummy.jpg"} <Image
alt={articles[0]?.title || "No Title"} src={articles[0]?.thumbnailUrl || "/dummy.jpg"}
fill alt={articles[0]?.title || "No Title"}
className="object-cover" fill
/> className="object-cover"
<div className="absolute inset-0 bg-gradient-to-t from-black/70 to-transparent"></div> />
<div className="absolute bottom-4 left-4 right-4 text-white"> <div className="absolute inset-0 bg-gradient-to-t from-black/70 to-transparent"></div>
<span className="bg-black/70 text-xs px-2 py-1 rounded"> <div className="absolute bottom-4 left-4 right-4 text-white">
{articles[0]?.categories?.[0]?.title || "Uncategorized"} <span className="bg-black/70 text-xs px-2 py-1 rounded">
</span> {articles[0]?.categories?.[0]?.title || "Uncategorized"}
<h2 className="text-2xl font-bold mt-2">{articles[0]?.title}</h2> </span>
<p className="text-sm mt-1"> <h2 className="text-2xl font-bold mt-2">{articles[0]?.title}</h2>
<span className="font-semibold"> <p className="text-sm mt-1">
{articles[0]?.createdByName} <span className="font-semibold">
</span>{" "} {articles[0]?.createdByName}
{" "} </span>{" "}
{articles[0]?.createdAt && {" "}
new Date(articles[0].createdAt).toLocaleDateString("id-ID", { {articles[0]?.createdAt &&
day: "numeric", new Date(articles[0].createdAt).toLocaleDateString("id-ID", {
month: "long", day: "numeric",
year: "numeric", month: "long",
})} year: "numeric",
</p> })}
</div> </p>
</div>
</Link>
</div> </div>
{/* Kanan - 3 berita */} {/* Kanan - 3 berita */}
@ -86,33 +89,38 @@ export default function HeaderNews() {
key={item.id || `dummy-${idx}`} key={item.id || `dummy-${idx}`}
className="relative overflow-hidden" className="relative overflow-hidden"
> >
<Image <Link className="flex" href={`/detail/${item?.id}`}>
src={item.thumbnailUrl || "/dummy.jpg"} <Image
alt={item.title || "No Title"} src={item.thumbnailUrl || "/dummy.jpg"}
fill alt={item.title || "No Title"}
className="object-cover" fill
/> className="object-cover"
<div className="absolute inset-0 bg-gradient-to-t from-black/70 to-transparent"></div> />
<div className="absolute bottom-2 left-2 right-2 text-white"> <div className="absolute inset-0 bg-gradient-to-t from-black/70 to-transparent"></div>
<span className="bg-black/70 text-[10px] px-2 py-1 rounded"> <div className="absolute bottom-2 left-2 right-2 text-white">
{item.categories?.[0]?.title || "Uncategorized"} <span className="bg-black/70 text-[10px] px-2 py-1 rounded">
</span> {item.categories?.[0]?.title || "Uncategorized"}
<h3 className="text-sm font-semibold mt-1 line-clamp-2"> </span>
{item.title} <h3 className="text-sm font-semibold mt-1 line-clamp-2">
</h3> {item.title}
<p className="text-[10px]"> </h3>
<span className="font-semibold"> <p className="text-[10px]">
{item.createdByName} <span className="font-semibold">
</span>{" "} {item.createdByName}
{" "} </span>{" "}
{item.createdAt && {" "}
new Date(item.createdAt).toLocaleDateString("id-ID", { {item.createdAt &&
day: "numeric", new Date(item.createdAt).toLocaleDateString(
month: "short", "id-ID",
year: "numeric", {
})} day: "numeric",
</p> month: "short",
</div> year: "numeric",
}
)}
</p>
</div>
</Link>
</div> </div>
) )
)} )}
@ -121,36 +129,38 @@ export default function HeaderNews() {
{/* Bawah: 1 berita besar */} {/* Bawah: 1 berita besar */}
{articles[3] && ( {articles[3] && (
<div className="relative overflow-hidden"> <div className="relative overflow-hidden">
<Image <Link className="flex" href={`/detail/${articles[3]?.id}`}>
src={articles[3]?.thumbnailUrl || "/dummy.jpg"} <Image
alt={articles[3]?.title || "No Title"} src={articles[3]?.thumbnailUrl || "/dummy.jpg"}
fill alt={articles[3]?.title || "No Title"}
className="object-cover" fill
/> className="object-cover"
<div className="absolute inset-0 bg-gradient-to-t from-black/70 to-transparent"></div> />
<div className="absolute bottom-2 left-2 right-2 text-white"> <div className="absolute inset-0 bg-gradient-to-t from-black/70 to-transparent"></div>
<span className="bg-black/70 text-xs px-2 py-1 rounded"> <div className="absolute bottom-2 left-2 right-2 text-white">
{articles[3]?.categories?.[0]?.title || "Uncategorized"} <span className="bg-black/70 text-xs px-2 py-1 rounded">
</span> {articles[3]?.categories?.[0]?.title || "Uncategorized"}
<h3 className="text-base font-semibold mt-1"> </span>
{articles[3]?.title} <h3 className="text-base font-semibold mt-1">
</h3> {articles[3]?.title}
<p className="text-xs"> </h3>
<span className="font-semibold"> <p className="text-xs">
{articles[3]?.createdByName} <span className="font-semibold">
</span>{" "} {articles[3]?.createdByName}
{" "} </span>{" "}
{articles[3]?.createdAt && {" "}
new Date(articles[3].createdAt).toLocaleDateString( {articles[3]?.createdAt &&
"id-ID", new Date(articles[3].createdAt).toLocaleDateString(
{ "id-ID",
day: "numeric", {
month: "long", day: "numeric",
year: "numeric", month: "long",
} year: "numeric",
)} }
</p> )}
</div> </p>
</div>
</Link>
</div> </div>
)} )}
</div> </div>

View File

@ -20,12 +20,21 @@ export default function Navbar() {
{/* Menu (opsional kalau nanti ada navigasi) */} {/* Menu (opsional kalau nanti ada navigasi) */}
<nav className="hidden md:flex items-center gap-6 text-sm font-medium text-gray-700"> <nav className="hidden md:flex items-center gap-6 text-sm font-medium text-gray-700">
<a href="#" className="hover:text-blue-600"> <a href="/" className="hover:text-blue-600">
Home Home
</a> </a>
<a href="/category/latest-news" className="hover:text-blue-600"> <a href="/category/latest-news" className="hover:text-blue-600">
Berita Terbaru Berita Terbaru
</a> </a>
<a href="/category/popular-news" className="hover:text-blue-600">
Berita Populer
</a>
<a href="/category/protect" className="hover:text-blue-600">
Jaga Negeri
</a>
<a href="/category/opinion-news" className="hover:text-blue-600">
Berita Opini
</a>
<div className="flex items-center gap-4 text-sm"> <div className="flex items-center gap-4 text-sm">
<Link <Link

View File

@ -3,6 +3,7 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Image from "next/image"; import Image from "next/image";
import { getListArticle } from "@/service/article"; import { getListArticle } from "@/service/article";
import Link from "next/link";
type Article = { type Article = {
id: number; id: number;
@ -53,21 +54,23 @@ export default function Story() {
<div className="grid grid-cols-1 md:grid-cols-4 gap-4"> <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
{articles.map((item) => ( {articles.map((item) => (
<div key={item.id} className="relative overflow-hidden h-72"> <div key={item.id} className="relative overflow-hidden h-72">
<Image <Link className="flex" href={`/detail/${item?.id}`}>
src={item.thumbnailUrl || "/dummy.jpg"} <Image
alt={item.title} src={item.thumbnailUrl || "/dummy.jpg"}
fill alt={item.title}
className="object-cover" fill
/> className="object-cover"
<div className="absolute inset-0 bg-black/40"></div> />
<div className="absolute inset-0 flex flex-col items-center justify-center text-center px-4 text-white"> <div className="absolute inset-0 bg-black/40"></div>
<span className="bg-black/70 text-xs px-3 py-1 rounded mb-2"> <div className="absolute inset-0 flex flex-col items-center justify-center text-center px-4 text-white">
{item.categories[0]?.title || "Uncategorized"} <span className="bg-black/70 text-xs px-3 py-1 rounded mb-2">
</span> {item.categories[0]?.title || "Uncategorized"}
<h3 className="text-sm font-semibold leading-snug line-clamp-3"> </span>
{item.title} <h3 className="text-sm font-semibold leading-snug line-clamp-3">
</h3> {item.title}
</div> </h3>
</div>
</Link>
</div> </div>
))} ))}
</div> </div>

View File

@ -46,6 +46,7 @@ import {
TableCell, TableCell,
} from "@/components/ui/table"; } from "@/components/ui/table";
import CustomPagination from "../layout/custom-pagination"; import CustomPagination from "../layout/custom-pagination";
import { $ZodNumberDef } from "zod/v4/core";
const columns = [ const columns = [
{ name: "No", uid: "no" }, { name: "No", uid: "no" },
@ -173,11 +174,11 @@ export default function ArticleTable() {
initState(); initState();
}; };
const copyUrlArticle = async (id: number, slug: string) => { const copyUrlArticle = async (id: $ZodNumberDef) => {
const url = const url =
`${window.location.protocol}//${window.location.host}` + `${window.location.protocol}//${window.location.host}` +
"/news/detail/" + "/detail/" +
`${id}-${slug}`; `${id}`;
try { try {
await navigator.clipboard.writeText(url); await navigator.clipboard.writeText(url);
successToast("Success", "Article Copy to Clipboard"); successToast("Success", "Article Copy to Clipboard");
@ -228,9 +229,7 @@ export default function ArticleTable() {
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent className="w-56"> <DropdownMenuContent className="w-56">
<DropdownMenuItem <DropdownMenuItem onClick={() => copyUrlArticle(article.id)}>
onClick={() => copyUrlArticle(article.id, article.slug)}
>
<CopyIcon className="mr-2 h-4 w-4" /> <CopyIcon className="mr-2 h-4 w-4" />
Copy Url Article Copy Url Article
</DropdownMenuItem> </DropdownMenuItem>

BIN
public/author.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB