update
This commit is contained in:
parent
af3649fcb0
commit
75b8564a41
|
|
@ -0,0 +1,17 @@
|
||||||
|
import BreakingNews from "@/components/landing-page/breaking-news";
|
||||||
|
import Footer from "@/components/landing-page/footer";
|
||||||
|
import HeaderLatest from "@/components/landing-page/header-bumn";
|
||||||
|
import Navbar from "@/components/landing-page/navbar";
|
||||||
|
import BumnNews from "@/components/landing-page/news";
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen flex-col font-[family-name:var(--font-geist-sans)] bg-white">
|
||||||
|
<Navbar />
|
||||||
|
|
||||||
|
<BumnNews />
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import BreakingNews from "@/components/landing-page/breaking-news";
|
||||||
|
import Footer from "@/components/landing-page/footer";
|
||||||
|
import HeaderLatest from "@/components/landing-page/header-bumn";
|
||||||
|
import Navbar from "@/components/landing-page/navbar";
|
||||||
|
import BumnNews from "@/components/landing-page/news";
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen flex-col font-[family-name:var(--font-geist-sans)] bg-white">
|
||||||
|
<Navbar />
|
||||||
|
|
||||||
|
<BumnNews />
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import BreakingNews from "@/components/landing-page/breaking-news";
|
||||||
|
import Footer from "@/components/landing-page/footer";
|
||||||
|
import HeaderLatest from "@/components/landing-page/header-bumn";
|
||||||
|
import Navbar from "@/components/landing-page/navbar";
|
||||||
|
import BumnNews from "@/components/landing-page/news";
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen flex-col font-[family-name:var(--font-geist-sans)] bg-white">
|
||||||
|
<Navbar />
|
||||||
|
|
||||||
|
<BumnNews />
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import BreakingNews from "@/components/landing-page/breaking-news";
|
||||||
|
import Footer from "@/components/landing-page/footer";
|
||||||
|
import HeaderLatest from "@/components/landing-page/header-bumn";
|
||||||
|
import Navbar from "@/components/landing-page/navbar";
|
||||||
|
import BumnNews from "@/components/landing-page/news";
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen flex-col font-[family-name:var(--font-geist-sans)] bg-white">
|
||||||
|
<Navbar />
|
||||||
|
|
||||||
|
<BumnNews />
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import BreakingNews from "@/components/landing-page/breaking-news";
|
||||||
|
import Footer from "@/components/landing-page/footer";
|
||||||
|
import HeaderLatest from "@/components/landing-page/header-bumn";
|
||||||
|
import Navbar from "@/components/landing-page/navbar";
|
||||||
|
import BumnNews from "@/components/landing-page/news";
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen flex-col font-[family-name:var(--font-geist-sans)] bg-white">
|
||||||
|
<Navbar />
|
||||||
|
|
||||||
|
<BumnNews />
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import BreakingNews from "@/components/landing-page/breaking-news";
|
||||||
|
import Footer from "@/components/landing-page/footer";
|
||||||
|
import HeaderLatest from "@/components/landing-page/header-bumn";
|
||||||
|
import Navbar from "@/components/landing-page/navbar";
|
||||||
|
import BumnNews from "@/components/landing-page/news";
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-screen flex-col font-[family-name:var(--font-geist-sans)] bg-white">
|
||||||
|
<Navbar />
|
||||||
|
|
||||||
|
<BumnNews />
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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-orange-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-orange-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]?.fileUrl ? (
|
{/* Gambar utama */}
|
||||||
|
<div className="w-full">
|
||||||
<Image
|
<Image
|
||||||
src={articleDetail.files[0].fileUrl}
|
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-orange-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-orange-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-orange-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-orange-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-orange-600 hover:bg-orange-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>
|
||||||
|
|
|
||||||
|
|
@ -197,8 +197,8 @@ const TinyMCEEditor: React.FC<TinyMCEEditorProps> = ({
|
||||||
height,
|
height,
|
||||||
language,
|
language,
|
||||||
placeholder,
|
placeholder,
|
||||||
readonly: readOnly,
|
// readonly: readOnly,
|
||||||
disabled,
|
// disabled,
|
||||||
branding: false,
|
branding: false,
|
||||||
elementpath: false,
|
elementpath: false,
|
||||||
resize: false,
|
resize: false,
|
||||||
|
|
@ -270,9 +270,9 @@ const TinyMCEEditor: React.FC<TinyMCEEditorProps> = ({
|
||||||
onInit={handleEditorInit}
|
onInit={handleEditorInit}
|
||||||
initialValue={initialData}
|
initialValue={initialData}
|
||||||
onEditorChange={handleEditorChange}
|
onEditorChange={handleEditorChange}
|
||||||
disabled={disabled}
|
disabled={disabled || readOnly}
|
||||||
apiKey={process.env.NEXT_PUBLIC_TINYMCE_API_KEY}
|
apiKey={process.env.NEXT_PUBLIC_TINYMCE_API_KEY}
|
||||||
// init={editorConfig}
|
init={editorConfig}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Status bar */}
|
{/* Status bar */}
|
||||||
|
|
|
||||||
|
|
@ -524,13 +524,21 @@ export default function CreateArticleForm() {
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="single">Single Article</SelectItem>
|
<SelectItem value="single">Single Article</SelectItem>
|
||||||
<SelectItem value="rewrite">Content Rewrite</SelectItem>
|
{/* <SelectItem value="rewrite">Content Rewrite</SelectItem> */}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
{selectedWritingType === "single" ? (
|
{selectedWritingType === "single" ? (
|
||||||
<GenerateSingleArticleForm
|
<GenerateSingleArticleForm
|
||||||
content={(data) => {
|
content={(data) => {
|
||||||
setDiseData(data);
|
setDiseData(data);
|
||||||
|
// setValue("title", data?.title ?? "", {
|
||||||
|
// shouldValidate: true,
|
||||||
|
// shouldDirty: true,
|
||||||
|
// });
|
||||||
|
// setValue("slug", generateSlug(data?.title ?? ""), {
|
||||||
|
// shouldValidate: true,
|
||||||
|
// shouldDirty: true,
|
||||||
|
// });
|
||||||
setValue(
|
setValue(
|
||||||
"description",
|
"description",
|
||||||
data?.articleBody ? data?.articleBody : ""
|
data?.articleBody ? data?.articleBody : ""
|
||||||
|
|
|
||||||
|
|
@ -681,6 +681,7 @@ export default function EditArticleForm(props: { isDetail: boolean }) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
type="button"
|
||||||
className=" border-none rounded-full"
|
className=" border-none rounded-full"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
color="danger"
|
color="danger"
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,20 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "@/components/ui/select";
|
import {
|
||||||
|
Select,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
} from "@/components/ui/select";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { close, error, loading } from "@/config/swal";
|
import { close, error, loading } from "@/config/swal";
|
||||||
import { delay } from "@/utils/global";
|
import { delay } from "@/utils/global";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { getDetailArticle, getGenerateRewriter } from "@/service/generate-article";
|
import {
|
||||||
|
getDetailArticle,
|
||||||
|
getGenerateRewriter,
|
||||||
|
} from "@/service/generate-article";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Loader2 } from "lucide-react";
|
import { Loader2 } from "lucide-react";
|
||||||
import GetSeoScore from "./get-seo-score-form";
|
import GetSeoScore from "./get-seo-score-form";
|
||||||
|
|
@ -69,8 +78,11 @@ interface DiseData {
|
||||||
additionalKeywords: string;
|
additionalKeywords: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function GenerateContentRewriteForm(props: { content: (data: DiseData) => void }) {
|
export default function GenerateContentRewriteForm(props: {
|
||||||
const [selectedWritingSyle, setSelectedWritingStyle] = useState("Informational");
|
content: (data: DiseData) => void;
|
||||||
|
}) {
|
||||||
|
const [selectedWritingSyle, setSelectedWritingStyle] =
|
||||||
|
useState("Informational");
|
||||||
const [selectedArticleSize, setSelectedArticleSize] = useState("News");
|
const [selectedArticleSize, setSelectedArticleSize] = useState("News");
|
||||||
const [selectedLanguage, setSelectedLanguage] = useState("id");
|
const [selectedLanguage, setSelectedLanguage] = useState("id");
|
||||||
const [mainKeyword, setMainKeyword] = useState("");
|
const [mainKeyword, setMainKeyword] = useState("");
|
||||||
|
|
@ -166,7 +178,10 @@ export default function GenerateContentRewriteForm(props: { content: (data: Dise
|
||||||
))}
|
))}
|
||||||
</SelectSection>
|
</SelectSection>
|
||||||
</Select> */}
|
</Select> */}
|
||||||
<Select value={selectedWritingSyle} onValueChange={(value) => setSelectedWritingStyle(value)}>
|
<Select
|
||||||
|
value={selectedWritingSyle}
|
||||||
|
onValueChange={(value) => setSelectedWritingStyle(value)}
|
||||||
|
>
|
||||||
<SelectTrigger className="w-full border rounded-lg text-black dark:border-gray-400">
|
<SelectTrigger className="w-full border rounded-lg text-black dark:border-gray-400">
|
||||||
<SelectValue placeholder="Writing Style" />
|
<SelectValue placeholder="Writing Style" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
|
|
@ -198,7 +213,10 @@ export default function GenerateContentRewriteForm(props: { content: (data: Dise
|
||||||
))}
|
))}
|
||||||
</SelectSection>
|
</SelectSection>
|
||||||
</Select> */}
|
</Select> */}
|
||||||
<Select value={selectedArticleSize} onValueChange={(value) => setSelectedArticleSize(value)}>
|
<Select
|
||||||
|
value={selectedArticleSize}
|
||||||
|
onValueChange={(value) => setSelectedArticleSize(value)}
|
||||||
|
>
|
||||||
<SelectTrigger className="w-full border rounded-lg text-black dark:border-gray-400">
|
<SelectTrigger className="w-full border rounded-lg text-black dark:border-gray-400">
|
||||||
<SelectValue placeholder="Writing Style" />
|
<SelectValue placeholder="Writing Style" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
|
|
@ -229,7 +247,10 @@ export default function GenerateContentRewriteForm(props: { content: (data: Dise
|
||||||
<SelectItem key="en">English</SelectItem>
|
<SelectItem key="en">English</SelectItem>
|
||||||
</SelectSection>
|
</SelectSection>
|
||||||
</Select> */}
|
</Select> */}
|
||||||
<Select value={selectedLanguage} onValueChange={(value) => setSelectedLanguage(value)}>
|
<Select
|
||||||
|
value={selectedLanguage}
|
||||||
|
onValueChange={(value) => setSelectedLanguage(value)}
|
||||||
|
>
|
||||||
<SelectTrigger className="w-full border rounded-lg text-black dark:border-gray-400">
|
<SelectTrigger className="w-full border rounded-lg text-black dark:border-gray-400">
|
||||||
<SelectValue placeholder="Writing Style" />
|
<SelectValue placeholder="Writing Style" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
|
|
@ -239,6 +260,7 @@ export default function GenerateContentRewriteForm(props: { content: (data: Dise
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col mt-3">
|
<div className="flex flex-col mt-3">
|
||||||
<div className="flex flex-row gap-2 items-center">
|
<div className="flex flex-row gap-2 items-center">
|
||||||
<p className="text-sm">Text</p>
|
<p className="text-sm">Text</p>
|
||||||
|
|
@ -246,9 +268,16 @@ export default function GenerateContentRewriteForm(props: { content: (data: Dise
|
||||||
<div className="w-[78vw] lg:w-full">
|
<div className="w-[78vw] lg:w-full">
|
||||||
<CustomEditor onChange={setMainKeyword} initialData={mainKeyword} />
|
<CustomEditor onChange={setMainKeyword} initialData={mainKeyword} />
|
||||||
</div>
|
</div>
|
||||||
{mainKeyword == "" && <p className="text-red-400 text-sm">Required</p>}
|
{mainKeyword == "" && (
|
||||||
|
<p className="text-red-400 text-sm">Required</p>
|
||||||
|
)}
|
||||||
{articleIds.length < 3 && (
|
{articleIds.length < 3 && (
|
||||||
<Button onClick={onSubmit} type="button" disabled={mainKeyword === "" || isLoading} className="my-5 w-full py-5 text-xs md:text-base">
|
<Button
|
||||||
|
onClick={onSubmit}
|
||||||
|
type="button"
|
||||||
|
disabled={mainKeyword === "" || isLoading}
|
||||||
|
className="my-5 w-full py-5 text-xs md:text-base"
|
||||||
|
>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||||
|
|
@ -263,7 +292,14 @@ export default function GenerateContentRewriteForm(props: { content: (data: Dise
|
||||||
{articleIds.length > 0 && (
|
{articleIds.length > 0 && (
|
||||||
<div className="flex flex-row gap-1 mt-2">
|
<div className="flex flex-row gap-1 mt-2">
|
||||||
{articleIds?.map((id, index) => (
|
{articleIds?.map((id, index) => (
|
||||||
<Button key={id} onClick={() => setSelectedId(id)} disabled={isLoading && selectedId === id} variant={selectedId === id ? "default" : "outline"} className="flex items-center gap-2">
|
<Button
|
||||||
|
type="button"
|
||||||
|
key={id}
|
||||||
|
onClick={() => setSelectedId(id)}
|
||||||
|
disabled={isLoading && selectedId === id}
|
||||||
|
variant={selectedId === id ? "default" : "outline"}
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
>
|
||||||
{isLoading && selectedId === id ? (
|
{isLoading && selectedId === id ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 animate-spin" />
|
<Loader2 className="w-4 h-4 animate-spin" />
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ export default function GenerateSingleArticleForm(props: {
|
||||||
const [additionalKeyword, setAdditionalKeyword] = useState("");
|
const [additionalKeyword, setAdditionalKeyword] = useState("");
|
||||||
const [articleIds, setArticleIds] = useState<number[]>([]);
|
const [articleIds, setArticleIds] = useState<number[]>([]);
|
||||||
const [selectedId, setSelectedId] = useState<number>();
|
const [selectedId, setSelectedId] = useState<number>();
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const generateAll = async (keyword: string | undefined) => {
|
const generateAll = async (keyword: string | undefined) => {
|
||||||
if (keyword) {
|
if (keyword) {
|
||||||
|
|
@ -319,6 +319,7 @@ export default function GenerateSingleArticleForm(props: {
|
||||||
<div className="flex flex-row gap-2 items-center">
|
<div className="flex flex-row gap-2 items-center">
|
||||||
<p className="text-sm">Main Keyword</p>
|
<p className="text-sm">Main Keyword</p>
|
||||||
<Button
|
<Button
|
||||||
|
type="button"
|
||||||
variant="default"
|
variant="default"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => generateAll(mainKeyword)}
|
onClick={() => generateAll(mainKeyword)}
|
||||||
|
|
@ -350,6 +351,7 @@ export default function GenerateSingleArticleForm(props: {
|
||||||
<div className="flex flex-row gap-2 items-center mt-3">
|
<div className="flex flex-row gap-2 items-center mt-3">
|
||||||
<p className="text-sm">Title</p>
|
<p className="text-sm">Title</p>
|
||||||
<Button
|
<Button
|
||||||
|
type="button"
|
||||||
variant="default"
|
variant="default"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => generateTitle(mainKeyword)}
|
onClick={() => generateTitle(mainKeyword)}
|
||||||
|
|
@ -373,6 +375,7 @@ export default function GenerateSingleArticleForm(props: {
|
||||||
<div className="flex flex-row gap-2 items-center mt-2">
|
<div className="flex flex-row gap-2 items-center mt-2">
|
||||||
<p className="text-sm">Additional Keyword</p>
|
<p className="text-sm">Additional Keyword</p>
|
||||||
<Button
|
<Button
|
||||||
|
type="button"
|
||||||
className="text-sm"
|
className="text-sm"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => generateKeywords(mainKeyword)}
|
onClick={() => generateKeywords(mainKeyword)}
|
||||||
|
|
@ -417,6 +420,7 @@ export default function GenerateSingleArticleForm(props: {
|
||||||
<div className="flex flex-row gap-1 mt-2">
|
<div className="flex flex-row gap-1 mt-2">
|
||||||
{articleIds.map((id, index) => (
|
{articleIds.map((id, index) => (
|
||||||
<Button
|
<Button
|
||||||
|
type="button"
|
||||||
key={id}
|
key={id}
|
||||||
onClick={() => setSelectedId(id)}
|
onClick={() => setSelectedId(id)}
|
||||||
disabled={isLoading && selectedId === id}
|
disabled={isLoading && selectedId === id}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { useEffect, useState } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { Calendar } from "lucide-react";
|
import { Calendar } from "lucide-react";
|
||||||
import { getListArticle } from "@/service/article";
|
import { getListArticle } from "@/service/article";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
type Article = {
|
type Article = {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
@ -83,58 +84,60 @@ export default function BreakingNews() {
|
||||||
<div className="w-9/12">
|
<div className="w-9/12">
|
||||||
{mainArticle && (
|
{mainArticle && (
|
||||||
<div className="">
|
<div className="">
|
||||||
<div className="grid md:grid-cols-2 gap-6 items-center">
|
<Link href={`/detail/${mainArticle?.id}`}>
|
||||||
{/* LEFT - Image */}
|
<div className="grid md:grid-cols-2 gap-6 items-center">
|
||||||
<div className="relative w-full h-[220px] md:h-[300px] overflow-hidden">
|
{/* LEFT - Image */}
|
||||||
<Image
|
<div className="relative w-full h-[220px] md:h-[300px] overflow-hidden">
|
||||||
src={
|
<Image
|
||||||
mainArticle.thumbnailUrl ||
|
src={
|
||||||
mainArticle.files?.[0]?.fileUrl ||
|
mainArticle.thumbnailUrl ||
|
||||||
"/placeholder.png"
|
mainArticle.files?.[0]?.fileUrl ||
|
||||||
}
|
"/placeholder.png"
|
||||||
alt={mainArticle.title}
|
}
|
||||||
fill
|
alt={mainArticle.title}
|
||||||
className="object-cover"
|
fill
|
||||||
/>
|
className="object-cover"
|
||||||
</div>
|
/>
|
||||||
|
|
||||||
{/* RIGHT - Content */}
|
|
||||||
<div className="flex flex-col justify-center">
|
|
||||||
<span className="bg-yellow-400 text-black px-2 py-1 text-xs font-bold inline-block mb-3 w-fit">
|
|
||||||
{mainArticle.categoryName?.toUpperCase()}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<h2 className="text-xl md:text-2xl font-bold mb-2 leading-snug hover:underline cursor-pointer">
|
|
||||||
{mainArticle.title}
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div className="flex items-center gap-4 text-xs text-gray-500 mb-3">
|
|
||||||
<span className="font-semibold text-blue-600">
|
|
||||||
{mainArticle.createdByName}
|
|
||||||
</span>
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<Calendar size={14} />
|
|
||||||
{new Date(mainArticle.createdAt).toLocaleDateString(
|
|
||||||
"en-GB",
|
|
||||||
{
|
|
||||||
day: "2-digit",
|
|
||||||
month: "long",
|
|
||||||
year: "numeric",
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<span>0</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-sm text-gray-600 mb-4 line-clamp-3">
|
{/* RIGHT - Content */}
|
||||||
{mainArticle.description}
|
<div className="flex flex-col justify-center">
|
||||||
</p>
|
<span className="bg-yellow-400 text-black px-2 py-1 text-xs font-bold inline-block mb-3 w-fit">
|
||||||
|
{mainArticle.categoryName?.toUpperCase()}
|
||||||
|
</span>
|
||||||
|
|
||||||
<button className="border border-gray-400 px-4 py-2 text-xs font-semibold hover:bg-black hover:text-white transition w-fit">
|
<h2 className="text-xl md:text-2xl font-bold mb-2 leading-snug hover:underline cursor-pointer">
|
||||||
READ MORE
|
{mainArticle.title}
|
||||||
</button>
|
</h2>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-4 text-xs text-gray-500 mb-3">
|
||||||
|
<span className="font-semibold text-blue-600">
|
||||||
|
{mainArticle.createdByName}
|
||||||
|
</span>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<Calendar size={14} />
|
||||||
|
{new Date(mainArticle.createdAt).toLocaleDateString(
|
||||||
|
"en-GB",
|
||||||
|
{
|
||||||
|
day: "2-digit",
|
||||||
|
month: "long",
|
||||||
|
year: "numeric",
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span>0</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-sm text-gray-600 mb-4 line-clamp-3">
|
||||||
|
{mainArticle.description}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<button className="border border-gray-400 px-4 py-2 text-xs font-semibold hover:bg-black hover:text-white transition w-fit">
|
||||||
|
READ MORE
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="relative mb-2 h-[120px] overflow-hidden flex items-center border my-8">
|
<div className="relative mb-2 h-[120px] overflow-hidden flex items-center border my-8">
|
||||||
|
|
@ -165,33 +168,38 @@ export default function BreakingNews() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={article.id} className="group">
|
<div key={article.id} className="group">
|
||||||
{/* Image */}
|
<Link href={`/detail/${article?.id}`}>
|
||||||
<div className="relative w-full h-[180px] overflow-hidden mb-3">
|
{/* Image */}
|
||||||
<Image
|
<div className="relative w-full h-[180px] overflow-hidden mb-3">
|
||||||
src={imageUrl}
|
<Image
|
||||||
alt={article.title}
|
src={imageUrl}
|
||||||
fill
|
alt={article.title}
|
||||||
className="object-cover group-hover:scale-105 transition-transform duration-300"
|
fill
|
||||||
/>
|
className="object-cover group-hover:scale-105 transition-transform duration-300"
|
||||||
<span className="absolute bottom-2 left-2 bg-yellow-400 text-black px-2 py-1 text-[10px] font-bold">
|
/>
|
||||||
{category.toUpperCase()}
|
<span className="absolute bottom-2 left-2 bg-yellow-400 text-black px-2 py-1 text-[10px] font-bold">
|
||||||
</span>
|
{category.toUpperCase()}
|
||||||
</div>
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Title */}
|
{/* Title */}
|
||||||
<h3 className="font-semibold text-sm md:text-base mb-2 leading-snug group-hover:underline cursor-pointer">
|
<h3 className="font-semibold text-sm md:text-base mb-2 leading-snug group-hover:underline cursor-pointer">
|
||||||
{article.title}
|
{article.title}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
{/* Date */}
|
{/* Date */}
|
||||||
<div className="flex items-center gap-2 text-xs text-gray-500">
|
<div className="flex items-center gap-2 text-xs text-gray-500">
|
||||||
<Calendar size={14} />
|
<Calendar size={14} />
|
||||||
{new Date(article.createdAt).toLocaleDateString("en-GB", {
|
{new Date(article.createdAt).toLocaleDateString(
|
||||||
day: "2-digit",
|
"en-GB",
|
||||||
month: "long",
|
{
|
||||||
year: "numeric",
|
day: "2-digit",
|
||||||
})}
|
month: "long",
|
||||||
</div>
|
year: "numeric",
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
@ -219,48 +227,50 @@ export default function BreakingNews() {
|
||||||
"/placeholder.png";
|
"/placeholder.png";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div key={article.id}>
|
||||||
key={article.id}
|
<Link
|
||||||
className="grid grid-cols-1 md:grid-cols-3 gap-4"
|
className="grid grid-cols-1 md:grid-cols-3 gap-4"
|
||||||
>
|
href={`/detail/${article?.id}`}
|
||||||
{/* LEFT - Image */}
|
>
|
||||||
<div className="relative w-full h-[180px] md:h-[140px] rounded-md overflow-hidden md:col-span-1">
|
{/* LEFT - Image */}
|
||||||
<Image
|
<div className="relative w-full h-[180px] md:h-[140px] rounded-md overflow-hidden md:col-span-1">
|
||||||
src={imageUrl}
|
<Image
|
||||||
alt={article.title}
|
src={imageUrl}
|
||||||
fill
|
alt={article.title}
|
||||||
className="object-cover"
|
fill
|
||||||
/>
|
className="object-cover"
|
||||||
</div>
|
/>
|
||||||
|
|
||||||
{/* RIGHT - Content */}
|
|
||||||
<div className="md:col-span-2 flex flex-col justify-center">
|
|
||||||
<h3 className="font-bold text-lg md:text-xl mb-2 leading-snug hover:underline cursor-pointer">
|
|
||||||
{article.title}
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<div className="flex items-center gap-4 text-xs text-gray-500 mb-2">
|
|
||||||
<span className="font-semibold text-blue-600">
|
|
||||||
{article.createdByName || "DODDODYDOD"}
|
|
||||||
</span>
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<Calendar size={14} />
|
|
||||||
{new Date(article.createdAt).toLocaleDateString(
|
|
||||||
"en-GB",
|
|
||||||
{
|
|
||||||
day: "2-digit",
|
|
||||||
month: "long",
|
|
||||||
year: "numeric",
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<span>0</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-sm text-gray-600 line-clamp-2">
|
{/* RIGHT - Content */}
|
||||||
{article.description}
|
<div className="md:col-span-2 flex flex-col justify-center">
|
||||||
</p>
|
<h3 className="font-bold text-lg md:text-xl mb-2 leading-snug hover:underline cursor-pointer">
|
||||||
</div>
|
{article.title}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-4 text-xs text-gray-500 mb-2">
|
||||||
|
<span className="font-semibold text-blue-600">
|
||||||
|
{article.createdByName || "DODDODYDOD"}
|
||||||
|
</span>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<Calendar size={14} />
|
||||||
|
{new Date(article.createdAt).toLocaleDateString(
|
||||||
|
"en-GB",
|
||||||
|
{
|
||||||
|
day: "2-digit",
|
||||||
|
month: "long",
|
||||||
|
year: "numeric",
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span>0</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-sm text-gray-600 line-clamp-2">
|
||||||
|
{article.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
@ -291,31 +301,36 @@ export default function BreakingNews() {
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{sideArticles.map((a) => (
|
{sideArticles.map((a) => (
|
||||||
<div key={a.id} className="flex gap-3 items-center">
|
<div key={a.id} className="flex gap-3 items-center">
|
||||||
<div className="relative w-20 h-16 flex-shrink-0 rounded overflow-hidden">
|
<Link
|
||||||
<Image
|
className="flex gap-3 items-center"
|
||||||
src={
|
href={`/detail/${a?.id}`}
|
||||||
a.thumbnailUrl ||
|
>
|
||||||
a.files?.[0]?.fileUrl ||
|
<div className="relative w-20 h-16 flex-shrink-0 rounded overflow-hidden">
|
||||||
"/placeholder.png"
|
<Image
|
||||||
}
|
src={
|
||||||
alt={a.title}
|
a.thumbnailUrl ||
|
||||||
fill
|
a.files?.[0]?.fileUrl ||
|
||||||
className="object-cover"
|
"/placeholder.png"
|
||||||
/>
|
}
|
||||||
</div>
|
alt={a.title}
|
||||||
<div className="flex-1">
|
fill
|
||||||
<h4 className="text-sm font-semibold line-clamp-2 hover:underline">
|
className="object-cover"
|
||||||
{a.title}
|
/>
|
||||||
</h4>
|
|
||||||
<div className="flex items-center gap-1 text-xs text-gray-500 mt-1">
|
|
||||||
<Calendar size={12} />
|
|
||||||
{new Date(a.createdAt).toLocaleDateString("en-GB", {
|
|
||||||
day: "2-digit",
|
|
||||||
month: "long",
|
|
||||||
year: "numeric",
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="flex-1">
|
||||||
|
<h4 className="text-sm font-semibold line-clamp-2 hover:underline">
|
||||||
|
{a.title}
|
||||||
|
</h4>
|
||||||
|
<div className="flex items-center gap-1 text-xs text-gray-500 mt-1">
|
||||||
|
<Calendar size={12} />
|
||||||
|
{new Date(a.createdAt).toLocaleDateString("en-GB", {
|
||||||
|
day: "2-digit",
|
||||||
|
month: "long",
|
||||||
|
year: "numeric",
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -365,48 +380,55 @@ export default function BreakingNews() {
|
||||||
{/* Artikel Utama (Top 1) */}
|
{/* Artikel Utama (Top 1) */}
|
||||||
{popularArticles.length > 0 && (
|
{popularArticles.length > 0 && (
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<div className="relative w-full h-[220px] md:h-[280px] rounded overflow-hidden">
|
<Link href={`/detail/${popularArticles[0]?.id}`}>
|
||||||
<Image
|
<div className="relative w-full h-[220px] md:h-[280px] rounded overflow-hidden">
|
||||||
src={
|
<Image
|
||||||
popularArticles[0].thumbnailUrl ||
|
src={
|
||||||
popularArticles[0].files?.[0]?.fileUrl ||
|
popularArticles[0].thumbnailUrl ||
|
||||||
"/placeholder.png"
|
popularArticles[0].files?.[0]?.fileUrl ||
|
||||||
}
|
"/placeholder.png"
|
||||||
alt={popularArticles[0].title}
|
}
|
||||||
fill
|
alt={popularArticles[0].title}
|
||||||
className="object-cover"
|
fill
|
||||||
/>
|
className="object-cover"
|
||||||
</div>
|
/>
|
||||||
<div className="flex items-start justify-between mt-3">
|
</div>
|
||||||
<h3 className="font-bold text-base md:text-lg leading-snug max-w-[85%]">
|
<div className="flex items-start justify-between mt-3">
|
||||||
{popularArticles[0].title}
|
<h3 className="font-bold text-base md:text-lg leading-snug max-w-[85%]">
|
||||||
</h3>
|
{popularArticles[0].title}
|
||||||
<span className="text-3xl font-bold text-gray-300">01</span>
|
</h3>
|
||||||
</div>
|
<span className="text-3xl font-bold text-gray-300">01</span>
|
||||||
<div className="flex items-center gap-2 text-xs text-gray-500 mt-1">
|
</div>
|
||||||
<span>154 Shares</span>
|
<div className="flex items-center gap-2 text-xs text-gray-500 mt-1">
|
||||||
</div>
|
<span>154 Shares</span>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Artikel 2 - 5 */}
|
{/* Artikel 2 - 5 */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{popularArticles.slice(1, 5).map((a, i) => (
|
{popularArticles.slice(1, 5).map((a, i) => (
|
||||||
<div key={a.id} className="flex items-center gap-3">
|
<div key={a.id}>
|
||||||
{/* Nomor Urut */}
|
<Link
|
||||||
<div className="w-10 h-10 flex items-center justify-center rounded-full border text-sm font-bold text-gray-500">
|
className="flex items-center gap-3"
|
||||||
{String(i + 2).padStart(2, "0")}
|
href={`/detail/${a?.id}`}
|
||||||
</div>
|
>
|
||||||
|
{/* Nomor Urut */}
|
||||||
{/* Judul & Info */}
|
<div className="w-10 h-10 flex items-center justify-center rounded-full border text-sm font-bold text-gray-500">
|
||||||
<div className="flex-1">
|
{String(i + 2).padStart(2, "0")}
|
||||||
<h4 className="text-sm font-semibold line-clamp-2 hover:underline">
|
|
||||||
{a.title}
|
|
||||||
</h4>
|
|
||||||
<div className="flex items-center gap-2 text-xs text-gray-400 mt-1">
|
|
||||||
<span>{154 - i} Shares</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
{/* Judul & Info */}
|
||||||
|
<div className="flex-1">
|
||||||
|
<h4 className="text-sm font-semibold line-clamp-2 hover:underline">
|
||||||
|
{a.title}
|
||||||
|
</h4>
|
||||||
|
<div className="flex items-center gap-2 text-xs text-gray-400 mt-1">
|
||||||
|
<span>{154 - i} Shares</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { useEffect, useState } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { Calendar } from "lucide-react";
|
import { Calendar } from "lucide-react";
|
||||||
import { getListArticle } from "@/service/article";
|
import { getListArticle } from "@/service/article";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
type Article = {
|
type Article = {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
@ -72,38 +73,40 @@ export default function Header() {
|
||||||
key={article.id}
|
key={article.id}
|
||||||
className="relative h-[438px] md:h-[438px] overflow-hidden"
|
className="relative h-[438px] md:h-[438px] overflow-hidden"
|
||||||
>
|
>
|
||||||
{/* Background Image */}
|
<Link href={`/detail/${article?.id}`}>
|
||||||
<Image
|
{/* Background Image */}
|
||||||
src={imageUrl}
|
<Image
|
||||||
alt={article.title}
|
src={imageUrl}
|
||||||
fill
|
alt={article.title}
|
||||||
className="object-cover"
|
fill
|
||||||
/>
|
className="object-cover"
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Overlay Warna */}
|
{/* Overlay Warna */}
|
||||||
<div
|
<div
|
||||||
className={`absolute inset-0 ${
|
className={`absolute inset-0 ${
|
||||||
overlayColors[idx] || "bg-black/50"
|
overlayColors[idx] || "bg-black/50"
|
||||||
}`}
|
}`}
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="absolute inset-0 flex flex-col items-center justify-center p-6 text-white text-center">
|
<div className="absolute inset-0 flex flex-col items-center justify-center p-6 text-white text-center">
|
||||||
<span className="bg-yellow-400 text-black px-2 py-1 text-xs font-bold inline-block mb-3">
|
<span className="bg-yellow-400 text-black px-2 py-1 text-xs font-bold inline-block mb-3">
|
||||||
{category.toUpperCase()}
|
{category.toUpperCase()}
|
||||||
</span>
|
</span>
|
||||||
<h3 className="text-lg md:text-2xl font-bold mb-3 leading-snug">
|
<h3 className="text-lg md:text-2xl font-bold mb-3 leading-snug">
|
||||||
{article.title}
|
{article.title}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="flex items-center gap-2 text-xs md:text-sm text-gray-200">
|
<div className="flex items-center gap-2 text-xs md:text-sm text-gray-200">
|
||||||
<Calendar size={14} />
|
<Calendar size={14} />
|
||||||
{new Date(article.createdAt).toLocaleDateString("en-GB", {
|
{new Date(article.createdAt).toLocaleDateString("en-GB", {
|
||||||
day: "2-digit",
|
day: "2-digit",
|
||||||
month: "long",
|
month: "long",
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
})}
|
})}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
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";
|
||||||
|
import { usePathname } from "next/navigation";
|
||||||
|
|
||||||
type Article = {
|
type Article = {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
@ -24,7 +26,19 @@ type Article = {
|
||||||
export default function BumnNews() {
|
export default function BumnNews() {
|
||||||
const [articles, setArticles] = useState<Article[]>([]);
|
const [articles, setArticles] = useState<Article[]>([]);
|
||||||
const [popular, setPopular] = useState<Article[]>([]);
|
const [popular, setPopular] = useState<Article[]>([]);
|
||||||
const [categoryName, setCategoryName] = useState("Bumn News");
|
const pathname = usePathname(); // ⬅️ "/bumn-news" misalnya
|
||||||
|
const categoryName =
|
||||||
|
pathname
|
||||||
|
.split("/")
|
||||||
|
.filter(Boolean)
|
||||||
|
.pop()
|
||||||
|
?.replace(/-/g, " ") // ubah slug jadi teks rapi
|
||||||
|
?.replace(/\b\w/g, (c) => c.toUpperCase()) || "News";
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchArticles();
|
||||||
|
fetchPopular();
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchArticles();
|
fetchArticles();
|
||||||
|
|
@ -84,30 +98,35 @@ export default function BumnNews() {
|
||||||
<h1 className="text-3xl font-bold">{categoryName}</h1>
|
<h1 className="text-3xl font-bold">{categoryName}</h1>
|
||||||
</div>
|
</div>
|
||||||
{articles.map((item) => (
|
{articles.map((item) => (
|
||||||
<div key={item.id} className="flex gap-4 border-b pb-4">
|
<div key={item.id}>
|
||||||
<Image
|
<Link
|
||||||
src={item.thumbnailUrl || "/dummy.jpg"}
|
className="flex gap-4 border-b pb-4"
|
||||||
alt={item.title}
|
href={`/detail/${item?.id}`}
|
||||||
width={160}
|
>
|
||||||
height={100}
|
<Image
|
||||||
className="object-cover rounded-md w-40 h-28"
|
src={item.thumbnailUrl || "/dummy.jpg"}
|
||||||
/>
|
alt={item.title}
|
||||||
<div className="flex-1">
|
width={160}
|
||||||
<h3 className="text-base font-semibold hover:text-blue-600 cursor-pointer">
|
height={100}
|
||||||
{item.title}
|
className="object-cover rounded-md w-40 h-28"
|
||||||
</h3>
|
/>
|
||||||
<p className="text-xs text-gray-500 mt-1">
|
<div className="flex-1">
|
||||||
By {item.createdByName} •{" "}
|
<h3 className="text-base font-semibold hover:text-blue-600 cursor-pointer">
|
||||||
{new Date(item.createdAt).toLocaleDateString("id-ID", {
|
{item.title}
|
||||||
day: "numeric",
|
</h3>
|
||||||
month: "long",
|
<p className="text-xs text-gray-500 mt-1">
|
||||||
year: "numeric",
|
By {item.createdByName} •{" "}
|
||||||
})}
|
{new Date(item.createdAt).toLocaleDateString("id-ID", {
|
||||||
</p>
|
day: "numeric",
|
||||||
<p className="text-sm text-gray-600 mt-2 line-clamp-2">
|
month: "long",
|
||||||
{item.description}
|
year: "numeric",
|
||||||
</p>
|
})}
|
||||||
</div>
|
</p>
|
||||||
|
<p className="text-sm text-gray-600 mt-2 line-clamp-2">
|
||||||
|
{item.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -147,43 +166,47 @@ export default function BumnNews() {
|
||||||
<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 href={`/detail/${popular[0]?.id}`}>
|
||||||
src={popular[0]?.files?.[0]?.fileUrl || "/dummy.jpg"}
|
<Image
|
||||||
alt={
|
src={popular[0]?.files?.[0]?.fileUrl || "/dummy.jpg"}
|
||||||
popular[0]?.files?.[0]?.file_alt ||
|
alt={
|
||||||
popular[0]?.title ||
|
popular[0]?.files?.[0]?.file_alt ||
|
||||||
"No Title"
|
popular[0]?.title ||
|
||||||
}
|
"No Title"
|
||||||
width={400}
|
}
|
||||||
height={200}
|
width={400}
|
||||||
className="w-full h-48 object-cover rounded-md"
|
height={200}
|
||||||
/>
|
className="w-full h-48 object-cover rounded-md"
|
||||||
<div className="mt-2">
|
/>
|
||||||
<h5 className="text-sm font-semibold hover:text-blue-600 cursor-pointer">
|
<div className="mt-2">
|
||||||
{popular[0]?.title}
|
<h5 className="text-sm font-semibold hover:text-blue-600 cursor-pointer">
|
||||||
</h5>
|
{popular[0]?.title}
|
||||||
<span className="absolute top-2 right-2 text-4xl font-bold text-gray-300/80">
|
</h5>
|
||||||
01
|
<span className="absolute top-2 right-2 text-4xl font-bold text-gray-300/80">
|
||||||
</span>
|
01
|
||||||
</div>
|
</span>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Item sisanya */}
|
{/* Item sisanya */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{popular.slice(1).map((item, i) => (
|
{popular.slice(1).map((item, i) => (
|
||||||
<div
|
<div key={item.id}>
|
||||||
key={item.id}
|
<Link
|
||||||
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"
|
||||||
>
|
href={`/detail/${item?.id}`}
|
||||||
<span className="text-lg font-bold text-gray-400">
|
>
|
||||||
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>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue