web-humas-fe/components/page/detail-news.tsx

401 lines
14 KiB
TypeScript

"use client";
import {
convertDateFormat,
formatMonthString,
formatTextToHtmlTag,
} from "@/utils/global";
import {
CalendarIcon,
ChevronLeftIcon,
ChevronRightIcon,
ClockIcon,
EyeIcon,
EyeIconMdi,
FacebookIcon,
SquareFacebookIcon,
SquareLinkedInIcon,
SquareWhatsappIcon,
SquareXIcon,
UserIcon,
} from "../icons";
import { Button } from "@heroui/button";
import { useParams, usePathname } from "next/navigation";
import Link from "next/link";
import { useEffect, useState } from "react";
import { image } from "@heroui/theme";
import Cookies from "js-cookie";
import { saveActivity } from "@/services/activity-log";
import { Accordion, AccordionItem, Image } from "@heroui/react";
const token = Cookies.get("access_token");
const uid = Cookies.get("uie");
export default function DetailNews(props: { data: any; listArticle: any }) {
const { data, listArticle } = props;
const [prevArticle, setPrevArticle] = useState("");
const [nextArticle, setNextArticle] = useState("");
const [imageNow, setImageNow] = useState(0);
const pathname = usePathname();
const params = useParams();
const id: any = params?.id;
const shareText = "Humas Polri";
const [filteredFiles, setFilteredFiles] = useState<any>([]);
useEffect(() => {
getUniqueBaseFiles(data?.files);
}, [data]);
function extractBaseName(fileName: string): string {
const match = fileName.match(/^(.+?)_\w+\.(jpg|jpeg|png|webp)$/i);
return match ? match[1] : fileName;
}
const getUniqueBaseFiles = (files: any) => {
if (files && files.length > 0) {
const seen = new Set<string>();
const result: any = [];
for (const file of files) {
const baseName = extractBaseName(file.file_name);
if (!seen.has(baseName)) {
seen.add(baseName);
result.push(file);
}
}
setFilteredFiles(result);
}
};
const handleShare = async (platform: string) => {
let shareLink = "";
const urls = "https://new-humas.polri.go.id/" + pathname;
let req: any = {
activityTypeId: 3,
url: "https://new-humas.polri.go.id/" + pathname,
articleId: Number(id?.split("-")[0]),
};
if (uid) {
req.userId = Number(uid);
}
const resActivity = await saveActivity(req, token);
switch (platform) {
case "facebook":
shareLink = `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(
urls
)}`;
break;
case "x":
shareLink = `https://x.com/intent/tweet?url=${encodeURIComponent(
urls
)}&text=${encodeURIComponent(shareText)}`;
break;
case "linkedin":
shareLink = `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(
urls
)}`;
break;
case "whatsapp":
shareLink = `https://wa.me/?text=${encodeURIComponent(
shareText + " " + urls
)}`;
break;
default:
break;
}
const popupWidth = 800;
const popupHeight = 600;
const left = window.screenX + (window.innerWidth - popupWidth) / 2;
const top = window.screenY + (window.innerHeight - popupHeight) / 2;
window.open(
shareLink,
"_blank",
`width=${popupWidth},height=${popupHeight},top=${top},left=${left},resizable=no,scrollbars=no,toolbar=no,menubar=no,status=no`
);
};
useEffect(() => {
if (listArticle) {
const index = listArticle?.findIndex(
(item: any) => item?.id === data?.id
);
if (index - 1 == -1) {
setPrevArticle("");
} else {
const prevString = `${listArticle[index - 1]?.id}-${
listArticle[index - 1]?.slug
}`;
setPrevArticle(prevString);
}
if (index + 1 == listArticle?.length) {
setNextArticle("");
} else {
const nextString = `${listArticle[index + 1]?.id}-${
listArticle[index + 1]?.slug
}`;
setNextArticle(nextString);
}
}
}, [data, listArticle]);
function removeImgTags(htmlString?: { __html: string }) {
const parser = new DOMParser();
const doc = parser.parseFromString(String(htmlString?.__html), "text/html");
const images = doc.querySelectorAll("img");
images.forEach((img) => img.remove());
return { __html: doc.body.innerHTML };
}
return (
<div className="flex flex-col gap-2 py-4 lg:w-[65%] lg:mx-auto">
<p className="font-semibold text-xl lg:text-3xl">{data?.title}</p>
<div className="flex flex-row items-center py-1 text-xs lg:text-lg gap-2 lg:gap-4">
<div className="flex flex-row items-center gap-1">
<UserIcon size={16} className="lg:hidden" />
<UserIcon size={22} className="hidden lg:block" />
<p className="uppercase">{data?.createdByName}</p>
</div>
<div className="flex flex-row items-center gap-1">
<CalendarIcon size={16} className="lg:hidden" />
<CalendarIcon size={24} className="hidden lg:block" />
<p>{formatMonthString(data?.updatedAt)}</p>
</div>
<div className="flex flex-row items-center">
<ClockIcon size={16} className="lg:hidden" />
<ClockIcon size={24} className="hidden lg:block" />
<p>{`${new Date(data?.updatedAt)
.getHours()
.toString()
.padStart(2, "0")}:${new Date(data?.updatedAt)
.getMinutes()
.toString()
.padStart(2, "0")}`}</p>
</div>
<p className="flex items-center gap-1">
<EyeIconMdi size={16} className="lg:hidden" />
<EyeIconMdi size={16} className="hidden lg:block" />
{data?.viewCount === null ? 0 : data?.viewCount}
</p>
</div>
<div className="flex justify-center my-2 lg:my-5">
{filteredFiles ? (
filteredFiles[0]?.file_name.split(".")[1].includes("doc") ||
filteredFiles[0]?.file_name.split(".")[1].includes("pdf") ? (
<Image
classNames={{
wrapper: "!w-full !max-w-full",
img: "!w-full",
}}
alt="Main Image"
src={data.thumbnailUrl}
className="object-cover w-[100%] rounded-md"
/>
) : (
filteredFiles?.length > 0 && (
<Image
// classNames={{
// wrapper: "!w-full !max-w-full",
// img: "!w-full",
// }}
alt="Main Image"
src={filteredFiles[imageNow]?.file_url}
className="object-cover w-auto h-[360px] md:h-[480px] mx-auto rounded-md"
/>
)
)
) : (
""
)}
</div>
{filteredFiles?.length > 0 &&
(filteredFiles[0].file_name.split(".")[1].includes("doc") ||
filteredFiles[0].file_name.split(".")[1].includes("pdf") ? (
filteredFiles?.map((file: any, index: number) => (
<Accordion key={file?.id} variant="splitted" className="px-0">
<AccordionItem
key={file?.id}
aria-label={file?.title}
className="p-2"
title={
<p className="text-black dark:text-white font-semibold text-xs md:text-sm">
{file.file_alt}
{/* {`File ${index + 1}`} */}
</p>
}
>
<div className="bg-[#FFFFFF] rounded-md font-medium text-xs md:text-sm">
<div className="flex justify-between items-center border text-black dark:text-white border-gray-300 p-3 rounded-t-md bg-gray-100 dark:bg-stone-900">
<div className="w-[35%] md:w-[20%]">Nama File</div>
<div className="w-[65%] md:w-[80%] text-center border-l border-gray-300">
{file?.file_name}
</div>
</div>
<div className="flex justify-between border items-center border-gray-300 p-3 text-black">
<div className="w-[35%] md:w-[20%]">Ukuran File</div>
<div className="w-[65%] md:w-[80%] text-center border-l border-gray-300">
{Math.round(file?.size / 100) / 10 > 1000 ? (
<>{(Math.round(file?.size / 100) / 10000).toFixed(1)}</>
) : (
<>{(Math.round(file?.size / 100) / 10).toFixed(1)}</>
)}
{" kb"}
</div>
</div>
<div className="flex justify-between items-center border rounded-b-md border-gray-300 p-3 text-black">
<div className="w-[35%] md:w-[20%]">Tanggal Publish</div>
<div className="w-[65%] md:w-[80%] text-center border-l border-gray-300">
{formatMonthString(file?.created_at)}
</div>
</div>
</div>
<Link href={file?.file_url} target="_blank">
<Button
className="w-full bg-[#DD8306] text-sm font-semibold text-white mt-2"
radius="sm"
// onPress={() => doDownload(file?.fileName, file?.title)}
>
Buka File
</Button>
</Link>
</AccordionItem>
</Accordion>
))
) : (
<div className="flex flex-row gap-3 flex-nowrap overflow-x-auto">
{filteredFiles?.map((file: any, index: number) => (
<a
key={file.id}
onClick={() => setImageNow(index)}
className="cursor-pointer"
>
<Image
alt="NextUI hero Image"
src={file?.file_url}
className="object-cover w-[75px] lg:w-[150px] h-[50px] lg:h-[100px] rounded-md"
/>
</a>
))}
</div>
))}
<div
dangerouslySetInnerHTML={removeImgTags(
formatTextToHtmlTag(data?.htmlDescription)
)}
className="text-sm lg:text-xl lg:leading-8 text-justify"
/>
<div className="bg-gray-50 dark:bg-black text-black dark:text-white rounded-lg justify-center items-center p-4 flex flex-col gap-3">
<p className="text-lg border-b-3 border-red-600 font-semibold">TAGS</p>
<div className="flex flex-wrap gap-2">
{data?.categories?.map((category: any) => (
<Link
href={`/news/all?category_id=${category?.id}`}
key={category?.id}
className="text-sm "
>
<Button className="bg-[#BE0106] text-white px-2 py-2 rounded-md ">
{category?.title}
</Button>
</Link>
))}
</div>
<div className="flex flex-wrap gap-2">
{data?.tags?.split(",").map(
(tag: any) =>
tag !== "" && (
<Link href={``} key={tag} className="text-xs">
<Button
className="border-[#BE0106] px-2 py-2 rounded-md "
variant="bordered"
size="sm"
>
{tag}
</Button>
</Link>
)
)}
</div>
</div>
<div className="grid grid-cols-2 md:flex lg:justify-between gap-2 lg:gap-10 my-8">
<Button
variant="solid"
radius="none"
onPress={() => handleShare("facebook")}
className="lg:w-[80%] bg-[#3b5998] text-white px-4 py-2 flex flex-row"
>
<div className="w-1/4 ">
<SquareFacebookIcon />
</div>
<div className=" text-xl ">Facebook</div>
</Button>
<Button
variant="solid"
radius="none"
onPress={() => handleShare("x")}
className="lg:w-[80%] bg-black text-white px-4 py-2 flex flex-row"
>
<div className="w-1/4 ">
<SquareXIcon />
</div>
<div className="w-3/4 text-xl ">X</div>
</Button>
<Button
variant="solid"
radius="none"
onPress={() => handleShare("linkedin")}
className="lg:w-[80%] bg-[#0E76A8] text-white px-4 py-2 flex flex-row"
>
<div className="w-1/4 ">
<SquareLinkedInIcon />
</div>
<div className="w-3/4 text-xl ">LinkedIn</div>
</Button>
<Button
variant="solid"
radius="none"
onPress={() => handleShare("whatsapp")}
className="lg:w-[80%] bg-[#25D366] text-white px-4 py-2 flex flex-row"
>
<div className="w-1/4 ">
<SquareWhatsappIcon />
</div>
<div className="w-3/4 text-xl ">Whatsapp</div>
</Button>
</div>
<div className="grid grid-cols-2 ">
{prevArticle === "" ? (
<p className="flex flex-row items-center gap-3">
<ChevronLeftIcon /> Sebelumnya
</p>
) : (
<Link
href={`/news/detail/${prevArticle}`}
className="flex flex-row items-center gap-3 font-semibold"
>
<ChevronLeftIcon /> Sebelumnya
</Link>
)}
{nextArticle === "" ? (
<p className="flex flex-row justify-end items-center gap-3">
Selanjutnya <ChevronRightIcon />
</p>
) : (
<Link
href={`/news/detail/${nextArticle}`}
className="flex flex-row justify-end items-center gap-3 font-semibold"
>
Selanjutnya <ChevronRightIcon />
</Link>
)}
</div>
</div>
);
}