2025-09-16 08:29:07 +00:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import { useState, useEffect } from "react";
|
|
|
|
|
import Image from "next/image";
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
import { ThumbsUp, ThumbsDown } from "lucide-react";
|
|
|
|
|
import { Card } from "../ui/card";
|
|
|
|
|
import Link from "next/link";
|
2025-10-12 14:33:30 +00:00
|
|
|
import { useRouter } from "next/navigation";
|
2025-09-17 02:47:48 +00:00
|
|
|
import { listData, listArticles } from "@/service/landing/landing";
|
2025-10-10 16:30:17 +00:00
|
|
|
import { toggleBookmark, getBookmarkSummaryForUser } from "@/service/content";
|
|
|
|
|
import { getCookiesDecrypt } from "@/lib/utils";
|
2025-09-16 08:29:07 +00:00
|
|
|
import { Swiper, SwiperSlide } from "swiper/react";
|
|
|
|
|
import "swiper/css";
|
|
|
|
|
import "swiper/css/navigation";
|
|
|
|
|
import { Navigation } from "swiper/modules";
|
2025-10-10 16:30:17 +00:00
|
|
|
import Swal from "sweetalert2";
|
|
|
|
|
import withReactContent from "sweetalert2-react-content";
|
2025-09-16 08:29:07 +00:00
|
|
|
|
|
|
|
|
// Format tanggal
|
|
|
|
|
function formatTanggal(dateString: string) {
|
|
|
|
|
if (!dateString) return "";
|
|
|
|
|
return (
|
|
|
|
|
new Date(dateString)
|
|
|
|
|
.toLocaleString("id-ID", {
|
|
|
|
|
day: "2-digit",
|
|
|
|
|
month: "short",
|
|
|
|
|
year: "numeric",
|
|
|
|
|
hour: "2-digit",
|
|
|
|
|
minute: "2-digit",
|
|
|
|
|
hour12: false,
|
|
|
|
|
timeZone: "Asia/Jakarta",
|
|
|
|
|
})
|
|
|
|
|
.replace(/\./g, ":") + " WIB"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function MediaUpdate() {
|
|
|
|
|
const [tab, setTab] = useState<"latest" | "popular">("latest");
|
2025-10-12 14:57:46 +00:00
|
|
|
const [contentType, setContentType] = useState<"audiovisual" | "audio" | "foto" | "text">("foto");
|
2025-09-16 08:29:07 +00:00
|
|
|
const [dataToRender, setDataToRender] = useState<any[]>([]);
|
2025-10-12 14:33:30 +00:00
|
|
|
const [filteredData, setFilteredData] = useState<any[]>([]);
|
2025-10-10 16:30:17 +00:00
|
|
|
const [bookmarkedIds, setBookmarkedIds] = useState<Set<number>>(new Set());
|
2025-09-16 08:29:07 +00:00
|
|
|
const [loading, setLoading] = useState(true);
|
2025-10-12 14:33:30 +00:00
|
|
|
const [currentTypeId, setCurrentTypeId] = useState<string>("1");
|
|
|
|
|
const router = useRouter();
|
2025-10-10 16:30:17 +00:00
|
|
|
const MySwal = withReactContent(Swal);
|
2025-09-16 08:29:07 +00:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
fetchData(tab);
|
|
|
|
|
}, [tab]);
|
|
|
|
|
|
2025-10-12 14:33:30 +00:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (contentType !== "all") {
|
|
|
|
|
fetchData(tab);
|
|
|
|
|
} else {
|
|
|
|
|
filterDataByContentType();
|
|
|
|
|
}
|
|
|
|
|
}, [contentType]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
filterDataByContentType();
|
|
|
|
|
}, [dataToRender]);
|
|
|
|
|
|
|
|
|
|
// Function to get typeId based on content type
|
|
|
|
|
function getTypeIdByContentType(contentType: string): string {
|
|
|
|
|
switch (contentType) {
|
|
|
|
|
case "audiovisual":
|
|
|
|
|
return "2"; // Video
|
|
|
|
|
case "foto":
|
|
|
|
|
return "1"; // Image
|
|
|
|
|
case "audio":
|
|
|
|
|
return "4"; // Audio
|
|
|
|
|
case "text":
|
|
|
|
|
return "3"; // Text
|
|
|
|
|
default:
|
|
|
|
|
return "1"; // Default to Image
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Function to get link based on typeId (same as header.tsx)
|
|
|
|
|
function getLink(item: any) {
|
|
|
|
|
switch (item?.typeId) {
|
|
|
|
|
case 1:
|
|
|
|
|
return `/content/image/detail/${item?.id}`;
|
|
|
|
|
case 2:
|
|
|
|
|
return `/content/video/detail/${item?.id}`;
|
|
|
|
|
case 3:
|
|
|
|
|
return `/content/text/detail/${item?.id}`;
|
|
|
|
|
case 4:
|
|
|
|
|
return `/content/audio/detail/${item?.id}`;
|
|
|
|
|
default:
|
|
|
|
|
return "#";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-12 14:57:46 +00:00
|
|
|
// Function to get content type link for "Lihat lebih banyak" button
|
|
|
|
|
function getContentTypeLink() {
|
|
|
|
|
switch (contentType) {
|
|
|
|
|
case "audio":
|
|
|
|
|
return "/content/audio";
|
|
|
|
|
case "foto":
|
|
|
|
|
return "/content/image";
|
|
|
|
|
case "audiovisual":
|
|
|
|
|
return "/content/video";
|
|
|
|
|
case "text":
|
|
|
|
|
return "/content/text";
|
|
|
|
|
case "all":
|
|
|
|
|
default:
|
|
|
|
|
return "/content/image"; // Default to image page
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-12 14:33:30 +00:00
|
|
|
// Function to filter data by content type
|
|
|
|
|
function filterDataByContentType() {
|
|
|
|
|
if (contentType === "all") {
|
|
|
|
|
setFilteredData(dataToRender);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const filtered = dataToRender.filter((item) => {
|
|
|
|
|
// Determine content type based on item properties
|
|
|
|
|
const hasVideo = item.videoUrl || item.videoPath;
|
|
|
|
|
const hasAudio = item.audioUrl || item.audioPath;
|
|
|
|
|
const hasImage = item.smallThumbnailLink || item.thumbnailUrl || item.imageUrl;
|
|
|
|
|
const hasText = item.content || item.description;
|
|
|
|
|
|
|
|
|
|
switch (contentType) {
|
|
|
|
|
case "audiovisual":
|
|
|
|
|
return hasVideo && (hasAudio || hasImage);
|
|
|
|
|
case "audio":
|
|
|
|
|
return hasAudio && !hasVideo;
|
|
|
|
|
case "foto":
|
|
|
|
|
return hasImage && !hasVideo && !hasAudio;
|
|
|
|
|
case "text":
|
|
|
|
|
return hasText && !hasVideo && !hasAudio && !hasImage;
|
|
|
|
|
default:
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
setFilteredData(filtered);
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-16 08:29:07 +00:00
|
|
|
async function fetchData(section: "latest" | "popular") {
|
|
|
|
|
try {
|
|
|
|
|
setLoading(true);
|
2025-10-10 16:30:17 +00:00
|
|
|
|
2025-10-12 14:33:30 +00:00
|
|
|
// Determine typeId based on contentType
|
|
|
|
|
const typeId = contentType === "all" ? 1 : parseInt(getTypeIdByContentType(contentType));
|
|
|
|
|
setCurrentTypeId(typeId.toString());
|
|
|
|
|
|
2025-10-10 16:30:17 +00:00
|
|
|
// 🔹 Ambil data artikel
|
2025-09-17 02:47:48 +00:00
|
|
|
const response = await listArticles(
|
2025-10-10 16:30:17 +00:00
|
|
|
1,
|
|
|
|
|
20,
|
2025-10-12 14:33:30 +00:00
|
|
|
typeId, // Dynamic typeId based on content type
|
2025-10-10 16:30:17 +00:00
|
|
|
undefined,
|
|
|
|
|
undefined,
|
2025-09-17 02:47:48 +00:00
|
|
|
section === "latest" ? "createdAt" : "viewCount"
|
2025-09-16 08:29:07 +00:00
|
|
|
);
|
2025-10-10 16:30:17 +00:00
|
|
|
|
|
|
|
|
let articlesData: any[] = [];
|
|
|
|
|
|
2025-09-17 02:47:48 +00:00
|
|
|
if (response?.error) {
|
2025-10-10 16:30:17 +00:00
|
|
|
console.error("Articles API failed, fallback ke old API");
|
2025-09-17 02:47:48 +00:00
|
|
|
const fallbackRes = await listData(
|
2025-10-12 14:33:30 +00:00
|
|
|
typeId.toString(),
|
2025-09-17 02:47:48 +00:00
|
|
|
"",
|
|
|
|
|
"",
|
|
|
|
|
20,
|
|
|
|
|
0,
|
|
|
|
|
section === "latest" ? "createdAt" : "clickCount",
|
|
|
|
|
"",
|
|
|
|
|
"",
|
|
|
|
|
""
|
|
|
|
|
);
|
2025-10-10 16:30:17 +00:00
|
|
|
articlesData = fallbackRes?.data?.data?.content || [];
|
|
|
|
|
} else {
|
|
|
|
|
articlesData = response?.data?.data || [];
|
2025-09-17 02:47:48 +00:00
|
|
|
}
|
|
|
|
|
|
2025-10-10 16:30:17 +00:00
|
|
|
// 🔹 Normalisasi struktur data
|
2025-09-17 02:47:48 +00:00
|
|
|
const transformedData = articlesData.map((article: any) => ({
|
|
|
|
|
id: article.id,
|
|
|
|
|
title: article.title,
|
2025-10-10 16:30:17 +00:00
|
|
|
category:
|
|
|
|
|
article.categoryName ||
|
|
|
|
|
(article.categories && article.categories[0]?.title) ||
|
|
|
|
|
"Tanpa Kategori",
|
2025-09-17 02:47:48 +00:00
|
|
|
createdAt: article.createdAt,
|
|
|
|
|
smallThumbnailLink: article.thumbnailUrl,
|
2025-10-12 14:33:30 +00:00
|
|
|
label: article.categoryName,
|
2025-10-10 16:30:17 +00:00
|
|
|
...article,
|
2025-09-17 02:47:48 +00:00
|
|
|
}));
|
2025-10-10 16:30:17 +00:00
|
|
|
|
2025-09-17 02:47:48 +00:00
|
|
|
setDataToRender(transformedData);
|
2025-10-10 16:30:17 +00:00
|
|
|
|
|
|
|
|
// 🔹 Sinkronisasi bookmark
|
|
|
|
|
const roleId = Number(getCookiesDecrypt("urie"));
|
|
|
|
|
if (roleId && !isNaN(roleId)) {
|
|
|
|
|
const savedLocal = localStorage.getItem("bookmarkedIds");
|
|
|
|
|
let localSet = new Set<number>();
|
|
|
|
|
if (savedLocal) {
|
|
|
|
|
localSet = new Set(JSON.parse(savedLocal));
|
|
|
|
|
setBookmarkedIds(localSet);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const res = await getBookmarkSummaryForUser();
|
|
|
|
|
const bookmarks =
|
|
|
|
|
res?.data?.data?.recentBookmarks ||
|
|
|
|
|
res?.data?.data?.bookmarks ||
|
|
|
|
|
res?.data?.data ||
|
|
|
|
|
[];
|
|
|
|
|
|
|
|
|
|
const ids = new Set<number>(
|
|
|
|
|
(Array.isArray(bookmarks) ? bookmarks : [])
|
|
|
|
|
.map((b: any) => Number(b.articleId ?? b.id ?? b.article?.id))
|
|
|
|
|
.filter((x) => !isNaN(x))
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const merged = new Set([...localSet, ...ids]);
|
|
|
|
|
setBookmarkedIds(merged);
|
|
|
|
|
localStorage.setItem(
|
|
|
|
|
"bookmarkedIds",
|
|
|
|
|
JSON.stringify(Array.from(merged))
|
2025-09-17 02:47:48 +00:00
|
|
|
);
|
|
|
|
|
}
|
2025-10-10 16:30:17 +00:00
|
|
|
} catch (err) {
|
|
|
|
|
console.error("Gagal memuat data:", err);
|
2025-09-16 08:29:07 +00:00
|
|
|
} finally {
|
|
|
|
|
setLoading(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-10 16:30:17 +00:00
|
|
|
// 🔹 Simpan perubahan bookmark ke localStorage
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (bookmarkedIds.size > 0) {
|
|
|
|
|
localStorage.setItem(
|
|
|
|
|
"bookmarkedIds",
|
|
|
|
|
JSON.stringify(Array.from(bookmarkedIds))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}, [bookmarkedIds]);
|
|
|
|
|
|
|
|
|
|
const handleSave = async (id: number) => {
|
|
|
|
|
const roleId = Number(getCookiesDecrypt("urie"));
|
|
|
|
|
if (!roleId || isNaN(roleId)) {
|
|
|
|
|
MySwal.fire({
|
|
|
|
|
icon: "warning",
|
|
|
|
|
title: "Login diperlukan",
|
|
|
|
|
text: "Silakan login terlebih dahulu untuk menyimpan artikel.",
|
|
|
|
|
confirmButtonText: "Login Sekarang",
|
|
|
|
|
confirmButtonColor: "#d33",
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const res = await toggleBookmark(id);
|
|
|
|
|
if (res?.error) {
|
|
|
|
|
MySwal.fire({
|
|
|
|
|
icon: "error",
|
|
|
|
|
title: "Gagal",
|
|
|
|
|
text: "Gagal menyimpan artikel.",
|
|
|
|
|
confirmButtonColor: "#d33",
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
const updated = new Set(bookmarkedIds);
|
|
|
|
|
updated.add(Number(id));
|
|
|
|
|
setBookmarkedIds(updated);
|
|
|
|
|
localStorage.setItem(
|
|
|
|
|
"bookmarkedIds",
|
|
|
|
|
JSON.stringify(Array.from(updated))
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
MySwal.fire({
|
|
|
|
|
icon: "success",
|
|
|
|
|
title: "Berhasil",
|
|
|
|
|
text: "Artikel berhasil disimpan ke bookmark.",
|
|
|
|
|
timer: 1500,
|
|
|
|
|
showConfirmButton: false,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error("Error saving bookmark:", err);
|
|
|
|
|
MySwal.fire({
|
|
|
|
|
icon: "error",
|
|
|
|
|
title: "Kesalahan",
|
|
|
|
|
text: "Terjadi kesalahan saat menyimpan artikel.",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-09-16 08:29:07 +00:00
|
|
|
return (
|
|
|
|
|
<section className="bg-white px-4 py-10 border max-w-[1350px] mx-auto rounded-md border-[#CDD5DF] my-10">
|
|
|
|
|
<div className="max-w-screen-xl mx-auto">
|
|
|
|
|
<h2 className="text-2xl font-semibold text-center mb-6">
|
|
|
|
|
Media Update
|
|
|
|
|
</h2>
|
|
|
|
|
|
2025-10-12 14:33:30 +00:00
|
|
|
{/* Main Tab */}
|
|
|
|
|
<div className="flex justify-center mb-6 bg-white">
|
|
|
|
|
<Card className="bg-[#FFFFFF] rounded-xl flex flex-row p-3 gap-2 shadow-md border border-gray-200">
|
2025-09-16 08:29:07 +00:00
|
|
|
<button
|
|
|
|
|
onClick={() => setTab("latest")}
|
2025-10-12 14:33:30 +00:00
|
|
|
className={`px-6 py-3 rounded-lg text-sm font-semibold transition-all duration-200 ${
|
|
|
|
|
tab === "latest"
|
|
|
|
|
? "bg-[#C6A455] text-white shadow-sm"
|
|
|
|
|
: "text-[#C6A455] hover:bg-[#C6A455]/10"
|
2025-09-16 08:29:07 +00:00
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
Konten Terbaru
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setTab("popular")}
|
2025-10-12 14:33:30 +00:00
|
|
|
className={`px-6 py-3 rounded-lg text-sm font-semibold transition-all duration-200 ${
|
|
|
|
|
tab === "popular"
|
|
|
|
|
? "bg-[#C6A455] text-white shadow-sm"
|
|
|
|
|
: "text-[#C6A455] hover:bg-[#C6A455]/10"
|
2025-09-16 08:29:07 +00:00
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
Konten Terpopuler
|
|
|
|
|
</button>
|
|
|
|
|
</Card>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-10-12 14:33:30 +00:00
|
|
|
{/* Content Type Filter */}
|
|
|
|
|
<div className="flex justify-center mb-8">
|
|
|
|
|
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 rounded-2xl p-4 border-2 border-blue-100 shadow-lg">
|
|
|
|
|
<div className="flex flex-wrap justify-center gap-2">
|
2025-10-12 14:57:46 +00:00
|
|
|
{/* <button
|
2025-10-12 14:33:30 +00:00
|
|
|
onClick={() => setContentType("all")}
|
|
|
|
|
className={`px-4 py-2 rounded-full text-xs font-semibold transition-all duration-300 transform hover:scale-105 ${
|
|
|
|
|
contentType === "all"
|
|
|
|
|
? "bg-gradient-to-r from-blue-500 to-indigo-600 text-white shadow-lg ring-2 ring-blue-300"
|
|
|
|
|
: "bg-white text-blue-600 border-2 border-blue-200 hover:border-blue-400 hover:shadow-md"
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
📋 Semua
|
2025-10-12 14:57:46 +00:00
|
|
|
</button> */}
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setContentType("foto")}
|
|
|
|
|
className={`px-4 py-2 rounded-full text-xs font-semibold transition-all duration-300 transform hover:scale-105 ${
|
|
|
|
|
contentType === "foto"
|
|
|
|
|
? "bg-gradient-to-r from-orange-500 to-red-600 text-white shadow-lg ring-2 ring-orange-300"
|
|
|
|
|
: "bg-white text-orange-600 border-2 border-orange-200 hover:border-orange-400 hover:shadow-md"
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
📸 Foto
|
2025-10-12 14:33:30 +00:00
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setContentType("audiovisual")}
|
|
|
|
|
className={`px-4 py-2 rounded-full text-xs font-semibold transition-all duration-300 transform hover:scale-105 ${
|
|
|
|
|
contentType === "audiovisual"
|
|
|
|
|
? "bg-gradient-to-r from-purple-500 to-pink-600 text-white shadow-lg ring-2 ring-purple-300"
|
|
|
|
|
: "bg-white text-purple-600 border-2 border-purple-200 hover:border-purple-400 hover:shadow-md"
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
🎬 Audio Visual
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setContentType("audio")}
|
|
|
|
|
className={`px-4 py-2 rounded-full text-xs font-semibold transition-all duration-300 transform hover:scale-105 ${
|
|
|
|
|
contentType === "audio"
|
|
|
|
|
? "bg-gradient-to-r from-green-500 to-emerald-600 text-white shadow-lg ring-2 ring-green-300"
|
|
|
|
|
: "bg-white text-green-600 border-2 border-green-200 hover:border-green-400 hover:shadow-md"
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
🎵 Audio
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setContentType("text")}
|
|
|
|
|
className={`px-4 py-2 rounded-full text-xs font-semibold transition-all duration-300 transform hover:scale-105 ${
|
|
|
|
|
contentType === "text"
|
|
|
|
|
? "bg-gradient-to-r from-gray-500 to-slate-600 text-white shadow-lg ring-2 ring-gray-300"
|
|
|
|
|
: "bg-white text-gray-600 border-2 border-gray-200 hover:border-gray-400 hover:shadow-md"
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
📝 Text
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-09-16 08:29:07 +00:00
|
|
|
{/* Slider */}
|
|
|
|
|
{loading ? (
|
|
|
|
|
<p className="text-center">Loading...</p>
|
|
|
|
|
) : (
|
|
|
|
|
<Swiper
|
|
|
|
|
modules={[Navigation]}
|
|
|
|
|
navigation
|
|
|
|
|
spaceBetween={20}
|
|
|
|
|
slidesPerView={1}
|
|
|
|
|
breakpoints={{
|
|
|
|
|
640: { slidesPerView: 2 },
|
|
|
|
|
1024: { slidesPerView: 4 },
|
|
|
|
|
}}
|
|
|
|
|
>
|
2025-10-12 14:33:30 +00:00
|
|
|
{filteredData.map((item) => (
|
2025-09-16 08:29:07 +00:00
|
|
|
<SwiperSlide key={item.id}>
|
|
|
|
|
<div className="rounded-xl shadow-md overflow-hidden bg-white">
|
|
|
|
|
<div className="w-full h-[204px] relative">
|
2025-10-12 14:33:30 +00:00
|
|
|
<Link href={getLink(item)}>
|
|
|
|
|
<Image
|
|
|
|
|
src={item.smallThumbnailLink || "/placeholder.png"}
|
|
|
|
|
alt={item.title || "No Title"}
|
|
|
|
|
fill
|
|
|
|
|
className="object-cover cursor-pointer hover:opacity-90 transition-opacity"
|
|
|
|
|
/>
|
|
|
|
|
</Link>
|
2025-09-16 08:29:07 +00:00
|
|
|
</div>
|
|
|
|
|
<div className="p-3">
|
|
|
|
|
<div className="flex gap-2 mb-1">
|
|
|
|
|
<span className="text-xs text-white px-2 py-0.5 rounded bg-blue-600">
|
2025-10-12 14:33:30 +00:00
|
|
|
{item.clientName || "Tanpa Kategori"}
|
2025-09-16 08:29:07 +00:00
|
|
|
</span>
|
|
|
|
|
<span className="text-xs font-medium text-[#b3882e]">
|
|
|
|
|
{item.label || ""}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-xs text-gray-500 mb-1">
|
|
|
|
|
{formatTanggal(item.createdAt)}
|
|
|
|
|
</p>
|
2025-10-12 14:33:30 +00:00
|
|
|
<Link href={getLink(item)}>
|
|
|
|
|
<p className="text-sm font-semibold mb-3 line-clamp-2 cursor-pointer hover:text-blue-600 transition-colors">
|
|
|
|
|
{item.title}
|
|
|
|
|
</p>
|
|
|
|
|
</Link>
|
2025-09-16 08:29:07 +00:00
|
|
|
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
<div className="flex gap-2 text-gray-600">
|
|
|
|
|
<ThumbsUp className="w-4 h-4 cursor-pointer" />
|
|
|
|
|
<ThumbsDown className="w-4 h-4 cursor-pointer" />
|
|
|
|
|
</div>
|
|
|
|
|
<Button
|
2025-10-10 16:30:17 +00:00
|
|
|
onClick={() => handleSave(item.id)}
|
|
|
|
|
disabled={bookmarkedIds.has(Number(item.id))}
|
2025-09-16 08:29:07 +00:00
|
|
|
variant="default"
|
|
|
|
|
size="sm"
|
2025-10-10 16:30:17 +00:00
|
|
|
className={`rounded px-4 ${
|
|
|
|
|
bookmarkedIds.has(Number(item.id))
|
|
|
|
|
? "bg-gray-400 cursor-not-allowed text-white"
|
|
|
|
|
: "bg-blue-600 text-white hover:bg-blue-700"
|
|
|
|
|
}`}
|
2025-09-16 08:29:07 +00:00
|
|
|
>
|
2025-10-10 16:30:17 +00:00
|
|
|
{bookmarkedIds.has(Number(item.id)) ? "Saved" : "Save"}
|
2025-09-16 08:29:07 +00:00
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</SwiperSlide>
|
|
|
|
|
))}
|
|
|
|
|
</Swiper>
|
|
|
|
|
)}
|
|
|
|
|
|
2025-10-12 14:57:46 +00:00
|
|
|
{/* Lihat lebih banyak - hanya muncul jika ada data */}
|
|
|
|
|
{filteredData.length > 0 && (
|
|
|
|
|
<div className="text-center mt-10">
|
|
|
|
|
<Link
|
|
|
|
|
href={getContentTypeLink()}
|
2025-09-16 08:29:07 +00:00
|
|
|
>
|
2025-10-12 14:57:46 +00:00
|
|
|
<Button
|
|
|
|
|
size={"lg"}
|
|
|
|
|
className="text-[#b3882e] bg-transparent border border-[#b3882e] px-6 py-2 rounded-s-sm text-sm font-medium hover:bg-[#b3882e]/10 transition"
|
|
|
|
|
>
|
|
|
|
|
Lihat Lebih Banyak
|
|
|
|
|
</Button>
|
|
|
|
|
</Link>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2025-09-16 08:29:07 +00:00
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-10-10 16:30:17 +00:00
|
|
|
|
|
|
|
|
// "use client";
|
|
|
|
|
|
|
|
|
|
// import { useState, useEffect } from "react";
|
|
|
|
|
// import Image from "next/image";
|
|
|
|
|
// import { Button } from "@/components/ui/button";
|
|
|
|
|
// import { ThumbsUp, ThumbsDown } from "lucide-react";
|
|
|
|
|
// import { Card } from "../ui/card";
|
|
|
|
|
// import Link from "next/link";
|
|
|
|
|
// import { listData, listArticles } from "@/service/landing/landing";
|
|
|
|
|
// import { Swiper, SwiperSlide } from "swiper/react";
|
|
|
|
|
// import "swiper/css";
|
|
|
|
|
// import "swiper/css/navigation";
|
|
|
|
|
// import { Navigation } from "swiper/modules";
|
|
|
|
|
|
|
|
|
|
// // Format tanggal
|
|
|
|
|
// function formatTanggal(dateString: string) {
|
|
|
|
|
// if (!dateString) return "";
|
|
|
|
|
// return (
|
|
|
|
|
// new Date(dateString)
|
|
|
|
|
// .toLocaleString("id-ID", {
|
|
|
|
|
// day: "2-digit",
|
|
|
|
|
// month: "short",
|
|
|
|
|
// year: "numeric",
|
|
|
|
|
// hour: "2-digit",
|
|
|
|
|
// minute: "2-digit",
|
|
|
|
|
// hour12: false,
|
|
|
|
|
// timeZone: "Asia/Jakarta",
|
|
|
|
|
// })
|
|
|
|
|
// .replace(/\./g, ":") + " WIB"
|
|
|
|
|
// );
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// export default function MediaUpdate() {
|
|
|
|
|
// const [tab, setTab] = useState<"latest" | "popular">("latest");
|
|
|
|
|
// const [dataToRender, setDataToRender] = useState<any[]>([]);
|
|
|
|
|
// const [loading, setLoading] = useState(true);
|
|
|
|
|
|
|
|
|
|
// useEffect(() => {
|
|
|
|
|
// fetchData(tab);
|
|
|
|
|
// }, [tab]);
|
|
|
|
|
|
|
|
|
|
// async function fetchData(section: "latest" | "popular") {
|
|
|
|
|
// try {
|
|
|
|
|
// setLoading(true);
|
|
|
|
|
|
|
|
|
|
// // Use new Articles API
|
|
|
|
|
// const response = await listArticles(
|
|
|
|
|
// 1,
|
|
|
|
|
// 20,
|
|
|
|
|
// 1, // typeId for images
|
|
|
|
|
// undefined,
|
|
|
|
|
// undefined,
|
|
|
|
|
// section === "latest" ? "createdAt" : "viewCount"
|
|
|
|
|
// );
|
|
|
|
|
|
|
|
|
|
// console.log("Media Update Articles API response:", response);
|
|
|
|
|
|
|
|
|
|
// if (response?.error) {
|
|
|
|
|
// console.error("Articles API failed, falling back to old API");
|
|
|
|
|
// // Fallback to old API
|
|
|
|
|
// const fallbackRes = await listData(
|
|
|
|
|
// "1",
|
|
|
|
|
// "",
|
|
|
|
|
// "",
|
|
|
|
|
// 20,
|
|
|
|
|
// 0,
|
|
|
|
|
// section === "latest" ? "createdAt" : "clickCount",
|
|
|
|
|
// "",
|
|
|
|
|
// "",
|
|
|
|
|
// ""
|
|
|
|
|
// );
|
|
|
|
|
// setDataToRender(fallbackRes?.data?.data?.content || []);
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// // Handle new API response structure
|
|
|
|
|
// const articlesData = response?.data?.data || [];
|
|
|
|
|
|
|
|
|
|
// // Transform articles data to match old structure for backward compatibility
|
|
|
|
|
// const transformedData = articlesData.map((article: any) => ({
|
|
|
|
|
// id: article.id,
|
|
|
|
|
// title: article.title,
|
|
|
|
|
// category: article.categoryName || (article.categories && article.categories[0]?.title) || "Tanpa Kategori",
|
|
|
|
|
// createdAt: article.createdAt,
|
|
|
|
|
// smallThumbnailLink: article.thumbnailUrl,
|
|
|
|
|
// label: article.typeId === 1 ? "Image" : article.typeId === 2 ? "Video" : article.typeId === 3 ? "Text" : article.typeId === 4 ? "Audio" : "",
|
|
|
|
|
// ...article
|
|
|
|
|
// }));
|
|
|
|
|
|
|
|
|
|
// setDataToRender(transformedData);
|
|
|
|
|
// } catch (err) {
|
|
|
|
|
// console.error("Gagal memuat data:", err);
|
|
|
|
|
// // Try fallback to old API if new API fails
|
|
|
|
|
// try {
|
|
|
|
|
// const fallbackRes = await listData(
|
|
|
|
|
// "1",
|
|
|
|
|
// "",
|
|
|
|
|
// "",
|
|
|
|
|
// 20,
|
|
|
|
|
// 0,
|
|
|
|
|
// section === "latest" ? "createdAt" : "clickCount",
|
|
|
|
|
// "",
|
|
|
|
|
// "",
|
|
|
|
|
// ""
|
|
|
|
|
// );
|
|
|
|
|
// setDataToRender(fallbackRes?.data?.data?.content || []);
|
|
|
|
|
// } catch (fallbackError) {
|
|
|
|
|
// console.error("Fallback API also failed:", fallbackError);
|
|
|
|
|
// }
|
|
|
|
|
// } finally {
|
|
|
|
|
// setLoading(false);
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// return (
|
|
|
|
|
// <section className="bg-white px-4 py-10 border max-w-[1350px] mx-auto rounded-md border-[#CDD5DF] my-10">
|
|
|
|
|
// <div className="max-w-screen-xl mx-auto">
|
|
|
|
|
// <h2 className="text-2xl font-semibold text-center mb-6">
|
|
|
|
|
// Media Update
|
|
|
|
|
// </h2>
|
|
|
|
|
|
|
|
|
|
// {/* Tab */}
|
|
|
|
|
// <div className="flex justify-center mb-8 bg-white">
|
|
|
|
|
// <Card className="bg-[#FFFFFF] rounded-xl flex flex-row p-3 gap-2">
|
|
|
|
|
// <button
|
|
|
|
|
// onClick={() => setTab("latest")}
|
|
|
|
|
// className={`px-5 py-2 rounded-lg text-sm font-medium ${
|
|
|
|
|
// tab === "latest" ? "bg-[#C6A455] text-white" : "text-[#C6A455]"
|
|
|
|
|
// }`}
|
|
|
|
|
// >
|
|
|
|
|
// Konten Terbaru
|
|
|
|
|
// </button>
|
|
|
|
|
// <button
|
|
|
|
|
// onClick={() => setTab("popular")}
|
|
|
|
|
// className={`px-5 py-2 rounded-lg text-sm font-medium ${
|
|
|
|
|
// tab === "popular" ? "bg-[#C6A455] text-white" : "text-[#C6A455]"
|
|
|
|
|
// }`}
|
|
|
|
|
// >
|
|
|
|
|
// Konten Terpopuler
|
|
|
|
|
// </button>
|
|
|
|
|
// </Card>
|
|
|
|
|
// </div>
|
|
|
|
|
|
|
|
|
|
// {/* Slider */}
|
|
|
|
|
// {loading ? (
|
|
|
|
|
// <p className="text-center">Loading...</p>
|
|
|
|
|
// ) : (
|
|
|
|
|
// <Swiper
|
|
|
|
|
// modules={[Navigation]}
|
|
|
|
|
// navigation
|
|
|
|
|
// spaceBetween={20}
|
|
|
|
|
// slidesPerView={1}
|
|
|
|
|
// breakpoints={{
|
|
|
|
|
// 640: { slidesPerView: 2 },
|
|
|
|
|
// 1024: { slidesPerView: 4 },
|
|
|
|
|
// }}
|
|
|
|
|
// >
|
|
|
|
|
// {dataToRender.map((item) => (
|
|
|
|
|
// <SwiperSlide key={item.id}>
|
|
|
|
|
// <div className="rounded-xl shadow-md overflow-hidden bg-white">
|
|
|
|
|
// <div className="w-full h-[204px] relative">
|
|
|
|
|
// <Image
|
|
|
|
|
// src={item.smallThumbnailLink || "/placeholder.png"}
|
|
|
|
|
// alt={item.title || "No Title"}
|
|
|
|
|
// fill
|
|
|
|
|
// className="object-cover"
|
|
|
|
|
// />
|
|
|
|
|
// </div>
|
|
|
|
|
// <div className="p-3">
|
|
|
|
|
// <div className="flex gap-2 mb-1">
|
|
|
|
|
// <span className="text-xs text-white px-2 py-0.5 rounded bg-blue-600">
|
|
|
|
|
// {item.category || "Tanpa Kategori"}
|
|
|
|
|
// </span>
|
|
|
|
|
// <span className="text-xs font-medium text-[#b3882e]">
|
|
|
|
|
// {item.label || ""}
|
|
|
|
|
// </span>
|
|
|
|
|
// </div>
|
|
|
|
|
// <p className="text-xs text-gray-500 mb-1">
|
|
|
|
|
// {formatTanggal(item.createdAt)}
|
|
|
|
|
// </p>
|
|
|
|
|
// <p className="text-sm font-semibold mb-3 line-clamp-2">
|
|
|
|
|
// {item.title}
|
|
|
|
|
// </p>
|
|
|
|
|
|
|
|
|
|
// <div className="flex items-center justify-between">
|
|
|
|
|
// <div className="flex gap-2 text-gray-600">
|
|
|
|
|
// <ThumbsUp className="w-4 h-4 cursor-pointer" />
|
|
|
|
|
// <ThumbsDown className="w-4 h-4 cursor-pointer" />
|
|
|
|
|
// </div>
|
|
|
|
|
// <Button
|
|
|
|
|
// variant="default"
|
|
|
|
|
// size="sm"
|
|
|
|
|
// className="text-white bg-blue-600 rounded px-4"
|
|
|
|
|
// >
|
|
|
|
|
// Save
|
|
|
|
|
// </Button>
|
|
|
|
|
// </div>
|
|
|
|
|
// </div>
|
|
|
|
|
// </div>
|
|
|
|
|
// </SwiperSlide>
|
|
|
|
|
// ))}
|
|
|
|
|
// </Swiper>
|
|
|
|
|
// )}
|
|
|
|
|
|
|
|
|
|
// {/* Lihat lebih banyak */}
|
|
|
|
|
// <div className="text-center mt-10">
|
|
|
|
|
// <Link
|
|
|
|
|
// href={
|
|
|
|
|
// tab === "latest"
|
|
|
|
|
// ? "https://mediahub.polri.go.id/"
|
|
|
|
|
// : "https://tribratanews.polri.go.id/"
|
|
|
|
|
// }
|
|
|
|
|
// >
|
|
|
|
|
// <Button
|
|
|
|
|
// size={"lg"}
|
|
|
|
|
// className="text-[#b3882e] bg-transparent border border-[#b3882e] px-6 py-2 rounded-s-sm text-sm font-medium hover:bg-[#b3882e]/10 transition"
|
|
|
|
|
// >
|
|
|
|
|
// Lihat Lebih Banyak
|
|
|
|
|
// </Button>
|
|
|
|
|
// </Link>
|
|
|
|
|
// </div>
|
|
|
|
|
// </div>
|
|
|
|
|
// </section>
|
|
|
|
|
// );
|
|
|
|
|
// }
|