kontenhumas-fe/components/landing-page/header.tsx

351 lines
9.9 KiB
TypeScript

"use client";
import Image from "next/image";
import Link from "next/link";
import { ThumbsUp, ThumbsDown } from "lucide-react";
import { useEffect, useState } from "react";
import { useParams, useRouter } from "next/navigation";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { listData, listArticles } from "@/service/landing/landing";
import { getCookiesDecrypt } from "@/lib/utils";
import { toggleBookmark, getBookmarkSummaryForUser } from "@/service/content";
export default function Header() {
const [data, setData] = useState<any[]>([]);
const [bookmarkedIds, setBookmarkedIds] = useState<Set<number>>(new Set());
const router = useRouter();
const params = useParams();
const MySwal = withReactContent(Swal);
// Get slug from URL params
const slug = params?.slug as string;
useEffect(() => {
const fetchData = async () => {
try {
// 🔹 Ambil artikel
const response = await listArticles(
1,
5,
undefined,
undefined,
undefined,
"createdAt",
slug
);
let articlesData: any[] = [];
if (response?.error) {
// fallback ke API lama
const fallbackResponse = await listData(
"",
"",
"",
5,
0,
"createdAt",
"",
"",
""
);
articlesData = fallbackResponse?.data?.data?.content || [];
} else {
articlesData = response?.data?.data || [];
}
// 🔹 Transform agar seragam
const transformed = articlesData.map((article: any) => ({
id: article.id,
title: article.title,
categoryName:
article.categoryName ||
(article.categories && article.categories[0]?.title) ||
"",
createdAt: article.createdAt,
smallThumbnailLink: article.thumbnailUrl,
fileTypeId: article.typeId,
clientName: article.clientName,
categories: article.categories,
label:
article.typeId === 1
? "Image"
: article.typeId === 2
? "Video"
: article.typeId === 3
? "Text"
: article.typeId === 4
? "Audio"
: "",
}));
setData(transformed);
const roleId = Number(getCookiesDecrypt("urie"));
if (roleId && !isNaN(roleId)) {
const saved = localStorage.getItem("bookmarkedIds");
let localSet = new Set<number>();
if (saved) {
localSet = new Set(JSON.parse(saved));
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))
);
}
} catch (error) {
console.error("Gagal memuat data:", error);
}
};
fetchData();
}, []);
// Simpan setiap kali state berubah
useEffect(() => {
if (bookmarkedIds.size > 0) {
localStorage.setItem(
"bookmarkedIds",
JSON.stringify(Array.from(bookmarkedIds))
);
}
}, [bookmarkedIds]);
return (
<section className="max-w-[1350px] mx-auto px-4">
<div className="flex flex-col lg:flex-row gap-6 py-6">
{data.length > 0 && (
<Card
item={data[0]}
isBig
isInitiallyBookmarked={bookmarkedIds.has(Number(data[0].id))}
onSaved={(id) =>
setBookmarkedIds((prev) => new Set([...prev, Number(id)]))
}
/>
)}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 w-full">
{data.slice(1, 5).map((item) => (
<Card
key={item.id}
item={item}
isInitiallyBookmarked={bookmarkedIds.has(Number(item.id))}
onSaved={(id) =>
setBookmarkedIds((prev) => new Set([...prev, Number(id)]))
}
/>
))}
</div>
</div>
<div className="relative w-full h-48 sm:h-64 md:h-80 lg:h-[460px] mt-4 rounded-xl">
<Image
src={"/PPS.png"}
alt={"pps"}
fill
className="object-cover rounded-xl"
/>
</div>
</section>
);
}
function Card({
item,
isBig = false,
isInitiallyBookmarked = false,
onSaved,
}: {
item: any;
isBig?: boolean;
isInitiallyBookmarked?: boolean;
onSaved?: (id: number) => void;
}) {
const router = useRouter();
const MySwal = withReactContent(Swal);
const [isSaving, setIsSaving] = useState(false);
const [isBookmarked, setIsBookmarked] = useState(isInitiallyBookmarked);
useEffect(() => {
setIsBookmarked(isInitiallyBookmarked);
}, [isInitiallyBookmarked]);
const getLink = () => {
switch (item?.fileTypeId) {
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 "#";
}
};
const handleSave = async () => {
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",
}).then(() => router.push("/auth"));
return;
}
try {
setIsSaving(true);
const res = await toggleBookmark(item.id);
if (res?.error) {
MySwal.fire({
icon: "error",
title: "Gagal",
text: "Gagal menyimpan artikel.",
confirmButtonColor: "#d33",
});
} else {
setIsBookmarked(true);
onSaved?.(item.id);
// 🔹 Simpan ke localStorage
const saved = localStorage.getItem("bookmarkedIds");
const newSet = new Set<number>(saved ? JSON.parse(saved) : []);
newSet.add(Number(item.id));
localStorage.setItem(
"bookmarkedIds",
JSON.stringify(Array.from(newSet))
);
MySwal.fire({
icon: "success",
title: "Berhasil",
text: "Artikel berhasil disimpan ke bookmark.",
confirmButtonColor: "#3085d6",
timer: 1500,
showConfirmButton: false,
});
}
} catch (err) {
console.error("Toggle bookmark error:", err);
MySwal.fire({
icon: "error",
title: "Kesalahan",
text: "Terjadi kesalahan saat menyimpan artikel.",
confirmButtonColor: "#d33",
});
} finally {
setIsSaving(false);
}
};
return (
<div>
<div
className={`rounded-xl overflow-hidden shadow hover:shadow-lg transition-all bg-white ${
isBig
? "w-full lg:max-w-[670px] lg:min-h-[680px]"
: "w-full h-[350px] md:h-[330px]"
}`}
>
<div
className={`relative ${
isBig ? "aspect-[3/2] lg:h-[525px]" : "aspect-video"
} w-full`}
>
<Link href={getLink()}>
<Image
src={item.smallThumbnailLink || "/contributor.png"}
alt={item.title}
fill
className="object-cover"
/>
</Link>
</div>
<div className="p-4 space-y-2">
<div className="flex items-center gap-2 text-xs font-semibold flex-wrap">
<span className="bg-emerald-600 text-white px-2 py-0.5 rounded">
{item.clientName}
</span>
<span className="text-orange-600">
{item.categories?.map((cat: any) => cat.title).join(", ")}
</span>
</div>
<div className="text-xs text-gray-500">
{new Date(item.createdAt)
.toLocaleString("id-ID", {
day: "2-digit",
month: "short",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
hour12: false,
timeZone: "Asia/Jakarta",
})
.replace(".", ":")}{" "}
WIB
</div>
<Link href={getLink()}>
<h3 className="text-sm font-semibold leading-snug line-clamp-2">
{item.title}
</h3>
</Link>
<div className="flex justify-between items-center pt-2">
<div className="flex gap-2 text-gray-500">
<ThumbsUp
size={18}
className="cursor-pointer hover:text-[#F60100]"
/>
<ThumbsDown
size={18}
className="cursor-pointer hover:text-red-600"
/>
</div>
<button
onClick={handleSave}
disabled={isSaving || isBookmarked}
className={`text-sm px-3 py-1 rounded-md transition-all duration-200 ${
isBookmarked
? "bg-gray-400 text-white cursor-not-allowed"
: "bg-[#F60100] text-white hover:bg-[#c90000]"
}`}
>
{isSaving ? "Saving..." : isBookmarked ? "Saved" : "Save"}
</button>
</div>
</div>
</div>
</div>
);
}