kontenhumas-fe/components/main/content/document-detail.tsx

624 lines
22 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useState, useEffect } from "react";
import { Calendar, Eye } from "lucide-react";
import { Button } from "@/components/ui/button";
import Link from "next/link";
import {
FaFacebookF,
FaTiktok,
FaYoutube,
FaWhatsapp,
FaInstagram,
FaTwitter,
FaCheck,
FaLink,
FaShareAlt,
} from "react-icons/fa";
import { getDetail } from "@/service/landing/landing";
import { getArticleDetail } from "@/service/content/content";
export default function DocumentDetail({ id }: { id: number }) {
const [data, setData] = useState<any>(null);
const [loading, setLoading] = useState(true);
const [copied, setCopied] = useState(false);
const [showShareMenu, setShowShareMenu] = useState(false);
const [selectedDoc, setSelectedDoc] = useState(0);
// Copy link
const handleCopyLink = async () => {
try {
await navigator.clipboard.writeText(window.location.href);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (err) {
console.error("Gagal menyalin:", err);
}
};
const SocialItem = ({
icon,
label,
}: {
icon: React.ReactNode;
label: string;
}) => (
<div className="flex items-center gap-3 cursor-pointer hover:opacity-80">
<div className="bg-[#C6A455] p-2 rounded-full text-white">{icon}</div>
<span className="text-sm">{label}</span>
</div>
);
useEffect(() => {
const fetchDetail = async () => {
try {
setLoading(true);
// 1⃣ Coba ambil dari API baru
const res = await getArticleDetail(id);
console.log("Response artikel:", res);
const article = res?.data?.data;
if (article) {
const mappedData = {
id: article.id,
title: article.title,
description: article.description,
createdAt: article.createdAt,
clickCount: article.viewCount,
creatorGroupLevelName: article.createdByName || "Unknown",
uploadedBy: {
publisher: article.createdByName || "MABES POLRI",
},
// files:
// article.files?.map((f: any) => ({
// id: f.id,
// fileName: f.file_name,
// url: f.file_url,
// secondaryUrl: f.file_url,
// fileThumbnail: f.file_thumbnail,
// fileAlt: f.file_alt,
// size: f.size,
// createdAt: f.created_at,
// updatedAt: f.updated_at,
// })) || [],
files:
article.files?.map((f: any) => ({
id: f.id,
fileName: f.fileName || f.file_name,
url: f.fileUrl || f.file_url,
secondaryUrl: f.fileUrl || f.file_url, // 🟢 gunakan field yang benar
fileThumbnail: f.fileThumbnail || f.file_thumbnail,
fileAlt: f.fileAlt || f.file_alt,
size: f.size,
createdAt: f.created_at,
updatedAt: f.updated_at,
})) || [],
};
setData(mappedData);
return;
}
// 2⃣ Fallback ke API lama
// const fallback = await getDetail(id);
// setData(fallback?.data?.data);
} catch (err) {
console.error("Gagal ambil detail:", err);
// try {
// const fallback = await getDetail(id);
// setData(fallback?.data?.data);
// } catch (err2) {
// console.error("Fallback gagal:", err2);
// }
} finally {
setLoading(false);
}
};
if (id) fetchDetail();
}, [id]);
if (loading) {
return (
<div className="max-w-6xl mx-auto px-4 py-6">
<p>Memuat data...</p>
</div>
);
}
if (!data) {
return (
<div className="max-w-6xl mx-auto px-4 py-6">
<p>Data tidak ditemukan</p>
</div>
);
}
return (
<div className="max-w-6xl mx-auto px-4 py-6 space-y-6">
{/* Viewer dokumen utama */}
<div className="relative">
{data?.files?.[selectedDoc]?.secondaryUrl ? (
<>
{console.log(
"📄 File URL:",
data?.files?.[selectedDoc]?.secondaryUrl
)}
<iframe
key={selectedDoc}
src={`https://docs.google.com/gview?url=${encodeURIComponent(
data?.files?.[selectedDoc]?.secondaryUrl || ""
)}&embedded=true`}
className="rounded-lg w-full h-[600px] border"
onError={(e) => {
console.error("❌ Gagal load iframe:", e);
window.open(data?.files?.[selectedDoc]?.secondaryUrl, "_blank");
}}
/>
<p className="text-center text-xs text-gray-500 mt-2">
Jika preview tidak muncul,{" "}
<a
href={data?.files?.[selectedDoc]?.secondaryUrl}
target="_blank"
className="text-blue-600 underline"
>
klik di sini untuk buka file
</a>
</p>
</>
) : (
<div className="w-full h-[300px] flex items-center justify-center bg-gray-100 text-gray-500 rounded-lg">
Tidak ada file untuk ditampilkan.
</div>
)}
</div>
{/* <div className="bg-[#e0c350] flex items-center justify-center h-[170px] text-white">
<svg
xmlns="http://www.w3.org/2000/svg"
width="150"
height="150"
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.207zM7 7.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5M7.5 9a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1zM7 11.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5M5.5 8a.5.5 0 1 0 0-1a.5.5 0 0 0 0 1M6 9.5a.5.5 0 1 1-1 0a.5.5 0 0 1 1 0M5.5 12a.5.5 0 1 0 0-1a.5.5 0 0 0 0 1"
/>
</svg>
</div> */}
{/* Pilihan dokumen */}
<div className="py-4 px-1 flex flex-row gap-3 flex-wrap">
{data?.files?.map((file: any, index: number) => (
<button
key={file.id}
onClick={() => setSelectedDoc(index)}
className={`px-4 py-2 rounded-md border cursor-pointer hover:bg-gray-100 ${
selectedDoc === index ? "border-red-600 bg-gray-50" : ""
}`}
>
📄 {file?.fileName || `Dokumen ${index + 1}`}
</button>
))}
</div>
{/* Informasi artikel */}
<div className="flex flex-wrap items-center gap-2 text-sm text-muted-foreground">
<span className="font-semibold text-black border-r-2 pr-2 border-black">
by {data?.uploadedBy?.publisher || "MABES POLRI"}
</span>
<span className="flex items-center gap-1 text-black">
<Calendar className="w-4 h-4" />
{new Date(data.createdAt)
.toLocaleString("id-ID", {
day: "2-digit",
month: "short",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
hour12: false,
timeZone: "Asia/Jakarta",
})
.replace(".", ":")}{" "}
WIB
</span>
<span className="flex items-center gap-1 border-r-2 pr-2 border-black text-black">
<Eye className="w-4 h-4" />
{data.clickCount || 0}
</span>
<span className="text-black">
Creator: {data.creatorGroupLevelName}
</span>
</div>
{/* Konten deskripsi */}
<div className="flex flex-col md:flex-row gap-6 mt-6">
<div className="flex-1 space-y-4">
<h1 className="text-xl font-bold">{data.title}</h1>
<div className="text-base text-gray-700 leading-relaxed space-y-3">
<p>{data.description}</p>
</div>
{/* Tombol aksi bawah */}
<div className="flex flex-wrap justify-center gap-4 my-20">
<div className="flex gap-2 items-center">
<Button
onClick={handleCopyLink}
size="lg"
className="justify-start bg-black text-white rounded-full"
>
{copied ? <FaCheck /> : <FaLink />}
</Button>
<span>COPY LINK</span>
</div>
<div className="flex gap-2 items-center">
<Button
onClick={() => setShowShareMenu(!showShareMenu)}
size="lg"
className="justify-start bg-[#C6A455] text-white rounded-full"
>
<FaShareAlt />
</Button>
<span>SHARE</span>
{showShareMenu && (
<div className="absolute mt-10 bg-white p-4 rounded-lg shadow-lg flex flex-col gap-3 w-48 z-10">
<SocialItem icon={<FaFacebookF />} label="Facebook" />
<SocialItem icon={<FaTiktok />} label="TikTok" />
<SocialItem icon={<FaYoutube />} label="YouTube" />
<SocialItem icon={<FaWhatsapp />} label="WhatsApp" />
<SocialItem icon={<FaInstagram />} label="Instagram" />
<SocialItem icon={<FaTwitter />} label="Twitter" />
</div>
)}
</div>
<div className="flex gap-2 items-center">
<Link href={`/content/video/comment/${id}`}>
<Button
variant="default"
size="lg"
className="justify-start bg-[#FFAD10] rounded-full mr-2 text-white"
>
💬
</Button>
COMMENT
</Link>
</div>
</div>
</div>
</div>
</div>
);
}
// "use client";
// import Image from "next/image";
// import { Calendar, Clock, Eye } from "lucide-react";
// import { Button } from "@/components/ui/button";
// import Link from "next/link";
// import { useState, useEffect } from "react";
// import {
// FaFacebookF,
// FaTiktok,
// FaYoutube,
// FaWhatsapp,
// FaInstagram,
// FaTwitter,
// FaCheck,
// FaLink,
// FaShareAlt,
// } from "react-icons/fa";
// import { getDetail, getArticleDetail } from "@/service/landing/landing";
// export default function DocumentDetail({ id }: { id: string }) {
// const [copied, setCopied] = useState(false);
// const [showShareMenu, setShowShareMenu] = useState(false);
// const [data, setData] = useState<any>(null);
// const [loading, setLoading] = useState(true);
// const [selectedDoc, setSelectedDoc] = useState(0);
// const [isLoading, setIsLoading] = useState<any>(true);
// useEffect(() => {
// const timer = setTimeout(() => {
// setIsLoading(false);
// }, 3000);
// return () => clearTimeout(timer);
// }, []);
// const handleCopyLink = async () => {
// try {
// await navigator.clipboard.writeText(window.location.href);
// setCopied(true);
// setTimeout(() => setCopied(false), 2000);
// } catch (err) {
// console.error("Failed to copy: ", err);
// }
// };
// const SocialItem = ({
// icon,
// label,
// }: {
// icon: React.ReactNode;
// label: string;
// }) => (
// <div className="flex items-center gap-3 cursor-pointer hover:opacity-80">
// <div className="bg-[#C6A455] p-2 rounded-full text-white">{icon}</div>
// <span className="text-sm">{label}</span>
// </div>
// );
// useEffect(() => {
// const fetchDetail = async () => {
// try {
// setLoading(true);
// // Try new Articles API first
// const response = await getArticleDetail(id);
// console.log("Article Detail API response:", response);
// if (response?.error) {
// console.error("Articles API failed, falling back to old API");
// // Fallback to old API
// const fallbackResponse = await getDetail(id);
// setData(fallbackResponse?.data?.data);
// console.log(
// "doc",
// fallbackResponse?.data?.data.files[selectedDoc]?.secondaryUrl
// );
// return;
// }
// // Handle new API response structure
// const articleData = response?.data?.data;
// if (articleData) {
// // Transform article data to match old structure for backward compatibility
// const transformedData = {
// id: articleData.id,
// title: articleData.title,
// description: articleData.description,
// createdAt: articleData.createdAt,
// clickCount: articleData.viewCount,
// creatorGroupLevelName: articleData.createdByName || "Unknown",
// uploadedBy: {
// publisher: articleData.createdByName || "MABES POLRI"
// },
// files: articleData.files?.map((file: any) => ({
// id: file.id,
// url: file.file_url,
// fileName: file.file_name,
// filePath: file.file_path,
// fileThumbnail: file.file_thumbnail,
// fileAlt: file.file_alt,
// widthPixel: file.width_pixel,
// heightPixel: file.height_pixel,
// size: file.size,
// downloadCount: file.download_count,
// createdAt: file.created_at,
// updatedAt: file.updated_at,
// secondaryUrl: file.file_url, // For document files, use same URL
// ...file
// })) || [],
// ...articleData
// };
// setData(transformedData);
// console.log(
// "doc",
// transformedData.files[selectedDoc]?.secondaryUrl
// );
// }
// } catch (error) {
// console.error("Error fetching detail:", error);
// // Try fallback to old API if new API fails
// try {
// const fallbackResponse = await getDetail(id);
// setData(fallbackResponse?.data?.data);
// console.log(
// "doc",
// fallbackResponse?.data?.data.files[selectedDoc]?.secondaryUrl
// );
// } catch (fallbackError) {
// console.error("Fallback API also failed:", fallbackError);
// }
// } finally {
// setLoading(false);
// }
// };
// if (id) fetchDetail();
// }, [id]);
// if (loading) {
// return (
// <div className="max-w-6xl mx-auto px-4 py-6">
// <p>Loading...</p>
// </div>
// );
// }
// if (!data) {
// return (
// <div className="max-w-6xl mx-auto px-4 py-6">
// <p>Data tidak ditemukan</p>
// </div>
// );
// }
// return (
// <div className="max-w-6xl mx-auto px-4 py-6 space-y-6">
// <div className="relative">
// <iframe
// src={data?.files[selectedDoc]?.secondaryUrl}
// className="rounded-lg h-[300px] w-screen lg:h-[600px] lg:w-full"
// />
// </div>
// <div className="py-4 px-1 flex flex-row gap-3 flex-wrap">
// {data?.files?.map((file: any, index: number) => (
// <button
// key={file?.id}
// onClick={() => setSelectedDoc(index)}
// className={`px-4 py-2 rounded-md border cursor-pointer hover:bg-gray-100 ${
// selectedDoc === index ? "border-red-600 bg-gray-50" : ""
// }`}
// >
// 📄 {file?.fileName || `Dokumen ${index + 1}`}
// </button>
// ))}
// </div>
// <div className="flex flex-col md:flex-row md:items-center md:justify-between text-sm text-muted-foreground">
// <div className="flex flex-wrap items-center gap-2">
// <span className="font-semibold text-black border-r-2 pr-2 border-black">
// by {data?.uploadedBy?.publisher || "MABES POLRI"}
// </span>
// <span className="flex items-center gap-1 text-black">
// <Calendar className="w-4 h-4" />
// {new Date(data.createdAt)
// .toLocaleString("id-ID", {
// day: "2-digit",
// month: "short",
// year: "numeric",
// hour: "2-digit",
// minute: "2-digit",
// hour12: false,
// timeZone: "Asia/Jakarta",
// })
// .replace(".", ":")}{" "}
// WIB
// </span>
// {/* <span className="flex items-center gap-1 border-r-2 pr-2 border-black text-black">
// <Clock className="w-4 h-4" />
// {data.time || "-"}
// </span> */}
// <span className="flex items-center gap-1 border-r-2 pr-2 border-black text-black">
// <Eye className="w-4 h-4" />
// {data.clickCount || 0}
// </span>
// <span className="text-black">
// {" "}
// Creator: {data.creatorGroupLevelName}
// </span>
// </div>
// </div>
// <div className="flex flex-col md:flex-row gap-6 mt-6">
// {/* Sidebar actions */}
// <div className="hidden md:flex flex-col gap-4 relative z-10">
// <div className="flex gap-2 items-center">
// <Button
// onClick={handleCopyLink}
// size="lg"
// className="justify-start bg-black text-white rounded-full"
// >
// {copied ? <FaCheck /> : <FaLink />}
// </Button>
// <span>COPY LINK</span>
// </div>
// <div className="flex gap-2 items-center relative">
// <Button
// onClick={() => setShowShareMenu(!showShareMenu)}
// size="lg"
// className="justify-start bg-[#C6A455] text-white rounded-full"
// >
// <FaShareAlt />
// </Button>
// <span>SHARE</span>
// {showShareMenu && (
// <div className="absolute left-16 top-0 bg-white p-4 rounded-lg shadow-lg flex flex-col gap-3 w-48">
// <SocialItem icon={<FaFacebookF />} label="Facebook" />
// <SocialItem icon={<FaTiktok />} label="TikTok" />
// <SocialItem icon={<FaYoutube />} label="YouTube" />
// <SocialItem icon={<FaWhatsapp />} label="WhatsApp" />
// <SocialItem icon={<FaInstagram />} label="Instagram" />
// <SocialItem icon={<FaTwitter />} label="Twitter" />
// </div>
// )}
// </div>
// <div className="flex gap-2 items-center">
// <Link href={`/content/video/comment/${id}`}>
// <Button
// variant="default"
// size="lg"
// className="justify-start bg-[#FFAD10] rounded-full mr-2 text-white"
// >
// <svg
// xmlns="http://www.w3.org/2000/svg"
// width="15"
// height="15"
// viewBox="0 0 24 24"
// >
// <path
// fill="currentColor"
// d="M10 3h4a8 8 0 1 1 0 16v3.5c-5-2-12-5-12-11.5a8 8 0 0 1 8-8"
// />
// </svg>
// </Button>
// COMMENT
// </Link>
// </div>
// </div>
// {/* Content */}
// <div className="flex-1 space-y-4">
// <h1 className="text-xl font-bold">{data.title}</h1>
// <div className="text-base text-gray-700 leading-relaxed space-y-3">
// <p>{data.description}</p>
// </div>
// {/* Actions bawah */}
// <div className="flex flex-wrap md:flex-row justify-center gap-4 my-20">
// <div className="flex gap-2 items-center">
// <Button
// onClick={handleCopyLink}
// size="lg"
// className="justify-start bg-black text-white rounded-full"
// >
// {copied ? <FaCheck /> : <FaLink />}
// </Button>
// <span>COPY LINK</span>
// </div>
// <div className="flex gap-2 items-center">
// <Button
// onClick={() => setShowShareMenu(!showShareMenu)}
// size="lg"
// className="justify-start bg-[#C6A455] text-white rounded-full"
// >
// <FaShareAlt />
// </Button>
// <span>SHARE</span>
// </div>
// <div className="flex gap-2 items-center">
// <Link href={`/content/video/comment/${id}`}>
// <Button
// variant="default"
// size="lg"
// className="justify-start bg-[#FFAD10] rounded-full mr-2 text-white"
// >
// <svg
// xmlns="http://www.w3.org/2000/svg"
// width="15"
// height="15"
// viewBox="0 0 24 24"
// >
// <path
// fill="currentColor"
// d="M10 3h4a8 8 0 1 1 0 16v3.5c-5-2-12-5-12-11.5a8 8 0 0 1 8-8"
// />
// </svg>
// </Button>
// COMMENT
// </Link>
// </div>
// </div>
// </div>
// </div>
// </div>
// );
// }