kontenhumas-fe/components/main/for-you-card.tsx

436 lines
14 KiB
TypeScript

"use client";
import { useState, useEffect } from "react";
import Image from "next/image";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import { Archive, Star, Trash2, Box, Undo2, HeartOff } from "lucide-react";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { getBookmarks } from "@/service/content";
import { BookmarkItem } from "@/service/content";
import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css";
import "swiper/css/navigation";
import { Navigation } from "swiper/modules";
// 🔹 Format tanggal WIB
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"
);
}
// 🔹 Link detail konten
function getLink(item: BookmarkItem) {
switch (item.article?.typeId) {
case 1:
return `/content/image/detail/${item.article?.id}`;
case 2:
return `/content/video/detail/${item.article?.id}`;
case 3:
return `/content/text/detail/${item.article?.id}`;
case 4:
return `/content/audio/detail/${item.article?.id}`;
default:
return "#";
}
}
type ForYouCardGridProps = {
filterType: "My Collections" | "Archives" | "Favorites";
selectedCategory?: string;
};
export default function ForYouCardGrid({ filterType }: ForYouCardGridProps) {
const [bookmarks, setBookmarks] = useState<BookmarkItem[]>([]);
const [loading, setLoading] = useState(true);
const [contentType, setContentType] = useState<
"image" | "video" | "text" | "audio"
>("image");
const MySwal = withReactContent(Swal);
useEffect(() => {
fetchBookmarks();
}, [filterType]);
const fetchBookmarks = async () => {
try {
setLoading(true);
const response = await getBookmarks(1, 50, filterType);
if (!response?.error) {
setBookmarks(response.data?.data || []);
} else {
MySwal.fire("Error", "Gagal memuat bookmark.", "error");
}
} catch (err) {
console.error(err);
MySwal.fire("Error", "Terjadi kesalahan saat memuat bookmark.", "error");
} finally {
setLoading(false);
}
};
// 🔹 Filter konten berdasarkan tab aktif
const filtered = bookmarks.filter((b) => {
const t = b.article?.typeId;
switch (contentType) {
case "image":
return t === 1;
case "video":
return t === 2;
case "text":
return t === 3;
case "audio":
return t === 4;
default:
return true;
}
});
// 🔹 Aksi tombol (dummy sementara)
const handleAction = (action: string, title: string) => {
MySwal.fire({
icon: "info",
title: action,
text: `${title} berhasil di${action.toLowerCase()}.`,
timer: 1200,
showConfirmButton: false,
});
};
const renderButtons = (bookmark: BookmarkItem) => {
const title = bookmark.article?.title || "Konten";
if (filterType === "Archives") {
// 🗃️ Unarchive + Delete
return (
<div className="flex gap-2 mt-3 justify-center">
<Button
size="sm"
variant="outline"
onClick={() => handleAction("Unarchive", title)}
className="flex items-center text-xs gap-1 text-blue-600 border-blue-200 hover:bg-blue-50 cursor-pointer"
>
<Undo2 className="w-[10px] h-[10px]" />
<p className="text-[10px]">Unarchive</p>
</Button>
<Button
size="sm"
variant="outline"
onClick={() => handleAction("Delete", title)}
className="flex items-center text-xs gap-1 text-red-600 border-red-200 hover:bg-red-50 cursor-pointer"
>
<Trash2 className="w-[10px] h-[10px]" />
<p className="text-[10px]">Delete</p>
</Button>
</div>
);
}
if (filterType === "Favorites") {
// 📦 Archive + 💔 Unfavorite
return (
<div className="flex gap-2 mt-3 justify-center">
<Button
size="sm"
variant="outline"
onClick={() => handleAction("Archive", title)}
className="flex items-center text-xs gap-1 text-gray-700 border-gray-300 hover:bg-gray-100 cursor-pointer"
>
<Archive className="w-[10px] h-[10px]" />
<p className="text-[10px]">Archive</p>
</Button>
<Button
size="sm"
variant="outline"
onClick={() => handleAction("Unfavorite", title)}
className="flex items-center gap-1 text-yellow-600 border-yellow-200 hover:bg-yellow-50 cursor-pointer"
>
<HeartOff className="w-[10px] h-[10px]" />
<p className="text-[10px]">Unfavorite</p>
</Button>
</div>
);
}
// Default: My Collections → Archive + Favorite
return (
<div className="flex gap-2 mt-3 justify-center">
<Button
size="sm"
variant="outline"
onClick={() => handleAction("Archive", title)}
className="flex items-center text-xs gap-1 text-gray-700 border-gray-300 hover:bg-gray-100 cursor-pointer"
>
<Archive className="w-[10px] h-[10px]" />
<p className="text-[10px]">Archive</p>
</Button>
<Button
size="sm"
variant="outline"
onClick={() => handleAction("Favorite", title)}
className="flex items-center gap-1 text-yellow-600 border-yellow-200 hover:bg-yellow-50 cursor-pointer"
>
<Star className="w-[10px] h-[10px]" />
<p className="text-[10px]">Favorite</p>
</Button>
</div>
);
};
const renderCard = (bookmark: BookmarkItem) => (
<div className="rounded-xl shadow-md bg-white hover:shadow-lg transition-shadow overflow-hidden">
{/* Gambar / ikon */}
{bookmark.article?.typeId === 3 ? (
// 📝 TEXT
<div className="bg-[#e0c350] flex items-center justify-center h-[200px] text-white">
<svg
xmlns="http://www.w3.org/2000/svg"
width="80"
height="80"
viewBox="0 0 16 16"
>
<path
fill="currentColor"
d="M5 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V5.414a1.5 1.5 0 0 0-.44-1.06L9.647 1.439A1.5 1.5 0 0 0 8.586 1zM4 3a1 1 0 0 1 1-1h3v2.5A1.5 1.5 0 0 0 9.5 6H12v7a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1zm7.793 2H9.5a.5.5 0 0 1-.5-.5V2.207z"
/>
</svg>
</div>
) : bookmark.article?.typeId === 4 ? (
// 🎵 AUDIO
<div className="flex items-center justify-center bg-[#bb3523] w-full h-[200px] text-white">
<svg
xmlns="http://www.w3.org/2000/svg"
width="100"
height="100"
viewBox="0 0 20 20"
>
<path
fill="currentColor"
d="M14.702 2.226A1 1 0 0 1 16 3.18v6.027a5.5 5.5 0 0 0-1-.184V6.18L8 8.368V15.5a2.5 2.5 0 1 1-1-2V5.368a1 1 0 0 1 .702-.955zM8 7.32l7-2.187V3.18L8 5.368zM5.5 14a1.5 1.5 0 1 0 0 3a1.5 1.5 0 0 0 0-3m13.5.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0m-2.265-.436l-2.994-1.65a.5.5 0 0 0-.741.438v3.3a.5.5 0 0 0 .741.438l2.994-1.65a.5.5 0 0 0 0-.876"
/>
</svg>
</div>
) : (
// 📸 / 🎬
<div className="relative w-full h-[200px]">
<Link href={getLink(bookmark)}>
<Image
src={bookmark.article?.thumbnailUrl || "/placeholder.png"}
alt={bookmark.article?.title || "No Title"}
fill
className="object-cover cursor-pointer hover:opacity-90 transition"
/>
</Link>
</div>
)}
{/* Caption */}
<div className="p-3">
<p className="text-xs text-gray-500 mb-1">
Disimpan: {formatTanggal(bookmark.createdAt)}
</p>
<Link href={getLink(bookmark)}>
<p className="text-sm font-semibold line-clamp-2 hover:text-blue-600 transition-colors">
{bookmark.article?.title}
</p>
</Link>
{renderButtons(bookmark)}
</div>
</div>
);
if (loading)
return (
<div className="text-center py-12 text-gray-500">Memuat konten...</div>
);
if (filtered.length === 0)
return (
<div className="text-center py-12">
<div className="text-gray-400 text-6xl mb-4">📂</div>
<h3 className="text-xl font-semibold text-gray-600 mb-2">
Tidak Ada Konten di {filterType}
</h3>
</div>
);
return (
<section className="bg-white px-4 py-6 border rounded-md border-[#CDD5DF]">
{/* Tab Filter Konten */}
<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">
{["image", "video", "audio", "text"].map((type) => (
<button
key={type}
onClick={() => setContentType(type as any)}
className={`px-4 py-2 rounded-full text-xs font-semibold transition-all duration-300 transform hover:scale-105 ${
contentType === type
? "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"
}`}
>
{type === "image"
? "📸 Foto"
: type === "video"
? "🎬 Audio Visual"
: type === "audio"
? "🎵 Audio"
: "📝 Text"}
</button>
))}
</div>
</div>
</div>
{/* Slider Konten */}
<Swiper
modules={[Navigation]}
navigation
spaceBetween={20}
slidesPerView={1.2}
breakpoints={{
640: { slidesPerView: 2.2 },
1024: { slidesPerView: 3.2 },
1280: { slidesPerView: 4.2 },
}}
>
{filtered.map((bookmark) => (
<SwiperSlide key={bookmark.id}>{renderCard(bookmark)}</SwiperSlide>
))}
</Swiper>
</section>
);
}
// "use client";
// import { useEffect, useState } from "react";
// import PublicationKlFilter from "./publication-filter";
// import { Button } from "@/components/ui/button";
// import { mediaWishlist } from "@/service/landing/landing";
// const itemsPerPage = 9;
// type PublicationCardGridProps = {
// selectedCategory: string;
// title?: string;
// refresh?: boolean;
// categoryFilter?: string[];
// formatFilter?: string[];
// isInstitute?: boolean;
// instituteId?: string;
// sortBy?: string;
// };
// export default function ForYouCardGrid({
// selectedCategory,
// title,
// refresh,
// isInstitute = false,
// instituteId = "",
// }: PublicationCardGridProps) {
// const [currentPage, setCurrentPage] = useState(0);
// const [contentImage, setContentImage] = useState([]);
// const [totalPages, setTotalPages] = useState(1);
// const [categoryFilter] = useState([]);
// const [formatFilter] = useState([]);
// const [sortBy] = useState();
// useEffect(() => {
// getDataImage();
// }, [currentPage, selectedCategory, title, refresh]);
// async function getDataImage() {
// const filter =
// categoryFilter?.length > 0
// ? categoryFilter?.sort().join(",")
// : selectedCategory || "";
// const name = title ?? "";
// const format = formatFilter?.join(",") ?? "";
// const response = await mediaWishlist(
// "1",
// isInstitute ? instituteId : "",
// name,
// filter,
// "12",
// currentPage,
// sortBy,
// format
// );
// setTotalPages(response?.data?.data?.totalPages || 1);
// setContentImage(response?.data?.data?.content || []);
// }
// const goToPage = (page: number) => {
// setCurrentPage(page);
// };
// return (
// <div className="space-y-6">
// {/* Grid Card */}
// <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
// {contentImage.length === 0 ? (
// <div className="col-span-3 text-center text-muted-foreground">
// Tidak ada konten ditemukan.
// </div>
// ) : (
// contentImage.map((item: any, idx: number) => (
// <PublicationKlFilter
// key={item.id}
// id={item.id} // ✅ tambahkan ini
// image={item.mediaUpload?.smallThumbnailLink}
// label={item.mediaType?.name ?? "UNKNOWN"}
// category={item.tags ?? "-"}
// date={new Date(item?.mediaUpload?.createdAt).toLocaleString(
// "id-ID",
// {
// day: "2-digit",
// month: "short",
// year: "numeric",
// hour: "2-digit",
// minute: "2-digit",
// timeZoneName: "short",
// }
// )}
// title={item?.mediaUpload?.title}
// description={""} // Optional: Tambahkan jika tersedia dari API
// />
// ))
// )}
// </div>
// {/* Pagination */}
// <div className="flex justify-center gap-2">
// {Array.from({ length: totalPages }, (_, i) => (
// <Button
// key={i}
// variant={currentPage === i + 1 ? "default" : "outline"}
// size="sm"
// onClick={() => goToPage(i + 1)}
// >
// {i + 1}
// </Button>
// ))}
// </div>
// </div>
// );
// }