This commit is contained in:
Sabda Yagra 2025-09-11 20:59:17 +07:00
parent fdb2a161f6
commit 037f3f374c
10 changed files with 567 additions and 384 deletions

View File

@ -762,13 +762,13 @@ const FilterPage = () => {
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
{audioData?.map((audio: any) => ( {audioData?.map((audio: any) => (
<div key={audio?.id}> <div key={audio?.id}>
<div <Link
// href={`/audio/detail/${audio?.slug}`} href={prefixPath + `/audio/detail/${audio?.slug}`}
onClick={() => // onClick={() =>
router.push( // router.push(
prefixPath + `/audio/detail/${audio?.slug}` // prefixPath + `/audio/detail/${audio?.slug}`
) // )
} // }
className="flex flex-row items-center bg-white dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full" className="flex flex-row items-center bg-white dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full"
> >
<div className="flex justify-center lg:justify-between w-full gap-2 lg:gap-6"> <div className="flex justify-center lg:justify-between w-full gap-2 lg:gap-6">
@ -843,7 +843,7 @@ const FilterPage = () => {
/> />
</div> </div>
</div> </div>
</div> </Link>
</div> </div>
))} ))}
</div> </div>

View File

@ -827,13 +827,13 @@ const FilterPage = () => {
{documentData?.length > 0 ? ( {documentData?.length > 0 ? (
<div className=" grid grid-cols-1 md:grid-cols-2 gap-6"> <div className=" grid grid-cols-1 md:grid-cols-2 gap-6">
{documentData?.map((document: any) => ( {documentData?.map((document: any) => (
<div <Link
// href={`/document/detail/${document?.slug}`} href={ prefixPath + `/document/detail/${document?.slug}`}
onClick={() => // onClick={() =>
router.push( // router.push(
prefixPath + `/document/detail/${document?.slug}` // prefixPath + `/document/detail/${document?.slug}`
) // )
} // }
key={document?.id} key={document?.id}
className="flex flex-col bg-yellow-500 sm:flex-row items-center dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full" className="flex flex-col bg-yellow-500 sm:flex-row items-center dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full"
> >
@ -879,7 +879,7 @@ const FilterPage = () => {
Download Dokumen Download Dokumen
</div> </div>
</div> </div>
</div> </Link>
))} ))}
</div> </div>
) : ( ) : (

View File

@ -815,13 +815,13 @@ const FilterPage = () => {
className="hover:scale-105 transition-transform duration-300" className="hover:scale-105 transition-transform duration-300"
> >
<CardContent className="flex flex-col text-xs lg:text-sm w-full p-0"> <CardContent className="flex flex-col text-xs lg:text-sm w-full p-0">
<div <Link
// href={`/image/detail/${image?.slug}`} href={ prefixPath + `/image/detail/${image?.slug}`}
onClick={() => // onClick={() =>
router.push( // router.push(
prefixPath + `/image/detail/${image?.slug}` // prefixPath + `/image/detail/${image?.slug}`
) // )
} // }
> >
{/* <img src={image?.thumbnailLink} className="h-60 object-cover items-center justify-center cursor-pointer rounded-lg" /> */} {/* <img src={image?.thumbnailLink} className="h-60 object-cover items-center justify-center cursor-pointer rounded-lg" /> */}
<div className="img-container h-60 bg-[#e9e9e9] cursor-pointer rounded-lg"> <div className="img-container h-60 bg-[#e9e9e9] cursor-pointer rounded-lg">
@ -854,7 +854,7 @@ const FilterPage = () => {
<div className="font-semibold pr-3 pb-3 mx-2 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible w-full"> <div className="font-semibold pr-3 pb-3 mx-2 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible w-full">
{image?.title} {image?.title}
</div> </div>
</div> </Link>
</CardContent> </CardContent>
</Card> </Card>
))} ))}

View File

@ -1,344 +1,476 @@
"use client"; // "use client";
import { useParams, usePathname } from "next/navigation"; // import { useParams, usePathname } from "next/navigation";
// import React, { useEffect, useState } from "react";
// import { Icon } from "@iconify/react/dist/iconify.js";
// import {
// checkWishlistStatus,
// deleteWishlist,
// getDetail,
// saveWishlist,
// } from "@/service/landing/landing";
// import VideoPlayer from "@/utils/video-player";
// import NewContent from "@/components/landing-page/new-content";
// import { Link, useRouter } from "@/i18n/routing";
// import { Textarea } from "@/components/ui/textarea";
// import { getCookiesDecrypt } from "@/lib/utils";
// import { close, error, loading } from "@/config/swal";
// import { useToast } from "@/components/ui/use-toast";
// import { postActivityLog } from "@/service/content/content";
// const DetailVideo = () => {
// const [selectedSize, setSelectedSize] = useState<string>("L");
// const [selectedTab, setSelectedTab] = useState("video");
// const router = useRouter();
// const pathname = usePathname();
// const params = useParams();
// const slug = String(params?.slug);
// const [detailDataVideo, setDetailDataVideo] = useState<any>();
// const [isSaved, setIsSaved] = useState(false);
// const [wishlistId, setWishlistId] = useState();
// const { toast } = useToast();
// const [isDownloadAll, setIsDownloadAll] = useState(false);
// const [downloadProgress, setDownloadProgress] = useState(0);
// const [isFromSPIT, setIsFromSPIT] = useState(false);
// const [main, setMain] = useState<any>();
// const [resolutionSelected, setResolutionSelected] = useState("720");
// const [selectedVideo, setSelectedVideo] = useState(0);
// const userId = getCookiesDecrypt("uie");
// useEffect(() => {
// initFetch();
// checkWishlist();
// sendActivityLog(2);
// }, []);
// const initFetch = async () => {
// const response = await getDetail(String(slug));
// console.log("detailVideo", response);
// setIsFromSPIT(response?.data?.data?.isFromSPIT);
// setMain({
// id: response?.data?.data?.files[0]?.id,
// type: response?.data?.data?.fileType.name,
// url:
// Number(response?.data?.data?.fileType?.id) == 4
// ? response?.data?.data?.files[0]?.secondaryUrl
// : Number(response?.data?.data?.fileType?.id) == 2
// ? `${process.env.NEXT_PUBLIC_API}/media/view?id=${response?.data?.data?.files[0]?.id}&operation=file&type=video`
// : response?.data?.data?.files[0]?.url,
// thumbnailFileUrl: response?.data?.data?.files[0]?.thumbnailFileUrl,
// names: response?.data?.data?.files[0]?.fileName,
// format: response?.data?.data?.files[0]?.format,
// widthPixel: response?.data?.data?.files[0]?.widthPixel,
// heightPixel: response?.data?.data?.files[0]?.heightPixel,
// size: response?.data?.data?.files[0]?.size,
// caption: response?.data?.data?.files[0]?.caption,
// });
// setDetailDataVideo(response?.data?.data);
// };
// const doBookmark = async () => {
// if (userId) {
// const data = {
// mediaUploadId: slug?.split("-")?.[0],
// };
// loading();
// const res = await saveWishlist(data);
// if (res?.error) {
// error(res.message);
// return false;
// }
// close();
// toast({
// title: "Konten berhasil disimpan",
// });
// checkWishlist();
// } else {
// router.push("/auth");
// }
// };
// async function checkWishlist() {
// if (userId) {
// const res = await checkWishlistStatus(slug.split("-")?.[0]);
// console.log(res?.data?.data);
// const isAlreadyOnWishlist = res?.data?.data !== "-1";
// setWishlistId(res?.data?.data);
// setIsSaved(isAlreadyOnWishlist);
// }
// }
// const handleDeleteWishlist = async () => {
// if (userId) {
// loading();
// const res = await deleteWishlist(wishlistId);
// if (res?.error) {
// error(res.message);
// return false;
// }
// toast({
// title: "Konten berhasil dihapus",
// });
// close();
// checkWishlist();
// } else {
// router.push("/auth");
// }
// };
// const sizes = [
// { label: "XL", value: "3198 x 1798 px" },
// { label: "L", value: "2399 x 1349 px" },
// { label: "M", value: "1599 x 899 px" },
// { label: "S", value: "1066 x 599 px" },
// { label: "XS", value: "800 x 450 px" },
// ];
// async function sendActivityLog(activityTypeId: number) {
// const data = {
// activityTypeId,
// mediaId: slug.split("-")?.[0],
// url: window.location.href,
// };
// // set activity
// await postActivityLog(data);
// }
// const handleDownload = () => {
// if (downloadProgress === 0) {
// if (!userId) {
// router.push("/auth");
// } else {
// sendActivityLog(2);
// sendActivityLog(3);
// if (isDownloadAll) {
// let url: string;
// const baseId = slug.split("-")?.[0];
// // if (type === "1") {
// url = `${process.env.NEXT_PUBLIC_API}/media/file/download-zip?id=${baseId}&resolution=${resolutionSelected}`;
// // } else if (type === "2") {
// // url = `${process.env.NEXT_PUBLIC_API}/media/file/download-zip?id=${baseId}&resolution=${imageSizeSelected}`;
// // } else {
// // url = `${process.env.NEXT_PUBLIC_API}/media/file/download-zip?id=${baseId}`;
// // }
// downloadFile(url, "FileDownload.zip");
// } else {
// if (isFromSPIT && main?.url?.includes("spit.humas")) {
// downloadFile(`${main?.url}`, `${main.names}`);
// } else {
// const url = `${process.env.NEXT_PUBLIC_API}/media/view?id=${main?.id}&operation=file&type=video&resolution=${resolutionSelected}p`;
// downloadFile(url, `${main.names}`);
// }
// }
// // } else if (type === "1" && resolutionSelected?.length > 0) {
// // if (isFromSPIT && main?.url?.includes("spit.humas")) {
// // downloadFile(`${main?.url}`, `${main.names}`);
// // } else {
// // const url = `${process.env.NEXT_PUBLIC_API}/media/view?id=${main?.id}&operation=file&type=video&resolution=${resolutionSelected}p`;
// // downloadFile(url, `${main.names}`);
// // }
// // } else if (type === "2" && imageSizeSelected?.length > 0) {
// // const url = `${process.env.NEXT_PUBLIC_API}/media/view?id=${main?.id}&operation=file&type=image&resolution=${imageSizeSelected}`;
// // downloadFile(url, `${main.names}`);
// // } else if (type === "3" || type === "4") {
// // downloadFile(`${main?.url}`, `${main.names}`);
// // }
// }
// }
// };
// const downloadFile = (fileUrl: string, name: string) => {
// const xhr = new XMLHttpRequest();
// xhr.open("GET", fileUrl, true);
// xhr.responseType = "blob";
// xhr.addEventListener("progress", (event) => {
// if (event.lengthComputable) {
// const percentCompleted = Math.round((event.loaded / event.total) * 100);
// setDownloadProgress(percentCompleted);
// }
// });
// xhr.addEventListener("readystatechange", () => {
// if (xhr.readyState === 4 && xhr.status === 200) {
// const contentType =
// xhr.getResponseHeader("content-type") || "application/octet-stream";
// const extension = contentType.split("/")[1];
// const filename = `${name}.${extension}`;
// const blob = new Blob([xhr.response], {
// type: contentType,
// });
// const downloadUrl = URL.createObjectURL(blob);
// const a = document.createElement("a");
// a.href = downloadUrl;
// a.download = filename;
// document.body.append(a);
// a.click();
// a.remove();
// }
// });
// xhr.onloadend = () => {
// setDownloadProgress(0);
// };
// xhr.send();
// };
// return (
// <>
// <div className="px-4 md:px-24 py-4">
// {/* Container Utama */}
// <div className="rounded-md overflow-hidden md:flex">
// {/* Bagian Kiri */}
// <div className="md:w-3/4">
// {/* <div className="relative">
// <VideoPlayer url={detailDataVideo?.files[0]?.url} />
// <div className="absolute top-4 left-4"></div>
// </div> */}
// <div className="relative max-h-screen overflow-hidden">
// <div className="w-full max-h-screen aspect-video">
// <div className="w-full h-full object-contain">
// <VideoPlayer
// url={detailDataVideo?.files[selectedVideo]?.url}
// />
// </div>
// </div>
// <div className="absolute top-4 left-4"></div>
// </div>
// <div className="py-4 flex flex-row gap-3">
// {detailDataVideo?.files?.map((file: any, index: number) => (
// <div
// key={file?.id}
// onClick={() => setSelectedVideo(index)}
// className="cursor-pointer flex flex-col items-center gap-1"
// >
// <img
// src={file?.thumbnailFileUrl}
// alt={file?.fileName}
// className="w-32 h-20 object-cover rounded"
// />
// {/* <p className="text-sm text-center">{file?.fileName}</p> */}
// </div>
// ))}
// </div>
// {/* Footer Informasi */}
// <div className="text-sm text-gray-500 flex justify-between items-center border-t mt-4">
// <p className="flex flex-row items-center mt-3">
// oleh&nbsp;
// <span className="font-semibold text-black">
// {detailDataVideo?.uploadedBy?.userLevel?.name}
// </span>
// &nbsp;|&nbsp;Diupdate pada {detailDataVideo?.updatedAt}{" "}
// WIB&nbsp;|&nbsp;
// <Icon icon="formkit:eye" width="15" height="15" />
// &nbsp;
// {detailDataVideo?.clickCount}
// </p>
// <p className="mt-3">Kreator: {detailDataVideo?.creatorName}</p>
// </div>
// {/* Keterangan */}
// <div className="w-full">
// <h1 className="flex flex-row font-bold text-2xl my-8">
// {detailDataVideo?.title}
// </h1>
// <div
// className="font-light text-justify"
// dangerouslySetInnerHTML={{
// __html: detailDataVideo?.htmlDescription,
// }}
// />
// </div>
// </div>
// {/* Bagian Kanan */}
// <div className="md:w-1/4 p-4 bg-[#f7f7f7] rounded-lg mx-4 h-fit">
// {isSaved ? (
// <a
// onClick={() => handleDeleteWishlist()}
// className="flex flex-col mb-3 items-center justify-center cursor-pointer"
// >
// <Icon icon="material-symbols:bookmark" width={40} />
// <p className="text-base lg:text-lg">Hapus</p>
// </a>
// ) : (
// <a
// onClick={() => doBookmark()}
// className="flex flex-col mb-3 items-center justify-center cursor-pointer"
// >
// <Icon icon="material-symbols:bookmark-outline" width={40} />
// <p className="text-base lg:text-lg">Simpan</p>
// </a>
// )}
// {/* garis */}
// <div className="border-t border-black my-4"></div>
// <Link
// href={`/all/filter?title=polda&category=${detailDataVideo?.category.id}`}
// className="bg-red-600 text-white text-xs font-bold px-3 py-3 my-3 flex justify-center items-center rounded"
// >
// {detailDataVideo?.categoryName}
// </Link>
// <div className="flex justify-center flex-wrap gap-2 mb-4">
// {detailDataVideo?.tags.split(",").map((tag: string) => (
// <a
// onClick={() => router.push(`/all/filter?tag=${tag}`)}
// key={tag}
// className="bg-gray-200 text-gray-700 text-xs px-3 py-1 rounded-full cursor-pointer hover:bg-gray-500 hover:text-white"
// >
// {tag}
// </a>
// ))}
// </div>
// <div className="border-t border-black my-4"></div>
// {/* Opsi Ukuran Foto */}
// <h4 className="flex text-lg justify-center items-center font-semibold my-3">
// Opsi Ukuran Audio Visual
// </h4>
// <div className="border-t border-black my-4"></div>
// <div className="space-y-2">
// {sizes.map((size: any) => (
// <div className="flex flex-row justify-between">
// <div
// key={size.label}
// className="items-center flex flex-row gap-2 cursor-pointer"
// >
// <input
// type="radio"
// name="size"
// value={size.label}
// checked={selectedSize === size.label}
// onChange={() => setSelectedSize(size.label)}
// className="text-red-600 focus:ring-red-600"
// />
// <div className="text-sm">{size.label}</div>
// </div>
// <div className="">
// <div className="text-sm">{size.value}</div>
// </div>
// </div>
// ))}
// </div>
// {/* Download Semua */}
// <div className="mt-4">
// <label className="flex items-center space-x-2 text-sm">
// <input
// type="checkbox"
// className="text-red-600 focus:ring-red-600"
// onChange={() => setIsDownloadAll(!isDownloadAll)}
// />
// <span>Download Semua File?</span>
// </label>
// </div>
// {/* Tombol Download */}
// <button
// onClick={handleDownload}
// className="mt-4 bg-red-600 text-white w-full py-2 flex justify-center items-center gap-1 rounded-md text-sm hover:bg-red-700"
// >
// <svg
// xmlns="http://www.w3.org/2000/svg"
// width="1em"
// height="1em"
// viewBox="0 0 24 24"
// >
// <path
// fill="white"
// d="m12 16l-5-5l1.4-1.45l2.6 2.6V4h2v8.15l2.6-2.6L17 11zm-6 4q-.825 0-1.412-.587T4 18v-3h2v3h12v-3h2v3q0 .825-.587 1.413T18 20z"
// />
// </svg>
// Download
// </button>
// </div>
// </div>
// </div>
// <div className="w-full mb-8">
// {/* Comment */}
// <div className="flex flex-col my-16 p-10 bg-[#f7f7f7]">
// <div className="gap-5 flex flex-col px-4 lg:px-14">
// <p className="flex items-start text-lg">Berikan Komentar</p>
// <Textarea
// placeholder="Type your comments here."
// className="flex w-full"
// />
// <button className="flex items-start bg-[#bb3523] text-white rounded-lg w-fit px-4 py-1">
// Kirim
// </button>
// </div>
// </div>
// {/* Konten Serupa */}
// <div className="px-4 lg:px-24">
// <NewContent group="polda" type={"similar"} />
// </div>
// </div>
// </>
// );
// };
// export default DetailVideo;
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { Icon } from "@iconify/react/dist/iconify.js"; import {
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing"; checkWishlistStatus,
import VideoPlayer from "@/utils/video-player"; createPublicSuggestion,
import NewContent from "@/components/landing-page/new-content"; deletePublicSuggestion,
import { Link, useRouter } from "@/i18n/routing"; deleteWishlist,
import { Textarea } from "@/components/ui/textarea"; getDetail,
import { getCookiesDecrypt } from "@/lib/utils"; getDetailMetaData,
import { close, error, loading } from "@/config/swal"; getPublicSuggestionList,
import { useToast } from "@/components/ui/use-toast"; saveWishlist,
import { postActivityLog } from "@/service/content/content"; } from "@/service/landing/landing";
import { Metadata } from "next";
import DetailImage from "@/components/main/image-detail";
import DetailVideo from "@/components/main/video-detail";
const DetailVideo = () => { interface Size {
const [selectedSize, setSelectedSize] = useState<string>("L"); label: string;
const [selectedTab, setSelectedTab] = useState("video"); value: string;
const router = useRouter(); }
const pathname = usePathname();
const params = useParams();
const slug = String(params?.slug);
const [detailDataVideo, setDetailDataVideo] = useState<any>();
const [isSaved, setIsSaved] = useState(false);
const [wishlistId, setWishlistId] = useState();
const { toast } = useToast();
const [isDownloadAll, setIsDownloadAll] = useState(false);
const [downloadProgress, setDownloadProgress] = useState(0);
const [isFromSPIT, setIsFromSPIT] = useState(false);
const [main, setMain] = useState<any>();
const [resolutionSelected, setResolutionSelected] = useState("720");
const userId = getCookiesDecrypt("uie"); type Props = {
params: {
useEffect(() => { title: string;
initFetch(); slug: string;
checkWishlist(); description: string;
sendActivityLog(2); thumbnailLink: string;
}, []);
const initFetch = async () => {
const response = await getDetail(String(slug));
console.log("detailVideo", response);
setIsFromSPIT(response?.data?.data?.isFromSPIT);
setMain({
id: response?.data?.data?.files[0]?.id,
type: response?.data?.data?.fileType.name,
url:
Number(response?.data?.data?.fileType?.id) == 4
? response?.data?.data?.files[0]?.secondaryUrl
: Number(response?.data?.data?.fileType?.id) == 2
? `${process.env.NEXT_PUBLIC_API}/media/view?id=${response?.data?.data?.files[0]?.id}&operation=file&type=video`
: response?.data?.data?.files[0]?.url,
thumbnailFileUrl: response?.data?.data?.files[0]?.thumbnailFileUrl,
names: response?.data?.data?.files[0]?.fileName,
format: response?.data?.data?.files[0]?.format,
widthPixel: response?.data?.data?.files[0]?.widthPixel,
heightPixel: response?.data?.data?.files[0]?.heightPixel,
size: response?.data?.data?.files[0]?.size,
caption: response?.data?.data?.files[0]?.caption,
});
setDetailDataVideo(response?.data?.data);
}; };
const doBookmark = async () => {
if (userId) {
const data = {
mediaUploadId: slug?.split("-")?.[0],
};
loading();
const res = await saveWishlist(data);
if (res?.error) {
error(res.message);
return false;
}
close();
toast({
title: "Konten berhasil disimpan",
});
checkWishlist();
} else {
router.push("/auth");
}
};
async function checkWishlist() {
if (userId) {
const res = await checkWishlistStatus(slug.split("-")?.[0]);
console.log(res?.data?.data);
const isAlreadyOnWishlist = res?.data?.data !== "-1";
setWishlistId(res?.data?.data);
setIsSaved(isAlreadyOnWishlist);
}
}
const handleDeleteWishlist = async () => {
if (userId) {
loading();
const res = await deleteWishlist(wishlistId);
if (res?.error) {
error(res.message);
return false;
}
toast({
title: "Konten berhasil dihapus",
});
close();
checkWishlist();
} else {
router.push("/auth");
}
};
const sizes = [
{ label: "XL", value: "3198 x 1798 px" },
{ label: "L", value: "2399 x 1349 px" },
{ label: "M", value: "1599 x 899 px" },
{ label: "S", value: "1066 x 599 px" },
{ label: "XS", value: "800 x 450 px" },
];
async function sendActivityLog(activityTypeId: number) {
const data = {
activityTypeId,
mediaId: slug.split("-")?.[0],
url: window.location.href,
};
// set activity
await postActivityLog(data);
}
const handleDownload = () => {
if (downloadProgress === 0) {
if (!userId) {
router.push("/auth");
} else {
sendActivityLog(2);
sendActivityLog(3);
if (isDownloadAll) {
let url: string;
const baseId = slug.split("-")?.[0];
// if (type === "1") {
url = `${process.env.NEXT_PUBLIC_API}/media/file/download-zip?id=${baseId}&resolution=${resolutionSelected}`;
// } else if (type === "2") {
// url = `${process.env.NEXT_PUBLIC_API}/media/file/download-zip?id=${baseId}&resolution=${imageSizeSelected}`;
// } else {
// url = `${process.env.NEXT_PUBLIC_API}/media/file/download-zip?id=${baseId}`;
// }
downloadFile(url, "FileDownload.zip");
} else {
if (isFromSPIT && main?.url?.includes("spit.humas")) {
downloadFile(`${main?.url}`, `${main.names}`);
} else {
const url = `${process.env.NEXT_PUBLIC_API}/media/view?id=${main?.id}&operation=file&type=video&resolution=${resolutionSelected}p`;
downloadFile(url, `${main.names}`);
}
}
// } else if (type === "1" && resolutionSelected?.length > 0) {
// if (isFromSPIT && main?.url?.includes("spit.humas")) {
// downloadFile(`${main?.url}`, `${main.names}`);
// } else {
// const url = `${process.env.NEXT_PUBLIC_API}/media/view?id=${main?.id}&operation=file&type=video&resolution=${resolutionSelected}p`;
// downloadFile(url, `${main.names}`);
// }
// } else if (type === "2" && imageSizeSelected?.length > 0) {
// const url = `${process.env.NEXT_PUBLIC_API}/media/view?id=${main?.id}&operation=file&type=image&resolution=${imageSizeSelected}`;
// downloadFile(url, `${main.names}`);
// } else if (type === "3" || type === "4") {
// downloadFile(`${main?.url}`, `${main.names}`);
// }
}
}
};
const downloadFile = (fileUrl: string, name: string) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", fileUrl, true);
xhr.responseType = "blob";
xhr.addEventListener("progress", (event) => {
if (event.lengthComputable) {
const percentCompleted = Math.round((event.loaded / event.total) * 100);
setDownloadProgress(percentCompleted);
}
});
xhr.addEventListener("readystatechange", () => {
if (xhr.readyState === 4 && xhr.status === 200) {
const contentType = xhr.getResponseHeader("content-type") || "application/octet-stream";
const extension = contentType.split("/")[1];
const filename = `${name}.${extension}`;
const blob = new Blob([xhr.response], {
type: contentType,
});
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = filename;
document.body.append(a);
a.click();
a.remove();
}
});
xhr.onloadend = () => {
setDownloadProgress(0);
};
xhr.send();
};
return (
<>
<div className="px-4 md:px-24 py-4">
{/* Container Utama */}
<div className="rounded-md overflow-hidden md:flex">
{/* Bagian Kiri */}
<div className="md:w-3/4">
<div className="relative">
<VideoPlayer url={detailDataVideo?.files[0]?.url} />
<div className="absolute top-4 left-4"></div>
</div>
{/* Footer Informasi */}
<div className="text-sm text-gray-500 flex justify-between items-center border-t mt-4">
<p className="flex flex-row items-center mt-3">
oleh&nbsp;
<span className="font-semibold text-black">{detailDataVideo?.uploadedBy?.userLevel?.name}</span>
&nbsp;|&nbsp;Diupdate pada {detailDataVideo?.updatedAt} WIB&nbsp;|&nbsp;
<Icon icon="formkit:eye" width="15" height="15" />
&nbsp;
{detailDataVideo?.clickCount}
</p>
<p className="mt-3">Kreator: {detailDataVideo?.creatorName}</p>
</div>
{/* Keterangan */}
<div className="w-full">
<h1 className="flex flex-row font-bold text-2xl my-8">{detailDataVideo?.title}</h1>
<div
className="font-light text-justify"
dangerouslySetInnerHTML={{
__html: detailDataVideo?.htmlDescription,
}}
/>
</div>
</div>
{/* Bagian Kanan */}
<div className="md:w-1/4 p-4 bg-[#f7f7f7] rounded-lg mx-4 h-fit">
{isSaved ? (
<a onClick={() => handleDeleteWishlist()} className="flex flex-col mb-3 items-center justify-center cursor-pointer">
<Icon icon="material-symbols:bookmark" width={40} />
<p className="text-base lg:text-lg">Hapus</p>
</a>
) : (
<a onClick={() => doBookmark()} className="flex flex-col mb-3 items-center justify-center cursor-pointer">
<Icon icon="material-symbols:bookmark-outline" width={40} />
<p className="text-base lg:text-lg">Simpan</p>
</a>
)}
{/* garis */}
<div className="border-t border-black my-4"></div>
<Link href={`/all/filter?title=polda&category=${detailDataVideo?.category.id}`} className="bg-red-600 text-white text-xs font-bold px-3 py-3 my-3 flex justify-center items-center rounded">
{detailDataVideo?.categoryName}
</Link>
<div className="flex justify-center flex-wrap gap-2 mb-4">
{detailDataVideo?.tags.split(",").map((tag: string) => (
<a onClick={() => router.push(`/all/filter?tag=${tag}`)} key={tag} className="bg-gray-200 text-gray-700 text-xs px-3 py-1 rounded-full cursor-pointer hover:bg-gray-500 hover:text-white">
{tag}
</a>
))}
</div>
<div className="border-t border-black my-4"></div>
{/* Opsi Ukuran Foto */}
<h4 className="flex text-lg justify-center items-center font-semibold my-3">Opsi Ukuran Audio Visual</h4>
<div className="border-t border-black my-4"></div>
<div className="space-y-2">
{sizes.map((size: any) => (
<div className="flex flex-row justify-between">
<div key={size.label} className="items-center flex flex-row gap-2 cursor-pointer">
<input type="radio" name="size" value={size.label} checked={selectedSize === size.label} onChange={() => setSelectedSize(size.label)} className="text-red-600 focus:ring-red-600" />
<div className="text-sm">{size.label}</div>
</div>
<div className="">
<div className="text-sm">{size.value}</div>
</div>
</div>
))}
</div>
{/* Download Semua */}
<div className="mt-4">
<label className="flex items-center space-x-2 text-sm">
<input type="checkbox" className="text-red-600 focus:ring-red-600" onChange={() => setIsDownloadAll(!isDownloadAll)} />
<span>Download Semua File?</span>
</label>
</div>
{/* Tombol Download */}
<button onClick={handleDownload} className="mt-4 bg-red-600 text-white w-full py-2 flex justify-center items-center gap-1 rounded-md text-sm hover:bg-red-700">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
<path fill="white" d="m12 16l-5-5l1.4-1.45l2.6 2.6V4h2v8.15l2.6-2.6L17 11zm-6 4q-.825 0-1.412-.587T4 18v-3h2v3h12v-3h2v3q0 .825-.587 1.413T18 20z" />
</svg>
Download
</button>
</div>
</div>
</div>
<div className="w-full mb-8">
{/* Comment */}
<div className="flex flex-col my-16 p-10 bg-[#f7f7f7]">
<div className="gap-5 flex flex-col px-4 lg:px-14">
<p className="flex items-start text-lg">Berikan Komentar</p>
<Textarea placeholder="Type your comments here." className="flex w-full" />
<button className="flex items-start bg-[#bb3523] text-white rounded-lg w-fit px-4 py-1">Kirim</button>
</div>
</div>
{/* Konten Serupa */}
<div className="px-4 lg:px-24">
<NewContent group="polda" type={"similar"} />
</div>
</div>
</>
);
}; };
export default DetailVideo; export async function generateMetadata({ params }: any): Promise<Metadata> {
const slug = String(params?.slug);
const res = await getDetailMetaData(String(slug));
const video = res?.data?.data;
// console.log("video", video);
return {
title: video.title,
description: video.description,
openGraph: {
title: video?.title,
description: video?.description,
videos: [`${video?.smallThumbnailLink}`],
},
};
}
export default async function DetailInfo({ params }: Props) {
return <DetailVideo />;
}

View File

@ -827,13 +827,13 @@ const FilterPage = () => {
className="hover:scale-110 transition-transform duration-300" className="hover:scale-110 transition-transform duration-300"
> >
<CardContent className="flex flex-col text-xs lg:text-sm w-full p-0"> <CardContent className="flex flex-col text-xs lg:text-sm w-full p-0">
<div <Link
// href={`/video/detail/${video?.slug}`} href={prefixPath + `/video/detail/${video?.slug}`}
onClick={() => // onClick={() =>
router.push( // router.push(
prefixPath + `/video/detail/${video?.slug}` // prefixPath + `/video/detail/${video?.slug}`
) // )
} // }
> >
{/* <img src={video?.thumbnailLink} className="h-60 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" /> */} {/* <img src={video?.thumbnailLink} className="h-60 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" /> */}
<div className="img-container h-60 bg-[#e9e9e9] cursor-pointer rounded-lg"> <div className="img-container h-60 bg-[#e9e9e9] cursor-pointer rounded-lg">
@ -862,7 +862,7 @@ const FilterPage = () => {
<div className="font-semibold pr-3 pb-3 mx-2 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible w-full"> <div className="font-semibold pr-3 pb-3 mx-2 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible w-full">
{video?.title} {video?.title}
</div> </div>
</div> </Link>
</CardContent> </CardContent>
</Card> </Card>
))} ))}

40
app/sitemap.xml/route.ts Normal file
View File

@ -0,0 +1,40 @@
"use server";
import { getListContent } from "@/service/landing/landing";
import { NextResponse } from "next/server";
export async function GET() {
const baseUrl = "https://mediahub.polri.go.id/in";
const response = await getListContent({ page: 1, limit: "100", search: "" });
const articles = response?.data?.data || [];
const urls = articles
.map(
(article: any) => `
<url>
<loc>${baseUrl}/${article.fileTypeId == 1 ? "image" : article.fileTypeId == 2 ? "video" : article.fileTypeId == 3 ? "document" : "audio"}/detail/${article.id}-${article.slug}</loc>
<lastmod>${new Date(article.updatedAt).toISOString()}</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>`
)
.join("");
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>${baseUrl}</loc>
<lastmod>${new Date().toISOString()}</lastmod>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
${urls}
</urlset>`;
return new NextResponse(sitemap, {
headers: {
"Content-Type": "application/xml",
},
});
}

View File

@ -147,9 +147,7 @@ export default function FormImageUpdate() {
let counterUpdateProgress = 0; let counterUpdateProgress = 0;
const [progressList, setProgressList] = useState<any>([]); const [progressList, setProgressList] = useState<any>([]);
let uploadPersen = 0; let uploadPersen = 0;
const isDetailOfRegionShowed = false; const isDetailOfRegionShowed = false;
const [isStartUpload, setIsStartUpload] = useState(false); const [isStartUpload, setIsStartUpload] = useState(false);
const [counterProgress, setCounterProgress] = useState(0); const [counterProgress, setCounterProgress] = useState(0);
const t = useTranslations("Form"); const t = useTranslations("Form");
@ -2329,16 +2327,27 @@ export default function FormImageUpdate() {
{tags.map((tag, index) => ( {tags.map((tag, index) => (
<span <span
key={index} key={index}
className="flex items-center gap-2 px-2 py-1 rounded-lg bg-black text-white text-sm" className="flex items-center gap-2 px-2 py-1 rounded-lg bg-black text-white text-sm"
> >
{/* Hidden span untuk ukur lebar teks */}
<span
className="absolute opacity-0 whitespace-pre"
id={`tag-span-${index}`}
>
{tag || " "}
</span>
<input <input
type="text" type="text"
value={tag} value={tag}
onChange={(e) => handleEditTag(index, e.target.value)} onChange={(e) => handleEditTag(index, e.target.value)}
className="bg-black text-white border-none focus:outline-none w-auto" style={{
width: `${Math.max(tag.length, 1)}ch`,
}}
className="bg-black text-white border-none focus:outline-none"
/> />
<button <button
value={tag}
type="button" type="button"
onClick={() => handleRemoveTag(index)} onClick={() => handleRemoveTag(index)}
className="remove-tag-button text-white" className="remove-tag-button text-white"
@ -2348,6 +2357,7 @@ export default function FormImageUpdate() {
</span> </span>
))} ))}
</div> </div>
{/* <div className="flex flex-wrap gap-2"> {/* <div className="flex flex-wrap gap-2">
{detail?.tags?.split(",").map((tag, index) => ( {detail?.tags?.split(",").map((tag, index) => (
<Badge <Badge
@ -2361,7 +2371,7 @@ export default function FormImageUpdate() {
</div> </div>
</div> </div>
<div className="px-3 py-3"> <div className="px-3 py-3">
<div className="flex flex-col gap-6 space-y-2"> <div className="flex flex-col gap-3 space-y-2">
<Label> <Label>
{t("publish-target", { defaultValue: "Publish Target" })} {t("publish-target", { defaultValue: "Publish Target" })}
</Label> </Label>

View File

@ -190,10 +190,11 @@ export default function FilterImageComponent(props: {
<CarouselContent> <CarouselContent>
{newContent?.map((image: any) => ( {newContent?.map((image: any) => (
<CarouselItem key={image?.id} className="md:basis-1/2 lg:basis-1/3"> <CarouselItem key={image?.id} className="md:basis-1/2 lg:basis-1/3">
<div <Link
onClick={() => href={prefixPath + `/image/detail/${image?.slug}`}
router.push(prefixPath + `/image/detail/${image?.slug}`) // onClick={() =>
} // router.push(prefixPath + `/image/detail/${image?.slug}`)
// }
className="cursor-pointer relative group overflow-hidden bg-white dark:bg-black dark:border dark:border-gray-500 rounded-xl shadow-md hover:shadow-lg transition-shadow duration-300" className="cursor-pointer relative group overflow-hidden bg-white dark:bg-black dark:border dark:border-gray-500 rounded-xl shadow-md hover:shadow-lg transition-shadow duration-300"
> >
{/* Image with motion effect */} {/* Image with motion effect */}
@ -260,7 +261,7 @@ export default function FilterImageComponent(props: {
</p> </p>
*/} */}
</div> </div>
</div> </Link>
</CarouselItem> </CarouselItem>
))} ))}
</CarouselContent> </CarouselContent>

View File

@ -578,7 +578,7 @@ const DetailImage = (data: any) => {
height={1440} height={1440}
src={detailDataImage?.files[selectedImage]?.url} src={detailDataImage?.files[selectedImage]?.url}
alt="Main" alt="Main"
className="rounded-lg h-[300px] w-screen lg:h-[600px] lg:w-full object-contain" className="rounded-lg h-[300px] w-screen lg:h-[570px] lg:w-full object-contain"
/> />
<div className="absolute top-4 right-4"></div> <div className="absolute top-4 right-4"></div>

View File

@ -472,9 +472,9 @@ const DetailVideo = () => {
<VideoPlayer url={detailDataVideo?.files[selectedVideo]?.url} /> <VideoPlayer url={detailDataVideo?.files[selectedVideo]?.url} />
<div className="absolute top-4 left-4"></div> <div className="absolute top-4 left-4"></div>
</div> */} </div> */}
<div className="relative max-h-screen overflow-hidden"> <div className="relative max-h-[600px] overflow-hidden">
<div className="w-full max-h-screen aspect-video"> <div className="w-full max-h-[600px] aspect-video">
<div className="w-full h-full object-contain"> <div className="w-full h-[600px] object-contain">
<VideoPlayer <VideoPlayer
url={detailDataVideo?.files[selectedVideo]?.url} url={detailDataVideo?.files[selectedVideo]?.url}
/> />