feat: pull main

This commit is contained in:
sabdayagra 2025-01-08 10:04:44 +07:00
commit b05884384e
60 changed files with 9610 additions and 334 deletions

View File

@ -15,7 +15,6 @@ import {
import { Link } from "@/components/navigation";
import { formatDateToIndonesian, generateLocalizedPath } from "@/utils/globals";
import { Icon } from "@iconify/react/dist/iconify.js";
import newContent from "@/components/landing-page/new-content";
import { locale } from "dayjs";
import { useEffect, useState } from "react";
import { getListContent } from "@/service/landing/landing";

View File

@ -0,0 +1,334 @@
"use client";
import { useParams, usePathname } from "next/navigation";
import React, { useEffect, useState } from "react";
import { Icon } from "@iconify/react/dist/iconify.js";
import NewContent from "@/components/landing-page/new-content";
import { Link, useRouter } from "@/i18n/routing";
import { Textarea } from "@/components/ui/textarea";
import { BarWave } from "react-cssfx-loading";
import { useToast } from "@/components/ui/use-toast";
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
import { getCookiesDecrypt } from "@/lib/utils";
import { close, error, loading } from "@/config/swal";
const DetailAudio = () => {
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 [detailDataAudio, setDetailDataAudio] = 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 [imageSizeSelected, setImageSizeSelected] = useState("l");
const userId = getCookiesDecrypt("uie");
useEffect(() => {
initFetch();
checkWishlist();
}, []);
const initFetch = async () => {
const response = await getDetail(String(slug));
console.log("detailAudio", 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,
});
setDetailDataAudio(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");
}
};
async function sendActivityLog(activityTypeId: number) {
const data = {
activityTypeId,
mediaId: slug.split("-")?.[0],
url: window.location.href,
};
// set activity
// const response = await postActivityLog(data, token);
// console.log(response);
}
const handleDownload = () => {
if (downloadProgress === 0) {
if (!userId) {
router.push("/auth/login");
} 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 {
downloadFile(`${main?.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();
};
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" },
];
return (
<>
<div className="min-h-screen 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 flex justify-center">
<img src="/assets/audio-btn.png" />
<BarWave color="#ffc831" width="60px" height="25px" duration="2s" />
<div className="absolute top-4 left-4"></div>
</div>
{/* Footer Informasi */}
<div className="p-4 text-sm text-gray-500 flex justify-between items-center border-t mt-4">
<p className="flex flex-row items-center">
oleh&nbsp;<span className="font-semibold text-black">{detailDataAudio?.uploadedBy?.userLevel?.name}</span>&nbsp;|&nbsp;Diupdate pada {detailDataAudio?.updatedAt} WIB&nbsp;|&nbsp;
<Icon icon="formkit:eye" width="15" height="15" />
&nbsp;
{detailDataAudio?.clickCount}
</p>
<p>Kreator: {detailDataAudio?.creatorName}</p>
</div>
{/* Keterangan */}
<div className="md:w-3/4">
<h1 className="flex flex-row font-bold text-2xl mx-5 my-8">{detailDataAudio?.title}</h1>
<div className="font-light text-justify" dangerouslySetInnerHTML={{ __html: detailDataAudio?.htmlDescription }} />
</div>
</div>
{/* Bagian Kanan */}
<div className="md:w-1/4 p-4 h-fit bg-gray-300 rounded-lg mx-4">
{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=${detailDataAudio?.category.id}`} className="bg-red-600 text-white text-xs font-bold px-3 py-3 my-3 flex justify-center items-center rounded">
{detailDataAudio?.category?.name}
</Link>
<div className="flex justify-center flex-wrap gap-2 mb-4">
{detailDataAudio?.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">
{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</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 gap-5 p-10 bg-gray-300">
<p className="flex items-start text-lg">Berikan Komentar</p>
<Textarea placeholder="Type your comments here." className="flex items-start justify-center" />
<button className="flex items-start bg-[#bb3523] rounded-lg w-fit px-4 py-1">Kirim</button>
</div>
{/* Konten Serupa */}
<div className="px-4">
<NewContent group="polda" type={"similar"} />
</div>
</div>
</>
);
};
export default DetailAudio;

View File

@ -0,0 +1,520 @@
"use client";
import React, { useEffect, useState } from "react";
import { Card, CardContent } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import { Icon } from "@iconify/react/dist/iconify.js";
import { formatDateToIndonesian, getOnlyDate, getOnlyMonthAndYear } from "@/utils/globals";
import { useParams, usePathname, useSearchParams } from "next/navigation";
import { getUserLevelListByParent, listCategory, listData, listDataRegional } from "@/service/landing/landing";
import { ColumnDef, ColumnFiltersState, PaginationState, SortingState, VisibilityState, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
import LandingPagination from "@/components/landing-page/pagination";
import { Reveal } from "@/components/landing-page/Reveal";
import { Link, useRouter } from "@/i18n/routing";
import { Input } from "@/components/ui/input";
import ReactDatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { close, loading } from "@/config/swal";
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@/components/ui/carousel";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
];
const FilterPage = () => {
const router = useRouter();
const asPath = usePathname();
const params = useParams();
const searchParams = useSearchParams();
const locale = params?.locale;
const [audioData, setAudioData] = useState<any>();
const [totalData, setTotalData] = React.useState<number>(1);
const [totalPage, setTotalPage] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [page, setPage] = useState(1);
const [totalContent, setTotalContent] = useState();
const [change, setChange] = useState(false);
const sortBy = searchParams?.get("sortBy");
const title = searchParams?.get("title");
const categorie = searchParams?.get("category");
const group = searchParams?.get("group");
const [contentImage, setContentImage] = useState([]);
const [, setGetTotalPage] = useState();
let typingTimer: any;
const doneTypingInterval = 1500;
const [search, setSearch] = useState();
const [categoryFilter, setCategoryFilter] = useState<any>([]);
const [monthYearFilter, setMonthYearFilter] = useState<any>();
const [searchTitle, setSearchTitle] = useState<string>("");
const [sortByOpt, setSortByOpt] = useState<any>(sortBy === "popular" ? "clickCount" : "createdAt");
const isRegional = asPath?.includes("regional");
const isSatker = asPath?.includes("satker");
const [formatFilter, setFormatFilter] = useState<any>([]);
const pages = page ? page - 1 : 0;
const [startDateString, setStartDateString] = useState<any>();
const [endDateString, setEndDateString] = useState<any>();
const [dateRange, setDateRange] = useState<any>([null, null]);
const [calenderState, setCalenderState] = useState(false);
const [handleClose, setHandleClose] = useState(false);
const [categories, setCategories] = useState([]);
const [userLevels, setUserLevels] = useState([]);
const poldaName = params?.polda_name;
let prefixPath = poldaName ? `/polda/${poldaName}` : "/";
const t = useTranslations("FilterPage");
// const [startDate, endDate] = dateRange;
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) {
setPage(Number(pageFromUrl));
}
}, [searchParams]);
useEffect(() => {
async function initState() {
getCategories();
// getSelectedCategory();
if (isSatker) {
getUserLevels();
}
}
initState();
}, []);
useEffect(() => {
if (categorie) {
setCategoryFilter(categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
console.log("Kategori", categorie, categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
}
}, [categorie]);
// useEffect(() => {
// fetchData();
// }, [page, sortBy, sortByOpt, title]);
useEffect(() => {
async function initState() {
if (isRegional) {
getDataRegional();
} else {
getDataAll();
}
}
console.log(monthYearFilter, "monthFilter");
initState();
}, [change, asPath, monthYearFilter, page, sortBy, sortByOpt, title, startDateString, endDateString, categorie, formatFilter]);
async function getCategories() {
const category = await listCategory("4");
const resCategory = category?.data?.data?.content;
setCategories(resCategory);
}
useEffect(() => {
function initState() {
if (dateRange[0] != null && dateRange[1] != null) {
setStartDateString(getOnlyDate(dateRange[0]));
setEndDateString(getOnlyDate(dateRange[1]));
setHandleClose(true);
console.log("date range", dateRange, getOnlyDate(dateRange[0]));
}
}
initState();
}, [calenderState]);
async function getDataAll() {
if (poldaName && String(poldaName)?.length > 1) {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
const filterGroup = group == undefined ? String(poldaName) : group;
loading();
const response = await listData(
"4",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
filterGroup,
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
const data = response?.data?.data;
const contentData = data?.content;
setAudioData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response?.data?.data?.totalElements);
} else {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listData(
"4",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
const data = response?.data?.data;
const contentData = data?.content;
setAudioData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response?.data?.data?.totalElements);
}
}
const handleCategoryFilter = (e: boolean, id: string) => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
};
const handleFormatFilter = (e: boolean, id: string) => {
let filter = [...formatFilter];
if (e) {
filter = [...formatFilter, id];
} else {
filter.splice(formatFilter.indexOf(id), 1);
}
console.log("Format filter", filter);
setFormatFilter(filter);
};
const cleanCheckbox = () => {
setCategoryFilter([]);
setFormatFilter([]);
router.push(`?category=&title=`);
setDateRange([null, null]);
setMonthYearFilter(null);
setChange(!change);
};
async function getDataRegional() {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listDataRegional(
"1",
name,
filter,
format,
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : "",
12,
pages,
sortByOpt
);
close();
setGetTotalPage(response?.data?.data?.totalPages);
setContentImage(response?.data?.data?.content);
setTotalContent(response?.data?.data?.totalElements);
}
const table = useReactTable({
data: audioData,
columns: columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
onPaginationChange: setPagination,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
pagination,
},
});
function getSelectedCategory() {
const filter = [];
if (categorie) {
const categoryArr = categorie.split(",");
for (const element of categoryArr) {
filter.push(Number(element));
}
setCategoryFilter(filter);
}
}
const handleDeleteDate = () => {
setDateRange([null, null]);
setStartDateString("");
setEndDateString("");
setHandleClose(false);
};
const handleSorting = (e: any) => {
console.log(e.target.value);
if (e.target.value == "terbaru") {
setSortByOpt("createdAt");
} else {
setSortByOpt("clickCount");
}
setChange(!change);
};
async function getUserLevels() {
const res = await getUserLevelListByParent(761);
const userLevelList = res?.data?.data;
if (userLevelList !== null) {
let optionArr: any = [];
userLevelList?.map((option: any) => {
let optionData = {
id: option.id,
label: option.name,
value: option.id,
};
optionArr.push(optionData);
});
setUserLevels(optionArr);
}
}
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
async function doneTyping() {
if (searchTitle == "" || searchTitle == undefined) {
router.push("?title=");
} else {
router.push(`?title=${searchTitle}`);
}
}
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
return (
<div className="flex flex-col">
{/* Header */}
<div className="flex flex-col md:flex-row items-start gap-5 p-10 bg-[#f7f7f7] dark:bg-black">
<p>
{" "}
Audio {">"} <span className="font-bold">{t("allAudio")}</span>
</p>
<p className="font-bold">|</p>
<p>{`${t("thereIs")} ${totalContent} ${t("downloadableAudio")}`}</p>
</div>
{/* Left */}
<div className="flex flex-col lg:flex-row gap-6 p-4">
<div className="lg:w-[25%] w-full bg-[#f7f7f7] dark:bg-black p-4 rounded-lg shadow-md">
<h2 className="text-lg font-semibold mb-4 flex items-center gap-1">
<Icon icon="stash:filter-light" fontSize={30} />
Filter
</h2>
<div className="border-t border-black my-4 dark:border-white"></div>
<div className="space-y-6">
<div>
<label htmlFor="search" className="block text-sm font-medium text-gray-700 dark:text-white">
{t("search")}
</label>
<Input
value={searchTitle}
onChange={(e) => setSearchTitle(e.target.value)}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
type="text"
id="search"
placeholder={t("searchTitle")}
className="mt-1 w-full text-sm border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("monthYear")}</label>
<ReactDatePicker
selected={monthYearFilter}
className="mt-1 w-full text-xs border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
onChange={(date) => setMonthYearFilter(date)}
dateFormat="MM | yyyy"
placeholderText={t("selectYear")}
showMonthYearPicker
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("date")}</label>
<div className="flex flex-row justify justify-between gap-2">
<ReactDatePicker
selectsRange
className="mt-1 w-full border text-sm rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
startDate={dateRange[0]}
endDate={dateRange[1]}
onChange={(update) => {
setDateRange(update);
}}
placeholderText={t("searchDate")}
onCalendarClose={() => setCalenderState(!calenderState)}
/>
<div className="flex items-center">{handleClose ? <Icon icon="carbon:close-filled" onClick={handleDeleteDate} width="20" inline color="#216ba5" /> : ""}</div>
</div>
</div>
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">{t("categories")}</h3>
<ul className="mt-2 space-y-2">
{categories.map((category: any) => (
<li key={category?.id}>
<label className="inline-flex items-center" htmlFor={`${category.id}`}>
<Checkbox id={`${category.id}`} value={category.id} checked={categoryFilter.includes(String(category.id))} onCheckedChange={(e) => handleCategoryFilter(Boolean(e), category.id)} />
<span className="ml-2 text-gray-700 dark:text-white">{category?.name}</span>
</label>
</li>
))}
</ul>
</div>
{/* Garis */}
<div className="border-t border-black my-4 dark:border-white"></div>
{/* Garis */}
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">Format</h3>
<ul className="mt-2 space-y-2">
<li>
<label className="inline-flex items-center">
<Checkbox id="png" value="png" checked={formatFilter.includes("wav")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "wav")} />
<span className="ml-2 text-gray-700 dark:text-white">WAV</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="jpeg" value="jpeg" checked={formatFilter.includes("mp3")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "mp3")} />
<span className="ml-2 text-gray-700 dark:text-white">MP3</span>
</label>
</li>
</ul>
</div>
<div className="border-t border-black dark:border-white my-4"></div>
<div className="text-center">
<a onClick={cleanCheckbox} className="text-[#bb3523] cursor-pointer">
<b>Reset Filter</b>
</a>
</div>
</div>
</div>
{/* Konten Kanan */}
<Reveal>
<div className="flex-1">
<div className="flex flex-col items-end mb-4">
<h2 className="text-lg font-semibold">{t("sortBy")} </h2>
<select defaultValue={sortBy == "popular" ? "terpopuler" : "terbaru"} onChange={(e) => handleSorting(e)} className="border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500">
<option value="latest">{t("latest")}</option>
<option value="popular">{t("mostPopular")}</option>
</select>
</div>
{audioData?.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{audioData?.map((image: any) => (
<Carousel className="w-full max-w-7xl mx-auto">
<CarouselContent>
{audioData?.map((audio: any) => (
<CarouselItem key={audio?.id} className="md:basis-1/2 lg:basis-1/3">
<div className="flex flex-row gap-6">
<Link href={`${prefixPath}/audio/detail/${audio?.slug}`} className="flex flex-col sm: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 items-center justify-center bg-red-500 text-white rounded-lg w-16 h-8 lg:h-16">
<svg width="32" height="34" viewBox="0 0 32 34" fill="null" xmlns="http://www.w3.org/2000/svg">
<path
d="M23.404 0.452014C23.7033 0.35857 24.0204 0.336816 24.3297 0.388509C24.639 0.440203 24.9318 0.563895 25.1845 0.749599C25.4371 0.935304 25.6426 1.17782 25.7843 1.45756C25.9259 1.73731 25.9998 2.04644 26 2.36001V14.414C25.3462 14.2296 24.6766 14.1064 24 14.046V8.36001L10 12.736V27C10 28.1264 9.6197 29.2197 8.92071 30.1029C8.22172 30.9861 7.24499 31.6075 6.14877 31.8663C5.05255 32.125 3.90107 32.0061 2.88089 31.5287C1.86071 31.0514 1.03159 30.2435 0.52787 29.2361C0.024152 28.2286 -0.124656 27.0806 0.105556 25.9781C0.335768 24.8755 0.931513 23.883 1.79627 23.1613C2.66102 22.4396 3.74413 22.031 4.87009 22.0017C5.99606 21.9724 7.09893 22.3242 8.00001 23V6.73601C7.99982 6.30956 8.13596 5.8942 8.38854 5.55059C8.64112 5.20698 8.99692 4.9531 9.40401 4.82601L23.404 0.452014ZM10 10.64L24 6.26601V2.36001L10 6.73601V10.64ZM5.00001 24C4.20436 24 3.44129 24.3161 2.87869 24.8787C2.31608 25.4413 2.00001 26.2044 2.00001 27C2.00001 27.7957 2.31608 28.5587 2.87869 29.1213C3.44129 29.6839 4.20436 30 5.00001 30C5.79566 30 6.55872 29.6839 7.12133 29.1213C7.68394 28.5587 8.00001 27.7957 8.00001 27C8.00001 26.2044 7.68394 25.4413 7.12133 24.8787C6.55872 24.3161 5.79566 24 5.00001 24ZM32 25C32 27.387 31.0518 29.6761 29.364 31.364C27.6761 33.0518 25.387 34 23 34C20.6131 34 18.3239 33.0518 16.636 31.364C14.9482 29.6761 14 27.387 14 25C14 22.6131 14.9482 20.3239 16.636 18.6361C18.3239 16.9482 20.6131 16 23 16C25.387 16 27.6761 16.9482 29.364 18.6361C31.0518 20.3239 32 22.6131 32 25ZM27.47 24.128L21.482 20.828C21.3298 20.7443 21.1583 20.7016 20.9846 20.7043C20.8108 20.707 20.6408 20.7549 20.4912 20.8433C20.3416 20.9317 20.2176 21.0576 20.1315 21.2086C20.0453 21.3595 20 21.5302 20 21.704V28.304C20 28.4778 20.0453 28.6486 20.1315 28.7995C20.2176 28.9504 20.3416 29.0763 20.4912 29.1647C20.6408 29.2531 20.8108 29.301 20.9846 29.3037C21.1583 29.3064 21.3298 29.2638 21.482 29.18L27.47 25.88C27.6268 25.7937 27.7575 25.6669 27.8486 25.5128C27.9397 25.3587 27.9877 25.183 27.9877 25.004C27.9877 24.825 27.9397 24.6493 27.8486 24.4952C27.7575 24.3412 27.6268 24.2143 27.47 24.128Z"
fill="white"
/>
</svg>
</div>
<div className="flex flex-col flex-1">
<div className="text-gray-500 dark:text-gray-400 flex flex-row text-sm">
{formatDateToIndonesian(new Date(audio?.createdAt))} {audio?.timezone ? audio?.timezone : "WIB"} | <Icon icon="formkit:eye" width="15" height="15" /> {audio?.clickCount}{" "}
</div>
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm h-5 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible">{audio?.title}</div>
</div>
</Link>
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
))}
</div>
) : (
<p className="flex items-center justify-center text-black">
<img src="/assets/empty-data.png" alt="empty" className="h-60 w-60 my-4" />
</p>
)}
<LandingPagination table={table} totalData={totalData} totalPage={totalPage} />
</div>
</Reveal>
</div>
</div>
);
};
export default FilterPage;

View File

@ -0,0 +1,20 @@
import LayoutProvider from "@/providers/layout.provider";
import LayoutContentProvider from "@/providers/content.provider";
import DashCodeSidebar from "@/components/partials/sidebar";
import DashCodeFooter from "@/components/partials/footer";
import ThemeCustomize from "@/components/partials/customizer";
import DashCodeHeader from "@/components/partials/header";
import { redirect } from "@/components/navigation";
import Footer from "@/components/landing-page/footer";
import Navbar from "@/components/landing-page/navbar";
const layout = async ({ children }: { children: React.ReactNode }) => {
return (
<>
{children}
</>
);
};
export default layout;

View File

@ -0,0 +1,333 @@
"use client";
import { useParams, usePathname } from "next/navigation";
import React, { useEffect, useState } from "react";
import { Icon } from "@iconify/react/dist/iconify.js";
import NewContent from "@/components/landing-page/new-content";
import { Textarea } from "@/components/ui/textarea";
import { getCookiesDecrypt } from "@/lib/utils";
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
import { close, error, loading } from "@/config/swal";
import { useToast } from "@/components/ui/use-toast";
import { Link, useRouter } from "@/i18n/routing";
const DetailDocument = () => {
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 [detailDataDocument, setDetailDataDocument] = useState<any>();
const [selectedDocument, setSelectedDocument] = useState(0);
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 [imageSizeSelected, setImageSizeSelected] = useState("l");
const userId = getCookiesDecrypt("uie");
useEffect(() => {
initFetch();
checkWishlist();
}, []);
const initFetch = async () => {
const response = await getDetail(String(slug));
console.log("detailDocument", 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,
});
setDetailDataDocument(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
// const response = await postActivityLog(data, token);
// console.log(response);
}
const handleDownload = () => {
if (downloadProgress === 0) {
if (!userId) {
router.push("/auth/login");
} 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 {
downloadFile(`${main?.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">
<div className="rounded-md overflow-hidden md:flex">
{/* Bagian Kiri */}
<div className="md:w-3/4">
<div className="relative">
<img src={detailDataDocument?.files[selectedDocument]?.url} alt="Main" className="rounded-lg w-auto h-fit" />
<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">{detailDataDocument?.uploadedBy?.userLevel?.name}</span>&nbsp;|&nbsp;Diupdate pada {detailDataDocument?.updatedAt} WIB&nbsp;|&nbsp;
<Icon icon="formkit:eye" width="15" height="15" />
&nbsp;
{detailDataDocument?.clickCount}
</p>
<p className="mt-3">Kreator: {detailDataDocument?.creatorName}</p>
</div>
{/* Keterangan */}
<div className="md:w-3/4">
<h1 className="flex flex-row font-bold text-2xl my-8">{detailDataDocument?.title}</h1>
<div dangerouslySetInnerHTML={{ __html: detailDataDocument?.htmlDescription }} />
</div>
</div>
{/* Bagian Kanan */}
<div className="md:w-1/4 p-4 bg-[#f7f7f7] h-fit rounded-lg mx-4">
{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=${detailDataDocument?.category.id}`} className="bg-red-600 text-white text-xs font-bold px-3 py-3 my-3 flex justify-center items-center rounded">
{detailDataDocument?.category?.name}
</Link>
<div className="flex justify-center flex-wrap gap-2 mb-4">
{detailDataDocument?.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">
{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 Foto</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-16">
<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] rounded-lg text-white w-fit px-4 py-1">Kirim</button>
</div>
</div>
{/* Konten Serupa */}
<div className="">
<NewContent group="polda" type={"similar"} />
</div>
</div>
</>
);
};
export default DetailDocument;

View File

@ -0,0 +1,549 @@
"use client";
import React, { useEffect, useState } from "react";
import { Card, CardContent } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import { Icon } from "@iconify/react/dist/iconify.js";
import { formatDateToIndonesian, getOnlyDate, getOnlyMonthAndYear } from "@/utils/globals";
import { useParams, usePathname, useSearchParams } from "next/navigation";
import { getListContent, getUserLevelListByParent, listCategory, listData, listDataRegional } from "@/service/landing/landing";
import { ColumnDef, ColumnFiltersState, PaginationState, SortingState, VisibilityState, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
import LandingPagination from "@/components/landing-page/pagination";
import { Reveal } from "@/components/landing-page/Reveal";
import { Link, useRouter } from "@/i18n/routing";
import { Input } from "@/components/ui/input";
import ReactDatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { close, loading } from "@/config/swal";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
];
const FilterPage = () => {
const router = useRouter();
const asPath = usePathname();
const params = useParams();
const searchParams = useSearchParams();
const locale = params?.locale;
const [documentData, setDocumentData] = useState<any>();
const [totalData, setTotalData] = React.useState<number>(1);
const [totalPage, setTotalPage] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [page, setPage] = useState(1);
const [totalContent, setTotalContent] = useState();
const [change, setChange] = useState(false);
const sortBy = searchParams?.get("sortBy");
const title = searchParams?.get("title");
const categorie = searchParams?.get("category");
const group = searchParams?.get("group");
const [, setGetTotalPage] = useState();
const t = useTranslations("FilterPage");
let typingTimer: any;
const doneTypingInterval = 1500;
const [contentDocument, setContentDocument] = useState([]);
const [categoryFilter, setCategoryFilter] = useState<any>([]);
const [monthYearFilter, setMonthYearFilter] = useState<any>();
const [searchTitle, setSearchTitle] = useState<string>("");
const [sortByOpt, setSortByOpt] = useState<any>(sortBy === "popular" ? "clickCount" : "createdAt");
const isRegional = asPath?.includes("regional");
const isSatker = asPath?.includes("satker");
const [formatFilter, setFormatFilter] = useState<any>([]);
const pages = page ? page - 1 : 0;
const [startDateString, setStartDateString] = useState<any>();
const [endDateString, setEndDateString] = useState<any>();
const [dateRange, setDateRange] = useState<any>([null, null]);
const [calenderState, setCalenderState] = useState(false);
const [handleClose, setHandleClose] = useState(false);
const [categories, setCategories] = useState([]);
const [userLevels, setUserLevels] = useState([]);
const poldaName = params?.polda_name;
let prefixPath = poldaName ? `/polda/${poldaName}` : "/";
// const [startDate, endDate] = dateRange;
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) {
setPage(Number(pageFromUrl));
}
}, [searchParams]);
useEffect(() => {
async function initState() {
getCategories();
// getSelectedCategory();
if (isSatker) {
getUserLevels();
}
}
initState();
}, []);
useEffect(() => {
if (categorie) {
setCategoryFilter(categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
console.log("Kategori", categorie, categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
}
}, [categorie]);
// useEffect(() => {
// fetchData();
// }, [page, sortBy, sortByOpt, title]);
useEffect(() => {
async function initState() {
if (isRegional) {
getDataRegional();
} else {
getDataAll();
}
}
console.log(monthYearFilter, "monthFilter");
initState();
}, [change, asPath, monthYearFilter, page, sortBy, sortByOpt, title, startDateString, endDateString, categorie, formatFilter]);
async function getCategories() {
const category = await listCategory("3");
const resCategory = category?.data?.data?.content;
setCategories(resCategory);
}
useEffect(() => {
function initState() {
if (dateRange[0] != null && dateRange[1] != null) {
setStartDateString(getOnlyDate(dateRange[0]));
setEndDateString(getOnlyDate(dateRange[1]));
setHandleClose(true);
console.log("date range", dateRange, getOnlyDate(dateRange[0]));
}
}
initState();
}, [calenderState]);
async function getDataAll() {
if (poldaName && String(poldaName)?.length > 1) {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
const filterGroup = String(poldaName);
loading();
const response = await listData(
"3",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
filterGroup,
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
// setGetTotalPage(response?.data?.data?.totalPages);
// setContentImage(response?.data?.data?.content);
// setTotalContent(response?.data?.data?.totalElements);
const data = response?.data?.data;
const contentData = data?.content;
setDocumentData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response?.data?.data?.totalElements);
} else {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listData(
"3",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
// setGetTotalPage(response?.data?.data?.totalPages);
// setContentImage(response?.data?.data?.content);
// setTotalContent(response?.data?.data?.totalElements);
const data = response?.data?.data;
const contentData = data?.content;
setDocumentData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response?.data?.data?.totalElements);
}
}
const handleCategoryFilter = (e: boolean, id: string) => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
};
const handleFormatFilter = (e: boolean, id: string) => {
let filter = [...formatFilter];
if (e) {
filter = [...formatFilter, id];
} else {
filter.splice(formatFilter.indexOf(id), 1);
}
console.log("Format filter", filter);
setFormatFilter(filter);
};
const cleanCheckbox = () => {
setCategoryFilter([]);
setFormatFilter([]);
router.push(`?category=&title=`);
setDateRange([null, null]);
setMonthYearFilter(null);
setChange(!change);
};
async function getDataRegional() {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listDataRegional(
"3",
name,
filter,
format,
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : "",
12,
pages,
sortByOpt
);
close();
setGetTotalPage(response?.data?.data?.totalPages);
setContentDocument(response?.data?.data?.content);
setTotalContent(response?.data?.data?.totalElements);
}
const table = useReactTable({
data: documentData,
columns: columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
onPaginationChange: setPagination,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
pagination,
},
});
useEffect(() => {
initFetch();
}, [page]);
const initFetch = async () => {
const response = await getListContent({ page: page - 1, size: 6, sortBy: "createdAt", contentTypeId: "3" });
console.log(response);
setDocumentData(response?.data?.data?.content);
const data = response?.data?.data;
const contentData = data?.content;
setDocumentData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
};
function getSelectedCategory() {
const filter = [];
if (categorie) {
const categoryArr = categorie.split(",");
for (const element of categoryArr) {
filter.push(Number(element));
}
setCategoryFilter(filter);
}
}
const handleDeleteDate = () => {
setDateRange([null, null]);
setStartDateString("");
setEndDateString("");
setHandleClose(false);
};
const handleSorting = (e: any) => {
console.log(e.target.value);
if (e.target.value == "terbaru") {
setSortByOpt("createdAt");
} else {
setSortByOpt("clickCount");
}
setChange(!change);
};
async function getUserLevels() {
const res = await getUserLevelListByParent(761);
const userLevelList = res?.data?.data;
if (userLevelList !== null) {
let optionArr: any = [];
userLevelList?.map((option: any) => {
let optionData = {
id: option.id,
label: option.name,
value: option.id,
};
optionArr.push(optionData);
});
setUserLevels(optionArr);
}
}
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
async function doneTyping() {
if (searchTitle == "" || searchTitle == undefined) {
router.push("?title=");
} else {
router.push(`?title=${searchTitle}`);
}
}
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
return (
<div className="flex flex-col">
{/* Header */}
<div className="flex flex-col md:flex-row items-start gap-5 p-10 bg-[#f7f7f7] dark:bg-black">
<p>
{" "}
{t("text")} {">"} <span className="font-bold">{t("allText")}</span>
</p>
<p className="font-bold">|</p>
<p>{`${t("thereIs")} ${totalContent} ${t("downloadableText")}`}</p>
</div>
{/* Left */}
<div className="flex flex-col lg:flex-row gap-6 p-4">
<div className="lg:w-[25%] w-full bg-[#f7f7f7] dark:bg-black p-4 rounded-lg shadow-md">
<h2 className="text-lg font-semibold mb-4 flex items-center gap-1">
<Icon icon="stash:filter-light" fontSize={30} />
Filter
</h2>
<div className="border-t border-black my-4 dark:border-white"></div>
<div className="space-y-6">
<div>
<label htmlFor="search" className="block text-sm font-medium text-gray-700 dark:text-white">
{t("search")}
</label>
<Input
value={searchTitle}
onChange={(e) => setSearchTitle(e.target.value)}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
type="text"
id="search"
placeholder={t("searchTitle")}
className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("monthYear")}</label>
<ReactDatePicker
selected={monthYearFilter}
className="mt-1 w-full text-xs border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
onChange={(date) => setMonthYearFilter(date)}
dateFormat="MM | yyyy"
placeholderText={t("selectYear")}
showMonthYearPicker
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("date")}</label>
<div className="flex flex-row justify justify-between gap-2">
<ReactDatePicker
selectsRange
className="mt-1 w-full border text-sm rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
startDate={dateRange[0]}
endDate={dateRange[1]}
onChange={(update) => {
setDateRange(update);
}}
placeholderText={t("selectDate")}
onCalendarClose={() => setCalenderState(!calenderState)}
/>
<div className="flex items-center">{handleClose ? <Icon icon="carbon:close-filled" onClick={handleDeleteDate} width="20" inline color="#216ba5" /> : ""}</div>
</div>
</div>
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">{t("categories")}</h3>
<ul className="mt-2 space-y-2">
{categories.map((category: any) => (
<li key={category?.id}>
<label className="inline-flex items-center" htmlFor={`${category.id}`}>
<Checkbox id={`${category.id}`} value={category.id} checked={categoryFilter.includes(String(category.id))} onCheckedChange={(e) => handleCategoryFilter(Boolean(e), category.id)} />
<span className="ml-2 text-gray-700 dark:text-white">{category?.name}</span>
</label>
</li>
))}
</ul>
</div>
{/* Garis */}
<div className="border-t border-black my-4 dark:border-white"></div>
{/* Garis */}
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">Format</h3>
<ul className="mt-2 space-y-2">
<li>
<label className="inline-flex items-center">
<Checkbox id="doc" value="doc" checked={formatFilter.includes("doc")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "doc")} />
<span className="ml-2 text-gray-700 dark:text-white">DOC</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="docx" value="docx" checked={formatFilter.includes("docx")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "docx")} />
<span className="ml-2 text-gray-700 dark:text-white">DOCX</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="pdf" value="pdf" checked={formatFilter.includes("pdf")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "pdf")} />
<span className="ml-2 text-gray-700 dark:text-white">PDF</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="ppt" value="ppt" checked={formatFilter.includes("ppt")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "ppt")} />
<span className="ml-2 text-gray-700 dark:text-white">PPT</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="pptx" value="pptx" checked={formatFilter.includes("pptx")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "pptx")} />
<span className="ml-2 text-gray-700 dark:text-white">PPTX</span>
</label>
</li>
</ul>
</div>
<div className="border-t border-black dark:border-white my-4"></div>
<div className="text-center">
<a onClick={cleanCheckbox} className="text-[#bb3523] cursor-pointer">
<b>Reset Filter</b>
</a>
</div>
</div>
</div>
{/* Konten Kanan */}
<Reveal>
<div className="flex-1">
<div className="flex flex-col items-end mb-4">
<h2 className="text-lg font-semibold">{t("sortBy")}</h2>
<select defaultValue={sortBy == "popular" ? "terpopuler" : "terbaru"} onChange={(e) => handleSorting(e)} className="border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500">
<option value="latest">{t("latest")}</option>
<option value="popular">{t("mostPopular")}</option>
</select>
</div>
{documentData?.length > 0 ? (
<div className=" grid grid-cols-1 md:grid-cols-2 gap-6">
{documentData?.map((document: any) => (
<Link href={`${prefixPath}/document/detail/${document?.slug}`} 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">
<div className="flex items-center justify-center rounded-lg w-16 h-16">
<svg width="28" height="34" viewBox="0 0 28 34" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M5.6665 17.4167C5.6665 17.0851 5.7982 16.7672 6.03262 16.5328C6.26704 16.2984 6.58498 16.1667 6.9165 16.1667C7.24802 16.1667 7.56597 16.2984 7.80039 16.5328C8.03481 16.7672 8.1665 17.0851 8.1665 17.4167C8.1665 17.7482 8.03481 18.0661 7.80039 18.3005C7.56597 18.535 7.24802 18.6667 6.9165 18.6667C6.58498 18.6667 6.26704 18.535 6.03262 18.3005C5.7982 18.0661 5.6665 17.7482 5.6665 17.4167ZM6.9165 21.1667C6.58498 21.1667 6.26704 21.2984 6.03262 21.5328C5.7982 21.7672 5.6665 22.0851 5.6665 22.4167C5.6665 22.7482 5.7982 23.0661 6.03262 23.3005C6.26704 23.535 6.58498 23.6667 6.9165 23.6667C7.24802 23.6667 7.56597 23.535 7.80039 23.3005C8.03481 23.0661 8.1665 22.7482 8.1665 22.4167C8.1665 22.0851 8.03481 21.7672 7.80039 21.5328C7.56597 21.2984 7.24802 21.1667 6.9165 21.1667ZM5.6665 27.4167C5.6665 27.0851 5.7982 26.7672 6.03262 26.5328C6.26704 26.2984 6.58498 26.1667 6.9165 26.1667C7.24802 26.1667 7.56597 26.2984 7.80039 26.5328C8.03481 26.7672 8.1665 27.0851 8.1665 27.4167C8.1665 27.7482 8.03481 28.0661 7.80039 28.3005C7.56597 28.535 7.24802 28.6667 6.9165 28.6667C6.58498 28.6667 6.26704 28.535 6.03262 28.3005C5.7982 28.0661 5.6665 27.7482 5.6665 27.4167ZM11.9165 16.1667C11.585 16.1667 11.267 16.2984 11.0326 16.5328C10.7982 16.7672 10.6665 17.0851 10.6665 17.4167C10.6665 17.7482 10.7982 18.0661 11.0326 18.3005C11.267 18.535 11.585 18.6667 11.9165 18.6667H21.0832C21.4147 18.6667 21.7326 18.535 21.9671 18.3005C22.2015 18.0661 22.3332 17.7482 22.3332 17.4167C22.3332 17.0851 22.2015 16.7672 21.9671 16.5328C21.7326 16.2984 21.4147 16.1667 21.0832 16.1667H11.9165ZM10.6665 22.4167C10.6665 22.0851 10.7982 21.7672 11.0326 21.5328C11.267 21.2984 11.585 21.1667 11.9165 21.1667H21.0832C21.4147 21.1667 21.7326 21.2984 21.9671 21.5328C22.2015 21.7672 22.3332 22.0851 22.3332 22.4167C22.3332 22.7482 22.2015 23.0661 21.9671 23.3005C21.7326 23.535 21.4147 23.6667 21.0832 23.6667H11.9165C11.585 23.6667 11.267 23.535 11.0326 23.3005C10.7982 23.0661 10.6665 22.7482 10.6665 22.4167ZM11.9165 26.1667C11.585 26.1667 11.267 26.2984 11.0326 26.5328C10.7982 26.7672 10.6665 27.0851 10.6665 27.4167C10.6665 27.7482 10.7982 28.0661 11.0326 28.3005C11.267 28.535 11.585 28.6667 11.9165 28.6667H21.0832C21.4147 28.6667 21.7326 28.535 21.9671 28.3005C22.2015 28.0661 22.3332 27.7482 22.3332 27.4167C22.3332 27.0851 22.2015 26.7672 21.9671 26.5328C21.7326 26.2984 21.4147 26.1667 21.0832 26.1667H11.9165ZM26.3565 11.0233L16.6415 1.31C16.6157 1.28605 16.5885 1.26378 16.5598 1.24333C16.5392 1.22742 16.5192 1.21074 16.4998 1.19333C16.3852 1.08512 16.2632 0.984882 16.1348 0.893332C16.0922 0.865802 16.0476 0.841298 16.0015 0.819999L15.9215 0.779999L15.8382 0.731666C15.7482 0.679999 15.6565 0.626665 15.5615 0.586665C15.2296 0.454104 14.8783 0.376423 14.5215 0.356665C14.4885 0.354519 14.4557 0.350625 14.4232 0.344999C14.3779 0.338012 14.3323 0.334114 14.2865 0.333332H3.99984C3.11578 0.333332 2.26794 0.684521 1.64281 1.30964C1.01769 1.93476 0.666504 2.78261 0.666504 3.66667V30.3333C0.666504 31.2174 1.01769 32.0652 1.64281 32.6904C2.26794 33.3155 3.11578 33.6667 3.99984 33.6667H23.9998C24.8839 33.6667 25.7317 33.3155 26.3569 32.6904C26.982 32.0652 27.3332 31.2174 27.3332 30.3333V13.38C27.333 12.496 26.9817 11.6483 26.3565 11.0233ZM24.8332 30.3333C24.8332 30.5543 24.7454 30.7663 24.5891 30.9226C24.4328 31.0789 24.2208 31.1667 23.9998 31.1667H3.99984C3.77882 31.1667 3.56686 31.0789 3.41058 30.9226C3.2543 30.7663 3.1665 30.5543 3.1665 30.3333V3.66667C3.1665 3.44565 3.2543 3.23369 3.41058 3.07741C3.56686 2.92113 3.77882 2.83333 3.99984 2.83333H13.9998V10.3333C13.9998 11.2174 14.351 12.0652 14.9761 12.6904C15.6013 13.3155 16.4491 13.6667 17.3332 13.6667H24.8332V30.3333ZM16.4998 4.70166L22.9632 11.1667H17.3332C17.1122 11.1667 16.9002 11.0789 16.7439 10.9226C16.5876 10.7663 16.4998 10.5543 16.4998 10.3333V4.70166Z"
fill="black"
/>
</svg>
</div>
<div className="flex flex-col flex-1 gap-2">
<div className="text-gray-500 dark:text-gray-400 flex flex-row items-center gap-2 text-xs">
{formatDateToIndonesian(new Date(document?.createdAt))} {document?.timezone ? document?.timezone : "WIB"} | <Icon icon="formkit:eye" width="15" height="15" /> 518
</div>
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">{document?.title}</div>
<div className="flex gap-2 items-center text-xs text-red-500 dark:text-red-500">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512">
<path fill="#f00" d="M224 30v256h-64l96 128l96-128h-64V30zM32 434v48h448v-48z" />
</svg>
Download Dokumen
</div>
</div>
</Link>
))}
</div>
) : (
<p className="flex items-center justify-center">
<img src="/assets/empty-data.png" alt="empty" className="h-60 w-60 my-4" />
</p>
)}
<LandingPagination table={table} totalData={totalData} totalPage={totalPage} />
</div>
</Reveal>
</div>
</div>
);
};
export default FilterPage;

View File

@ -0,0 +1,20 @@
import LayoutProvider from "@/providers/layout.provider";
import LayoutContentProvider from "@/providers/content.provider";
import DashCodeSidebar from "@/components/partials/sidebar";
import DashCodeFooter from "@/components/partials/footer";
import ThemeCustomize from "@/components/partials/customizer";
import DashCodeHeader from "@/components/partials/header";
import { redirect } from "@/components/navigation";
import Footer from "@/components/landing-page/footer";
import Navbar from "@/components/landing-page/navbar";
const layout = async ({ children }: { children: React.ReactNode }) => {
return (
<>
{children}
</>
);
};
export default layout;

View File

@ -0,0 +1,347 @@
"use client";
import { Textarea } from "@/components/ui/textarea";
import { useParams, usePathname } from "next/navigation";
import React, { useEffect, useState } from "react";
import { Icon } from "@iconify/react/dist/iconify.js";
import NewContent from "@/components/landing-page/new-content";
import { useToast } from "@/components/ui/use-toast";
import { getCookiesDecrypt } from "@/lib/utils";
import { close, error, loading } from "@/config/swal";
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
import { Link, useRouter } from "@/i18n/routing";
const DetailInfo = () => {
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 [detailDataImage, setDetailDataImage] = useState<any>();
const [selectedImage, setSelectedImage] = useState(0);
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 [imageSizeSelected, setImageSizeSelected] = useState("l");
const userId = getCookiesDecrypt("uie");
useEffect(() => {
initFetch();
checkWishlist();
}, []);
const initFetch = async () => {
const response = await getDetail(String(slug));
console.log("detailImage", 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,
});
setDetailDataImage(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
// const response = await postActivityLog(data, token);
// console.log(response);
}
const handleDownload = () => {
if (downloadProgress === 0) {
if (!userId) {
router.push("/auth/login");
} 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}`);
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 === "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="min-h-screen px-4 md:px-24 py-4">
<div className="rounded-md overflow-hidden md:flex">
{/* Bagian Kiri */}
<div className="md:w-3/4">
{/* Gambar Besar */}
<div className="relative">
<img src={detailDataImage?.files[selectedImage]?.url} alt="Main" className="rounded-lg w-auto h-fit" />
<div className="absolute top-4 left-4"></div>
</div>
{/* Gambar bawah Kecil */}
<div className="py-4 flex flex-row gap-3">
{detailDataImage?.files?.map((file: any, index: number) => (
<a onClick={() => setSelectedImage(index)} key={file?.id}>
<img src={file?.url} className="w-[120px] h-[90px] object-cover rounded-md cursor-pointer hover:ring-2 hover:ring-red-600" />
</a>
))}
</div>
{/* Footer Informasi */}
<div className="text-sm text-gray-500 flex justify-between items-center border-t mt-4">
<div className="flex flex-row items-center mt-3 justify-between">
oleh&nbsp;<span className="font-semibold text-black">{detailDataImage?.uploadedBy?.userLevel?.name}</span>&nbsp; | &nbsp;Diupdate pada {detailDataImage?.updatedAt} WIB &nbsp;|&nbsp;
<Icon icon="formkit:eye" width="15" height="15" />
&nbsp; {detailDataImage?.clickCount} &nbsp;
<p className="flex text-end">Kreator: {detailDataImage?.creatorName}</p>
</div>
</div>
{/* Keterangan */}
<div className="w-full">
<h1 className="flex flex-row font-bold text-2xl my-8">{detailDataImage?.title}</h1>
<div className="font-light text-justify" dangerouslySetInnerHTML={{ __html: detailDataImage?.htmlDescription }} />
</div>
</div>
{/* Bagian Kanan */}
<div className="md:w-1/4 p-4 bg-[#f7f7f7] h-fit rounded-lg mx-4">
{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=${detailDataImage?.category.id}`} className="bg-red-600 text-white text-xs font-bold px-3 py-3 my-3 flex justify-center items-center rounded">
{detailDataImage?.category?.name}
</Link>
<div className="flex justify-center flex-wrap gap-2 mb-4">
{detailDataImage?.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">
{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 Foto</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] rounded-lg w-fit text-white px-4 py-1">Kirim</button>
</div>
</div>
{/* Konten Serupa */}
<div className="">
<NewContent group="polda" type={"similar"} />
</div>
</div>
</>
);
};
export default DetailInfo;

View File

@ -0,0 +1,515 @@
"use client";
import React, { useEffect, useState } from "react";
import { Card, CardContent } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import { Icon } from "@iconify/react/dist/iconify.js";
import { formatDateToIndonesian, getOnlyDate, getOnlyMonthAndYear } from "@/utils/globals";
import { useParams, usePathname, useSearchParams } from "next/navigation";
import { getUserLevelListByParent, listCategory, listData, listDataRegional } from "@/service/landing/landing";
import { ColumnDef, ColumnFiltersState, PaginationState, SortingState, VisibilityState, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
import LandingPagination from "@/components/landing-page/pagination";
import { Reveal } from "@/components/landing-page/Reveal";
import { Link, useRouter } from "@/i18n/routing";
import { Input } from "@/components/ui/input";
import ReactDatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { close, loading } from "@/config/swal";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
];
const FilterPage = () => {
const router = useRouter();
const asPath = usePathname();
const params = useParams();
const searchParams = useSearchParams();
const locale = params?.locale;
const [imageData, setImageData] = useState<any>();
const [totalData, setTotalData] = React.useState<number>(1);
const [totalPage, setTotalPage] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [page, setPage] = useState(1);
const [totalContent, setTotalContent] = useState();
const [change, setChange] = useState(false);
const sortBy = searchParams?.get("sortBy");
const title = searchParams?.get("title");
const categorie = searchParams?.get("category");
const group = searchParams?.get("group");
const [contentImage, setContentImage] = useState([]);
const [, setGetTotalPage] = useState();
let typingTimer: any;
const doneTypingInterval = 1500;
const [search, setSearch] = useState();
const [categoryFilter, setCategoryFilter] = useState<any>([]);
const [monthYearFilter, setMonthYearFilter] = useState<any>();
const [searchTitle, setSearchTitle] = useState<string>("");
const [sortByOpt, setSortByOpt] = useState<any>(sortBy === "popular" ? "clickCount" : "createdAt");
const isRegional = asPath?.includes("regional");
const isSatker = asPath?.includes("satker");
const [formatFilter, setFormatFilter] = useState<any>([]);
const pages = page ? page - 1 : 0;
const [startDateString, setStartDateString] = useState<any>();
const [endDateString, setEndDateString] = useState<any>();
const [dateRange, setDateRange] = useState<any>([null, null]);
const [calenderState, setCalenderState] = useState(false);
const [handleClose, setHandleClose] = useState(false);
const [categories, setCategories] = useState([]);
const [userLevels, setUserLevels] = useState([]);
const poldaName = params?.polda_name;
let prefixPath = poldaName ? `/polda/${poldaName}` : "/";
const t = useTranslations("FilterPage");
// const [startDate, endDate] = dateRange;
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) {
setPage(Number(pageFromUrl));
}
}, [searchParams]);
useEffect(() => {
async function initState() {
getCategories();
// getSelectedCategory();
if (isSatker) {
getUserLevels();
}
}
initState();
}, []);
useEffect(() => {
if (categorie) {
setCategoryFilter(categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
console.log("Kategori", categorie, categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
}
}, [categorie]);
// useEffect(() => {
// fetchData();
// }, [page, sortBy, sortByOpt, title]);
useEffect(() => {
async function initState() {
if (isRegional) {
getDataRegional();
} else {
getDataAll();
}
}
console.log(monthYearFilter, "monthFilter");
initState();
}, [change, asPath, monthYearFilter, page, sortBy, sortByOpt, title, startDateString, endDateString, categorie, formatFilter]);
async function getCategories() {
const category = await listCategory("1");
const resCategory = category?.data?.data?.content;
setCategories(resCategory);
}
useEffect(() => {
function initState() {
if (dateRange[0] != null && dateRange[1] != null) {
setStartDateString(getOnlyDate(dateRange[0]));
setEndDateString(getOnlyDate(dateRange[1]));
setHandleClose(true);
console.log("date range", dateRange, getOnlyDate(dateRange[0]));
}
}
initState();
}, [calenderState]);
async function getDataAll() {
if (poldaName && String(poldaName)?.length > 1) {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
const filterGroup = String(poldaName);
loading();
const response = await listData(
"1",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
filterGroup,
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
const data = response?.data?.data;
const contentData = data?.content;
setImageData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response?.data?.data?.totalElements);
} else {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listData(
"1",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
const data = response?.data?.data;
const contentData = data?.content;
setImageData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response?.data?.data?.totalElements);
}
}
const handleCategoryFilter = (e: boolean, id: string) => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
};
const handleFormatFilter = (e: boolean, id: string) => {
let filter = [...formatFilter];
if (e) {
filter = [...formatFilter, id];
} else {
filter.splice(formatFilter.indexOf(id), 1);
}
console.log("Format filter", filter);
setFormatFilter(filter);
};
const cleanCheckbox = () => {
setCategoryFilter([]);
setFormatFilter([]);
router.push(`?category=&title=`);
setDateRange([null, null]);
setMonthYearFilter(null);
setChange(!change);
};
async function getDataRegional() {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listDataRegional(
"1",
name,
filter,
format,
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : "",
12,
pages,
sortByOpt
);
close();
setGetTotalPage(response?.data?.data?.totalPages);
setContentImage(response?.data?.data?.content);
setTotalContent(response?.data?.data?.totalElements);
}
const table = useReactTable({
data: imageData,
columns: columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
onPaginationChange: setPagination,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
pagination,
},
});
function getSelectedCategory() {
const filter = [];
if (categorie) {
const categoryArr = categorie.split(",");
for (const element of categoryArr) {
filter.push(Number(element));
}
setCategoryFilter(filter);
}
}
const handleDeleteDate = () => {
setDateRange([null, null]);
setStartDateString("");
setEndDateString("");
setHandleClose(false);
};
const handleSorting = (e: any) => {
console.log(e.target.value);
if (e.target.value == "terbaru") {
setSortByOpt("createdAt");
} else {
setSortByOpt("clickCount");
}
setChange(!change);
};
async function getUserLevels() {
const res = await getUserLevelListByParent(761);
const userLevelList = res?.data?.data;
if (userLevelList !== null) {
let optionArr: any = [];
userLevelList?.map((option: any) => {
let optionData = {
id: option.id,
label: option.name,
value: option.id,
};
optionArr.push(optionData);
});
setUserLevels(optionArr);
}
}
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
async function doneTyping() {
if (searchTitle == "" || searchTitle == undefined) {
router.push("?title=");
} else {
router.push(`?title=${searchTitle}`);
}
}
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
return (
<div className="flex flex-col">
{/* Header */}
<div className="flex flex-col md:flex-row items-start gap-5 p-10 bg-[#f7f7f7] dark:bg-black">
<p>
{" "}
{t("image")} {">"} <span className="font-bold">{t("allImage")}</span>
</p>
<p className="font-bold">|</p>
<p>{`${t("thereIs")} ${totalContent} ${t("downloadableImage")}`}</p>
</div>
{/* Left */}
<div className="flex flex-col lg:flex-row gap-6 p-4">
<div className="lg:w-[25%] h-fit w-full bg-[#f7f7f7] dark:bg-black p-4 rounded-lg shadow-md">
<h2 className="text-lg font-semibold mb-4 flex items-center gap-1">
<Icon icon="stash:filter-light" fontSize={30} />
Filter
</h2>
<div className="border-t border-black my-4 dark:border-white"></div>
<div className="space-y-6">
<div>
<label htmlFor="search" className="block text-sm font-medium text-gray-700 dark:text-white">
{t("search")}
</label>
<Input
value={searchTitle}
onChange={(e) => setSearchTitle(e.target.value)}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
type="text"
id="search"
placeholder={t("searchTitle")}
className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("monthYear")}</label>
<ReactDatePicker
selected={monthYearFilter}
className="mt-1 w-full text-xs border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
onChange={(date) => setMonthYearFilter(date)}
dateFormat="MM | yyyy"
placeholderText={t("selectYear")}
showMonthYearPicker
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("date")}</label>
<div className="flex flex-row justify justify-between gap-2">
<ReactDatePicker
selectsRange
className="mt-1 w-full border text-sm rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
startDate={dateRange[0]}
endDate={dateRange[1]}
onChange={(update) => {
setDateRange(update);
}}
placeholderText={t("selectDate")}
onCalendarClose={() => setCalenderState(!calenderState)}
/>
<div className="flex items-center">{handleClose ? <Icon icon="carbon:close-filled" onClick={handleDeleteDate} width="20" inline color="#216ba5" /> : ""}</div>
</div>
</div>
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">{t("categories")}</h3>
<ul className="mt-2 space-y-2">
{categories.map((category: any) => (
<li key={category?.id}>
<label className="inline-flex items-center" htmlFor={`${category.id}`}>
<Checkbox id={`${category.id}`} value={category.id} checked={categoryFilter.includes(String(category.id))} onCheckedChange={(e) => handleCategoryFilter(Boolean(e), category.id)} />
<span className="ml-2 text-gray-700 dark:text-white">{category?.name}</span>
</label>
</li>
))}
</ul>
</div>
{/* Garis */}
<div className="border-t border-black my-4 dark:border-white"></div>
{/* Garis */}
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">Format</h3>
<ul className="mt-2 space-y-2">
<li>
<label className="inline-flex items-center">
<Checkbox id="png" value="png" checked={formatFilter.includes("png")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "png")} />
<span className="ml-2 text-gray-700 dark:text-white">PNG</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="jpeg" value="jpeg" checked={formatFilter.includes("jpeg")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "jpeg")} />
<span className="ml-2 text-gray-700 dark:text-white">JPEG</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="jpg" value="jpg" checked={formatFilter.includes("jpg")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "jpg")} />
<span className="ml-2 text-gray-700 dark:text-white">JPG</span>
</label>
</li>
</ul>
</div>
<div className="border-t border-black dark:border-white my-4"></div>
<div className="text-center">
<a onClick={cleanCheckbox} className="text-[#bb3523] cursor-pointer">
<b>Reset Filter</b>
</a>
</div>
</div>
</div>
{/* Konten Kanan */}
<Reveal>
<div className="flex-1">
<div className="flex flex-col items-end mb-4">
<h2 className="text-lg font-semibold">{t("sortBy")}</h2>
<select defaultValue={sortBy == "popular" ? "terpopuler" : "terbaru"} onChange={(e) => handleSorting(e)} className="border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500">
<option value="latest">{t("latest")}</option>
<option value="popular">{t("mostPopular")}</option>
</select>
</div>
{imageData?.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{imageData?.map((image: any) => (
<Card key={image?.id} className="hover:scale-110 transition-transform duration-300">
<CardContent className="flex flex-col text-xs lg:text-sm w-full p-0">
<Link href={`${prefixPath}/image/detail/${image?.slug}`}>
<img src={image?.thumbnailLink} className="h-60 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
<div className="flex flex-row items-center gap-2 text-[10px] mx-2">
{formatDateToIndonesian(new Date(image?.createdAt))} {image?.timezone ? image?.timezone : "WIB"}| <Icon icon="formkit:eye" width="15" height="15" />
{image?.clickCount}{" "}
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 20 20">
<path
fill="#f00"
d="M7.707 10.293a1 1 0 1 0-1.414 1.414l3 3a1 1 0 0 0 1.414 0l3-3a1 1 0 0 0-1.414-1.414L11 11.586V6h5a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h5v5.586zM9 4a1 1 0 0 1 2 0v2H9z"
/>
</svg>{" "}
</div>
<div className="font-semibold pr-3 pb-3 mx-2 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible w-full">{image?.title}</div>
</Link>
</CardContent>
</Card>
))}
</div>
) : (
<p className="flex items-center justify-center text-black">
<img src="/assets/empty-data.png" alt="empty" className="h-60 w-60 my-4" />
</p>
)}
<LandingPagination table={table} totalData={totalData} totalPage={totalPage} />
</div>
</Reveal>
</div>
</div>
);
};
export default FilterPage;

View File

@ -0,0 +1,12 @@
import Footer from "@/components/landing-page/footer";
import Navbar from "@/components/landing-page/navbar";
const layout = async ({ children }: { children: React.ReactNode }) => {
return (
<>
{children}
</>
);
};
export default layout;

View File

@ -0,0 +1,96 @@
"use client";
import { Textarea } from "@/components/ui/textarea";
import { useParams, usePathname, useRouter } from "next/navigation";
import React, { useEffect, useState } from "react";
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@/components/ui/carousel";
import { Link } from "@/i18n/routing";
import { getDetailIndeks, publicDetailBlog } from "@/service/landing/landing";
import { formatDateToIndonesian } from "@/utils/globals";
import { Icon } from "@iconify/react/dist/iconify.js";
const IndeksDetail = () => {
const [indeksData, setIndeksData] = useState<any>();
const params = useParams();
const slug = params?.slug;
const [relatedPost, setRelatedPost] = useState<any>();
useEffect(() => {
initFetch();
detailFetch();
}, []);
const initFetch = async () => {
const response = await getDetailIndeks();
console.log(response);
setRelatedPost(response?.data?.data?.content);
};
const detailFetch = async () => {
const response = await publicDetailBlog(slug);
console.log(response);
setIndeksData(response?.data?.data);
};
return (
<>
<div className="p-4 lg:px-60 lg:p-12">
{/* Judul */}
<div className="flex flex-col mb-5">
<h1 className="text-lg mb-2">Indeks / Detail</h1>
<h1 className="flex flex-row font-bold text-center text-2xl">{indeksData?.title}</h1>
</div>
{/* Gambar Utama */}
<div className="flex items-center justify-center">
<img src={indeksData?.thumbnailLink} alt="Main" className="h-[550px] w-full rounded-lg" />
</div>
{/* Footer Informasi */}
<div className="text-sm text-gray-500 flex justify-between items-center border-t mt-4">
<div className="flex flex-row items-center mt-3 justify-between">
oleh&nbsp;<span className="font-semibold text-black">{indeksData?.uploaderName}</span>&nbsp; | &nbsp;Diupdate pada {indeksData?.createdAt} WIB &nbsp;
</div>
</div>
{/* Keterangan */}
<div className="w-auto">
<p className="font-light text-justify" dangerouslySetInnerHTML={{ __html: indeksData?.description }} />
</div>
</div>
{/* Comment */}
<div className="w-full">
<div className="flex flex-col py-5 p-10 bg-[#f7f7f7]">
<div className="gap-5 flex flex-col px-4 lg:px-16">
<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="space-x-5 flex flex-col px-4 lg:px-16 py-16 gap-5">
<h1 className="font-bold text-base lg:text-xl px-4 lg:px-8">Post Terkait</h1>
<Carousel>
<CarouselContent className="w-full max-w-7xl">
{relatedPost?.map((relate: any) => (
<CarouselItem key={relate?.id} className="md:basis-1/2 lg:basis-1/3">
<Link href={`/indeks/detail/${relate?.slug}`} className="relative group overflow-hidden shadow-md hover:shadow-lg">
<img src={relate?.thumbnailLink} className="w-full rounded-lg h-40 lg:h-60 object-cover group-hover:scale-100 transition-transform duration-300" />
<div className="absolute bottom-0 left-0 right-0 bg-gray-600 border-l-4 border-[#bb3523] rounded-lg backdrop-blur-sm text-white p-2">
<span className="text-white bg-[#bb3523] rounded-md w-full h-full font-semibold uppercase text-sm px-4 py-1">{relate?.categoryName}</span>
<h1 className="text-sm lg:text-lg mb-2 font-semibold h-5 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible">{relate?.title}</h1>
<p className="flex flex-row items-center text-[10px] gap-2">
{formatDateToIndonesian(new Date(relate?.createdAt))} {relate?.timezone ? relate?.timezone : "WIB"} | <Icon icon="formkit:eye" width="15" height="15" /> {relate.clickCount}{" "}
</p>
</div>
</Link>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
</div>
</div>
</>
);
};
export default IndeksDetail;

View File

@ -0,0 +1,20 @@
import LayoutProvider from "@/providers/layout.provider";
import LayoutContentProvider from "@/providers/content.provider";
import DashCodeSidebar from "@/components/partials/sidebar";
import DashCodeFooter from "@/components/partials/footer";
import ThemeCustomize from "@/components/partials/customizer";
import DashCodeHeader from "@/components/partials/header";
import { redirect } from "@/components/navigation";
import Footer from "@/components/landing-page/footer";
import Navbar from "@/components/landing-page/navbar";
const layout = async ({ children }: { children: React.ReactNode }) => {
return (
<>
{children}
</>
);
};
export default layout;

View File

@ -0,0 +1,116 @@
"use client";
import { Link } from "@/i18n/routing";
import { getIndeksData } from "@/service/landing/landing";
import { formatDateToIndonesian } from "@/utils/globals";
import { usePathname } from "next/navigation";
import React, { useEffect, useState } from "react";
const Indeks: React.FC = () => {
const pathname = usePathname();
const [indeksData, setIndeksData] = useState<any>();
let count: number = 0;
useEffect(() => {
if (indeksData) {
const intervalId = setInterval(() => {
count = (count + 1) % indeksData.length;
}, 5000);
return () => clearInterval(intervalId);
}
}, [indeksData]);
useEffect(() => {
initFetch();
}, []);
const initFetch = async () => {
const response = await getIndeksData();
console.log(response);
setIndeksData(response?.data?.data?.content);
};
return (
<div className="px-4 lg:px-14">
{/* Hero Left */}
<div className="flex flex-col lg:flex-row items-start gap-8 px-4 lg:px-10 py-4 mx-auto">
<div className="lg:w-[60%] w-full lg:h-full">
{indeksData?.map(
(indeks: any, index: number) =>
index == count && (
<div key={indeks?.id} className="relative h-[310px] lg:h-[435px]">
<img src={indeks?.thumbnailLink} alt="image" className="w-full h-[310px] lg:h-[435px] rounded-lg" />
<div className="absolute bottom-0 left-0 right-0 bg-transparent backdrop-blur-sm text-white p-4 rounded-b-lg">
<span className="text-white bg-[#bb3523] rounded-md w-full h-full font-semibold uppercase text-sm px-4 py-1">{indeks?.categoryName}</span>
<Link href={`/indeks/detail/${indeks?.slug}`}>
<h2 className="text-2xl font-bold mt-2">{indeks?.title}</h2>
</Link>
<p className="text-xs flex flex-row items-center gap-1 mt-1 ml-2">
{formatDateToIndonesian(new Date(indeks?.createdAt))} {indeks?.timezone ? indeks?.timezone : "WIB"}|{" "}
<svg xmlns="http://www.w3.org/2000/svg" width="1.2em" height="1.2em" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M11.5 18c4 0 7.46-2.22 9.24-5.5C18.96 9.22 15.5 7 11.5 7s-7.46 2.22-9.24 5.5C4.04 15.78 7.5 18 11.5 18m0-12c4.56 0 8.5 2.65 10.36 6.5C20 16.35 16.06 19 11.5 19S3 16.35 1.14 12.5C3 8.65 6.94 6 11.5 6m0 2C14 8 16 10 16 12.5S14 17 11.5 17S7 15 7 12.5S9 8 11.5 8m0 1A3.5 3.5 0 0 0 8 12.5a3.5 3.5 0 0 0 3.5 3.5a3.5 3.5 0 0 0 3.5-3.5A3.5 3.5 0 0 0 11.5 9"
/>
</svg>{" "}
{indeks?.clickCount}
</p>
</div>
</div>
)
)}
</div>
{/* Hero Right */}
<div className="lg:w-[40%] w-full space-y-2">
{indeksData?.map(
(indeksRight: any, index: number) =>
(index == count + 1 || index == count + 2) && (
<div key={indeksRight?.id} className="relative h-[310px] lg:h-[215px]">
<img src={indeksRight?.thumbnailLink} alt="image" className="w-full h-[310px] lg:h-[215px] rounded-lg " />
<div className="absolute bottom-0 left-0 right-0 bg-transparent backdrop-blur-sm text-white p-4 rounded-b-lg">
<span className="text-white bg-[#bb3523] rounded-md w-full h-full font-semibold uppercase text-sm px-4 py-1">{indeksRight?.categoryName}</span>
<Link href={`/indeks/detail/${indeksRight?.slug}`}>
<h2 className="text-xl font-bold mt-2">{indeksRight?.title}</h2>
</Link>
<p className="text-xs flex flex-row items-center gap-1 mt-1 ml-2">
{formatDateToIndonesian(new Date(indeksRight?.createdAt))} {indeksRight?.timezone ? indeksRight?.timezone : "WIB"}|{" "}
<svg xmlns="http://www.w3.org/2000/svg" width="1.2em" height="1.2em" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M11.5 18c4 0 7.46-2.22 9.24-5.5C18.96 9.22 15.5 7 11.5 7s-7.46 2.22-9.24 5.5C4.04 15.78 7.5 18 11.5 18m0-12c4.56 0 8.5 2.65 10.36 6.5C20 16.35 16.06 19 11.5 19S3 16.35 1.14 12.5C3 8.65 6.94 6 11.5 6m0 2C14 8 16 10 16 12.5S14 17 11.5 17S7 15 7 12.5S9 8 11.5 8m0 1A3.5 3.5 0 0 0 8 12.5a3.5 3.5 0 0 0 3.5 3.5a3.5 3.5 0 0 0 3.5-3.5A3.5 3.5 0 0 0 11.5 9"
/>
</svg>{" "}
{indeksRight?.clickCount}
</p>
</div>
</div>
)
)}
</div>
</div>
{/* Bottom */}
<div className="px-4 lg:px-7 py-4">
<div className="flex flex-col gap-4">
{indeksData?.map(
(indeksBottom: any, index: number) =>
index < 3 && (
<div key={indeksBottom?.id} className="flex flex-col md:flex-row items-start p-4 gap-4">
<img src={indeksBottom?.thumbnailLink} alt="" className="h-40 object-cover rounded-lg w-full lg:w-full lg:h-[300px]" />
<div className="flex flex-col justify-between w-full">
<p className="text-sm">{indeksBottom?.date}</p>
<Link href={`/indeks/detail/${indeksBottom?.slug}`} className="text-2xl font-semibold text-gray-800">
{indeksBottom?.title}
</Link>
<p className="text-sm text-gray-600 mt-2">{indeksBottom?.description}</p>
</div>
</div>
)
)}
</div>
</div>
</div>
);
};
export default Indeks;

View File

@ -11,8 +11,8 @@ const page = () => {
<div>
<HeaderBanner />
<WelcomePolda />
<NewContent type="latest" />
<NewContent type="popular" />
<NewContent group="polda" type="latest" />
<NewContent group="polda" type="popular" />
<ContentCategory />
</div>
);

View File

@ -0,0 +1,20 @@
import LayoutProvider from "@/providers/layout.provider";
import LayoutContentProvider from "@/providers/content.provider";
import DashCodeSidebar from "@/components/partials/sidebar";
import DashCodeFooter from "@/components/partials/footer";
import ThemeCustomize from "@/components/partials/customizer";
import DashCodeHeader from "@/components/partials/header";
import { redirect } from "@/components/navigation";
import Footer from "@/components/landing-page/footer";
import Navbar from "@/components/landing-page/navbar";
const layout = async ({ children }: { children: React.ReactNode }) => {
return (
<>
{children}
</>
);
};
export default layout;

View File

@ -0,0 +1,741 @@
"use client";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { CalendarIcon } from "lucide-react";
import React, { useEffect, useState } from "react";
import { format } from "date-fns";
import { cn } from "@/lib/utils";
import { Checkbox } from "@/components/ui/checkbox";
import { Icon } from "@iconify/react/dist/iconify.js";
import { detailSchedule, listSchedule, listScheduleNextPublic, listSchedulePrevPublic, listScheduleTodayPublic } from "@/service/schedule/schedule";
import { useRouter } from "@/i18n/routing";
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog";
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
import { close, loading } from "@/config/swal";
const timeList = [
{
id: "6",
time: "06:00",
},
{
id: "7",
time: "07:00",
},
{
id: "8",
time: "08:00",
},
{
id: "9",
time: "09:00",
},
{
id: "10",
time: "10:00",
},
{
id: "11",
time: "11:00",
},
{
id: "12",
time: "12:00",
},
{
id: "13",
time: "13:00",
},
{
id: "14",
time: "14:00",
},
{
id: "15",
time: "15:00",
},
{
id: "16",
time: "16:00",
},
{
id: "17",
time: "17:00",
},
{
id: "18",
time: "18:00",
},
{
id: "19",
time: "19:00",
},
{
id: "20",
time: "20:00",
},
{
id: "21",
time: "21:00",
},
{
id: "22",
time: "22:00",
},
{
id: "23",
time: "23:00",
},
{
id: "24",
time: "24:00",
},
{
id: "1",
time: "01:00",
},
{
id: "2",
time: "02:00",
},
{
id: "3",
time: "03:00",
},
{
id: "4",
time: "04:00",
},
{
id: "5",
time: "05:00",
},
];
const Schedule = (props: any) => {
const router = useRouter();
const [startDate, setStartDate] = useState<Date | undefined>(new Date());
const [dateAWeek, setDateAWeek] = useState<string[]>([]);
const [scheduleSearch, setScheduleSearch] = useState();
const [todayList, setTodayList] = useState([]);
const [prevdayList, setPrevdayList] = useState([]);
const [nextdayList, setNextdayList] = useState([]);
const [isOpen, setIsOpen] = React.useState(false);
const [schedules, setSchedules] = useState([]);
const [openDialog, setOpenDialog] = useState(false);
const [detail, setDetail] = useState<any>();
const [content, setContent] = useState();
const { id } = props;
useEffect(() => {
async function getDataSchedule() {
const response = await detailSchedule(id);
setDetail(response?.data?.data);
setContent(response?.data?.data?.files);
}
getDataSchedule();
}, [id]);
useEffect(() => {
async function initState() {
getDataByDate();
// const group = isPolda ? asPath.split("/")[2] : regionFilter?.join(",");
const resSchedule = await listSchedule();
setSchedules(resSchedule.data?.data);
console.log(resSchedule);
setDateAWeek(dateList);
}
initState();
}, []);
async function getDataByDate() {
const resToday = await listScheduleTodayPublic();
const today = resToday.data?.data;
setTodayList(today);
const resNext = await listScheduleNextPublic();
const next = resNext.data?.data;
setNextdayList(next);
const resPrev = await listSchedulePrevPublic();
const prev = resPrev.data?.data;
setPrevdayList(prev);
}
const curr = new Date();
const startDays = (curr.getDay() + 7 - 1) % 7;
curr.setDate(curr.getDate() - startDays);
const dateListInit = [];
curr.setDate(curr.getDate() - curr.getDay() + 1);
for (let i = 0; i < 7; i++) {
dateListInit.push(new Date(curr).toISOString().slice(0, 10));
curr.setDate(curr.getDate() + 1);
}
const [dateList, setDateList] = useState<string[]>(dateListInit);
const handleChangeDate = (date: Date | undefined) => {
setStartDate(date);
const dateListTemp = [];
const curr = date;
if (curr) {
const startDays = (curr.getDay() + 7 - 1) % 7;
curr.setDate(curr.getDate() - startDays);
curr.setDate(curr.getDate() - curr.getDay() + 1);
for (let i = 0; i < 7; i++) {
dateListTemp.push(new Date(curr).toISOString().slice(0, 10));
curr.setDate(curr.getDate() + 1);
}
console.log("Change Date :", dateListTemp);
setDateList(dateListTemp);
setDateAWeek(dateListTemp);
}
};
function getLastWeek(today: Date | undefined) {
if (today) {
return new Date(today.getFullYear(), today.getMonth(), today.getDate() - 7);
}
}
function getNextWeek(today: Date | undefined) {
if (today) {
return new Date(today.getFullYear(), today.getMonth(), today.getDate() + 7);
}
}
const changeNextWeek = () => {
console.log("Today :", startDate);
const dayInNextWeek = getNextWeek(startDate);
console.log("Next week :", dayInNextWeek);
const dateListTemp = [];
const curr = dayInNextWeek;
if (curr) {
const startDays = (curr.getDay() + 7 - 1) % 7;
curr.setDate(curr.getDate() - startDays);
curr.setDate(curr.getDate() - curr.getDay() + 1);
for (let i = 0; i < 7; i++) {
const newDate = new Date(curr);
if (i == 0) {
setStartDate(newDate);
}
const dateFormatter = Intl.DateTimeFormat("sv-SE");
dateListTemp.push(dateFormatter.format(newDate));
curr.setDate(curr.getDate() + 1);
}
console.log(dateListTemp);
setDateList(dateListTemp);
setDateAWeek(dateListTemp);
}
};
const changePrevWeek = () => {
console.log(startDate);
const dayInPrevWeek = getLastWeek(startDate);
console.log("Prev week :", dayInPrevWeek);
console.log(startDate);
const dateListTemp = [];
const curr = dayInPrevWeek;
if (curr) {
const startDays = (curr.getDay() + 7 - 1) % 7;
curr.setDate(curr.getDate() - startDays);
curr.setDate(curr.getDate() - curr.getDay() + 1);
for (let i = 0; i < 7; i++) {
const newDate = new Date(curr);
if (i == 0) {
setStartDate(newDate);
}
const dateFormatter = Intl.DateTimeFormat("sv-SE");
dateListTemp.push(dateFormatter.format(newDate));
curr.setDate(curr.getDate() + 1);
}
console.log(dateListTemp);
setDateList(dateListTemp);
setDateAWeek(dateListTemp);
}
};
const getItem = async (itemFound: any) => {
loading();
const response = await detailSchedule(itemFound?.id);
setDetail(response?.data?.data);
setContent(response?.data?.data?.files);
console.log("item Found", itemFound);
close();
setOpenDialog(true);
};
function setItemSchedule(id: string, date: string) {
const itemFound: any = schedules?.filter((s: any) => s.dateInRange.includes(date) && s.timeIndex.split(",").includes(id));
if (itemFound?.length > 0) {
if (itemFound?.length == 1) {
return (
<a
className={`cursor-pointer text-center ${Number(itemFound[0]?.uploaderLevelNumber) == 1 ? "bg-yellow-300" : Number(itemFound[0]?.uploaderLevelNumber) == 2 ? "bg-blue-400" : "bg-gray-500"}`}
onClick={() => {
getItem(itemFound[0]);
}}
>
<p>
<b>{itemFound[0]?.title}</b>
</p>
{itemFound[0].isYoutube == true ? <p className="live">LIVE</p> : ""}
{/* <p className="address">{itemFound[0].address}</p> */}
</a>
);
}
// for (let i = 0; i < itemFound.length; i++) {
// const item = itemFound[i];
// }
return (
<div className="text-left">
<p>
<b>{`${itemFound?.length} Jadwal Bersamaan`}</b>
</p>
<DropdownMenu>
<DropdownMenuTrigger className="font-bold text-blue-300">Lihat Jadwal</DropdownMenuTrigger>
<DropdownMenuContent>
{itemFound?.map((list: any) => (
<DropdownMenuItem
key={list.id}
className="cursor-pointer"
onClick={() => {
getItem(itemFound[0]);
}}
>
<p>
<b>{list.title}</b>
</p>
{list.isYoutube == true ? <p className="live">LIVE</p> : ""}
{/* <p className="address">{list.address}</p> */}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
<div className="border-0 dropdown-menu schedule-list" aria-labelledby="view-schedule"></div>
</div>
);
}
}
return (
<>
{/* Awal Komponen Kiri */}
<div className="relative px-4 lg:px-10 lg:py-10 py-4 bg-[#f7f7f7] dark:bg-slate-800">
<Popover>
<PopoverTrigger asChild>
<Button variant={"outline"} className={cn("w-[240px] py-4 justify-start text-left font-normal", !startDate && "text-muted-foreground")}>
<CalendarIcon />
{startDate ? format(startDate, "MMM yyyy") : <span>Pick a date</span>}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={startDate}
onSelect={(e) => {
handleChangeDate(e);
}}
initialFocus
/>
</PopoverContent>
</Popover>
<div className="container relative py-4">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<a className="text-black flex flex-row w-fit gap-2 py-4 items-center cursor-pointer">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
<path fill="#000" d="M20 3H4a1 1 0 0 0-1 1v2.227l.008.223a3 3 0 0 0 .772 1.795L8 12.886V21a1 1 0 0 0 1.316.949l6-2l.108-.043A1 1 0 0 0 16 19v-6.586l4.121-4.12A3 3 0 0 0 21 6.171V4a1 1 0 0 0-1-1" />
</svg>
Filter
<svg className="flex items-center justify-center" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
<path fill="currentColor" fill-rule="evenodd" d="m6 7l6 6l6-6l2 2l-8 8l-8-8z" />
</svg>
</a>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="flex p-0 rounded-md">
<DropdownMenuItem className="flex flex-col items-center justify-between gap-1.5 p-2 border-b text-default-600 rounded-none">
<div className="gap-6 flex flex-row justify-end">
<div> Filter</div>
<button className="text-blue-400">Simpan</button>
</div>
<div className="border w-full border-t border-slate-500"></div>
<div className="overflow-y-auto flex flex-col gap-2 h-[200px] ">
<p>Region Filter</p>
<div className="mt-2 gap-2 flex flex-row">
<Checkbox id="terms" />
<p>POLDA METRO JAYA</p>
</div>
<div className="mt-2 gap-2 flex flex-row">
<Checkbox id="terms" />
<p>POLDA METRO JAYA</p>
</div>
<div className="mt-2 gap-2 flex flex-row">
<Checkbox id="terms" />
<p>POLDA METRO JAYA</p>
</div>
<div className="mt-2 gap-2 flex flex-row">
<Checkbox id="terms" />
<p>POLDA METRO JAYA</p>
</div>
<div className="mt-2 gap-2 flex flex-row">
<Checkbox id="terms" />
<p>POLDA METRO JAYA</p>
</div>
<div className="mt-2 gap-2 flex flex-row">
<Checkbox id="terms" />
<p>POLDA METRO JAYA</p>
</div>
<div className="mt-2 gap-2 flex flex-row">
<Checkbox id="terms" />
<p>POLDA METRO JAYA</p>
</div>
<div className="mt-2 gap-2 flex flex-row">
<Checkbox id="terms" />
<p>POLDA METRO JAYA</p>
</div>
<div className="mt-2 gap-2 flex flex-row">
<Checkbox id="terms" />
<p>POLDA METRO JAYA</p>
</div>
</div>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className="flex flex-col lg:flex-row gap-6">
<div className="h-[500px] overflow-y-auto w-3/4 ">
<div className="container-fluid relative">
<div className="grid grid-cols-1 mt-8">
<div className="relative block bg-white dark:bg-slate-900">
<table className="w-full text-sm text-start">
<thead className="text-md">
<tr className="h-full">
<th className="text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[120px]">Time Table</th>
<th className="text-center border border-r-0 border-gray-100 dark:border-gray-700 py-6 w-[20px]">
<a onClick={() => changePrevWeek()} className="cursor-pointer h-fit self-center bottom-0">
<svg xmlns="http://www.w3.org/2000/svg" width="40px" height="40px" viewBox="0 0 24 24">
<path fill="currentColor" d="M12.29 8.71L9.7 11.3a.996.996 0 0 0 0 1.41l2.59 2.59c.63.63 1.71.18 1.71-.71V9.41c0-.89-1.08-1.33-1.71-.7" />
</svg>
</a>
</th>
<th className={`text-center cursor-pointer border border-l-0 h-full border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${new Date().toISOString().slice(0, 10) == dateAWeek[0] ? "bg-red-600 text-white" : ""}`}>
{/* <a className="cursor-pointer h-fit self-center bottom-0" >
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24">
<path fill="currentColor" d="M12.29 8.71L9.7 11.3a.996.996 0 0 0 0 1.41l2.59 2.59c.63.63 1.71.18 1.71-.71V9.41c0-.89-1.08-1.33-1.71-.7" />
</svg>
</a>{" "} */}
<div className="flex flex-col ">
<p className="text-2xl">{dateAWeek[0]?.split("-")[2]}</p>
<p>Monday</p>
</div>
</th>
<th className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${new Date().toISOString().slice(0, 10) == dateAWeek[1] ? "bg-[#BE0106] text-white rounded-lg" : ""}`}>
<div className="text-2xl">{dateAWeek[1]?.split("-")[2]}</div>Tuesday
</th>
<th className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${new Date().toISOString().slice(0, 10) == dateAWeek[2] ? "bg-[#BE0106] text-white rounded-lg" : ""}`}>
<div className="text-2xl">{dateAWeek[2]?.split("-")[2]}</div>Wednesday
</th>
<th className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${new Date().toISOString().slice(0, 10) == dateAWeek[3] ? "bg-[#BE0106] text-white rounded-lg" : ""}`}>
<div className="text-2xl">{dateAWeek[3]?.split("-")[2]}</div>Thursday
</th>
<th className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${new Date().toISOString().slice(0, 10) == dateAWeek[4] ? "bg-[#BE0106] text-white rounded-lg" : ""}`}>
<div className="text-2xl">{dateAWeek[4]?.split("-")[2]}</div>Friday
</th>
<th className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${new Date().toISOString().slice(0, 10) == dateAWeek[5] ? "bg-[#BE0106] text-white rounded-lg" : ""}`}>
<div className="text-2xl">{dateAWeek[5]?.split("-")[2]}</div>Saturday
</th>
<th
onClick={() => changeNextWeek()}
className={`text-center border cursor-pointer border-r-0 border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${
new Date().toISOString().slice(0, 10) == dateAWeek[6] ? "bg-[#BE0106] text-white rounded-lg" : ""
}`}
>
<div className="flex flex-col ">
<p className="text-2xl">{dateAWeek[6]?.split("-")[2]}</p>
<p>Sunday</p>
</div>
{/* <a className="cursor-pointer h-fit p-0 m-0 self-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24">
<path fill="currentColor" d="m11.71 15.29l2.59-2.59a.996.996 0 0 0 0-1.41L11.71 8.7c-.63-.62-1.71-.18-1.71.71v5.17c0 .9 1.08 1.34 1.71.71" />
</svg>
</a> */}
</th>
<th className="text-center border-l-0 border border-gray-100 dark:border-gray-700 py-6 w-[20px]">
<a onClick={() => changeNextWeek()} className="cursor-pointer">
<svg xmlns="http://www.w3.org/2000/svg" width="40px" height="40px" viewBox="0 0 24 24">
<path fill="currentColor" d="m11.71 15.29l2.59-2.59a.996.996 0 0 0 0-1.41L11.71 8.7c-.63-.62-1.71-.18-1.71.71v5.17c0 .9 1.08 1.34 1.71.71" />
</svg>
</a>
</th>
</tr>
</thead>
<tbody>
{timeList.map((times) => (
<tr key={times.id}>
<th className="text-center border border-gray-100 dark:border-gray-700 py-5">{times.time}</th>
<td colSpan={2} className="p-3 border border-gray-100 dark:border-gray-700 ">
{setItemSchedule(times.id, dateList[0])}
</td>
<td className="border border-gray-100 dark:border-gray-700">{setItemSchedule(times.id, dateList[1])}</td>
<td className="border border-gray-100 dark:border-gray-700">{setItemSchedule(times.id, dateList[2])}</td>
<td className="border border-gray-100 dark:border-gray-700">{setItemSchedule(times.id, dateList[3])}</td>
<td className="p-3 border border-gray-100 dark:border-gray-700">{setItemSchedule(times.id, dateList[4])}</td>
<td className="border border-gray-100 dark:border-gray-700">{setItemSchedule(times.id, dateList[5])}</td>
<td colSpan={2} className="border border-gray-100 dark:border-gray-700">
{setItemSchedule(times.id, dateList[6])}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
{/* Component Kanan */}
<div className="w-1/4 flex flex-col gap-6">
<div className="relative text-gray-600 dark:text-white">
<input type="text" placeholder="Masukkan Judul Jadwal" className="pl-8 pr-4 py-1 w-full border rounded-full text-sm focus:outline-none" />
<span className="absolute left-2 top-1/2 transform -translate-y-1/2">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
<path fill="currentColor" d="M10.5 2a8.5 8.5 0 1 0 5.262 15.176l3.652 3.652a1 1 0 0 0 1.414-1.414l-3.652-3.652A8.5 8.5 0 0 0 10.5 2M4 10.5a6.5 6.5 0 1 1 13 0a6.5 6.5 0 0 1-13 0" />
</g>
</svg>
</span>
</div>
{/* jadwal hari ini */}
{/* <Collapsible className="border border-slate-400 p-2 rounded-lg" open={isOpen} onOpenChange={setIsOpen}>
<CollapsibleTrigger>
<h5 className="py-2 flex justify-between items-center">
Jadwal Hari Ini
<span className="flex items-end">
<Icon icon="fa:angle-down" className="ml-1" />
</span>
</h5>
</CollapsibleTrigger>
{todayList?.map((list: any) => (
<CollapsibleContent className={`flex flex-row gap-3 ${isOpen ? "border-b-2 border-black my-3" : ""}`}>
<div className="border-l-4 border-red-700 pl-1 h-fit font-bold text-lg">{new Date(list.startDate).getDate()}</div>
<div className="flex flex-col">
<h3 className="font-bold">{list?.title}</h3>
<p className="flex flex-row items-center gap-2">
<Icon icon="iconamoon:clock-thin" />
{list?.startTime} - {list?.endTime} WIB
</p>
<p className="flex flex-row items-start gap-2 ">
<Icon icon="bxs:map" width={40} />
{list?.address}
</p>
<p>Pembicara :</p>
<p className="flex flex-row items-center gap-2">
<Icon icon="ic:round-person" />
{list?.speakerTitle}&nbsp;{list?.speakerName}
</p>
</div>
</CollapsibleContent>
))}
</Collapsible> */}
<Accordion type="single" collapsible className="w-full">
<AccordionItem value="item-1">
<AccordionTrigger>Jadwal Hari ini</AccordionTrigger>
{todayList?.map((list: any) => (
<AccordionContent key={list?.id} className="flex flex-row gap-3">
<div className="border-l-4 border-red-700 pl-1 h-fit font-bold text-lg">{new Date(list.startDate).getDate()}</div>
<div className="flex flex-col gap-2">
<h3 className="font-bold">{list?.title}</h3>
<p className="flex flex-row items-center gap-2">
<Icon icon="iconamoon:clock-thin" />
{list?.startTime} - {list?.endTime} WIB
</p>
<p className="flex flex-row items-start gap-2 ">
<Icon icon="bxs:map" width={40} />
{list?.address}
</p>
<p>Pembicara :</p>
<p className="flex flex-row items-center gap-2">
<Icon icon="ic:round-person" />
{list?.speakerTitle}&nbsp;{list?.speakerName}
</p>
</div>
</AccordionContent>
))}
</AccordionItem>
<AccordionItem value="item-2">
<AccordionTrigger>Jadwal Sebelumnya</AccordionTrigger>
{prevdayList?.map((list: any) => (
<AccordionContent key={list?.id} className="flex flex-row gap-3">
<div className="border-l-4 border-red-700 pl-1 h-fit font-bold text-lg">{new Date(list.startDate).getDate()}</div>
<div className="flex flex-col gap-2">
<h3 className="font-bold">{list?.title}</h3>
<p className="flex flex-row items-center gap-2">
<Icon icon="iconamoon:clock-thin" />
{list?.startTime} - {list?.endTime} WIB
</p>
<p className="flex flex-row items-start gap-2 ">
<Icon icon="bxs:map" width={40} />
{list?.address}
</p>
<p>Pembicara :</p>
<p className="flex flex-row items-center gap-2">
<Icon icon="ic:round-person" />
{list?.speakerTitle}&nbsp;{list?.speakerName}
</p>
</div>
</AccordionContent>
))}
</AccordionItem>
<AccordionItem value="item-3">
<AccordionTrigger>Jadwal Selanjutnya</AccordionTrigger>
{nextdayList?.map((list: any) => (
<AccordionContent key={list?.id} className="flex flex-row gap-3">
<div className="border-l-4 border-red-700 pl-1 h-fit font-bold text-lg">{new Date(list.startDate).getDate()}</div>
<div className="flex flex-col gap-2">
<h3 className="font-bold">{list?.title}</h3>
<p className="flex flex-row items-center gap-2">
<Icon icon="iconamoon:clock-thin" />
{list?.startTime} - {list?.endTime} WIB
</p>
<p className="flex flex-row items-start gap-2 ">
<Icon icon="bxs:map" width={40} />
{list?.address}
</p>
<p>Pembicara :</p>
<p className="flex flex-row items-center gap-2">
<Icon icon="ic:round-person" />
{list?.speakerTitle}&nbsp;{list?.speakerName}
</p>
</div>
</AccordionContent>
))}
</AccordionItem>
</Accordion>
{/* jadwal sebelumnya */}
{/* <Collapsible className="border border-slate-400 p-2 rounded-lg" open={isOpen} onOpenChange={setIsOpen}>
<CollapsibleTrigger>
<h5 className="py-2 flex justify-between items-center">
Jadwal Sebelumnya
<span className="flex items-end">
<Icon icon="fa:angle-down" className="ml-1" />
</span>
</h5>
</CollapsibleTrigger>
{prevdayList?.map((list: any) => (
<CollapsibleContent className={`flex flex-row gap-3 ${isOpen ? "border-b-2 border-black my-3" : ""}`}>
<div className="border-l-4 border-red-700 pl-1 h-fit font-bold text-lg">{new Date(list.startDate).getDate()}</div>
<div className="flex flex-col">
<h3 className="font-bold">{list?.title}</h3>
<p className="flex flex-row items-center gap-2">
<Icon icon="iconamoon:clock-thin" />
{list?.startTime} - {list?.endTime} WIB
</p>
<p className="flex flex-row items-start gap-2 ">
<Icon icon="bxs:map" width={40} />
{list?.address}
</p>
<p>Pembicara :</p>
<p className="flex flex-row items-center gap-2">
<Icon icon="ic:round-person" />
{list?.speakerTitle}&nbsp;{list?.speakerName}
</p>
</div>
</CollapsibleContent>
))}
</Collapsible> */}
{/* jadwal selanjutnya */}
{/* <Collapsible className="border border-slate-400 p-2 rounded-lg" open={isOpen} onOpenChange={setIsOpen}>
<CollapsibleTrigger>
<h5 className="py-2 flex justify-end items-center">
Jadwal Selanjutnya
<span className="flex items-end">
<Icon icon="fa:angle-down" className="ml-1 flex" />
</span>
</h5>
</CollapsibleTrigger>
{nextdayList?.map((list: any) => (
<CollapsibleContent className={`flex flex-row gap-3 ${isOpen ? "border-b-2 border-black my-3" : ""}`}>
<div className="border-l-4 border-red-700 pl-1 h-fit font-bold text-lg">{new Date(list.startDate).getDate()}</div>
<div className="flex flex-col">
<h3 className="font-bold">{list?.title}</h3>
<p className="flex flex-row items-center gap-2">
<Icon icon="iconamoon:clock-thin" />
{list?.startTime} - {list?.endTime} WIB
</p>
<p className="flex flex-row items-start gap-2 ">
<Icon icon="bxs:map" width={40} />
{list?.address}
</p>
<p>Pembicara :</p>
<p className="flex flex-row items-center gap-2">
<Icon icon="ic:round-person" />
{list?.speakerTitle}&nbsp;{list?.speakerName}
</p>
</div>
</CollapsibleContent>
))}
</Collapsible> */}
</div>
</div>
</div>
<AlertDialog open={openDialog} onOpenChange={setOpenDialog}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
<h1 className="my-4 font-light">JADWAL / {detail?.isYoutube == true ? "LIVE STREAMING" : "DETAIL"}</h1>
<p className="font-bold">{detail?.title}</p>
</AlertDialogTitle>
<AlertDialogDescription>
<p className="flex flex-row items-center gap-2">
<Icon icon="iconamoon:clock-thin" />
{detail?.date} {detail?.startTime} - {detail?.endTime} {detail?.timezone ? detail.timezone : "WIB"}
</p>
</AlertDialogDescription>
<AlertDialogDescription>
<p className="flex flex-row items-start gap-2 ">
<Icon icon="bxs:map" width={30} />
{detail?.address}
</p>
</AlertDialogDescription>
<AlertDialogDescription>
<p className="flex flex-row items-center gap-2">
<Icon icon="ic:round-person" />
{detail?.speakerTitle || ""} {detail?.speakerName || ""}{" "}
</p>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogAction>Close</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</>
);
};
export default Schedule;

View File

@ -0,0 +1,343 @@
"use client";
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";
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 userId = getCookiesDecrypt("uie");
useEffect(() => {
initFetch();
checkWishlist();
}, []);
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
// const response = await postActivityLog(data, token);
// console.log(response);
}
const handleDownload = () => {
if (downloadProgress === 0) {
if (!userId) {
router.push("/auth/login");
} 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="">
<NewContent group="polda" type={"similar"} />
</div>
</div>
</>
);
};
export default DetailVideo;

View File

@ -0,0 +1,545 @@
"use client";
import React, { useEffect, useState } from "react";
import { Card, CardContent } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import { Icon } from "@iconify/react/dist/iconify.js";
import { formatDateToIndonesian, getOnlyDate, getOnlyMonthAndYear } from "@/utils/globals";
import { useParams, usePathname, useSearchParams } from "next/navigation";
import { getListContent, getUserLevelListByParent, listCategory, listData, listDataRegional } from "@/service/landing/landing";
import { ColumnDef, ColumnFiltersState, PaginationState, SortingState, VisibilityState, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
import LandingPagination from "@/components/landing-page/pagination";
import { Reveal } from "@/components/landing-page/Reveal";
import { Link, useRouter } from "@/i18n/routing";
import { Input } from "@/components/ui/input";
import ReactDatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { close, loading } from "@/config/swal";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
];
const FilterPage = () => {
const router = useRouter();
const asPath = usePathname();
const params = useParams();
const searchParams = useSearchParams();
const locale = params?.locale;
const [videoData, setVideoData] = useState<any>();
const [totalData, setTotalData] = React.useState<number>(1);
const [totalPage, setTotalPage] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [page, setPage] = useState(1);
const [totalContent, setTotalContent] = useState();
const [change, setChange] = useState(false);
const sortBy = searchParams?.get("sortBy");
const title = searchParams?.get("title");
const categorie = searchParams?.get("category");
const group = searchParams?.get("group");
const [, setGetTotalPage] = useState();
const [contentVideo, setContentVideo] = useState([]);
const [categoryFilter, setCategoryFilter] = useState<any>([]);
const [monthYearFilter, setMonthYearFilter] = useState<any>();
const [searchTitle, setSearchTitle] = useState<string>("");
const [sortByOpt, setSortByOpt] = useState<any>(sortBy === "popular" ? "clickCount" : "createdAt");
const isRegional = asPath?.includes("regional");
const isSatker = asPath?.includes("satker");
const [formatFilter, setFormatFilter] = useState<any>([]);
const pages = page ? page - 1 : 0;
const [startDateString, setStartDateString] = useState<any>();
const [endDateString, setEndDateString] = useState<any>();
const [dateRange, setDateRange] = useState<any>([null, null]);
const [calenderState, setCalenderState] = useState(false);
const [handleClose, setHandleClose] = useState(false);
const [categories, setCategories] = useState([]);
const [userLevels, setUserLevels] = useState([]);
const poldaName = params?.polda_name;
let prefixPath = poldaName ? `/polda/${poldaName}` : "/";
const t = useTranslations("FilterPage");
// const [startDate, endDate] = dateRange;
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) {
setPage(Number(pageFromUrl));
}
}, [searchParams]);
useEffect(() => {
async function initState() {
getCategories();
// getSelectedCategory();
if (isSatker) {
getUserLevels();
}
}
initState();
}, []);
useEffect(() => {
if (categorie) {
setCategoryFilter(categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
console.log("Kategori", categorie, categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
}
}, [categorie]);
// useEffect(() => {
// fetchData();
// }, [page, sortBy, sortByOpt, title]);
useEffect(() => {
async function initState() {
if (isRegional) {
getDataRegional();
} else {
getDataAll();
}
}
console.log(monthYearFilter, "monthFilter");
initState();
}, [change, asPath, monthYearFilter, page, sortBy, sortByOpt, title, startDateString, endDateString, categorie, formatFilter]);
async function getCategories() {
const category = await listCategory("2");
const resCategory = category?.data?.data?.content;
setCategories(resCategory);
}
useEffect(() => {
function initState() {
if (dateRange[0] != null && dateRange[1] != null) {
setStartDateString(getOnlyDate(dateRange[0]));
setEndDateString(getOnlyDate(dateRange[1]));
setHandleClose(true);
console.log("date range", dateRange, getOnlyDate(dateRange[0]));
}
}
initState();
}, [calenderState]);
async function getDataAll() {
if (poldaName && String(poldaName)?.length > 1) {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
const filterGroup = String(poldaName);
loading();
const response = await listData(
"2",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
filterGroup,
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
const data = response?.data?.data;
const contentData = data?.content;
setVideoData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response?.data?.data?.totalElements);
} else {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listData(
"2",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
const data = response?.data?.data;
const contentData = data?.content;
setVideoData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response?.data?.data?.totalElements);
}
}
const handleCategoryFilter = (e: boolean, id: string) => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
};
const handleFormatFilter = (e: boolean, id: string) => {
let filter = [...formatFilter];
if (e) {
filter = [...formatFilter, id];
} else {
filter.splice(formatFilter.indexOf(id), 1);
}
console.log("Format filter", filter);
setFormatFilter(filter);
};
const cleanCheckbox = () => {
setCategoryFilter([]);
setFormatFilter([]);
router.push(`?category=&title=`);
setDateRange([null, null]);
setMonthYearFilter(null);
setChange(!change);
};
async function getDataRegional() {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listDataRegional(
"2",
name,
filter,
format,
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : "",
12,
pages,
sortByOpt
);
close();
setGetTotalPage(response?.data?.data?.totalPages);
setContentVideo(response?.data?.data?.content);
setTotalContent(response?.data?.data?.totalElements);
}
const table = useReactTable({
data: videoData,
columns: columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
onPaginationChange: setPagination,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
pagination,
},
});
useEffect(() => {
initFetch();
}, [page]);
const initFetch = async () => {
const response = await getListContent({
page: page - 1,
size: 6,
sortBy: "createdAt",
contentTypeId: "2",
});
console.log(response);
setVideoData(response?.data?.data?.content);
const data = response?.data?.data;
const contentData = data?.content;
setVideoData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
};
function getSelectedCategory() {
const filter = [];
if (categorie) {
const categoryArr = categorie.split(",");
for (const element of categoryArr) {
filter.push(Number(element));
}
setCategoryFilter(filter);
}
}
const handleDeleteDate = () => {
setDateRange([null, null]);
setStartDateString("");
setEndDateString("");
setHandleClose(false);
};
const handleSorting = (e: any) => {
console.log(e.target.value);
if (e.target.value == "terbaru") {
setSortByOpt("createdAt");
} else {
setSortByOpt("clickCount");
}
setChange(!change);
};
async function getUserLevels() {
const res = await getUserLevelListByParent(761);
const userLevelList = res?.data?.data;
if (userLevelList !== null) {
let optionArr: any = [];
userLevelList?.map((option: any) => {
let optionData = {
id: option.id,
label: option.name,
value: option.id,
};
optionArr.push(optionData);
});
setUserLevels(optionArr);
}
}
let typingTimer: any;
const doneTypingInterval = 1500;
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
async function doneTyping() {
if (searchTitle == "" || searchTitle == undefined) {
router.push("?title=");
} else {
router.push(`?title=${searchTitle}`);
}
}
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
return (
<div className="flex flex-col">
{/* Header */}
<div className="flex flex-col md:flex-row items-start gap-5 p-10 bg-[#f7f7f7] dark:bg-black">
<p>
{" "}
{t("video")}&nbsp;
{">"} <span className="font-bold">{t("allVideo")}</span>
</p>
<p className="font-bold">|</p>
<p>{`${t("thereIs")} ${totalContent} ${t("downloadableVideo")}`}</p>
</div>
{/* Left */}
<div className="flex flex-col lg:flex-row gap-6 p-4">
<div className="lg:w-[25%] w-full bg-[#f7f7f7] dark:bg-black p-4 rounded-lg shadow-md">
<h2 className="text-lg font-semibold mb-4 flex items-center gap-1">
<Icon icon="stash:filter-light" fontSize={30} />
Filter
</h2>
<div className="border-t border-black my-4 dark:border-white"></div>
<div className="space-y-6">
<div>
<label htmlFor="search" className="block text-sm font-medium text-gray-700 dark:text-white">
{t("search")}
</label>
<Input
value={searchTitle}
onChange={(e) => setSearchTitle(e.target.value)}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
type="text"
id="search"
placeholder={t("searchTitle")}
className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("monthYear")}</label>
<ReactDatePicker
selected={monthYearFilter}
className="mt-1 w-full text-xs border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
onChange={(date) => setMonthYearFilter(date)}
dateFormat="MM | yyyy"
placeholderText={t("selectYear")}
showMonthYearPicker
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("date")}</label>
<div className="flex flex-row justify justify-between gap-2">
<ReactDatePicker
selectsRange
className="mt-1 w-full text-sm border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
startDate={dateRange[0]}
endDate={dateRange[1]}
onChange={(update) => {
setDateRange(update);
}}
placeholderText={t("selectDate")}
onCalendarClose={() => setCalenderState(!calenderState)}
/>
<div className="flex items-center">{handleClose ? <Icon icon="carbon:close-filled" onClick={handleDeleteDate} width="20" inline color="#216ba5" /> : ""}</div>
</div>
</div>
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">{t("categories")}</h3>
<ul className="mt-2 space-y-2">
{categories.map((category: any) => (
<li key={category?.id}>
<label className="inline-flex items-center" htmlFor={`${category.id}`}>
<Checkbox id={`${category.id}`} value={category.id} checked={categoryFilter.includes(String(category.id))} onCheckedChange={(e) => handleCategoryFilter(Boolean(e), category.id)} />
<span className="ml-2 text-gray-700 dark:text-white">{category?.name}</span>
</label>
</li>
))}
</ul>
</div>
{/* Garis */}
<div className="border-t border-black my-4 dark:border-white"></div>
{/* Garis */}
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">Format</h3>
<ul className="mt-2 space-y-2">
<li>
<label className="inline-flex items-center">
<Checkbox id="mk4" value="mk4" checked={formatFilter.includes("mk4")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "mk4")} />
<span className="ml-2 text-gray-700 dark:text-white">MK4</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="mov" value="mov" checked={formatFilter.includes("mov")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "mov")} />
<span className="ml-2 text-gray-700 dark:text-white">MOV</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="mp4" value="mp4" checked={formatFilter.includes("mp4")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "mp4")} />
<span className="ml-2 text-gray-700 dark:text-white">MP4</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="avi" value="avi" checked={formatFilter.includes("avi")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "avi")} />
<span className="ml-2 text-gray-700 dark:text-white">AVI</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="wmv" value="wmv" checked={formatFilter.includes("wmv")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "wmv")} />
<span className="ml-2 text-gray-700 dark:text-white">WMV</span>
</label>
</li>
</ul>
</div>
<div className="border-t border-black dark:border-white my-4"></div>
<div className="text-center">
<a onClick={cleanCheckbox} className="text-[#bb3523] cursor-pointer">
<b>Reset Filter</b>
</a>
</div>
</div>
</div>
{/* Konten Kanan */}
<Reveal>
<div className="flex-1">
<div className="flex flex-col items-end mb-4">
<h2 className="text-lg font-semibold">{t("sortBy")}</h2>
<select defaultValue={sortBy == "popular" ? "terpopuler" : "terbaru"} onChange={(e) => handleSorting(e)} className="border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500">
<option value="latest">{t("latest")}</option>
<option value="popular">{t("mostPopular")}</option>
</select>
</div>
{videoData?.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{videoData?.map((video: any) => (
<Card key={video?.id} className="hover:scale-110 transition-transform duration-300">
<CardContent className="flex flex-col text-xs lg:text-sm w-full p-0">
<Link href={`${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" />
<div className="flex flex-row items-center gap-2 text-[10px] mx-2">
{formatDateToIndonesian(new Date(video?.createdAt))} {video?.timezone ? video?.timezone : "WIB"}| <Icon icon="formkit:eye" width="15" height="15" />
{video?.clickCount}{" "}
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 20 20">
<path
fill="#f00"
d="M7.707 10.293a1 1 0 1 0-1.414 1.414l3 3a1 1 0 0 0 1.414 0l3-3a1 1 0 0 0-1.414-1.414L11 11.586V6h5a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h5v5.586zM9 4a1 1 0 0 1 2 0v2H9z"
/>
</svg>{" "}
</div>
<div className="font-semibold pr-3 pb-3 mx-2 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible w-full">{video?.title}</div>
</Link>
</CardContent>
</Card>
))}
</div>
) : (
<p className="flex items-center justify-center">
<img src="/assets/empty-data.png" alt="empty" className="h-60 w-60 my-4" />
</p>
)}
<LandingPagination table={table} totalData={totalData} totalPage={totalPage} />
</div>
</Reveal>
</div>
</div>
);
};
export default FilterPage;

View File

@ -0,0 +1,20 @@
import LayoutProvider from "@/providers/layout.provider";
import LayoutContentProvider from "@/providers/content.provider";
import DashCodeSidebar from "@/components/partials/sidebar";
import DashCodeFooter from "@/components/partials/footer";
import ThemeCustomize from "@/components/partials/customizer";
import DashCodeHeader from "@/components/partials/header";
import { redirect } from "@/components/navigation";
import Footer from "@/components/landing-page/footer";
import Navbar from "@/components/landing-page/navbar";
const layout = async ({ children }: { children: React.ReactNode }) => {
return (
<>
{children}
</>
);
};
export default layout;

View File

@ -0,0 +1,334 @@
"use client";
import { useParams, usePathname } from "next/navigation";
import React, { useEffect, useState } from "react";
import { Icon } from "@iconify/react/dist/iconify.js";
import NewContent from "@/components/landing-page/new-content";
import { Link, useRouter } from "@/i18n/routing";
import { Textarea } from "@/components/ui/textarea";
import { BarWave } from "react-cssfx-loading";
import { useToast } from "@/components/ui/use-toast";
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
import { getCookiesDecrypt } from "@/lib/utils";
import { close, error, loading } from "@/config/swal";
const DetailAudio = () => {
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 [detailDataAudio, setDetailDataAudio] = 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 [imageSizeSelected, setImageSizeSelected] = useState("l");
const userId = getCookiesDecrypt("uie");
useEffect(() => {
initFetch();
checkWishlist();
}, []);
const initFetch = async () => {
const response = await getDetail(String(slug));
console.log("detailAudio", 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,
});
setDetailDataAudio(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");
}
};
async function sendActivityLog(activityTypeId: number) {
const data = {
activityTypeId,
mediaId: slug.split("-")?.[0],
url: window.location.href,
};
// set activity
// const response = await postActivityLog(data, token);
// console.log(response);
}
const handleDownload = () => {
if (downloadProgress === 0) {
if (!userId) {
router.push("/auth/login");
} 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 {
downloadFile(`${main?.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();
};
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" },
];
return (
<>
<div className="min-h-screen 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 flex justify-center">
<img src="/assets/audio-btn.png" />
<BarWave color="#ffc831" width="60px" height="25px" duration="2s" />
<div className="absolute top-4 left-4"></div>
</div>
{/* Footer Informasi */}
<div className="p-4 text-sm text-gray-500 flex justify-between items-center border-t mt-4">
<p className="flex flex-row items-center">
oleh&nbsp;<span className="font-semibold text-black">{detailDataAudio?.uploadedBy?.userLevel?.name}</span>&nbsp;|&nbsp;Diupdate pada {detailDataAudio?.updatedAt} WIB&nbsp;|&nbsp;
<Icon icon="formkit:eye" width="15" height="15" />
&nbsp;
{detailDataAudio?.clickCount}
</p>
<p>Kreator: {detailDataAudio?.creatorName}</p>
</div>
{/* Keterangan */}
<div className="md:w-3/4">
<h1 className="flex flex-row font-bold text-2xl mx-5 my-8">{detailDataAudio?.title}</h1>
<div className="font-light text-justify" dangerouslySetInnerHTML={{ __html: detailDataAudio?.htmlDescription }} />
</div>
</div>
{/* Bagian Kanan */}
<div className="md:w-1/4 p-4 h-fit bg-gray-300 rounded-lg mx-4">
{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=${detailDataAudio?.category.id}`} className="bg-red-600 text-white text-xs font-bold px-3 py-3 my-3 flex justify-center items-center rounded">
{detailDataAudio?.category?.name}
</Link>
<div className="flex justify-center flex-wrap gap-2 mb-4">
{detailDataAudio?.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">
{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</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 gap-5 p-10 bg-gray-300">
<p className="flex items-start text-lg">Berikan Komentar</p>
<Textarea placeholder="Type your comments here." className="flex items-start justify-center" />
<button className="flex items-start bg-[#bb3523] rounded-lg w-fit px-4 py-1">Kirim</button>
</div>
{/* Konten Serupa */}
<div className="px-4">
<NewContent group="satker" type={"similar"} />
</div>
</div>
</>
);
};
export default DetailAudio;

View File

@ -0,0 +1,520 @@
"use client";
import React, { useEffect, useState } from "react";
import { Card, CardContent } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import { Icon } from "@iconify/react/dist/iconify.js";
import { formatDateToIndonesian, getOnlyDate, getOnlyMonthAndYear } from "@/utils/globals";
import { useParams, usePathname, useSearchParams } from "next/navigation";
import { getUserLevelListByParent, listCategory, listData, listDataRegional } from "@/service/landing/landing";
import { ColumnDef, ColumnFiltersState, PaginationState, SortingState, VisibilityState, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
import LandingPagination from "@/components/landing-page/pagination";
import { Reveal } from "@/components/landing-page/Reveal";
import { Link, useRouter } from "@/i18n/routing";
import { Input } from "@/components/ui/input";
import ReactDatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { close, loading } from "@/config/swal";
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@/components/ui/carousel";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
];
const FilterPage = () => {
const router = useRouter();
const asPath = usePathname();
const params = useParams();
const searchParams = useSearchParams();
const locale = params?.locale;
const [audioData, setAudioData] = useState<any>();
const [totalData, setTotalData] = React.useState<number>(1);
const [totalPage, setTotalPage] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [page, setPage] = useState(1);
const [totalContent, setTotalContent] = useState();
const [change, setChange] = useState(false);
const sortBy = searchParams?.get("sortBy");
const title = searchParams?.get("title");
const categorie = searchParams?.get("category");
const group = searchParams?.get("group");
const [contentImage, setContentImage] = useState([]);
const [, setGetTotalPage] = useState();
let typingTimer: any;
const doneTypingInterval = 1500;
const [search, setSearch] = useState();
const [categoryFilter, setCategoryFilter] = useState<any>([]);
const [monthYearFilter, setMonthYearFilter] = useState<any>();
const [searchTitle, setSearchTitle] = useState<string>("");
const [sortByOpt, setSortByOpt] = useState<any>(sortBy === "popular" ? "clickCount" : "createdAt");
const isRegional = asPath?.includes("regional");
const isSatker = asPath?.includes("satker");
const [formatFilter, setFormatFilter] = useState<any>([]);
const pages = page ? page - 1 : 0;
const [startDateString, setStartDateString] = useState<any>();
const [endDateString, setEndDateString] = useState<any>();
const [dateRange, setDateRange] = useState<any>([null, null]);
const [calenderState, setCalenderState] = useState(false);
const [handleClose, setHandleClose] = useState(false);
const [categories, setCategories] = useState([]);
const [userLevels, setUserLevels] = useState([]);
const satkerName = params?.satker_name;
let prefixPath = satkerName ? `/satker/${satkerName}` : "/";
const t = useTranslations("FilterPage");
// const [startDate, endDate] = dateRange;
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) {
setPage(Number(pageFromUrl));
}
}, [searchParams]);
useEffect(() => {
async function initState() {
getCategories();
// getSelectedCategory();
if (isSatker) {
getUserLevels();
}
}
initState();
}, []);
useEffect(() => {
if (categorie) {
setCategoryFilter(categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
console.log("Kategori", categorie, categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
}
}, [categorie]);
// useEffect(() => {
// fetchData();
// }, [page, sortBy, sortByOpt, title]);
useEffect(() => {
async function initState() {
if (isRegional) {
getDataRegional();
} else {
getDataAll();
}
}
console.log(monthYearFilter, "monthFilter");
initState();
}, [change, asPath, monthYearFilter, page, sortBy, sortByOpt, title, startDateString, endDateString, categorie, formatFilter]);
async function getCategories() {
const category = await listCategory("4");
const resCategory = category?.data?.data?.content;
setCategories(resCategory);
}
useEffect(() => {
function initState() {
if (dateRange[0] != null && dateRange[1] != null) {
setStartDateString(getOnlyDate(dateRange[0]));
setEndDateString(getOnlyDate(dateRange[1]));
setHandleClose(true);
console.log("date range", dateRange, getOnlyDate(dateRange[0]));
}
}
initState();
}, [calenderState]);
async function getDataAll() {
if (satkerName && String(satkerName)?.length > 1) {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
const filterGroup = "satker-" + String(satkerName);
loading();
const response = await listData(
"4",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
filterGroup,
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
const data = response?.data?.data;
const contentData = data?.content;
setAudioData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response?.data?.data?.totalElements);
} else {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listData(
"4",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
const data = response?.data?.data;
const contentData = data?.content;
setAudioData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response?.data?.data?.totalElements);
}
}
const handleCategoryFilter = (e: boolean, id: string) => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
};
const handleFormatFilter = (e: boolean, id: string) => {
let filter = [...formatFilter];
if (e) {
filter = [...formatFilter, id];
} else {
filter.splice(formatFilter.indexOf(id), 1);
}
console.log("Format filter", filter);
setFormatFilter(filter);
};
const cleanCheckbox = () => {
setCategoryFilter([]);
setFormatFilter([]);
router.push(`?category=&title=`);
setDateRange([null, null]);
setMonthYearFilter(null);
setChange(!change);
};
async function getDataRegional() {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listDataRegional(
"1",
name,
filter,
format,
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : "",
12,
pages,
sortByOpt
);
close();
setGetTotalPage(response?.data?.data?.totalPages);
setContentImage(response?.data?.data?.content);
setTotalContent(response?.data?.data?.totalElements);
}
const table = useReactTable({
data: audioData,
columns: columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
onPaginationChange: setPagination,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
pagination,
},
});
function getSelectedCategory() {
const filter = [];
if (categorie) {
const categoryArr = categorie.split(",");
for (const element of categoryArr) {
filter.push(Number(element));
}
setCategoryFilter(filter);
}
}
const handleDeleteDate = () => {
setDateRange([null, null]);
setStartDateString("");
setEndDateString("");
setHandleClose(false);
};
const handleSorting = (e: any) => {
console.log(e.target.value);
if (e.target.value == "terbaru") {
setSortByOpt("createdAt");
} else {
setSortByOpt("clickCount");
}
setChange(!change);
};
async function getUserLevels() {
const res = await getUserLevelListByParent(761);
const userLevelList = res?.data?.data;
if (userLevelList !== null) {
let optionArr: any = [];
userLevelList?.map((option: any) => {
let optionData = {
id: option.id,
label: option.name,
value: option.id,
};
optionArr.push(optionData);
});
setUserLevels(optionArr);
}
}
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
async function doneTyping() {
if (searchTitle == "" || searchTitle == undefined) {
router.push("?title=");
} else {
router.push(`?title=${searchTitle}`);
}
}
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
return (
<div className="flex flex-col">
{/* Header */}
<div className="flex flex-col md:flex-row items-start gap-5 p-10 bg-[#f7f7f7] dark:bg-black">
<p>
{" "}
Audio {">"} <span className="font-bold">{t("allAudio")}</span>
</p>
<p className="font-bold">|</p>
<p>{`${t("thereIs")} ${totalContent} ${t("downloadableAudio")}`}</p>
</div>
{/* Left */}
<div className="flex flex-col lg:flex-row gap-6 p-4">
<div className="lg:w-[25%] w-full bg-[#f7f7f7] dark:bg-black p-4 rounded-lg shadow-md">
<h2 className="text-lg font-semibold mb-4 flex items-center gap-1">
<Icon icon="stash:filter-light" fontSize={30} />
Filter
</h2>
<div className="border-t border-black my-4 dark:border-white"></div>
<div className="space-y-6">
<div>
<label htmlFor="search" className="block text-sm font-medium text-gray-700 dark:text-white">
{t("search")}
</label>
<Input
value={searchTitle}
onChange={(e) => setSearchTitle(e.target.value)}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
type="text"
id="search"
placeholder={t("searchTitle")}
className="mt-1 w-full text-sm border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("monthYear")}</label>
<ReactDatePicker
selected={monthYearFilter}
className="mt-1 w-full text-xs border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
onChange={(date) => setMonthYearFilter(date)}
dateFormat="MM | yyyy"
placeholderText={t("selectYear")}
showMonthYearPicker
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("date")}</label>
<div className="flex flex-row justify justify-between gap-2">
<ReactDatePicker
selectsRange
className="mt-1 w-full border text-sm rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
startDate={dateRange[0]}
endDate={dateRange[1]}
onChange={(update) => {
setDateRange(update);
}}
placeholderText={t("searchDate")}
onCalendarClose={() => setCalenderState(!calenderState)}
/>
<div className="flex items-center">{handleClose ? <Icon icon="carbon:close-filled" onClick={handleDeleteDate} width="20" inline color="#216ba5" /> : ""}</div>
</div>
</div>
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">{t("categories")}</h3>
<ul className="mt-2 space-y-2">
{categories.map((category: any) => (
<li key={category?.id}>
<label className="inline-flex items-center" htmlFor={`${category.id}`}>
<Checkbox id={`${category.id}`} value={category.id} checked={categoryFilter.includes(String(category.id))} onCheckedChange={(e) => handleCategoryFilter(Boolean(e), category.id)} />
<span className="ml-2 text-gray-700 dark:text-white">{category?.name}</span>
</label>
</li>
))}
</ul>
</div>
{/* Garis */}
<div className="border-t border-black my-4 dark:border-white"></div>
{/* Garis */}
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">Format</h3>
<ul className="mt-2 space-y-2">
<li>
<label className="inline-flex items-center">
<Checkbox id="png" value="png" checked={formatFilter.includes("wav")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "wav")} />
<span className="ml-2 text-gray-700 dark:text-white">WAV</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="jpeg" value="jpeg" checked={formatFilter.includes("mp3")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "mp3")} />
<span className="ml-2 text-gray-700 dark:text-white">MP3</span>
</label>
</li>
</ul>
</div>
<div className="border-t border-black dark:border-white my-4"></div>
<div className="text-center">
<a onClick={cleanCheckbox} className="text-[#bb3523] cursor-pointer">
<b>Reset Filter</b>
</a>
</div>
</div>
</div>
{/* Konten Kanan */}
<Reveal>
<div className="flex-1">
<div className="flex flex-col items-end mb-4">
<h2 className="text-lg font-semibold">{t("sortBy")} </h2>
<select defaultValue={sortBy == "popular" ? "terpopuler" : "terbaru"} onChange={(e) => handleSorting(e)} className="border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500">
<option value="latest">{t("latest")}</option>
<option value="popular">{t("mostPopular")}</option>
</select>
</div>
{audioData?.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{audioData?.map((image: any) => (
<Carousel className="w-full max-w-7xl mx-auto">
<CarouselContent>
{audioData?.map((audio: any) => (
<CarouselItem key={audio?.id} className="md:basis-1/2 lg:basis-1/3">
<div className="flex flex-row gap-6">
<Link href={`${prefixPath}/audio/detail/${audio?.slug}`} className="flex flex-col sm: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 items-center justify-center bg-red-500 text-white rounded-lg w-16 h-8 lg:h-16">
<svg width="32" height="34" viewBox="0 0 32 34" fill="null" xmlns="http://www.w3.org/2000/svg">
<path
d="M23.404 0.452014C23.7033 0.35857 24.0204 0.336816 24.3297 0.388509C24.639 0.440203 24.9318 0.563895 25.1845 0.749599C25.4371 0.935304 25.6426 1.17782 25.7843 1.45756C25.9259 1.73731 25.9998 2.04644 26 2.36001V14.414C25.3462 14.2296 24.6766 14.1064 24 14.046V8.36001L10 12.736V27C10 28.1264 9.6197 29.2197 8.92071 30.1029C8.22172 30.9861 7.24499 31.6075 6.14877 31.8663C5.05255 32.125 3.90107 32.0061 2.88089 31.5287C1.86071 31.0514 1.03159 30.2435 0.52787 29.2361C0.024152 28.2286 -0.124656 27.0806 0.105556 25.9781C0.335768 24.8755 0.931513 23.883 1.79627 23.1613C2.66102 22.4396 3.74413 22.031 4.87009 22.0017C5.99606 21.9724 7.09893 22.3242 8.00001 23V6.73601C7.99982 6.30956 8.13596 5.8942 8.38854 5.55059C8.64112 5.20698 8.99692 4.9531 9.40401 4.82601L23.404 0.452014ZM10 10.64L24 6.26601V2.36001L10 6.73601V10.64ZM5.00001 24C4.20436 24 3.44129 24.3161 2.87869 24.8787C2.31608 25.4413 2.00001 26.2044 2.00001 27C2.00001 27.7957 2.31608 28.5587 2.87869 29.1213C3.44129 29.6839 4.20436 30 5.00001 30C5.79566 30 6.55872 29.6839 7.12133 29.1213C7.68394 28.5587 8.00001 27.7957 8.00001 27C8.00001 26.2044 7.68394 25.4413 7.12133 24.8787C6.55872 24.3161 5.79566 24 5.00001 24ZM32 25C32 27.387 31.0518 29.6761 29.364 31.364C27.6761 33.0518 25.387 34 23 34C20.6131 34 18.3239 33.0518 16.636 31.364C14.9482 29.6761 14 27.387 14 25C14 22.6131 14.9482 20.3239 16.636 18.6361C18.3239 16.9482 20.6131 16 23 16C25.387 16 27.6761 16.9482 29.364 18.6361C31.0518 20.3239 32 22.6131 32 25ZM27.47 24.128L21.482 20.828C21.3298 20.7443 21.1583 20.7016 20.9846 20.7043C20.8108 20.707 20.6408 20.7549 20.4912 20.8433C20.3416 20.9317 20.2176 21.0576 20.1315 21.2086C20.0453 21.3595 20 21.5302 20 21.704V28.304C20 28.4778 20.0453 28.6486 20.1315 28.7995C20.2176 28.9504 20.3416 29.0763 20.4912 29.1647C20.6408 29.2531 20.8108 29.301 20.9846 29.3037C21.1583 29.3064 21.3298 29.2638 21.482 29.18L27.47 25.88C27.6268 25.7937 27.7575 25.6669 27.8486 25.5128C27.9397 25.3587 27.9877 25.183 27.9877 25.004C27.9877 24.825 27.9397 24.6493 27.8486 24.4952C27.7575 24.3412 27.6268 24.2143 27.47 24.128Z"
fill="white"
/>
</svg>
</div>
<div className="flex flex-col flex-1">
<div className="text-gray-500 dark:text-gray-400 flex flex-row text-sm">
{formatDateToIndonesian(new Date(audio?.createdAt))} {audio?.timezone ? audio?.timezone : "WIB"} | <Icon icon="formkit:eye" width="15" height="15" /> {audio?.clickCount}{" "}
</div>
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm h-5 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible">{audio?.title}</div>
</div>
</Link>
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
))}
</div>
) : (
<p className="flex items-center justify-center text-black">
<img src="/assets/empty-data.png" alt="empty" className="h-60 w-60 my-4" />
</p>
)}
<LandingPagination table={table} totalData={totalData} totalPage={totalPage} />
</div>
</Reveal>
</div>
</div>
);
};
export default FilterPage;

View File

@ -0,0 +1,20 @@
import LayoutProvider from "@/providers/layout.provider";
import LayoutContentProvider from "@/providers/content.provider";
import DashCodeSidebar from "@/components/partials/sidebar";
import DashCodeFooter from "@/components/partials/footer";
import ThemeCustomize from "@/components/partials/customizer";
import DashCodeHeader from "@/components/partials/header";
import { redirect } from "@/components/navigation";
import Footer from "@/components/landing-page/footer";
import Navbar from "@/components/landing-page/navbar";
const layout = async ({ children }: { children: React.ReactNode }) => {
return (
<>
{children}
</>
);
};
export default layout;

View File

@ -0,0 +1,333 @@
"use client";
import { useParams, usePathname } from "next/navigation";
import React, { useEffect, useState } from "react";
import { Icon } from "@iconify/react/dist/iconify.js";
import NewContent from "@/components/landing-page/new-content";
import { Textarea } from "@/components/ui/textarea";
import { getCookiesDecrypt } from "@/lib/utils";
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
import { close, error, loading } from "@/config/swal";
import { useToast } from "@/components/ui/use-toast";
import { Link, useRouter } from "@/i18n/routing";
const DetailDocument = () => {
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 [detailDataDocument, setDetailDataDocument] = useState<any>();
const [selectedDocument, setSelectedDocument] = useState(0);
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 [imageSizeSelected, setImageSizeSelected] = useState("l");
const userId = getCookiesDecrypt("uie");
useEffect(() => {
initFetch();
checkWishlist();
}, []);
const initFetch = async () => {
const response = await getDetail(String(slug));
console.log("detailDocument", 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,
});
setDetailDataDocument(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
// const response = await postActivityLog(data, token);
// console.log(response);
}
const handleDownload = () => {
if (downloadProgress === 0) {
if (!userId) {
router.push("/auth/login");
} 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 {
downloadFile(`${main?.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">
<div className="rounded-md overflow-hidden md:flex">
{/* Bagian Kiri */}
<div className="md:w-3/4">
<div className="relative">
<img src={detailDataDocument?.files[selectedDocument]?.url} alt="Main" className="rounded-lg w-auto h-fit" />
<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">{detailDataDocument?.uploadedBy?.userLevel?.name}</span>&nbsp;|&nbsp;Diupdate pada {detailDataDocument?.updatedAt} WIB&nbsp;|&nbsp;
<Icon icon="formkit:eye" width="15" height="15" />
&nbsp;
{detailDataDocument?.clickCount}
</p>
<p className="mt-3">Kreator: {detailDataDocument?.creatorName}</p>
</div>
{/* Keterangan */}
<div className="md:w-3/4">
<h1 className="flex flex-row font-bold text-2xl my-8">{detailDataDocument?.title}</h1>
<div dangerouslySetInnerHTML={{ __html: detailDataDocument?.htmlDescription }} />
</div>
</div>
{/* Bagian Kanan */}
<div className="md:w-1/4 p-4 bg-[#f7f7f7] h-fit rounded-lg mx-4">
{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=${detailDataDocument?.category.id}`} className="bg-red-600 text-white text-xs font-bold px-3 py-3 my-3 flex justify-center items-center rounded">
{detailDataDocument?.category?.name}
</Link>
<div className="flex justify-center flex-wrap gap-2 mb-4">
{detailDataDocument?.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">
{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 Foto</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-16">
<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] rounded-lg text-white w-fit px-4 py-1">Kirim</button>
</div>
</div>
{/* Konten Serupa */}
<div className="">
<NewContent group="satker" type={"similar"} />
</div>
</div>
</>
);
};
export default DetailDocument;

View File

@ -0,0 +1,549 @@
"use client";
import React, { useEffect, useState } from "react";
import { Card, CardContent } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import { Icon } from "@iconify/react/dist/iconify.js";
import { formatDateToIndonesian, getOnlyDate, getOnlyMonthAndYear } from "@/utils/globals";
import { useParams, usePathname, useSearchParams } from "next/navigation";
import { getListContent, getUserLevelListByParent, listCategory, listData, listDataRegional } from "@/service/landing/landing";
import { ColumnDef, ColumnFiltersState, PaginationState, SortingState, VisibilityState, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
import LandingPagination from "@/components/landing-page/pagination";
import { Reveal } from "@/components/landing-page/Reveal";
import { Link, useRouter } from "@/i18n/routing";
import { Input } from "@/components/ui/input";
import ReactDatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { close, loading } from "@/config/swal";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
];
const FilterPage = () => {
const router = useRouter();
const asPath = usePathname();
const params = useParams();
const searchParams = useSearchParams();
const locale = params?.locale;
const [documentData, setDocumentData] = useState<any>();
const [totalData, setTotalData] = React.useState<number>(1);
const [totalPage, setTotalPage] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [page, setPage] = useState(1);
const [totalContent, setTotalContent] = useState();
const [change, setChange] = useState(false);
const sortBy = searchParams?.get("sortBy");
const title = searchParams?.get("title");
const categorie = searchParams?.get("category");
const group = searchParams?.get("group");
const [, setGetTotalPage] = useState();
const t = useTranslations("FilterPage");
let typingTimer: any;
const doneTypingInterval = 1500;
const [contentDocument, setContentDocument] = useState([]);
const [categoryFilter, setCategoryFilter] = useState<any>([]);
const [monthYearFilter, setMonthYearFilter] = useState<any>();
const [searchTitle, setSearchTitle] = useState<string>("");
const [sortByOpt, setSortByOpt] = useState<any>(sortBy === "popular" ? "clickCount" : "createdAt");
const isRegional = asPath?.includes("regional");
const isSatker = asPath?.includes("satker");
const [formatFilter, setFormatFilter] = useState<any>([]);
const pages = page ? page - 1 : 0;
const [startDateString, setStartDateString] = useState<any>();
const [endDateString, setEndDateString] = useState<any>();
const [dateRange, setDateRange] = useState<any>([null, null]);
const [calenderState, setCalenderState] = useState(false);
const [handleClose, setHandleClose] = useState(false);
const [categories, setCategories] = useState([]);
const [userLevels, setUserLevels] = useState([]);
const satkerName = params?.satker_name;
let prefixPath = satkerName ? `/satker/${satkerName}` : "/";
// const [startDate, endDate] = dateRange;
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) {
setPage(Number(pageFromUrl));
}
}, [searchParams]);
useEffect(() => {
async function initState() {
getCategories();
// getSelectedCategory();
if (isSatker) {
getUserLevels();
}
}
initState();
}, []);
useEffect(() => {
if (categorie) {
setCategoryFilter(categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
console.log("Kategori", categorie, categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
}
}, [categorie]);
// useEffect(() => {
// fetchData();
// }, [page, sortBy, sortByOpt, title]);
useEffect(() => {
async function initState() {
if (isRegional) {
getDataRegional();
} else {
getDataAll();
}
}
console.log(monthYearFilter, "monthFilter");
initState();
}, [change, asPath, monthYearFilter, page, sortBy, sortByOpt, title, startDateString, endDateString, categorie, formatFilter]);
async function getCategories() {
const category = await listCategory("3");
const resCategory = category?.data?.data?.content;
setCategories(resCategory);
}
useEffect(() => {
function initState() {
if (dateRange[0] != null && dateRange[1] != null) {
setStartDateString(getOnlyDate(dateRange[0]));
setEndDateString(getOnlyDate(dateRange[1]));
setHandleClose(true);
console.log("date range", dateRange, getOnlyDate(dateRange[0]));
}
}
initState();
}, [calenderState]);
async function getDataAll() {
if (satkerName && String(satkerName)?.length > 1) {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
const filterGroup = "satker-" + String(satkerName);
loading();
const response = await listData(
"3",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
filterGroup,
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
// setGetTotalPage(response?.data?.data?.totalPages);
// setContentImage(response?.data?.data?.content);
// setTotalContent(response?.data?.data?.totalElements);
const data = response?.data?.data;
const contentData = data?.content;
setDocumentData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response?.data?.data?.totalElements);
} else {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listData(
"3",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
// setGetTotalPage(response?.data?.data?.totalPages);
// setContentImage(response?.data?.data?.content);
// setTotalContent(response?.data?.data?.totalElements);
const data = response?.data?.data;
const contentData = data?.content;
setDocumentData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response?.data?.data?.totalElements);
}
}
const handleCategoryFilter = (e: boolean, id: string) => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
};
const handleFormatFilter = (e: boolean, id: string) => {
let filter = [...formatFilter];
if (e) {
filter = [...formatFilter, id];
} else {
filter.splice(formatFilter.indexOf(id), 1);
}
console.log("Format filter", filter);
setFormatFilter(filter);
};
const cleanCheckbox = () => {
setCategoryFilter([]);
setFormatFilter([]);
router.push(`?category=&title=`);
setDateRange([null, null]);
setMonthYearFilter(null);
setChange(!change);
};
async function getDataRegional() {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listDataRegional(
"3",
name,
filter,
format,
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : "",
12,
pages,
sortByOpt
);
close();
setGetTotalPage(response?.data?.data?.totalPages);
setContentDocument(response?.data?.data?.content);
setTotalContent(response?.data?.data?.totalElements);
}
const table = useReactTable({
data: documentData,
columns: columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
onPaginationChange: setPagination,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
pagination,
},
});
useEffect(() => {
initFetch();
}, [page]);
const initFetch = async () => {
const response = await getListContent({ page: page - 1, size: 6, sortBy: "createdAt", contentTypeId: "3" });
console.log(response);
setDocumentData(response?.data?.data?.content);
const data = response?.data?.data;
const contentData = data?.content;
setDocumentData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
};
function getSelectedCategory() {
const filter = [];
if (categorie) {
const categoryArr = categorie.split(",");
for (const element of categoryArr) {
filter.push(Number(element));
}
setCategoryFilter(filter);
}
}
const handleDeleteDate = () => {
setDateRange([null, null]);
setStartDateString("");
setEndDateString("");
setHandleClose(false);
};
const handleSorting = (e: any) => {
console.log(e.target.value);
if (e.target.value == "terbaru") {
setSortByOpt("createdAt");
} else {
setSortByOpt("clickCount");
}
setChange(!change);
};
async function getUserLevels() {
const res = await getUserLevelListByParent(761);
const userLevelList = res?.data?.data;
if (userLevelList !== null) {
let optionArr: any = [];
userLevelList?.map((option: any) => {
let optionData = {
id: option.id,
label: option.name,
value: option.id,
};
optionArr.push(optionData);
});
setUserLevels(optionArr);
}
}
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
async function doneTyping() {
if (searchTitle == "" || searchTitle == undefined) {
router.push("?title=");
} else {
router.push(`?title=${searchTitle}`);
}
}
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
return (
<div className="flex flex-col">
{/* Header */}
<div className="flex flex-col md:flex-row items-start gap-5 p-10 bg-[#f7f7f7] dark:bg-black">
<p>
{" "}
{t("text")} {">"} <span className="font-bold">{t("allText")}</span>
</p>
<p className="font-bold">|</p>
<p>{`${t("thereIs")} ${totalContent} ${t("downloadableText")}`}</p>
</div>
{/* Left */}
<div className="flex flex-col lg:flex-row gap-6 p-4">
<div className="lg:w-[25%] w-full bg-[#f7f7f7] dark:bg-black p-4 rounded-lg shadow-md">
<h2 className="text-lg font-semibold mb-4 flex items-center gap-1">
<Icon icon="stash:filter-light" fontSize={30} />
Filter
</h2>
<div className="border-t border-black my-4 dark:border-white"></div>
<div className="space-y-6">
<div>
<label htmlFor="search" className="block text-sm font-medium text-gray-700 dark:text-white">
{t("search")}
</label>
<Input
value={searchTitle}
onChange={(e) => setSearchTitle(e.target.value)}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
type="text"
id="search"
placeholder={t("searchTitle")}
className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("monthYear")}</label>
<ReactDatePicker
selected={monthYearFilter}
className="mt-1 w-full text-xs border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
onChange={(date) => setMonthYearFilter(date)}
dateFormat="MM | yyyy"
placeholderText={t("selectYear")}
showMonthYearPicker
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("date")}</label>
<div className="flex flex-row justify justify-between gap-2">
<ReactDatePicker
selectsRange
className="mt-1 w-full border text-sm rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
startDate={dateRange[0]}
endDate={dateRange[1]}
onChange={(update) => {
setDateRange(update);
}}
placeholderText={t("selectDate")}
onCalendarClose={() => setCalenderState(!calenderState)}
/>
<div className="flex items-center">{handleClose ? <Icon icon="carbon:close-filled" onClick={handleDeleteDate} width="20" inline color="#216ba5" /> : ""}</div>
</div>
</div>
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">{t("categories")}</h3>
<ul className="mt-2 space-y-2">
{categories.map((category: any) => (
<li key={category?.id}>
<label className="inline-flex items-center" htmlFor={`${category.id}`}>
<Checkbox id={`${category.id}`} value={category.id} checked={categoryFilter.includes(String(category.id))} onCheckedChange={(e) => handleCategoryFilter(Boolean(e), category.id)} />
<span className="ml-2 text-gray-700 dark:text-white">{category?.name}</span>
</label>
</li>
))}
</ul>
</div>
{/* Garis */}
<div className="border-t border-black my-4 dark:border-white"></div>
{/* Garis */}
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">Format</h3>
<ul className="mt-2 space-y-2">
<li>
<label className="inline-flex items-center">
<Checkbox id="doc" value="doc" checked={formatFilter.includes("doc")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "doc")} />
<span className="ml-2 text-gray-700 dark:text-white">DOC</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="docx" value="docx" checked={formatFilter.includes("docx")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "docx")} />
<span className="ml-2 text-gray-700 dark:text-white">DOCX</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="pdf" value="pdf" checked={formatFilter.includes("pdf")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "pdf")} />
<span className="ml-2 text-gray-700 dark:text-white">PDF</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="ppt" value="ppt" checked={formatFilter.includes("ppt")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "ppt")} />
<span className="ml-2 text-gray-700 dark:text-white">PPT</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="pptx" value="pptx" checked={formatFilter.includes("pptx")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "pptx")} />
<span className="ml-2 text-gray-700 dark:text-white">PPTX</span>
</label>
</li>
</ul>
</div>
<div className="border-t border-black dark:border-white my-4"></div>
<div className="text-center">
<a onClick={cleanCheckbox} className="text-[#bb3523] cursor-pointer">
<b>Reset Filter</b>
</a>
</div>
</div>
</div>
{/* Konten Kanan */}
<Reveal>
<div className="flex-1">
<div className="flex flex-col items-end mb-4">
<h2 className="text-lg font-semibold">{t("sortBy")}</h2>
<select defaultValue={sortBy == "popular" ? "terpopuler" : "terbaru"} onChange={(e) => handleSorting(e)} className="border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500">
<option value="latest">{t("latest")}</option>
<option value="popular">{t("mostPopular")}</option>
</select>
</div>
{documentData?.length > 0 ? (
<div className=" grid grid-cols-1 md:grid-cols-2 gap-6">
{documentData?.map((document: any) => (
<Link href={`${prefixPath}/document/detail/${document?.slug}`} 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">
<div className="flex items-center justify-center rounded-lg w-16 h-16">
<svg width="28" height="34" viewBox="0 0 28 34" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M5.6665 17.4167C5.6665 17.0851 5.7982 16.7672 6.03262 16.5328C6.26704 16.2984 6.58498 16.1667 6.9165 16.1667C7.24802 16.1667 7.56597 16.2984 7.80039 16.5328C8.03481 16.7672 8.1665 17.0851 8.1665 17.4167C8.1665 17.7482 8.03481 18.0661 7.80039 18.3005C7.56597 18.535 7.24802 18.6667 6.9165 18.6667C6.58498 18.6667 6.26704 18.535 6.03262 18.3005C5.7982 18.0661 5.6665 17.7482 5.6665 17.4167ZM6.9165 21.1667C6.58498 21.1667 6.26704 21.2984 6.03262 21.5328C5.7982 21.7672 5.6665 22.0851 5.6665 22.4167C5.6665 22.7482 5.7982 23.0661 6.03262 23.3005C6.26704 23.535 6.58498 23.6667 6.9165 23.6667C7.24802 23.6667 7.56597 23.535 7.80039 23.3005C8.03481 23.0661 8.1665 22.7482 8.1665 22.4167C8.1665 22.0851 8.03481 21.7672 7.80039 21.5328C7.56597 21.2984 7.24802 21.1667 6.9165 21.1667ZM5.6665 27.4167C5.6665 27.0851 5.7982 26.7672 6.03262 26.5328C6.26704 26.2984 6.58498 26.1667 6.9165 26.1667C7.24802 26.1667 7.56597 26.2984 7.80039 26.5328C8.03481 26.7672 8.1665 27.0851 8.1665 27.4167C8.1665 27.7482 8.03481 28.0661 7.80039 28.3005C7.56597 28.535 7.24802 28.6667 6.9165 28.6667C6.58498 28.6667 6.26704 28.535 6.03262 28.3005C5.7982 28.0661 5.6665 27.7482 5.6665 27.4167ZM11.9165 16.1667C11.585 16.1667 11.267 16.2984 11.0326 16.5328C10.7982 16.7672 10.6665 17.0851 10.6665 17.4167C10.6665 17.7482 10.7982 18.0661 11.0326 18.3005C11.267 18.535 11.585 18.6667 11.9165 18.6667H21.0832C21.4147 18.6667 21.7326 18.535 21.9671 18.3005C22.2015 18.0661 22.3332 17.7482 22.3332 17.4167C22.3332 17.0851 22.2015 16.7672 21.9671 16.5328C21.7326 16.2984 21.4147 16.1667 21.0832 16.1667H11.9165ZM10.6665 22.4167C10.6665 22.0851 10.7982 21.7672 11.0326 21.5328C11.267 21.2984 11.585 21.1667 11.9165 21.1667H21.0832C21.4147 21.1667 21.7326 21.2984 21.9671 21.5328C22.2015 21.7672 22.3332 22.0851 22.3332 22.4167C22.3332 22.7482 22.2015 23.0661 21.9671 23.3005C21.7326 23.535 21.4147 23.6667 21.0832 23.6667H11.9165C11.585 23.6667 11.267 23.535 11.0326 23.3005C10.7982 23.0661 10.6665 22.7482 10.6665 22.4167ZM11.9165 26.1667C11.585 26.1667 11.267 26.2984 11.0326 26.5328C10.7982 26.7672 10.6665 27.0851 10.6665 27.4167C10.6665 27.7482 10.7982 28.0661 11.0326 28.3005C11.267 28.535 11.585 28.6667 11.9165 28.6667H21.0832C21.4147 28.6667 21.7326 28.535 21.9671 28.3005C22.2015 28.0661 22.3332 27.7482 22.3332 27.4167C22.3332 27.0851 22.2015 26.7672 21.9671 26.5328C21.7326 26.2984 21.4147 26.1667 21.0832 26.1667H11.9165ZM26.3565 11.0233L16.6415 1.31C16.6157 1.28605 16.5885 1.26378 16.5598 1.24333C16.5392 1.22742 16.5192 1.21074 16.4998 1.19333C16.3852 1.08512 16.2632 0.984882 16.1348 0.893332C16.0922 0.865802 16.0476 0.841298 16.0015 0.819999L15.9215 0.779999L15.8382 0.731666C15.7482 0.679999 15.6565 0.626665 15.5615 0.586665C15.2296 0.454104 14.8783 0.376423 14.5215 0.356665C14.4885 0.354519 14.4557 0.350625 14.4232 0.344999C14.3779 0.338012 14.3323 0.334114 14.2865 0.333332H3.99984C3.11578 0.333332 2.26794 0.684521 1.64281 1.30964C1.01769 1.93476 0.666504 2.78261 0.666504 3.66667V30.3333C0.666504 31.2174 1.01769 32.0652 1.64281 32.6904C2.26794 33.3155 3.11578 33.6667 3.99984 33.6667H23.9998C24.8839 33.6667 25.7317 33.3155 26.3569 32.6904C26.982 32.0652 27.3332 31.2174 27.3332 30.3333V13.38C27.333 12.496 26.9817 11.6483 26.3565 11.0233ZM24.8332 30.3333C24.8332 30.5543 24.7454 30.7663 24.5891 30.9226C24.4328 31.0789 24.2208 31.1667 23.9998 31.1667H3.99984C3.77882 31.1667 3.56686 31.0789 3.41058 30.9226C3.2543 30.7663 3.1665 30.5543 3.1665 30.3333V3.66667C3.1665 3.44565 3.2543 3.23369 3.41058 3.07741C3.56686 2.92113 3.77882 2.83333 3.99984 2.83333H13.9998V10.3333C13.9998 11.2174 14.351 12.0652 14.9761 12.6904C15.6013 13.3155 16.4491 13.6667 17.3332 13.6667H24.8332V30.3333ZM16.4998 4.70166L22.9632 11.1667H17.3332C17.1122 11.1667 16.9002 11.0789 16.7439 10.9226C16.5876 10.7663 16.4998 10.5543 16.4998 10.3333V4.70166Z"
fill="black"
/>
</svg>
</div>
<div className="flex flex-col flex-1 gap-2">
<div className="text-gray-500 dark:text-gray-400 flex flex-row items-center gap-2 text-xs">
{formatDateToIndonesian(new Date(document?.createdAt))} {document?.timezone ? document?.timezone : "WIB"} | <Icon icon="formkit:eye" width="15" height="15" /> 518
</div>
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">{document?.title}</div>
<div className="flex gap-2 items-center text-xs text-red-500 dark:text-red-500">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512">
<path fill="#f00" d="M224 30v256h-64l96 128l96-128h-64V30zM32 434v48h448v-48z" />
</svg>
Download Dokumen
</div>
</div>
</Link>
))}
</div>
) : (
<p className="flex items-center justify-center">
<img src="/assets/empty-data.png" alt="empty" className="h-60 w-60 my-4" />
</p>
)}
<LandingPagination table={table} totalData={totalData} totalPage={totalPage} />
</div>
</Reveal>
</div>
</div>
);
};
export default FilterPage;

View File

@ -0,0 +1,20 @@
import LayoutProvider from "@/providers/layout.provider";
import LayoutContentProvider from "@/providers/content.provider";
import DashCodeSidebar from "@/components/partials/sidebar";
import DashCodeFooter from "@/components/partials/footer";
import ThemeCustomize from "@/components/partials/customizer";
import DashCodeHeader from "@/components/partials/header";
import { redirect } from "@/components/navigation";
import Footer from "@/components/landing-page/footer";
import Navbar from "@/components/landing-page/navbar";
const layout = async ({ children }: { children: React.ReactNode }) => {
return (
<>
{children}
</>
);
};
export default layout;

View File

@ -0,0 +1,347 @@
"use client";
import { Textarea } from "@/components/ui/textarea";
import { useParams, usePathname } from "next/navigation";
import React, { useEffect, useState } from "react";
import { Icon } from "@iconify/react/dist/iconify.js";
import NewContent from "@/components/landing-page/new-content";
import { useToast } from "@/components/ui/use-toast";
import { getCookiesDecrypt } from "@/lib/utils";
import { close, error, loading } from "@/config/swal";
import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing";
import { Link, useRouter } from "@/i18n/routing";
const DetailInfo = () => {
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 [detailDataImage, setDetailDataImage] = useState<any>();
const [selectedImage, setSelectedImage] = useState(0);
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 [imageSizeSelected, setImageSizeSelected] = useState("l");
const userId = getCookiesDecrypt("uie");
useEffect(() => {
initFetch();
checkWishlist();
}, []);
const initFetch = async () => {
const response = await getDetail(String(slug));
console.log("detailImage", 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,
});
setDetailDataImage(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
// const response = await postActivityLog(data, token);
// console.log(response);
}
const handleDownload = () => {
if (downloadProgress === 0) {
if (!userId) {
router.push("/auth/login");
} 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}`);
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 === "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="min-h-screen px-4 md:px-24 py-4">
<div className="rounded-md overflow-hidden md:flex">
{/* Bagian Kiri */}
<div className="md:w-3/4">
{/* Gambar Besar */}
<div className="relative">
<img src={detailDataImage?.files[selectedImage]?.url} alt="Main" className="rounded-lg w-auto h-fit" />
<div className="absolute top-4 left-4"></div>
</div>
{/* Gambar bawah Kecil */}
<div className="py-4 flex flex-row gap-3">
{detailDataImage?.files?.map((file: any, index: number) => (
<a onClick={() => setSelectedImage(index)} key={file?.id}>
<img src={file?.url} className="w-[120px] h-[90px] object-cover rounded-md cursor-pointer hover:ring-2 hover:ring-red-600" />
</a>
))}
</div>
{/* Footer Informasi */}
<div className="text-sm text-gray-500 flex justify-between items-center border-t mt-4">
<div className="flex flex-row items-center mt-3 justify-between">
oleh&nbsp;<span className="font-semibold text-black">{detailDataImage?.uploadedBy?.userLevel?.name}</span>&nbsp; | &nbsp;Diupdate pada {detailDataImage?.updatedAt} WIB &nbsp;|&nbsp;
<Icon icon="formkit:eye" width="15" height="15" />
&nbsp; {detailDataImage?.clickCount} &nbsp;
<p className="flex text-end">Kreator: {detailDataImage?.creatorName}</p>
</div>
</div>
{/* Keterangan */}
<div className="w-full">
<h1 className="flex flex-row font-bold text-2xl my-8">{detailDataImage?.title}</h1>
<div className="font-light text-justify" dangerouslySetInnerHTML={{ __html: detailDataImage?.htmlDescription }} />
</div>
</div>
{/* Bagian Kanan */}
<div className="md:w-1/4 p-4 bg-[#f7f7f7] h-fit rounded-lg mx-4">
{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=${detailDataImage?.category.id}`} className="bg-red-600 text-white text-xs font-bold px-3 py-3 my-3 flex justify-center items-center rounded">
{detailDataImage?.category?.name}
</Link>
<div className="flex justify-center flex-wrap gap-2 mb-4">
{detailDataImage?.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">
{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 Foto</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] rounded-lg w-fit text-white px-4 py-1">Kirim</button>
</div>
</div>
{/* Konten Serupa */}
<div className="">
<NewContent group="satker" type={"similar"} />
</div>
</div>
</>
);
};
export default DetailInfo;

View File

@ -0,0 +1,515 @@
"use client";
import React, { useEffect, useState } from "react";
import { Card, CardContent } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import { Icon } from "@iconify/react/dist/iconify.js";
import { formatDateToIndonesian, getOnlyDate, getOnlyMonthAndYear } from "@/utils/globals";
import { useParams, usePathname, useSearchParams } from "next/navigation";
import { getUserLevelListByParent, listCategory, listData, listDataRegional } from "@/service/landing/landing";
import { ColumnDef, ColumnFiltersState, PaginationState, SortingState, VisibilityState, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
import LandingPagination from "@/components/landing-page/pagination";
import { Reveal } from "@/components/landing-page/Reveal";
import { Link, useRouter } from "@/i18n/routing";
import { Input } from "@/components/ui/input";
import ReactDatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { close, loading } from "@/config/swal";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
];
const FilterPage = () => {
const router = useRouter();
const asPath = usePathname();
const params = useParams();
const searchParams = useSearchParams();
const locale = params?.locale;
const [imageData, setImageData] = useState<any>();
const [totalData, setTotalData] = React.useState<number>(1);
const [totalPage, setTotalPage] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [page, setPage] = useState(1);
const [totalContent, setTotalContent] = useState();
const [change, setChange] = useState(false);
const sortBy = searchParams?.get("sortBy");
const title = searchParams?.get("title");
const categorie = searchParams?.get("category");
const group = searchParams?.get("group");
const [contentImage, setContentImage] = useState([]);
const [, setGetTotalPage] = useState();
let typingTimer: any;
const doneTypingInterval = 1500;
const [search, setSearch] = useState();
const [categoryFilter, setCategoryFilter] = useState<any>([]);
const [monthYearFilter, setMonthYearFilter] = useState<any>();
const [searchTitle, setSearchTitle] = useState<string>("");
const [sortByOpt, setSortByOpt] = useState<any>(sortBy === "popular" ? "clickCount" : "createdAt");
const isRegional = asPath?.includes("regional");
const isSatker = asPath?.includes("satker");
const [formatFilter, setFormatFilter] = useState<any>([]);
const pages = page ? page - 1 : 0;
const [startDateString, setStartDateString] = useState<any>();
const [endDateString, setEndDateString] = useState<any>();
const [dateRange, setDateRange] = useState<any>([null, null]);
const [calenderState, setCalenderState] = useState(false);
const [handleClose, setHandleClose] = useState(false);
const [categories, setCategories] = useState([]);
const [userLevels, setUserLevels] = useState([]);
const satkerName = params?.satker_name;
let prefixPath = satkerName ? `/satker/${satkerName}` : "/";
const t = useTranslations("FilterPage");
// const [startDate, endDate] = dateRange;
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) {
setPage(Number(pageFromUrl));
}
}, [searchParams]);
useEffect(() => {
async function initState() {
getCategories();
// getSelectedCategory();
if (isSatker) {
getUserLevels();
}
}
initState();
}, []);
useEffect(() => {
if (categorie) {
setCategoryFilter(categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
console.log("Kategori", categorie, categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
}
}, [categorie]);
// useEffect(() => {
// fetchData();
// }, [page, sortBy, sortByOpt, title]);
useEffect(() => {
async function initState() {
if (isRegional) {
getDataRegional();
} else {
getDataAll();
}
}
console.log(monthYearFilter, "monthFilter");
initState();
}, [change, asPath, monthYearFilter, page, sortBy, sortByOpt, title, startDateString, endDateString, categorie, formatFilter]);
async function getCategories() {
const category = await listCategory("1");
const resCategory = category?.data?.data?.content;
setCategories(resCategory);
}
useEffect(() => {
function initState() {
if (dateRange[0] != null && dateRange[1] != null) {
setStartDateString(getOnlyDate(dateRange[0]));
setEndDateString(getOnlyDate(dateRange[1]));
setHandleClose(true);
console.log("date range", dateRange, getOnlyDate(dateRange[0]));
}
}
initState();
}, [calenderState]);
async function getDataAll() {
if (satkerName && String(satkerName)?.length > 1) {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
const filterGroup = "satker-" + String(satkerName);
loading();
const response = await listData(
"1",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
filterGroup,
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
const data = response?.data?.data;
const contentData = data?.content;
setImageData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response?.data?.data?.totalElements);
} else {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listData(
"1",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
const data = response?.data?.data;
const contentData = data?.content;
setImageData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response?.data?.data?.totalElements);
}
}
const handleCategoryFilter = (e: boolean, id: string) => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
};
const handleFormatFilter = (e: boolean, id: string) => {
let filter = [...formatFilter];
if (e) {
filter = [...formatFilter, id];
} else {
filter.splice(formatFilter.indexOf(id), 1);
}
console.log("Format filter", filter);
setFormatFilter(filter);
};
const cleanCheckbox = () => {
setCategoryFilter([]);
setFormatFilter([]);
router.push(`?category=&title=`);
setDateRange([null, null]);
setMonthYearFilter(null);
setChange(!change);
};
async function getDataRegional() {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listDataRegional(
"1",
name,
filter,
format,
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : "",
12,
pages,
sortByOpt
);
close();
setGetTotalPage(response?.data?.data?.totalPages);
setContentImage(response?.data?.data?.content);
setTotalContent(response?.data?.data?.totalElements);
}
const table = useReactTable({
data: imageData,
columns: columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
onPaginationChange: setPagination,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
pagination,
},
});
function getSelectedCategory() {
const filter = [];
if (categorie) {
const categoryArr = categorie.split(",");
for (const element of categoryArr) {
filter.push(Number(element));
}
setCategoryFilter(filter);
}
}
const handleDeleteDate = () => {
setDateRange([null, null]);
setStartDateString("");
setEndDateString("");
setHandleClose(false);
};
const handleSorting = (e: any) => {
console.log(e.target.value);
if (e.target.value == "terbaru") {
setSortByOpt("createdAt");
} else {
setSortByOpt("clickCount");
}
setChange(!change);
};
async function getUserLevels() {
const res = await getUserLevelListByParent(761);
const userLevelList = res?.data?.data;
if (userLevelList !== null) {
let optionArr: any = [];
userLevelList?.map((option: any) => {
let optionData = {
id: option.id,
label: option.name,
value: option.id,
};
optionArr.push(optionData);
});
setUserLevels(optionArr);
}
}
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
async function doneTyping() {
if (searchTitle == "" || searchTitle == undefined) {
router.push("?title=");
} else {
router.push(`?title=${searchTitle}`);
}
}
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
return (
<div className="flex flex-col">
{/* Header */}
<div className="flex flex-col md:flex-row items-start gap-5 p-10 bg-[#f7f7f7] dark:bg-black">
<p>
{" "}
{t("image")} {">"} <span className="font-bold">{t("allImage")}</span>
</p>
<p className="font-bold">|</p>
<p>{`${t("thereIs")} ${totalContent} ${t("downloadableImage")}`}</p>
</div>
{/* Left */}
<div className="flex flex-col lg:flex-row gap-6 p-4">
<div className="lg:w-[25%] h-fit w-full bg-[#f7f7f7] dark:bg-black p-4 rounded-lg shadow-md">
<h2 className="text-lg font-semibold mb-4 flex items-center gap-1">
<Icon icon="stash:filter-light" fontSize={30} />
Filter
</h2>
<div className="border-t border-black my-4 dark:border-white"></div>
<div className="space-y-6">
<div>
<label htmlFor="search" className="block text-sm font-medium text-gray-700 dark:text-white">
{t("search")}
</label>
<Input
value={searchTitle}
onChange={(e) => setSearchTitle(e.target.value)}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
type="text"
id="search"
placeholder={t("searchTitle")}
className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("monthYear")}</label>
<ReactDatePicker
selected={monthYearFilter}
className="mt-1 w-full text-xs border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
onChange={(date) => setMonthYearFilter(date)}
dateFormat="MM | yyyy"
placeholderText={t("selectYear")}
showMonthYearPicker
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("date")}</label>
<div className="flex flex-row justify justify-between gap-2">
<ReactDatePicker
selectsRange
className="mt-1 w-full border text-sm rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
startDate={dateRange[0]}
endDate={dateRange[1]}
onChange={(update) => {
setDateRange(update);
}}
placeholderText={t("selectDate")}
onCalendarClose={() => setCalenderState(!calenderState)}
/>
<div className="flex items-center">{handleClose ? <Icon icon="carbon:close-filled" onClick={handleDeleteDate} width="20" inline color="#216ba5" /> : ""}</div>
</div>
</div>
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">{t("categories")}</h3>
<ul className="mt-2 space-y-2">
{categories.map((category: any) => (
<li key={category?.id}>
<label className="inline-flex items-center" htmlFor={`${category.id}`}>
<Checkbox id={`${category.id}`} value={category.id} checked={categoryFilter.includes(String(category.id))} onCheckedChange={(e) => handleCategoryFilter(Boolean(e), category.id)} />
<span className="ml-2 text-gray-700 dark:text-white">{category?.name}</span>
</label>
</li>
))}
</ul>
</div>
{/* Garis */}
<div className="border-t border-black my-4 dark:border-white"></div>
{/* Garis */}
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">Format</h3>
<ul className="mt-2 space-y-2">
<li>
<label className="inline-flex items-center">
<Checkbox id="png" value="png" checked={formatFilter.includes("png")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "png")} />
<span className="ml-2 text-gray-700 dark:text-white">PNG</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="jpeg" value="jpeg" checked={formatFilter.includes("jpeg")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "jpeg")} />
<span className="ml-2 text-gray-700 dark:text-white">JPEG</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="jpg" value="jpg" checked={formatFilter.includes("jpg")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "jpg")} />
<span className="ml-2 text-gray-700 dark:text-white">JPG</span>
</label>
</li>
</ul>
</div>
<div className="border-t border-black dark:border-white my-4"></div>
<div className="text-center">
<a onClick={cleanCheckbox} className="text-[#bb3523] cursor-pointer">
<b>Reset Filter</b>
</a>
</div>
</div>
</div>
{/* Konten Kanan */}
<Reveal>
<div className="flex-1">
<div className="flex flex-col items-end mb-4">
<h2 className="text-lg font-semibold">{t("sortBy")}</h2>
<select defaultValue={sortBy == "popular" ? "terpopuler" : "terbaru"} onChange={(e) => handleSorting(e)} className="border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500">
<option value="latest">{t("latest")}</option>
<option value="popular">{t("mostPopular")}</option>
</select>
</div>
{imageData?.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{imageData?.map((image: any) => (
<Card key={image?.id} className="hover:scale-110 transition-transform duration-300">
<CardContent className="flex flex-col text-xs lg:text-sm w-full p-0">
<Link href={`${prefixPath}/image/detail/${image?.slug}`}>
<img src={image?.thumbnailLink} className="h-60 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
<div className="flex flex-row items-center gap-2 text-[10px] mx-2">
{formatDateToIndonesian(new Date(image?.createdAt))} {image?.timezone ? image?.timezone : "WIB"}| <Icon icon="formkit:eye" width="15" height="15" />
{image?.clickCount}{" "}
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 20 20">
<path
fill="#f00"
d="M7.707 10.293a1 1 0 1 0-1.414 1.414l3 3a1 1 0 0 0 1.414 0l3-3a1 1 0 0 0-1.414-1.414L11 11.586V6h5a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h5v5.586zM9 4a1 1 0 0 1 2 0v2H9z"
/>
</svg>{" "}
</div>
<div className="font-semibold pr-3 pb-3 mx-2 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible w-full">{image?.title}</div>
</Link>
</CardContent>
</Card>
))}
</div>
) : (
<p className="flex items-center justify-center text-black">
<img src="/assets/empty-data.png" alt="empty" className="h-60 w-60 my-4" />
</p>
)}
<LandingPagination table={table} totalData={totalData} totalPage={totalPage} />
</div>
</Reveal>
</div>
</div>
);
};
export default FilterPage;

View File

@ -0,0 +1,12 @@
import Footer from "@/components/landing-page/footer";
import Navbar from "@/components/landing-page/navbar";
const layout = async ({ children }: { children: React.ReactNode }) => {
return (
<>
{children}
</>
);
};
export default layout;

View File

@ -0,0 +1,96 @@
"use client";
import { Textarea } from "@/components/ui/textarea";
import { useParams, usePathname, useRouter } from "next/navigation";
import React, { useEffect, useState } from "react";
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from "@/components/ui/carousel";
import { Link } from "@/i18n/routing";
import { getDetailIndeks, publicDetailBlog } from "@/service/landing/landing";
import { formatDateToIndonesian } from "@/utils/globals";
import { Icon } from "@iconify/react/dist/iconify.js";
const IndeksDetail = () => {
const [indeksData, setIndeksData] = useState<any>();
const params = useParams();
const slug = params?.slug;
const [relatedPost, setRelatedPost] = useState<any>();
useEffect(() => {
initFetch();
detailFetch();
}, []);
const initFetch = async () => {
const response = await getDetailIndeks();
console.log(response);
setRelatedPost(response?.data?.data?.content);
};
const detailFetch = async () => {
const response = await publicDetailBlog(slug);
console.log(response);
setIndeksData(response?.data?.data);
};
return (
<>
<div className="p-4 lg:px-60 lg:p-12">
{/* Judul */}
<div className="flex flex-col mb-5">
<h1 className="text-lg mb-2">Indeks / Detail</h1>
<h1 className="flex flex-row font-bold text-center text-2xl">{indeksData?.title}</h1>
</div>
{/* Gambar Utama */}
<div className="flex items-center justify-center">
<img src={indeksData?.thumbnailLink} alt="Main" className="h-[550px] w-full rounded-lg" />
</div>
{/* Footer Informasi */}
<div className="text-sm text-gray-500 flex justify-between items-center border-t mt-4">
<div className="flex flex-row items-center mt-3 justify-between">
oleh&nbsp;<span className="font-semibold text-black">{indeksData?.uploaderName}</span>&nbsp; | &nbsp;Diupdate pada {indeksData?.createdAt} WIB &nbsp;
</div>
</div>
{/* Keterangan */}
<div className="w-auto">
<p className="font-light text-justify" dangerouslySetInnerHTML={{ __html: indeksData?.description }} />
</div>
</div>
{/* Comment */}
<div className="w-full">
<div className="flex flex-col py-5 p-10 bg-[#f7f7f7]">
<div className="gap-5 flex flex-col px-4 lg:px-16">
<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="space-x-5 flex flex-col px-4 lg:px-16 py-16 gap-5">
<h1 className="font-bold text-base lg:text-xl px-4 lg:px-8">Post Terkait</h1>
<Carousel>
<CarouselContent className="w-full max-w-7xl">
{relatedPost?.map((relate: any) => (
<CarouselItem key={relate?.id} className="md:basis-1/2 lg:basis-1/3">
<Link href={`/indeks/detail/${relate?.slug}`} className="relative group overflow-hidden shadow-md hover:shadow-lg">
<img src={relate?.thumbnailLink} className="w-full rounded-lg h-40 lg:h-60 object-cover group-hover:scale-100 transition-transform duration-300" />
<div className="absolute bottom-0 left-0 right-0 bg-gray-600 border-l-4 border-[#bb3523] rounded-lg backdrop-blur-sm text-white p-2">
<span className="text-white bg-[#bb3523] rounded-md w-full h-full font-semibold uppercase text-sm px-4 py-1">{relate?.categoryName}</span>
<h1 className="text-sm lg:text-lg mb-2 font-semibold h-5 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible">{relate?.title}</h1>
<p className="flex flex-row items-center text-[10px] gap-2">
{formatDateToIndonesian(new Date(relate?.createdAt))} {relate?.timezone ? relate?.timezone : "WIB"} | <Icon icon="formkit:eye" width="15" height="15" /> {relate.clickCount}{" "}
</p>
</div>
</Link>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
</div>
</div>
</>
);
};
export default IndeksDetail;

View File

@ -0,0 +1,20 @@
import LayoutProvider from "@/providers/layout.provider";
import LayoutContentProvider from "@/providers/content.provider";
import DashCodeSidebar from "@/components/partials/sidebar";
import DashCodeFooter from "@/components/partials/footer";
import ThemeCustomize from "@/components/partials/customizer";
import DashCodeHeader from "@/components/partials/header";
import { redirect } from "@/components/navigation";
import Footer from "@/components/landing-page/footer";
import Navbar from "@/components/landing-page/navbar";
const layout = async ({ children }: { children: React.ReactNode }) => {
return (
<>
{children}
</>
);
};
export default layout;

View File

@ -0,0 +1,116 @@
"use client";
import { Link } from "@/i18n/routing";
import { getIndeksData } from "@/service/landing/landing";
import { formatDateToIndonesian } from "@/utils/globals";
import { usePathname } from "next/navigation";
import React, { useEffect, useState } from "react";
const Indeks: React.FC = () => {
const pathname = usePathname();
const [indeksData, setIndeksData] = useState<any>();
let count: number = 0;
useEffect(() => {
if (indeksData) {
const intervalId = setInterval(() => {
count = (count + 1) % indeksData.length;
}, 5000);
return () => clearInterval(intervalId);
}
}, [indeksData]);
useEffect(() => {
initFetch();
}, []);
const initFetch = async () => {
const response = await getIndeksData();
console.log(response);
setIndeksData(response?.data?.data?.content);
};
return (
<div className="px-4 lg:px-14">
{/* Hero Left */}
<div className="flex flex-col lg:flex-row items-start gap-8 px-4 lg:px-10 py-4 mx-auto">
<div className="lg:w-[60%] w-full lg:h-full">
{indeksData?.map(
(indeks: any, index: number) =>
index == count && (
<div key={indeks?.id} className="relative h-[310px] lg:h-[435px]">
<img src={indeks?.thumbnailLink} alt="image" className="w-full h-[310px] lg:h-[435px] rounded-lg" />
<div className="absolute bottom-0 left-0 right-0 bg-transparent backdrop-blur-sm text-white p-4 rounded-b-lg">
<span className="text-white bg-[#bb3523] rounded-md w-full h-full font-semibold uppercase text-sm px-4 py-1">{indeks?.categoryName}</span>
<Link href={`/indeks/detail/${indeks?.slug}`}>
<h2 className="text-2xl font-bold mt-2">{indeks?.title}</h2>
</Link>
<p className="text-xs flex flex-row items-center gap-1 mt-1 ml-2">
{formatDateToIndonesian(new Date(indeks?.createdAt))} {indeks?.timezone ? indeks?.timezone : "WIB"}|{" "}
<svg xmlns="http://www.w3.org/2000/svg" width="1.2em" height="1.2em" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M11.5 18c4 0 7.46-2.22 9.24-5.5C18.96 9.22 15.5 7 11.5 7s-7.46 2.22-9.24 5.5C4.04 15.78 7.5 18 11.5 18m0-12c4.56 0 8.5 2.65 10.36 6.5C20 16.35 16.06 19 11.5 19S3 16.35 1.14 12.5C3 8.65 6.94 6 11.5 6m0 2C14 8 16 10 16 12.5S14 17 11.5 17S7 15 7 12.5S9 8 11.5 8m0 1A3.5 3.5 0 0 0 8 12.5a3.5 3.5 0 0 0 3.5 3.5a3.5 3.5 0 0 0 3.5-3.5A3.5 3.5 0 0 0 11.5 9"
/>
</svg>{" "}
{indeks?.clickCount}
</p>
</div>
</div>
)
)}
</div>
{/* Hero Right */}
<div className="lg:w-[40%] w-full space-y-2">
{indeksData?.map(
(indeksRight: any, index: number) =>
(index == count + 1 || index == count + 2) && (
<div key={indeksRight?.id} className="relative h-[310px] lg:h-[215px]">
<img src={indeksRight?.thumbnailLink} alt="image" className="w-full h-[310px] lg:h-[215px] rounded-lg " />
<div className="absolute bottom-0 left-0 right-0 bg-transparent backdrop-blur-sm text-white p-4 rounded-b-lg">
<span className="text-white bg-[#bb3523] rounded-md w-full h-full font-semibold uppercase text-sm px-4 py-1">{indeksRight?.categoryName}</span>
<Link href={`/indeks/detail/${indeksRight?.slug}`}>
<h2 className="text-xl font-bold mt-2">{indeksRight?.title}</h2>
</Link>
<p className="text-xs flex flex-row items-center gap-1 mt-1 ml-2">
{formatDateToIndonesian(new Date(indeksRight?.createdAt))} {indeksRight?.timezone ? indeksRight?.timezone : "WIB"}|{" "}
<svg xmlns="http://www.w3.org/2000/svg" width="1.2em" height="1.2em" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M11.5 18c4 0 7.46-2.22 9.24-5.5C18.96 9.22 15.5 7 11.5 7s-7.46 2.22-9.24 5.5C4.04 15.78 7.5 18 11.5 18m0-12c4.56 0 8.5 2.65 10.36 6.5C20 16.35 16.06 19 11.5 19S3 16.35 1.14 12.5C3 8.65 6.94 6 11.5 6m0 2C14 8 16 10 16 12.5S14 17 11.5 17S7 15 7 12.5S9 8 11.5 8m0 1A3.5 3.5 0 0 0 8 12.5a3.5 3.5 0 0 0 3.5 3.5a3.5 3.5 0 0 0 3.5-3.5A3.5 3.5 0 0 0 11.5 9"
/>
</svg>{" "}
{indeksRight?.clickCount}
</p>
</div>
</div>
)
)}
</div>
</div>
{/* Bottom */}
<div className="px-4 lg:px-7 py-4">
<div className="flex flex-col gap-4">
{indeksData?.map(
(indeksBottom: any, index: number) =>
index < 3 && (
<div key={indeksBottom?.id} className="flex flex-col md:flex-row items-start p-4 gap-4">
<img src={indeksBottom?.thumbnailLink} alt="" className="h-40 object-cover rounded-lg w-full lg:w-full lg:h-[300px]" />
<div className="flex flex-col justify-between w-full">
<p className="text-sm">{indeksBottom?.date}</p>
<Link href={`/indeks/detail/${indeksBottom?.slug}`} className="text-2xl font-semibold text-gray-800">
{indeksBottom?.title}
</Link>
<p className="text-sm text-gray-600 mt-2">{indeksBottom?.description}</p>
</div>
</div>
)
)}
</div>
</div>
</div>
);
};
export default Indeks;

View File

@ -11,8 +11,8 @@ const page = () => {
<div>
<HeaderBannerSatker />
<WelcomeSatker />
<NewContent type="latest" />
<NewContent type="popular" />
<NewContent group="satker" type="latest" />
<NewContent group="satker" type="popular" />
<ContentCategory />
</div>
);

View File

@ -0,0 +1,20 @@
import LayoutProvider from "@/providers/layout.provider";
import LayoutContentProvider from "@/providers/content.provider";
import DashCodeSidebar from "@/components/partials/sidebar";
import DashCodeFooter from "@/components/partials/footer";
import ThemeCustomize from "@/components/partials/customizer";
import DashCodeHeader from "@/components/partials/header";
import { redirect } from "@/components/navigation";
import Footer from "@/components/landing-page/footer";
import Navbar from "@/components/landing-page/navbar";
const layout = async ({ children }: { children: React.ReactNode }) => {
return (
<>
{children}
</>
);
};
export default layout;

View File

@ -0,0 +1,741 @@
"use client";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { CalendarIcon } from "lucide-react";
import React, { useEffect, useState } from "react";
import { format } from "date-fns";
import { cn } from "@/lib/utils";
import { Checkbox } from "@/components/ui/checkbox";
import { Icon } from "@iconify/react/dist/iconify.js";
import { detailSchedule, listSchedule, listScheduleNextPublic, listSchedulePrevPublic, listScheduleTodayPublic } from "@/service/schedule/schedule";
import { useRouter } from "@/i18n/routing";
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog";
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
import { close, loading } from "@/config/swal";
const timeList = [
{
id: "6",
time: "06:00",
},
{
id: "7",
time: "07:00",
},
{
id: "8",
time: "08:00",
},
{
id: "9",
time: "09:00",
},
{
id: "10",
time: "10:00",
},
{
id: "11",
time: "11:00",
},
{
id: "12",
time: "12:00",
},
{
id: "13",
time: "13:00",
},
{
id: "14",
time: "14:00",
},
{
id: "15",
time: "15:00",
},
{
id: "16",
time: "16:00",
},
{
id: "17",
time: "17:00",
},
{
id: "18",
time: "18:00",
},
{
id: "19",
time: "19:00",
},
{
id: "20",
time: "20:00",
},
{
id: "21",
time: "21:00",
},
{
id: "22",
time: "22:00",
},
{
id: "23",
time: "23:00",
},
{
id: "24",
time: "24:00",
},
{
id: "1",
time: "01:00",
},
{
id: "2",
time: "02:00",
},
{
id: "3",
time: "03:00",
},
{
id: "4",
time: "04:00",
},
{
id: "5",
time: "05:00",
},
];
const Schedule = (props: any) => {
const router = useRouter();
const [startDate, setStartDate] = useState<Date | undefined>(new Date());
const [dateAWeek, setDateAWeek] = useState<string[]>([]);
const [scheduleSearch, setScheduleSearch] = useState();
const [todayList, setTodayList] = useState([]);
const [prevdayList, setPrevdayList] = useState([]);
const [nextdayList, setNextdayList] = useState([]);
const [isOpen, setIsOpen] = React.useState(false);
const [schedules, setSchedules] = useState([]);
const [openDialog, setOpenDialog] = useState(false);
const [detail, setDetail] = useState<any>();
const [content, setContent] = useState();
const { id } = props;
useEffect(() => {
async function getDataSchedule() {
const response = await detailSchedule(id);
setDetail(response?.data?.data);
setContent(response?.data?.data?.files);
}
getDataSchedule();
}, [id]);
useEffect(() => {
async function initState() {
getDataByDate();
// const group = isPolda ? asPath.split("/")[2] : regionFilter?.join(",");
const resSchedule = await listSchedule();
setSchedules(resSchedule.data?.data);
console.log(resSchedule);
setDateAWeek(dateList);
}
initState();
}, []);
async function getDataByDate() {
const resToday = await listScheduleTodayPublic();
const today = resToday.data?.data;
setTodayList(today);
const resNext = await listScheduleNextPublic();
const next = resNext.data?.data;
setNextdayList(next);
const resPrev = await listSchedulePrevPublic();
const prev = resPrev.data?.data;
setPrevdayList(prev);
}
const curr = new Date();
const startDays = (curr.getDay() + 7 - 1) % 7;
curr.setDate(curr.getDate() - startDays);
const dateListInit = [];
curr.setDate(curr.getDate() - curr.getDay() + 1);
for (let i = 0; i < 7; i++) {
dateListInit.push(new Date(curr).toISOString().slice(0, 10));
curr.setDate(curr.getDate() + 1);
}
const [dateList, setDateList] = useState<string[]>(dateListInit);
const handleChangeDate = (date: Date | undefined) => {
setStartDate(date);
const dateListTemp = [];
const curr = date;
if (curr) {
const startDays = (curr.getDay() + 7 - 1) % 7;
curr.setDate(curr.getDate() - startDays);
curr.setDate(curr.getDate() - curr.getDay() + 1);
for (let i = 0; i < 7; i++) {
dateListTemp.push(new Date(curr).toISOString().slice(0, 10));
curr.setDate(curr.getDate() + 1);
}
console.log("Change Date :", dateListTemp);
setDateList(dateListTemp);
setDateAWeek(dateListTemp);
}
};
function getLastWeek(today: Date | undefined) {
if (today) {
return new Date(today.getFullYear(), today.getMonth(), today.getDate() - 7);
}
}
function getNextWeek(today: Date | undefined) {
if (today) {
return new Date(today.getFullYear(), today.getMonth(), today.getDate() + 7);
}
}
const changeNextWeek = () => {
console.log("Today :", startDate);
const dayInNextWeek = getNextWeek(startDate);
console.log("Next week :", dayInNextWeek);
const dateListTemp = [];
const curr = dayInNextWeek;
if (curr) {
const startDays = (curr.getDay() + 7 - 1) % 7;
curr.setDate(curr.getDate() - startDays);
curr.setDate(curr.getDate() - curr.getDay() + 1);
for (let i = 0; i < 7; i++) {
const newDate = new Date(curr);
if (i == 0) {
setStartDate(newDate);
}
const dateFormatter = Intl.DateTimeFormat("sv-SE");
dateListTemp.push(dateFormatter.format(newDate));
curr.setDate(curr.getDate() + 1);
}
console.log(dateListTemp);
setDateList(dateListTemp);
setDateAWeek(dateListTemp);
}
};
const changePrevWeek = () => {
console.log(startDate);
const dayInPrevWeek = getLastWeek(startDate);
console.log("Prev week :", dayInPrevWeek);
console.log(startDate);
const dateListTemp = [];
const curr = dayInPrevWeek;
if (curr) {
const startDays = (curr.getDay() + 7 - 1) % 7;
curr.setDate(curr.getDate() - startDays);
curr.setDate(curr.getDate() - curr.getDay() + 1);
for (let i = 0; i < 7; i++) {
const newDate = new Date(curr);
if (i == 0) {
setStartDate(newDate);
}
const dateFormatter = Intl.DateTimeFormat("sv-SE");
dateListTemp.push(dateFormatter.format(newDate));
curr.setDate(curr.getDate() + 1);
}
console.log(dateListTemp);
setDateList(dateListTemp);
setDateAWeek(dateListTemp);
}
};
const getItem = async (itemFound: any) => {
loading();
const response = await detailSchedule(itemFound?.id);
setDetail(response?.data?.data);
setContent(response?.data?.data?.files);
console.log("item Found", itemFound);
close();
setOpenDialog(true);
};
function setItemSchedule(id: string, date: string) {
const itemFound: any = schedules?.filter((s: any) => s.dateInRange.includes(date) && s.timeIndex.split(",").includes(id));
if (itemFound?.length > 0) {
if (itemFound?.length == 1) {
return (
<a
className={`cursor-pointer text-center ${Number(itemFound[0]?.uploaderLevelNumber) == 1 ? "bg-yellow-300" : Number(itemFound[0]?.uploaderLevelNumber) == 2 ? "bg-blue-400" : "bg-gray-500"}`}
onClick={() => {
getItem(itemFound[0]);
}}
>
<p>
<b>{itemFound[0]?.title}</b>
</p>
{itemFound[0].isYoutube == true ? <p className="live">LIVE</p> : ""}
{/* <p className="address">{itemFound[0].address}</p> */}
</a>
);
}
// for (let i = 0; i < itemFound.length; i++) {
// const item = itemFound[i];
// }
return (
<div className="text-left">
<p>
<b>{`${itemFound?.length} Jadwal Bersamaan`}</b>
</p>
<DropdownMenu>
<DropdownMenuTrigger className="font-bold text-blue-300">Lihat Jadwal</DropdownMenuTrigger>
<DropdownMenuContent>
{itemFound?.map((list: any) => (
<DropdownMenuItem
key={list.id}
className="cursor-pointer"
onClick={() => {
getItem(itemFound[0]);
}}
>
<p>
<b>{list.title}</b>
</p>
{list.isYoutube == true ? <p className="live">LIVE</p> : ""}
{/* <p className="address">{list.address}</p> */}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
<div className="border-0 dropdown-menu schedule-list" aria-labelledby="view-schedule"></div>
</div>
);
}
}
return (
<>
{/* Awal Komponen Kiri */}
<div className="relative px-4 lg:px-10 lg:py-10 py-4 bg-[#f7f7f7] dark:bg-slate-800">
<Popover>
<PopoverTrigger asChild>
<Button variant={"outline"} className={cn("w-[240px] py-4 justify-start text-left font-normal", !startDate && "text-muted-foreground")}>
<CalendarIcon />
{startDate ? format(startDate, "MMM yyyy") : <span>Pick a date</span>}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={startDate}
onSelect={(e) => {
handleChangeDate(e);
}}
initialFocus
/>
</PopoverContent>
</Popover>
<div className="container relative py-4">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<a className="text-black flex flex-row w-fit gap-2 py-4 items-center cursor-pointer">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
<path fill="#000" d="M20 3H4a1 1 0 0 0-1 1v2.227l.008.223a3 3 0 0 0 .772 1.795L8 12.886V21a1 1 0 0 0 1.316.949l6-2l.108-.043A1 1 0 0 0 16 19v-6.586l4.121-4.12A3 3 0 0 0 21 6.171V4a1 1 0 0 0-1-1" />
</svg>
Filter
<svg className="flex items-center justify-center" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
<path fill="currentColor" fill-rule="evenodd" d="m6 7l6 6l6-6l2 2l-8 8l-8-8z" />
</svg>
</a>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="flex p-0 rounded-md">
<DropdownMenuItem className="flex flex-col items-center justify-between gap-1.5 p-2 border-b text-default-600 rounded-none">
<div className="gap-6 flex flex-row justify-end">
<div> Filter</div>
<button className="text-blue-400">Simpan</button>
</div>
<div className="border w-full border-t border-slate-500"></div>
<div className="overflow-y-auto flex flex-col gap-2 h-[200px] ">
<p>Region Filter</p>
<div className="mt-2 gap-2 flex flex-row">
<Checkbox id="terms" />
<p>POLDA METRO JAYA</p>
</div>
<div className="mt-2 gap-2 flex flex-row">
<Checkbox id="terms" />
<p>POLDA METRO JAYA</p>
</div>
<div className="mt-2 gap-2 flex flex-row">
<Checkbox id="terms" />
<p>POLDA METRO JAYA</p>
</div>
<div className="mt-2 gap-2 flex flex-row">
<Checkbox id="terms" />
<p>POLDA METRO JAYA</p>
</div>
<div className="mt-2 gap-2 flex flex-row">
<Checkbox id="terms" />
<p>POLDA METRO JAYA</p>
</div>
<div className="mt-2 gap-2 flex flex-row">
<Checkbox id="terms" />
<p>POLDA METRO JAYA</p>
</div>
<div className="mt-2 gap-2 flex flex-row">
<Checkbox id="terms" />
<p>POLDA METRO JAYA</p>
</div>
<div className="mt-2 gap-2 flex flex-row">
<Checkbox id="terms" />
<p>POLDA METRO JAYA</p>
</div>
<div className="mt-2 gap-2 flex flex-row">
<Checkbox id="terms" />
<p>POLDA METRO JAYA</p>
</div>
</div>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className="flex flex-col lg:flex-row gap-6">
<div className="h-[500px] overflow-y-auto w-3/4 ">
<div className="container-fluid relative">
<div className="grid grid-cols-1 mt-8">
<div className="relative block bg-white dark:bg-slate-900">
<table className="w-full text-sm text-start">
<thead className="text-md">
<tr className="h-full">
<th className="text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[120px]">Time Table</th>
<th className="text-center border border-r-0 border-gray-100 dark:border-gray-700 py-6 w-[20px]">
<a onClick={() => changePrevWeek()} className="cursor-pointer h-fit self-center bottom-0">
<svg xmlns="http://www.w3.org/2000/svg" width="40px" height="40px" viewBox="0 0 24 24">
<path fill="currentColor" d="M12.29 8.71L9.7 11.3a.996.996 0 0 0 0 1.41l2.59 2.59c.63.63 1.71.18 1.71-.71V9.41c0-.89-1.08-1.33-1.71-.7" />
</svg>
</a>
</th>
<th className={`text-center cursor-pointer border border-l-0 h-full border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${new Date().toISOString().slice(0, 10) == dateAWeek[0] ? "bg-red-600 text-white" : ""}`}>
{/* <a className="cursor-pointer h-fit self-center bottom-0" >
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24">
<path fill="currentColor" d="M12.29 8.71L9.7 11.3a.996.996 0 0 0 0 1.41l2.59 2.59c.63.63 1.71.18 1.71-.71V9.41c0-.89-1.08-1.33-1.71-.7" />
</svg>
</a>{" "} */}
<div className="flex flex-col ">
<p className="text-2xl">{dateAWeek[0]?.split("-")[2]}</p>
<p>Monday</p>
</div>
</th>
<th className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${new Date().toISOString().slice(0, 10) == dateAWeek[1] ? "bg-[#BE0106] text-white rounded-lg" : ""}`}>
<div className="text-2xl">{dateAWeek[1]?.split("-")[2]}</div>Tuesday
</th>
<th className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${new Date().toISOString().slice(0, 10) == dateAWeek[2] ? "bg-[#BE0106] text-white rounded-lg" : ""}`}>
<div className="text-2xl">{dateAWeek[2]?.split("-")[2]}</div>Wednesday
</th>
<th className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${new Date().toISOString().slice(0, 10) == dateAWeek[3] ? "bg-[#BE0106] text-white rounded-lg" : ""}`}>
<div className="text-2xl">{dateAWeek[3]?.split("-")[2]}</div>Thursday
</th>
<th className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${new Date().toISOString().slice(0, 10) == dateAWeek[4] ? "bg-[#BE0106] text-white rounded-lg" : ""}`}>
<div className="text-2xl">{dateAWeek[4]?.split("-")[2]}</div>Friday
</th>
<th className={`text-center border border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${new Date().toISOString().slice(0, 10) == dateAWeek[5] ? "bg-[#BE0106] text-white rounded-lg" : ""}`}>
<div className="text-2xl">{dateAWeek[5]?.split("-")[2]}</div>Saturday
</th>
<th
onClick={() => changeNextWeek()}
className={`text-center border cursor-pointer border-r-0 border-gray-100 dark:border-gray-700 py-6 min-w-[100px] ${
new Date().toISOString().slice(0, 10) == dateAWeek[6] ? "bg-[#BE0106] text-white rounded-lg" : ""
}`}
>
<div className="flex flex-col ">
<p className="text-2xl">{dateAWeek[6]?.split("-")[2]}</p>
<p>Sunday</p>
</div>
{/* <a className="cursor-pointer h-fit p-0 m-0 self-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24">
<path fill="currentColor" d="m11.71 15.29l2.59-2.59a.996.996 0 0 0 0-1.41L11.71 8.7c-.63-.62-1.71-.18-1.71.71v5.17c0 .9 1.08 1.34 1.71.71" />
</svg>
</a> */}
</th>
<th className="text-center border-l-0 border border-gray-100 dark:border-gray-700 py-6 w-[20px]">
<a onClick={() => changeNextWeek()} className="cursor-pointer">
<svg xmlns="http://www.w3.org/2000/svg" width="40px" height="40px" viewBox="0 0 24 24">
<path fill="currentColor" d="m11.71 15.29l2.59-2.59a.996.996 0 0 0 0-1.41L11.71 8.7c-.63-.62-1.71-.18-1.71.71v5.17c0 .9 1.08 1.34 1.71.71" />
</svg>
</a>
</th>
</tr>
</thead>
<tbody>
{timeList.map((times) => (
<tr key={times.id}>
<th className="text-center border border-gray-100 dark:border-gray-700 py-5">{times.time}</th>
<td colSpan={2} className="p-3 border border-gray-100 dark:border-gray-700 ">
{setItemSchedule(times.id, dateList[0])}
</td>
<td className="border border-gray-100 dark:border-gray-700">{setItemSchedule(times.id, dateList[1])}</td>
<td className="border border-gray-100 dark:border-gray-700">{setItemSchedule(times.id, dateList[2])}</td>
<td className="border border-gray-100 dark:border-gray-700">{setItemSchedule(times.id, dateList[3])}</td>
<td className="p-3 border border-gray-100 dark:border-gray-700">{setItemSchedule(times.id, dateList[4])}</td>
<td className="border border-gray-100 dark:border-gray-700">{setItemSchedule(times.id, dateList[5])}</td>
<td colSpan={2} className="border border-gray-100 dark:border-gray-700">
{setItemSchedule(times.id, dateList[6])}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
{/* Component Kanan */}
<div className="w-1/4 flex flex-col gap-6">
<div className="relative text-gray-600 dark:text-white">
<input type="text" placeholder="Masukkan Judul Jadwal" className="pl-8 pr-4 py-1 w-full border rounded-full text-sm focus:outline-none" />
<span className="absolute left-2 top-1/2 transform -translate-y-1/2">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
<g fill="none" fill-rule="evenodd">
<path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
<path fill="currentColor" d="M10.5 2a8.5 8.5 0 1 0 5.262 15.176l3.652 3.652a1 1 0 0 0 1.414-1.414l-3.652-3.652A8.5 8.5 0 0 0 10.5 2M4 10.5a6.5 6.5 0 1 1 13 0a6.5 6.5 0 0 1-13 0" />
</g>
</svg>
</span>
</div>
{/* jadwal hari ini */}
{/* <Collapsible className="border border-slate-400 p-2 rounded-lg" open={isOpen} onOpenChange={setIsOpen}>
<CollapsibleTrigger>
<h5 className="py-2 flex justify-between items-center">
Jadwal Hari Ini
<span className="flex items-end">
<Icon icon="fa:angle-down" className="ml-1" />
</span>
</h5>
</CollapsibleTrigger>
{todayList?.map((list: any) => (
<CollapsibleContent className={`flex flex-row gap-3 ${isOpen ? "border-b-2 border-black my-3" : ""}`}>
<div className="border-l-4 border-red-700 pl-1 h-fit font-bold text-lg">{new Date(list.startDate).getDate()}</div>
<div className="flex flex-col">
<h3 className="font-bold">{list?.title}</h3>
<p className="flex flex-row items-center gap-2">
<Icon icon="iconamoon:clock-thin" />
{list?.startTime} - {list?.endTime} WIB
</p>
<p className="flex flex-row items-start gap-2 ">
<Icon icon="bxs:map" width={40} />
{list?.address}
</p>
<p>Pembicara :</p>
<p className="flex flex-row items-center gap-2">
<Icon icon="ic:round-person" />
{list?.speakerTitle}&nbsp;{list?.speakerName}
</p>
</div>
</CollapsibleContent>
))}
</Collapsible> */}
<Accordion type="single" collapsible className="w-full">
<AccordionItem value="item-1">
<AccordionTrigger>Jadwal Hari ini</AccordionTrigger>
{todayList?.map((list: any) => (
<AccordionContent key={list?.id} className="flex flex-row gap-3">
<div className="border-l-4 border-red-700 pl-1 h-fit font-bold text-lg">{new Date(list.startDate).getDate()}</div>
<div className="flex flex-col gap-2">
<h3 className="font-bold">{list?.title}</h3>
<p className="flex flex-row items-center gap-2">
<Icon icon="iconamoon:clock-thin" />
{list?.startTime} - {list?.endTime} WIB
</p>
<p className="flex flex-row items-start gap-2 ">
<Icon icon="bxs:map" width={40} />
{list?.address}
</p>
<p>Pembicara :</p>
<p className="flex flex-row items-center gap-2">
<Icon icon="ic:round-person" />
{list?.speakerTitle}&nbsp;{list?.speakerName}
</p>
</div>
</AccordionContent>
))}
</AccordionItem>
<AccordionItem value="item-2">
<AccordionTrigger>Jadwal Sebelumnya</AccordionTrigger>
{prevdayList?.map((list: any) => (
<AccordionContent key={list?.id} className="flex flex-row gap-3">
<div className="border-l-4 border-red-700 pl-1 h-fit font-bold text-lg">{new Date(list.startDate).getDate()}</div>
<div className="flex flex-col gap-2">
<h3 className="font-bold">{list?.title}</h3>
<p className="flex flex-row items-center gap-2">
<Icon icon="iconamoon:clock-thin" />
{list?.startTime} - {list?.endTime} WIB
</p>
<p className="flex flex-row items-start gap-2 ">
<Icon icon="bxs:map" width={40} />
{list?.address}
</p>
<p>Pembicara :</p>
<p className="flex flex-row items-center gap-2">
<Icon icon="ic:round-person" />
{list?.speakerTitle}&nbsp;{list?.speakerName}
</p>
</div>
</AccordionContent>
))}
</AccordionItem>
<AccordionItem value="item-3">
<AccordionTrigger>Jadwal Selanjutnya</AccordionTrigger>
{nextdayList?.map((list: any) => (
<AccordionContent key={list?.id} className="flex flex-row gap-3">
<div className="border-l-4 border-red-700 pl-1 h-fit font-bold text-lg">{new Date(list.startDate).getDate()}</div>
<div className="flex flex-col gap-2">
<h3 className="font-bold">{list?.title}</h3>
<p className="flex flex-row items-center gap-2">
<Icon icon="iconamoon:clock-thin" />
{list?.startTime} - {list?.endTime} WIB
</p>
<p className="flex flex-row items-start gap-2 ">
<Icon icon="bxs:map" width={40} />
{list?.address}
</p>
<p>Pembicara :</p>
<p className="flex flex-row items-center gap-2">
<Icon icon="ic:round-person" />
{list?.speakerTitle}&nbsp;{list?.speakerName}
</p>
</div>
</AccordionContent>
))}
</AccordionItem>
</Accordion>
{/* jadwal sebelumnya */}
{/* <Collapsible className="border border-slate-400 p-2 rounded-lg" open={isOpen} onOpenChange={setIsOpen}>
<CollapsibleTrigger>
<h5 className="py-2 flex justify-between items-center">
Jadwal Sebelumnya
<span className="flex items-end">
<Icon icon="fa:angle-down" className="ml-1" />
</span>
</h5>
</CollapsibleTrigger>
{prevdayList?.map((list: any) => (
<CollapsibleContent className={`flex flex-row gap-3 ${isOpen ? "border-b-2 border-black my-3" : ""}`}>
<div className="border-l-4 border-red-700 pl-1 h-fit font-bold text-lg">{new Date(list.startDate).getDate()}</div>
<div className="flex flex-col">
<h3 className="font-bold">{list?.title}</h3>
<p className="flex flex-row items-center gap-2">
<Icon icon="iconamoon:clock-thin" />
{list?.startTime} - {list?.endTime} WIB
</p>
<p className="flex flex-row items-start gap-2 ">
<Icon icon="bxs:map" width={40} />
{list?.address}
</p>
<p>Pembicara :</p>
<p className="flex flex-row items-center gap-2">
<Icon icon="ic:round-person" />
{list?.speakerTitle}&nbsp;{list?.speakerName}
</p>
</div>
</CollapsibleContent>
))}
</Collapsible> */}
{/* jadwal selanjutnya */}
{/* <Collapsible className="border border-slate-400 p-2 rounded-lg" open={isOpen} onOpenChange={setIsOpen}>
<CollapsibleTrigger>
<h5 className="py-2 flex justify-end items-center">
Jadwal Selanjutnya
<span className="flex items-end">
<Icon icon="fa:angle-down" className="ml-1 flex" />
</span>
</h5>
</CollapsibleTrigger>
{nextdayList?.map((list: any) => (
<CollapsibleContent className={`flex flex-row gap-3 ${isOpen ? "border-b-2 border-black my-3" : ""}`}>
<div className="border-l-4 border-red-700 pl-1 h-fit font-bold text-lg">{new Date(list.startDate).getDate()}</div>
<div className="flex flex-col">
<h3 className="font-bold">{list?.title}</h3>
<p className="flex flex-row items-center gap-2">
<Icon icon="iconamoon:clock-thin" />
{list?.startTime} - {list?.endTime} WIB
</p>
<p className="flex flex-row items-start gap-2 ">
<Icon icon="bxs:map" width={40} />
{list?.address}
</p>
<p>Pembicara :</p>
<p className="flex flex-row items-center gap-2">
<Icon icon="ic:round-person" />
{list?.speakerTitle}&nbsp;{list?.speakerName}
</p>
</div>
</CollapsibleContent>
))}
</Collapsible> */}
</div>
</div>
</div>
<AlertDialog open={openDialog} onOpenChange={setOpenDialog}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
<h1 className="my-4 font-light">JADWAL / {detail?.isYoutube == true ? "LIVE STREAMING" : "DETAIL"}</h1>
<p className="font-bold">{detail?.title}</p>
</AlertDialogTitle>
<AlertDialogDescription>
<p className="flex flex-row items-center gap-2">
<Icon icon="iconamoon:clock-thin" />
{detail?.date} {detail?.startTime} - {detail?.endTime} {detail?.timezone ? detail.timezone : "WIB"}
</p>
</AlertDialogDescription>
<AlertDialogDescription>
<p className="flex flex-row items-start gap-2 ">
<Icon icon="bxs:map" width={30} />
{detail?.address}
</p>
</AlertDialogDescription>
<AlertDialogDescription>
<p className="flex flex-row items-center gap-2">
<Icon icon="ic:round-person" />
{detail?.speakerTitle || ""} {detail?.speakerName || ""}{" "}
</p>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogAction>Close</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</>
);
};
export default Schedule;

View File

@ -0,0 +1,343 @@
"use client";
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";
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 userId = getCookiesDecrypt("uie");
useEffect(() => {
initFetch();
checkWishlist();
}, []);
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
// const response = await postActivityLog(data, token);
// console.log(response);
}
const handleDownload = () => {
if (downloadProgress === 0) {
if (!userId) {
router.push("/auth/login");
} 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="">
<NewContent group="satker" type={"similar"} />
</div>
</div>
</>
);
};
export default DetailVideo;

View File

@ -0,0 +1,545 @@
"use client";
import React, { useEffect, useState } from "react";
import { Card, CardContent } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import { Icon } from "@iconify/react/dist/iconify.js";
import { formatDateToIndonesian, getOnlyDate, getOnlyMonthAndYear } from "@/utils/globals";
import { useParams, usePathname, useSearchParams } from "next/navigation";
import { getListContent, getUserLevelListByParent, listCategory, listData, listDataRegional } from "@/service/landing/landing";
import { ColumnDef, ColumnFiltersState, PaginationState, SortingState, VisibilityState, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
import LandingPagination from "@/components/landing-page/pagination";
import { Reveal } from "@/components/landing-page/Reveal";
import { Link, useRouter } from "@/i18n/routing";
import { Input } from "@/components/ui/input";
import ReactDatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { close, loading } from "@/config/swal";
import { useTranslations } from "next-intl";
const columns: ColumnDef<any>[] = [
{
accessorKey: "no",
header: "No",
cell: ({ row }) => <span>{row.getValue("no")}</span>,
},
];
const FilterPage = () => {
const router = useRouter();
const asPath = usePathname();
const params = useParams();
const searchParams = useSearchParams();
const locale = params?.locale;
const [videoData, setVideoData] = useState<any>();
const [totalData, setTotalData] = React.useState<number>(1);
const [totalPage, setTotalPage] = React.useState<number>(1);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const [pagination, setPagination] = React.useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [page, setPage] = useState(1);
const [totalContent, setTotalContent] = useState();
const [change, setChange] = useState(false);
const sortBy = searchParams?.get("sortBy");
const title = searchParams?.get("title");
const categorie = searchParams?.get("category");
const group = searchParams?.get("group");
const [, setGetTotalPage] = useState();
const [contentVideo, setContentVideo] = useState([]);
const [categoryFilter, setCategoryFilter] = useState<any>([]);
const [monthYearFilter, setMonthYearFilter] = useState<any>();
const [searchTitle, setSearchTitle] = useState<string>("");
const [sortByOpt, setSortByOpt] = useState<any>(sortBy === "popular" ? "clickCount" : "createdAt");
const isRegional = asPath?.includes("regional");
const isSatker = asPath?.includes("satker");
const [formatFilter, setFormatFilter] = useState<any>([]);
const pages = page ? page - 1 : 0;
const [startDateString, setStartDateString] = useState<any>();
const [endDateString, setEndDateString] = useState<any>();
const [dateRange, setDateRange] = useState<any>([null, null]);
const [calenderState, setCalenderState] = useState(false);
const [handleClose, setHandleClose] = useState(false);
const [categories, setCategories] = useState([]);
const [userLevels, setUserLevels] = useState([]);
const satkerName = params?.satker_name;
let prefixPath = satkerName ? `/satker/${satkerName}` : "/";
const t = useTranslations("FilterPage");
// const [startDate, endDate] = dateRange;
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) {
setPage(Number(pageFromUrl));
}
}, [searchParams]);
useEffect(() => {
async function initState() {
getCategories();
// getSelectedCategory();
if (isSatker) {
getUserLevels();
}
}
initState();
}, []);
useEffect(() => {
if (categorie) {
setCategoryFilter(categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
console.log("Kategori", categorie, categorie?.split("&")?.length > 1 ? categorie?.split("&") : [categorie]);
}
}, [categorie]);
// useEffect(() => {
// fetchData();
// }, [page, sortBy, sortByOpt, title]);
useEffect(() => {
async function initState() {
if (isRegional) {
getDataRegional();
} else {
getDataAll();
}
}
console.log(monthYearFilter, "monthFilter");
initState();
}, [change, asPath, monthYearFilter, page, sortBy, sortByOpt, title, startDateString, endDateString, categorie, formatFilter]);
async function getCategories() {
const category = await listCategory("2");
const resCategory = category?.data?.data?.content;
setCategories(resCategory);
}
useEffect(() => {
function initState() {
if (dateRange[0] != null && dateRange[1] != null) {
setStartDateString(getOnlyDate(dateRange[0]));
setEndDateString(getOnlyDate(dateRange[1]));
setHandleClose(true);
console.log("date range", dateRange, getOnlyDate(dateRange[0]));
}
}
initState();
}, [calenderState]);
async function getDataAll() {
if (satkerName && String(satkerName)?.length > 1) {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
const filterGroup = "satker-" + String(satkerName);
loading();
const response = await listData(
"2",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
filterGroup,
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
const data = response?.data?.data;
const contentData = data?.content;
setVideoData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response?.data?.data?.totalElements);
} else {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listData(
"2",
name,
filter,
12,
pages,
sortByOpt,
format,
"",
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : ""
);
close();
const data = response?.data?.data;
const contentData = data?.content;
setVideoData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
setTotalContent(response?.data?.data?.totalElements);
}
}
const handleCategoryFilter = (e: boolean, id: string) => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
};
const handleFormatFilter = (e: boolean, id: string) => {
let filter = [...formatFilter];
if (e) {
filter = [...formatFilter, id];
} else {
filter.splice(formatFilter.indexOf(id), 1);
}
console.log("Format filter", filter);
setFormatFilter(filter);
};
const cleanCheckbox = () => {
setCategoryFilter([]);
setFormatFilter([]);
router.push(`?category=&title=`);
setDateRange([null, null]);
setMonthYearFilter(null);
setChange(!change);
};
async function getDataRegional() {
const filter = categoryFilter?.length > 0 ? categoryFilter?.sort().join(",") : categorie || "";
const name = title == undefined ? "" : title;
const format = formatFilter == undefined ? "" : formatFilter?.join(",");
loading();
const response = await listDataRegional(
"2",
name,
filter,
format,
"",
startDateString,
endDateString,
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[0]?.replace("", "") : "",
monthYearFilter ? getOnlyMonthAndYear(monthYearFilter)?.split("/")[1] : "",
12,
pages,
sortByOpt
);
close();
setGetTotalPage(response?.data?.data?.totalPages);
setContentVideo(response?.data?.data?.content);
setTotalContent(response?.data?.data?.totalElements);
}
const table = useReactTable({
data: videoData,
columns: columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
onPaginationChange: setPagination,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
pagination,
},
});
useEffect(() => {
initFetch();
}, [page]);
const initFetch = async () => {
const response = await getListContent({
page: page - 1,
size: 6,
sortBy: "createdAt",
contentTypeId: "2",
});
console.log(response);
setVideoData(response?.data?.data?.content);
const data = response?.data?.data;
const contentData = data?.content;
setVideoData(contentData);
setTotalData(data?.totalElements);
setTotalPage(data?.totalPages);
};
function getSelectedCategory() {
const filter = [];
if (categorie) {
const categoryArr = categorie.split(",");
for (const element of categoryArr) {
filter.push(Number(element));
}
setCategoryFilter(filter);
}
}
const handleDeleteDate = () => {
setDateRange([null, null]);
setStartDateString("");
setEndDateString("");
setHandleClose(false);
};
const handleSorting = (e: any) => {
console.log(e.target.value);
if (e.target.value == "terbaru") {
setSortByOpt("createdAt");
} else {
setSortByOpt("clickCount");
}
setChange(!change);
};
async function getUserLevels() {
const res = await getUserLevelListByParent(761);
const userLevelList = res?.data?.data;
if (userLevelList !== null) {
let optionArr: any = [];
userLevelList?.map((option: any) => {
let optionData = {
id: option.id,
label: option.name,
value: option.id,
};
optionArr.push(optionData);
});
setUserLevels(optionArr);
}
}
let typingTimer: any;
const doneTypingInterval = 1500;
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
async function doneTyping() {
if (searchTitle == "" || searchTitle == undefined) {
router.push("?title=");
} else {
router.push(`?title=${searchTitle}`);
}
}
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
return (
<div className="flex flex-col">
{/* Header */}
<div className="flex flex-col md:flex-row items-start gap-5 p-10 bg-[#f7f7f7] dark:bg-black">
<p>
{" "}
{t("video")}&nbsp;
{">"} <span className="font-bold">{t("allVideo")}</span>
</p>
<p className="font-bold">|</p>
<p>{`${t("thereIs")} ${totalContent} ${t("downloadableVideo")}`}</p>
</div>
{/* Left */}
<div className="flex flex-col lg:flex-row gap-6 p-4">
<div className="lg:w-[25%] w-full bg-[#f7f7f7] dark:bg-black p-4 rounded-lg shadow-md">
<h2 className="text-lg font-semibold mb-4 flex items-center gap-1">
<Icon icon="stash:filter-light" fontSize={30} />
Filter
</h2>
<div className="border-t border-black my-4 dark:border-white"></div>
<div className="space-y-6">
<div>
<label htmlFor="search" className="block text-sm font-medium text-gray-700 dark:text-white">
{t("search")}
</label>
<Input
value={searchTitle}
onChange={(e) => setSearchTitle(e.target.value)}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
type="text"
id="search"
placeholder={t("searchTitle")}
className="mt-1 w-full border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("monthYear")}</label>
<ReactDatePicker
selected={monthYearFilter}
className="mt-1 w-full text-xs border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
onChange={(date) => setMonthYearFilter(date)}
dateFormat="MM | yyyy"
placeholderText={t("selectYear")}
showMonthYearPicker
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-white">{t("date")}</label>
<div className="flex flex-row justify justify-between gap-2">
<ReactDatePicker
selectsRange
className="mt-1 w-full text-sm border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500"
startDate={dateRange[0]}
endDate={dateRange[1]}
onChange={(update) => {
setDateRange(update);
}}
placeholderText={t("selectDate")}
onCalendarClose={() => setCalenderState(!calenderState)}
/>
<div className="flex items-center">{handleClose ? <Icon icon="carbon:close-filled" onClick={handleDeleteDate} width="20" inline color="#216ba5" /> : ""}</div>
</div>
</div>
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">{t("categories")}</h3>
<ul className="mt-2 space-y-2">
{categories.map((category: any) => (
<li key={category?.id}>
<label className="inline-flex items-center" htmlFor={`${category.id}`}>
<Checkbox id={`${category.id}`} value={category.id} checked={categoryFilter.includes(String(category.id))} onCheckedChange={(e) => handleCategoryFilter(Boolean(e), category.id)} />
<span className="ml-2 text-gray-700 dark:text-white">{category?.name}</span>
</label>
</li>
))}
</ul>
</div>
{/* Garis */}
<div className="border-t border-black my-4 dark:border-white"></div>
{/* Garis */}
<div>
<h3 className="text-sm font-medium text-gray-700 dark:text-white">Format</h3>
<ul className="mt-2 space-y-2">
<li>
<label className="inline-flex items-center">
<Checkbox id="mk4" value="mk4" checked={formatFilter.includes("mk4")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "mk4")} />
<span className="ml-2 text-gray-700 dark:text-white">MK4</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="mov" value="mov" checked={formatFilter.includes("mov")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "mov")} />
<span className="ml-2 text-gray-700 dark:text-white">MOV</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="mp4" value="mp4" checked={formatFilter.includes("mp4")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "mp4")} />
<span className="ml-2 text-gray-700 dark:text-white">MP4</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="avi" value="avi" checked={formatFilter.includes("avi")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "avi")} />
<span className="ml-2 text-gray-700 dark:text-white">AVI</span>
</label>
</li>
<li>
<label className="inline-flex items-center">
<Checkbox id="wmv" value="wmv" checked={formatFilter.includes("wmv")} onCheckedChange={(e) => handleFormatFilter(Boolean(e), "wmv")} />
<span className="ml-2 text-gray-700 dark:text-white">WMV</span>
</label>
</li>
</ul>
</div>
<div className="border-t border-black dark:border-white my-4"></div>
<div className="text-center">
<a onClick={cleanCheckbox} className="text-[#bb3523] cursor-pointer">
<b>Reset Filter</b>
</a>
</div>
</div>
</div>
{/* Konten Kanan */}
<Reveal>
<div className="flex-1">
<div className="flex flex-col items-end mb-4">
<h2 className="text-lg font-semibold">{t("sortBy")}</h2>
<select defaultValue={sortBy == "popular" ? "terpopuler" : "terbaru"} onChange={(e) => handleSorting(e)} className="border rounded-md py-2 px-3 focus:ring-red-500 focus:border-red-500">
<option value="latest">{t("latest")}</option>
<option value="popular">{t("mostPopular")}</option>
</select>
</div>
{videoData?.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{videoData?.map((video: any) => (
<Card key={video?.id} className="hover:scale-110 transition-transform duration-300">
<CardContent className="flex flex-col text-xs lg:text-sm w-full p-0">
<Link href={`${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" />
<div className="flex flex-row items-center gap-2 text-[10px] mx-2">
{formatDateToIndonesian(new Date(video?.createdAt))} {video?.timezone ? video?.timezone : "WIB"}| <Icon icon="formkit:eye" width="15" height="15" />
{video?.clickCount}{" "}
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 20 20">
<path
fill="#f00"
d="M7.707 10.293a1 1 0 1 0-1.414 1.414l3 3a1 1 0 0 0 1.414 0l3-3a1 1 0 0 0-1.414-1.414L11 11.586V6h5a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h5v5.586zM9 4a1 1 0 0 1 2 0v2H9z"
/>
</svg>{" "}
</div>
<div className="font-semibold pr-3 pb-3 mx-2 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible w-full">{video?.title}</div>
</Link>
</CardContent>
</Card>
))}
</div>
) : (
<p className="flex items-center justify-center">
<img src="/assets/empty-data.png" alt="empty" className="h-60 w-60 my-4" />
</p>
)}
<LandingPagination table={table} totalData={totalData} totalPage={totalPage} />
</div>
</Reveal>
</div>
</div>
);
};
export default FilterPage;

View File

@ -0,0 +1,20 @@
import LayoutProvider from "@/providers/layout.provider";
import LayoutContentProvider from "@/providers/content.provider";
import DashCodeSidebar from "@/components/partials/sidebar";
import DashCodeFooter from "@/components/partials/footer";
import ThemeCustomize from "@/components/partials/customizer";
import DashCodeHeader from "@/components/partials/header";
import { redirect } from "@/components/navigation";
import Footer from "@/components/landing-page/footer";
import Navbar from "@/components/landing-page/navbar";
const layout = async ({ children }: { children: React.ReactNode }) => {
return (
<>
{children}
</>
);
};
export default layout;

View File

@ -324,7 +324,7 @@ const DetailAudio = () => {
{/* Konten Serupa */}
<div className="px-4">
<NewContent type={"similar"} />
<NewContent group="mabes" type={"similar"} />
</div>
</div>
</>

View File

@ -323,7 +323,7 @@ const DetailDocument = () => {
{/* Konten Serupa */}
<div className="">
<NewContent type={"similar"} />
<NewContent group="mabes" type={"similar"} />
</div>
</div>
</>

View File

@ -337,7 +337,7 @@ const DetailInfo = () => {
{/* Konten Serupa */}
<div className="">
<NewContent type={"similar"} />
<NewContent group="mabes" type={"similar"} />
</div>
</div>
</>

View File

@ -333,7 +333,7 @@ const DetailVideo = () => {
{/* Konten Serupa */}
<div className="">
<NewContent type={"similar"} />
<NewContent group="mabes" type={"similar"} />
</div>
</div>
</>

View File

@ -19,8 +19,8 @@ const Home = ({ params: { locale } }: { params: { locale: string } }) => {
<Navbar />
<Hero />
<SearchSection />
<NewContent type="latest" />
<NewContent type="popular" />
<NewContent group="mabes" type="latest" />
<NewContent group="mabes" type="popular" />
{/* <PopularContent /> */}
<ContentCategory />
<Coverage />

View File

@ -243,8 +243,8 @@ export default function FormAudioDetail() {
setSelectedTarget(details.categoryId); // Untuk dropdown
const filesData = details.files || [];
const fileUrls = filesData.map((file: { thumbnailFileUrl: string }) =>
file.thumbnailFileUrl ? file.thumbnailFileUrl : "default-image.jpg"
const fileUrls = filesData.map((file: { secondaryUrl: string }) =>
file.secondaryUrl ? file.secondaryUrl : "default-image.jpg"
);
setDetailThumb(fileUrls);
}
@ -354,7 +354,7 @@ export default function FormAudioDetail() {
<div className="flex lg:flex-row gap-10">
<Card className="w-full lg:w-8/12">
<div className="px-6 py-6">
<p className="text-lg font-semibold mb-3">Form Konten Foto</p>
<p className="text-lg font-semibold mb-3">Form Konten Audio</p>
<div className="gap-5 mb-5">
{/* Input Title */}
<div className="space-y-2 py-3">
@ -418,25 +418,39 @@ export default function FormAudioDetail() {
)}
</div>
<Label className="text-xl text-black">File Media</Label>
<div className="w-full ">
<Label className="text-xl text-black">File Mediaaa</Label>
<div className="w-full">
<Swiper
thumbs={{ swiper: thumbsSwiper }}
modules={[FreeMode, Navigation, Thumbs]}
navigation={false}
className="w-full"
>
{detailThumb?.map((data: any) => (
<SwiperSlide key={data.id}>
<img
className="object-fill h-full w-full rounded-md"
src={data}
alt={` ${data.id}`}
/>
</SwiperSlide>
))}
{detailThumb?.map((data: any) => {
const isAudio =
data.endsWith(".webm") ||
data.endsWith(".mp3") ||
data.endsWith(".ogg");
return (
<SwiperSlide key={data.id}>
{isAudio ? (
<audio
className="object-fill h-full w-full rounded-md"
src={data.secondaryUrl}
controls
/>
) : (
<img
className="object-fill h-full w-full rounded-md"
src={data.secondaryUrl}
alt={` ${data.id}`}
/>
)}
</SwiperSlide>
);
})}
</Swiper>
<div className=" mt-2 ">
<div className="mt-2">
<Swiper
onSwiper={setThumbsSwiper}
slidesPerView={6}
@ -445,17 +459,30 @@ export default function FormAudioDetail() {
clickable: true,
}}
modules={[Pagination, Thumbs]}
// className="mySwiper2"
>
{detailThumb?.map((data: any) => (
<SwiperSlide key={data.id}>
<img
className="object-cover h-[60px] w-[80px]"
src={data}
alt={` ${data.id}`}
/>
</SwiperSlide>
))}
{detailThumb?.map((data: any) => {
const isAudio =
data.endsWith(".webm") ||
data.endsWith(".mp3") ||
data.endsWith(".ogg");
return (
<SwiperSlide key={data.id}>
{isAudio ? (
<audio
className="object-cover h-[60px] w-[80px]"
src={data.secondaryUrl}
controls
/>
) : (
<img
className="object-cover h-[60px] w-[80px]"
src={data.secondaryUrl}
alt={` ${data.id}`}
/>
)}
</SwiperSlide>
);
})}
</Swiper>
</div>
</div>

View File

@ -121,7 +121,7 @@ export default function FormAudio() {
const [detailData, setDetailData] = useState<any>(null);
const [articleImages, setArticleImages] = useState<string[]>([]);
const [isSwitchOn, setIsSwitchOn] = useState<boolean>(false);
const inputRef = useRef<HTMLInputElement>(null);
const [selectedTarget, setSelectedTarget] = useState("");
const [unitSelection, setUnitSelection] = useState({
allUnit: false,
@ -347,7 +347,9 @@ export default function FormAudio() {
const newTag = e.currentTarget.value.trim();
if (!tags.includes(newTag)) {
setTags((prevTags) => [...prevTags, newTag]); // Add new tag
// setValue("tags", ""); // Clear input field
if (inputRef.current) {
inputRef.current.value = ""; // Clear input field
}
}
}
};
@ -479,12 +481,7 @@ export default function FormAudio() {
close();
// showProgress();
files.map(async (item: any, index: number) => {
await uploadResumableFile(
index,
String(id),
item,
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0"
);
await uploadResumableFile(index, String(id), item, "0");
});
Cookies.remove("idCreate");
@ -528,7 +525,7 @@ export default function FormAudio() {
filename: file.name,
filetype: file.type,
duration,
isWatermark: "true", // hardcode
isWatermark: "false", // hardcode
},
onError: async (e: any) => {
console.log("Error upload :", e);
@ -1033,6 +1030,7 @@ export default function FormAudio() {
id="tags"
placeholder="Add a tag and press Enter"
onKeyDown={handleAddTag}
ref={inputRef}
/>
<div className="mt-3 ">
{tags.map((tag, index) => (

View File

@ -145,7 +145,7 @@ export default function FormAudioUpdate() {
const getCategories = async () => {
try {
const category = await listEnableCategory(fileTypeId);
const resCategory: Category[] = category.data.data.content;
const resCategory: Category[] = category?.data.data.content;
setCategories(resCategory);
console.log("data category", resCategory);
@ -171,7 +171,7 @@ export default function FormAudioUpdate() {
async function initState() {
if (id) {
const response = await detailMedia(id);
const details = response.data?.data;
const details = response?.data?.data;
setDetail(details);

View File

@ -133,11 +133,11 @@ export default function FormImageDetail() {
const [filePlacements, setFilePlacements] = useState<string[][]>([]);
const [detailThumb, setDetailThumb] = useState<any>([]);
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
const [isUserMabesApprover, setIsUserMabesApprover] = useState(false);
const [selectedTarget, setSelectedTarget] = useState("");
const [files, setFiles] = useState<FileType[]>([]);
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
const [isMabesApprover, setIsMabesApprover] = useState(false);
let fileTypeId = "1";
@ -157,7 +157,7 @@ export default function FormImageDetail() {
userLevelId == "216" &&
roleId == "3"
) {
setIsMabesApprover(true);
setIsUserMabesApprover(true);
}
}, [userLevelId, roleId]);
@ -306,7 +306,7 @@ export default function FormImageDetail() {
mediaUploadId: id,
statusId: status,
message: description,
files: isMabesApprover ? getPlacement() : [],
files: isUserMabesApprover ? getPlacement() : [],
};
loading();
const response = await submitApproval(data);
@ -330,9 +330,11 @@ export default function FormImageDetail() {
}
close();
submitApprovalSuccesss();
return false;
}
const setupPlacement = (
index: number,
placement: string,
@ -396,8 +398,10 @@ export default function FormImageDetail() {
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push("/in/contributor/content/image");
}).then((result) => {
if (result.isConfirmed) {
router.push("/contributor/content/image");
}
});
};
@ -516,7 +520,7 @@ export default function FormImageDetail() {
</div>
</Card>
<div className="w-4/12">
<Card className=" h-[800px]">
<Card className=" h-[900px]">
<div className="px-3 py-3">
<div className="space-y-2">
<Label>Kreator</Label>
@ -675,82 +679,84 @@ export default function FormImageDetail() {
<Icon icon="humbleicons:times" color="red" />
</a>
</div>
<div className="flex flex-row gap-2">
<div className="flex items-center space-x-2">
<Checkbox
id="terms"
value="all"
checked={filePlacements[index]?.includes(
"all"
)}
onCheckedChange={(e) =>
setupPlacement(index, "all", Boolean(e))
}
/>
<label
htmlFor="terms"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Semua
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="terms"
checked={filePlacements[index]?.includes(
"mabes"
)}
onCheckedChange={(e) =>
setupPlacement(index, "mabes", Boolean(e))
}
/>
<label
htmlFor="terms"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Nasional
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="terms"
checked={filePlacements[index]?.includes(
"polda"
)}
onCheckedChange={(e) =>
setupPlacement(index, "polda", Boolean(e))
}
/>
<label
htmlFor="terms"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Wilayah
</label>
</div>
{isUserMabesApprover &&
<div className="flex flex-row gap-2">
<div className="flex items-center space-x-2">
<Checkbox
id="terms"
value="all"
checked={filePlacements[index]?.includes(
"all"
)}
onCheckedChange={(e) =>
setupPlacement(index, "all", Boolean(e))
}
/>
<label
htmlFor="terms"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Semua
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="terms"
checked={filePlacements[index]?.includes(
"mabes"
)}
onCheckedChange={(e) =>
setupPlacement(index, "mabes", Boolean(e))
}
/>
<label
htmlFor="terms"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Nasional
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="terms"
checked={filePlacements[index]?.includes(
"polda"
)}
onCheckedChange={(e) =>
setupPlacement(index, "polda", Boolean(e))
}
/>
<label
htmlFor="terms"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Wilayah
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="terms"
checked={filePlacements[index]?.includes(
"international"
)}
onCheckedChange={(e) =>
setupPlacement(
index,
"international",
Boolean(e)
)
}
/>
<label
htmlFor="terms"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Internasional
</label>
<div className="flex items-center space-x-2">
<Checkbox
id="terms"
checked={filePlacements[index]?.includes(
"international"
)}
onCheckedChange={(e) =>
setupPlacement(
index,
"international",
Boolean(e)
)
}
/>
<label
htmlFor="terms"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Internasional
</label>
</div>
</div>
</div>
}
</div>
</div>
))

View File

@ -123,6 +123,7 @@ export default function FormImage() {
const [detailData, setDetailData] = useState<any>(null);
const [articleImages, setArticleImages] = useState<string[]>([]);
const [isSwitchOn, setIsSwitchOn] = useState<boolean>(false);
const inputRef = useRef<HTMLInputElement>(null);
const [content, setContent] = useState("");
@ -352,7 +353,9 @@ export default function FormImage() {
const newTag = e.currentTarget.value.trim();
if (!tags.includes(newTag)) {
setTags((prevTags) => [...prevTags, newTag]); // Add new tag
// setValue("tags", ""); // Clear input field
if (inputRef.current) {
inputRef.current.value = ""; // Clear input field
}
}
}
};
@ -1038,6 +1041,7 @@ export default function FormImage() {
id="tags"
placeholder="Add a tag and press Enter"
onKeyDown={handleAddTag}
ref={inputRef}
/>
<div className="mt-3 ">
{tags.map((tag, index) => (

View File

@ -487,106 +487,104 @@ export default function FormConvertSPIT() {
</Select>
</div>
</div>
<div>
<div className="">
<RadioGroup
onValueChange={(value) => setSelectedFileType(value)}
value={selectedFileType}
className=" grid-cols-1"
>
<div className="flex items-center space-x-2">
<div className="">
<RadioGroupItem value="original" id="original-file" />
<Label htmlFor="original-file">
Select Original File
</Label>
</div>
<div>
<div className="py-3">
<Label>Deskripsi</Label>
<Controller
control={control}
name="contentDescription"
render={({ field: { onChange, value } }) => (
// <JoditEditor
// ref={editor}
// value={detail?.contentDescription}
// onChange={onChange}
// className="dark:text-black"
// />
<CustomEditor
onChange={onChange}
initialData={detail?.contentDescription}
/>
)}
/>
{errors.contentDescription?.message && (
<p className="text-red-400 text-sm">
{errors.contentDescription.message}
</p>
<div className="py-3 ">
<Label>Deskripsi</Label>
<Controller
control={control}
name="contentDescription"
render={({ field: { onChange, value } }) => (
// <JoditEditor
// ref={editor}
// value={detail?.contentDescription}
// onChange={onChange}
// className="dark:text-black"
// />
<CustomEditor
onChange={onChange}
initialData={detail?.contentDescription}
/>
)}
</div>
<div className="my-2">
<Button
size="sm"
onClick={handleRewriteClick}
className="bg-blue-500 text-white py-2 px-4 rounded"
>
Content Rewrite
</Button>
</div>
{showRewriteEditor && (
<div>
{isGeneratedArticle && (
<div className="mt-3 pb-0 flex flex-row ">
{articleIds.map((id: string, index: number) => (
<button
key={index}
className={`mr-3 px-3 py-2 rounded-md ${
selectedArticleId === id
? "bg-green-500 text-white"
: "border-2 border-green-500 text-green-500"
}`}
onClick={() => handleArticleIdClick(id)}
>
{id}
</button>
))}
</div>
)}
<div className="flex items-center space-x-2 mt-3">
<RadioGroupItem value="rewrite" id="rewrite-file" />
<Label htmlFor="rewrite-file">
Select File Hasil Rewrite
</Label>
</div>
<div className="py-3">
<Label>File hasil Rewrite</Label>
<Controller
control={control}
name="contentRewriteDescription"
render={({ field: { onChange, value } }) =>
isLoadingData ? (
<div className="flex justify-center items-center h-40">
<p className="text-gray-500">
Loading Proses Data...
</p>
</div>
) : (
<CustomEditor
onChange={onChange}
initialData={articleBody || value}
/>
)
}
/>
{errors.contentRewriteDescription?.message && (
<p className="text-red-400 text-sm">
{errors.contentRewriteDescription.message}
</p>
)}
</div>
</div>
/>
{errors.contentDescription?.message && (
<p className="text-red-400 text-sm">
{errors.contentDescription.message}
</p>
)}
</div>
<div className="my-2">
<Button
size="sm"
onClick={handleRewriteClick}
className="bg-blue-500 text-white py-2 px-4 rounded"
>
Content Rewrite
</Button>
</div>
{showRewriteEditor && (
<div>
{isGeneratedArticle && (
<div className="mt-3 pb-0 flex flex-row ">
{articleIds.map((id: string, index: number) => (
<button
key={index}
className={`mr-3 px-3 py-2 rounded-md ${
selectedArticleId === id
? "bg-green-500 text-white"
: "border-2 border-green-500 text-green-500"
}`}
onClick={() => handleArticleIdClick(id)}
>
{id}
</button>
))}
</div>
)}
<div className="flex items-center space-x-2 mt-3">
<RadioGroupItem value="rewrite" id="rewrite-file" />
<Label htmlFor="rewrite-file">
Select File Hasil Rewrite
</Label>
</div>
<div className="py-3 ">
<Label>File hasil Rewrite</Label>
<Controller
control={control}
name="contentRewriteDescription"
render={({ field: { onChange, value } }) =>
isLoadingData ? (
<div className="flex justify-center items-center h-40">
<p className="text-gray-500">
Loading Proses Data...
</p>
</div>
) : (
<CustomEditor
onChange={onChange}
initialData={articleBody || value}
/>
)
}
/>
{errors.contentRewriteDescription?.message && (
<p className="text-red-400 text-sm">
{errors.contentRewriteDescription.message}
</p>
)}
</div>
</div>
)}
</RadioGroup>
</div>
<div>

View File

@ -243,8 +243,8 @@ export default function FormTeksDetail() {
setSelectedTarget(details.categoryId); // Untuk dropdown
const filesData = details.files || [];
const fileUrls = filesData.map((file: { thumbnailFileUrl: string }) =>
file.thumbnailFileUrl ? file.thumbnailFileUrl : "default-image.jpg"
const fileUrls = filesData.map((file: { url: string }) =>
file.url ? file.url : "default-image.jpg"
);
setDetailThumb(fileUrls);
}
@ -419,9 +419,8 @@ export default function FormTeksDetail() {
</p>
)}
</div>
<Label className="text-xl text-black">File Media</Label>
<div className="w-full ">
<div className="w-full">
<Swiper
thumbs={{ swiper: thumbsSwiper }}
modules={[FreeMode, Navigation, Thumbs]}
@ -430,32 +429,56 @@ export default function FormTeksDetail() {
>
{detailThumb?.map((data: any) => (
<SwiperSlide key={data.id}>
<img
className="object-fill h-full w-full rounded-md"
src={data}
alt={` ${data.id}`}
/>
{[".jpg", ".jpeg", ".png", ".webp"].includes(
data.format
) ? (
<img
className="object-fill h-full w-full rounded-md"
src={data.url}
alt={data.fileName}
/>
) : [".pdf", ".docx", ".txt"].includes(data.format) ? (
<iframe
className="w-full h-96 rounded-md"
src={data.url}
title={data.fileName}
/>
) : (
<a
href={data}
target="_blank"
rel="noopener noreferrer"
className="block text-blue-500 underline"
>
View {data.fileName}
</a>
)}
</SwiperSlide>
))}
</Swiper>
<div className=" mt-2 ">
<div className="mt-2">
<Swiper
onSwiper={setThumbsSwiper}
slidesPerView={6}
spaceBetween={8}
pagination={{
clickable: true,
}}
pagination={{ clickable: true }}
modules={[Pagination, Thumbs]}
// className="mySwiper2"
>
{detailThumb?.map((data: any) => (
<SwiperSlide key={data.id}>
<img
className="object-cover h-[60px] w-[80px]"
src={data}
alt={` ${data.id}`}
/>
{[".jpg", ".jpeg", ".png", ".webp"].includes(
data.format
) ? (
<img
className="object-cover h-[60px] w-[80px]"
src={data.url}
alt={data.fileName}
/>
) : (
<div className="h-[60px] w-[80px] flex items-center justify-center bg-gray-200 text-sm text-center text-gray-700 rounded-md">
{data?.format?.replace(".", "").toUpperCase()}
</div>
)}
</SwiperSlide>
))}
</Swiper>

View File

@ -121,6 +121,7 @@ export default function FormTeks() {
const [detailData, setDetailData] = useState<any>(null);
const [articleImages, setArticleImages] = useState<string[]>([]);
const [isSwitchOn, setIsSwitchOn] = useState<boolean>(false);
const inputRef = useRef<HTMLInputElement>(null);
const [selectedTarget, setSelectedTarget] = useState("");
const [unitSelection, setUnitSelection] = useState({
@ -346,7 +347,9 @@ export default function FormTeks() {
const newTag = e.currentTarget.value.trim();
if (!tags.includes(newTag)) {
setTags((prevTags) => [...prevTags, newTag]); // Add new tag
// setValue("tags", ""); // Clear input field
if (inputRef.current) {
inputRef.current.value = ""; // Clear input field
}
}
}
};
@ -478,12 +481,7 @@ export default function FormTeks() {
close();
// showProgress();
files.map(async (item: any, index: number) => {
await uploadResumableFile(
index,
String(id),
item,
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0"
);
await uploadResumableFile(index, String(id), item, "0");
});
Cookies.remove("idCreate");
@ -527,7 +525,7 @@ export default function FormTeks() {
filename: file.name,
filetype: file.type,
duration,
isWatermark: "true", // hardcode
isWatermark: "false", // hardcode
},
onError: async (e: any) => {
console.log("Error upload :", e);
@ -1032,6 +1030,7 @@ export default function FormTeks() {
id="tags"
placeholder="Add a tag and press Enter"
onKeyDown={handleAddTag}
ref={inputRef}
/>
<div className="mt-3 ">
{tags.map((tag, index) => (

View File

@ -134,10 +134,11 @@ export default function FormVideoDetail() {
const [detailVideo, setDetailVideo] = useState<any>([]);
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
const [filePlacements, setFilePlacements] = useState<string[][]>([]);
const [selectedTarget, setSelectedTarget] = useState("");
const [files, setFiles] = useState<FileType[]>([]);
const [rejectedFiles, setRejectedFiles] = useState<number[]>([]);
const [isMabesApprover, setIsMabesApprover] = useState(false);
const [isUserMabesApprover, setIsUserMabesApprover] = useState(false);
let fileTypeId = "2";
@ -168,7 +169,7 @@ export default function FormVideoDetail() {
userLevelId == "216" &&
roleId == "3"
) {
setIsMabesApprover(true);
setIsUserMabesApprover(true);
}
}, [userLevelId, roleId]);
@ -254,9 +255,16 @@ export default function FormVideoDetail() {
}, [refresh, setValue]);
const actionApproval = (e: string) => {
const temp = [];
for (const element of detail.files) {
temp.push([]);
}
setFilePlacements(temp);
setStatus(e);
setModalOpen(true);
setFiles(detail.files);
setDescription("");
setModalOpen(true);
};
const submit = async () => {
@ -286,8 +294,7 @@ export default function FormVideoDetail() {
mediaUploadId: id,
statusId: status,
message: description,
files: [],
// files: isMabesApprover ? getPlacement() : [],
files: isUserMabesApprover ? getPlacement() : [],
};
loading();
@ -310,10 +317,57 @@ export default function FormVideoDetail() {
}
close();
submitApprovalSuccesss();
return false;
}
const getPlacement = () => {
console.log("getPlaa", filePlacements);
const temp = [];
for (let i = 0; i < filePlacements?.length; i++) {
if (filePlacements[i].length !== 0) {
const now = filePlacements[i].filter((a) => a !== "all");
const data = { mediaFileId: files[i].id, placement: now.join(",") };
temp.push(data);
}
}
return temp;
};
const setupPlacement = (
index: number,
placement: string,
checked: boolean
) => {
let temp = [...filePlacements];
if (checked) {
if (placement === "all") {
temp[index] = ["all", "mabes", "polda", "international"];
} else {
const now = temp[index];
now.push(placement);
if (now.length === 3 && !now.includes("all")) {
now.push("all");
}
temp[index] = now;
}
} else {
if (placement === "all") {
temp[index] = [];
} else {
const now = temp[index].filter((a) => a !== placement);
console.log("now", now);
temp[index] = now;
if (now.length === 3 && now.includes("all")) {
const newData = now.filter((b) => b !== "all");
temp[index] = newData;
}
}
}
setFilePlacements(temp);
};
function handleDeleteFileApproval(id: number) {
const selectedFiles = files.filter((file) => file.id != id);
setFiles(selectedFiles);
@ -344,8 +398,10 @@ export default function FormVideoDetail() {
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push("/in/contributor/content/video");
}).then((result) => {
if (result.isConfirmed) {
router.push("/contributor/content/video");
}
});
};
@ -610,7 +666,7 @@ export default function FormVideoDetail() {
<DialogTitle>Berikan Komentar</DialogTitle>
</DialogHeader>
{status == "2"
? files?.map((file) => (
? files?.map((file, index) => (
<div
key={file.id}
className="flex flex-row gap-2 items-center"
@ -619,49 +675,92 @@ export default function FormVideoDetail() {
<div className="flex flex-col gap-2 w-full">
<div className="flex justify-between text-sm">
{file.fileName}
<a>
<a
onClick={() =>
handleDeleteFileApproval(file.id)
}
>
<Icon icon="humbleicons:times" color="red" />
</a>
</div>
<div className="flex flex-row gap-2">
<div className="flex items-center space-x-2">
<Checkbox id="terms" />
<label
htmlFor="terms"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Semua
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="terms" />
<label
htmlFor="terms"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Nasional
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="terms" />
<label
htmlFor="terms"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Wilayah
</label>
</div>
{isUserMabesApprover &&
<div className="flex flex-row gap-2">
<div className="flex items-center space-x-2">
<Checkbox
id="terms"
value="all"
checked={filePlacements[index]?.includes(
"all"
)}
onCheckedChange={(e) =>
setupPlacement(index, "all", Boolean(e))
}
/>
<label
htmlFor="terms"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Semua
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="terms"
checked={filePlacements[index]?.includes(
"mabes"
)}
onCheckedChange={(e) =>
setupPlacement(index, "mabes", Boolean(e))
}
/>
<label
htmlFor="terms"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Nasional
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="terms"
checked={filePlacements[index]?.includes(
"polda"
)}
onCheckedChange={(e) =>
setupPlacement(index, "polda", Boolean(e))
}
/>
<label
htmlFor="terms"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Wilayah
</label>
</div>
<div className="flex items-center space-x-2">
<Checkbox id="terms" />
<label
htmlFor="terms"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Internasional
</label>
<div className="flex items-center space-x-2">
<Checkbox
id="terms"
checked={filePlacements[index]?.includes(
"international"
)}
onCheckedChange={(e) =>
setupPlacement(
index,
"international",
Boolean(e)
)
}
/>
<label
htmlFor="terms"
className="text-xs font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Internasional
</label>
</div>
</div>
</div>
}
</div>
</div>
))
@ -744,7 +843,7 @@ export default function FormVideoDetail() {
<Button
type="button"
color="primary"
onClick={submitApprovalSuccesss}
onClick={() => submit()}
>
Submit
</Button>

View File

@ -121,6 +121,7 @@ export default function FormVideo() {
const [detailData, setDetailData] = useState<any>(null);
const [articleImages, setArticleImages] = useState<string[]>([]);
const [isSwitchOn, setIsSwitchOn] = useState<boolean>(false);
const inputRef = useRef<HTMLInputElement>(null);
const [selectedTarget, setSelectedTarget] = useState("");
const [unitSelection, setUnitSelection] = useState({
@ -346,7 +347,9 @@ export default function FormVideo() {
const newTag = e.currentTarget.value.trim();
if (!tags.includes(newTag)) {
setTags((prevTags) => [...prevTags, newTag]); // Add new tag
// setValue("tags", ""); // Clear input field
if (inputRef.current) {
inputRef.current.value = ""; // Clear input field
}
}
}
};
@ -478,12 +481,7 @@ export default function FormVideo() {
close();
// showProgress();
files.map(async (item: any, index: number) => {
await uploadResumableFile(
index,
String(id),
item,
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0"
);
await uploadResumableFile(index, String(id), item, "0");
});
Cookies.remove("idCreate");
@ -527,7 +525,7 @@ export default function FormVideo() {
filename: file.name,
filetype: file.type,
duration,
isWatermark: "true", // hardcode
isWatermark: "false", // hardcode
},
onError: async (e: any) => {
console.log("Error upload :", e);
@ -1024,10 +1022,10 @@ export default function FormVideo() {
)}
</div>
</div>
<div className="px-3 py-3">
{/* <div className="px-3 py-3">
<Label htmlFor="fileInput">Gambar Utama</Label>
<Input id="fileInput" type="file" onChange={handleImageChange} />
</div>
</div> */}
{preview && (
<div className="mt-3 px-3">
<img
@ -1045,6 +1043,7 @@ export default function FormVideo() {
id="tags"
placeholder="Add a tag and press Enter"
onKeyDown={handleAddTag}
ref={inputRef}
/>
<div className="mt-3 ">
{tags.map((tag, index) => (

View File

@ -33,6 +33,8 @@ import {
DialogTrigger,
} from "@/components/ui/dialog";
import { ChevronDown, ChevronUp } from "lucide-react";
import dynamic from "next/dynamic";
import { Link } from "@/components/navigation";
const taskSchema = z.object({
uniqueCode: z.string().min(1, { message: "Judul diperlukan" }),
@ -63,6 +65,13 @@ export type taskDetail = {
is_active: string;
};
const ViewEditor = dynamic(
() => {
return import("@/components/editor/view-editor");
},
{ ssr: false }
);
export default function FormTaskDetail() {
const MySwal = withReactContent(Swal);
const router = useRouter();
@ -342,6 +351,7 @@ export default function FormTaskDetail() {
<div className="flex items-center gap-2" key={key}>
<Checkbox
id={key}
disabled
checked={
unitSelection[key as keyof typeof unitSelection]
}
@ -373,6 +383,7 @@ export default function FormTaskDetail() {
<div key={polda.id} className="border p-2">
<Label className="flex items-center">
<Checkbox
disabled
checked={checkedLevels.has(polda.id)}
onCheckedChange={() =>
handleCheckboxChange(polda.id)
@ -395,6 +406,7 @@ export default function FormTaskDetail() {
<div className="ml-6 mt-2">
<Label className="block">
<Checkbox
disabled
checked={polda?.subDestination?.every(
(polres: any) =>
checkedLevels.has(polres.id)
@ -421,6 +433,7 @@ export default function FormTaskDetail() {
{polda?.subDestination?.map((polres: any) => (
<Label key={polres.id} className="block mt-1">
<Checkbox
disabled
checked={checkedLevels.has(polres.id)}
onCheckedChange={() =>
handleCheckboxChange(polres.id)
@ -496,6 +509,7 @@ export default function FormTaskDetail() {
<div className="flex items-center gap-2" key={key}>
<Checkbox
id={key}
disabled
checked={taskOutput[key as keyof typeof taskOutput]}
onCheckedChange={(value) =>
setTaskOutput({ ...taskOutput, [key]: value })
@ -508,11 +522,11 @@ export default function FormTaskDetail() {
))}
</div>
</div>
<div className="mt-6">
{/* <div className="mt-6">
<Label>Broadcast </Label>
<RadioGroup
value={broadcastType} // Nilai terpilih diambil dari state broadcastType
onValueChange={(value) => setBroadcastType(value)} // Mengatur nilai saat radio berubah
value={broadcastType}
onValueChange={(value) => setBroadcastType(value)}
className="flex flex-wrap gap-3"
>
<div className="flex items-center gap-2">
@ -528,19 +542,14 @@ export default function FormTaskDetail() {
<Label htmlFor="whatsapp">WhatsApp Blast</Label>
</div>
</RadioGroup>
</div>
</div> */}
<div className="mt-6">
<Label>Narasi Penugasan</Label>
<Controller
control={control}
name="naration"
render={({ field: { onChange, value } }) => (
<JoditEditor
ref={editor}
value={detail?.narration}
onChange={onChange}
className="dark:text-black"
/>
<ViewEditor initialData={detail?.narration} />
)}
/>
{errors.naration?.message && (
@ -551,11 +560,12 @@ export default function FormTaskDetail() {
</div>
</div>
{/* Submit Button */}
<div className="mt-4">
<Button type="submit" color="primary">
Submit
</Button>
<Link href={"/contributor/task"}>
<Button color="primary" variant={"outline"}>
Cancel
</Button>
</Link>
</div>
</form>
) : (

View File

@ -94,7 +94,7 @@ const Division = () => {
filteredList.map((region, index) =>
!seeAllValue ? (
index < 7 ? (
<Link href={`/polda/${region.slug}`} key={index} className="flex flex-col items-center text-center group">
<Link href={`/satker/${region.slug}`} key={index} className="flex flex-col items-center text-center group">
<div className="relative w-20 h-20 rounded-full border-2 border-[#bb3523] overflow-hidden mb-2 flex items-center justify-center">
<img src={region.logo} alt={region.name} className="w-3/4 h-3/4 object-contain group-hover:scale-110 transition-transform duration-300" />
</div>
@ -104,7 +104,7 @@ const Division = () => {
""
)
) : (
<Link href={`/polda/${region.slug}`} key={index} className="flex flex-col items-center text-center group">
<Link href={`/satker/${region.slug}`} key={index} className="flex flex-col items-center text-center group">
<div className="relative w-20 h-20 rounded-full border-2 border-[#bb3523] overflow-hidden mb-2 flex items-center justify-center">
<img src={region.logo} alt={region.name} className="w-3/4 h-3/4 object-contain group-hover:scale-110 transition-transform duration-300" />
</div>

View File

@ -26,7 +26,7 @@ const HeaderBannerSatker = () => {
// }
async function fetchData() {
const res = await listData("1", "", "", 5, 0, "createdAt", "", "", satkerName);
const res = await listData("1", "", "", 5, 0, "createdAt", "", "", "satker-"+satkerName);
let data = res?.data?.data?.content;
setContent(data);
setCenterPadding(`${Math.trunc(Number(window.innerWidth) / 10 + 40)}px`);

View File

@ -38,6 +38,8 @@ const Navbar = () => {
const router = useRouter();
const params = useParams();
const locale = params?.locale;
const poldaName = params?.polda_name;
const satkerName = params?.satker_name;
const [language, setLanguage] = useState<"id" | "en">("id");
const [isOpen, setIsOpen] = useState(false);
const fullname = getCookiesDecrypt("ufne");
@ -52,6 +54,8 @@ const Navbar = () => {
const [menuActive, setMenuActive] = useState<string>();
const [category, setCategory] = useState<any>();
let prefixPath = poldaName ? `/polda/${poldaName}` : satkerName ? `/satker/${satkerName}` : "/";
let active = "";
let menu = "";
@ -84,6 +88,9 @@ const Navbar = () => {
}
initState();
console.log("POldaaa : ", poldaName);
console.log("Satkkeeer : ", satkerName);
}, []);
useEffect(() => {
@ -133,7 +140,7 @@ const Navbar = () => {
<div className="bg-[#f7f7f7] dark:bg-black shadow-md sticky top-0 z-50">
<div className="flex items-center justify-between px-4 lg:px-20 py-4 gap-3">
{/* Logo */}
<Link href="/" className="flex items-center">
<Link href={prefixPath} className="flex items-center">
<img src="/assets/mediahub-logo.gif" alt="Media Hub Logo" className="object-contain h-20" />
</Link>
@ -168,13 +175,13 @@ const Navbar = () => {
</a>
</NavigationMenuTrigger>
<NavigationMenuContent className=" rounded-md overflow-hidden w-">
<NavigationMenuLink onClick={() => router.push("/image/filter")} className="flex place-items-start gap-1.5 p-2">
<NavigationMenuLink onClick={() => router.push(prefixPath+"/image/filter")} className="flex place-items-start gap-1.5 p-2">
<p className="text-slate-600 dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center px-5 py-2 cursor-pointer">
<FiImage className="mr-2" />
{t("image")}
</p>
</NavigationMenuLink>
<NavigationMenuLink onClick={() => router.push("/video/filter")} className="flex items-start gap-1.5 p-2 ">
<NavigationMenuLink onClick={() => router.push(prefixPath+"/video/filter")} className="flex items-start gap-1.5 p-2 ">
{pathname?.split("/")[1] == "in" ? (
<>
<p className="text-slate-600 text-sm dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center px-0 py-2 cursor-pointer">
@ -195,13 +202,13 @@ const Navbar = () => {
{t("video")}
</p> */}
</NavigationMenuLink>
<NavigationMenuLink onClick={() => router.push("/document/filter")} className="flex place-items-start gap-1.5 p-2">
<NavigationMenuLink onClick={() => router.push(prefixPath+"/document/filter")} className="flex place-items-start gap-1.5 p-2">
<p className="text-slate-600 dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center px-5 py-2 cursor-pointer">
<FiFile className="mr-2" />
{t("text")}
</p>
</NavigationMenuLink>
<NavigationMenuLink onClick={() => router.push("/audio/filter")} className="flex place-items-start gap-1.5 p-2 ">
<NavigationMenuLink onClick={() => router.push(prefixPath+"/audio/filter")} className="flex place-items-start gap-1.5 p-2 ">
<p className="text-slate-600 dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center px-5 py-2 cursor-pointer">
<FiMusic className="mr-2" />
{t("audio")}{" "}
@ -210,7 +217,7 @@ const Navbar = () => {
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<Link href="/schedule" legacyBehavior passHref>
<Link href={prefixPath+"/schedule"} legacyBehavior passHref>
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
<span>
<svg className="mr-2" width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
@ -225,7 +232,7 @@ const Navbar = () => {
</Link>
</NavigationMenuItem>
<NavigationMenuItem>
<Link href="/indeks" legacyBehavior passHref>
<Link href={prefixPath+"/indeks"} legacyBehavior passHref>
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
<span>
<svg className="mr-2" width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
@ -335,8 +342,8 @@ const Navbar = () => {
<div className="flex items-center gap-3 text-default-800">
<Image src={"/assets/avatar-profile.png"} alt={"Image"} width={36} height={36} className="rounded-full" />
<div>
<div className="text-sm font-medium capitalize lg:block hidden whitespace-nowrap">{detail?.fullname}</div>
<p className="text-xs whitespace-nowrap">({detail?.fullname})</p>
<div className="text-sm font-medium capitalize lg:block hidden whitespace-nowrap overflow-hidden truncate">{detail?.fullname}</div>
<p className="text-xs whitespace-nowrap overflow-hidden truncate">({detail?.fullname})</p>
</div>
<span className="text-base me-2.5 lg:inline-block hidden">
<Icon icon="heroicons-outline:chevron-down"></Icon>
@ -389,8 +396,8 @@ const Navbar = () => {
<div className="flex items-center gap-3 text-default-800">
<Image src={"/assets/avatar-profile.png"} alt={"Image"} width={36} height={36} className="rounded-full" />
<div>
<div className="text-sm font-medium capitalize lg:block hidden">{detail?.fullname}</div>
<p className="text-xs">({detail?.fullname})</p>
<div className="text-sm font-medium capitalize lg:block hidden whitespace-nowrap overflow-hidden truncate">{detail?.fullname}</div>
<p className="text-xs whitespace-nowrap overflow-hidden truncate">({detail?.fullname})</p>
</div>
<span className="text-base me-2.5 lg:inline-block hidden">
<Icon icon="heroicons-outline:chevron-down"></Icon>
@ -438,12 +445,12 @@ const Navbar = () => {
) : (
// Masuk and Daftar buttons for roleId === null
<div className="flex justify-center items-center mx-3 gap-5">
<Link href="/auth" className="w-full lg:w-max px-4 py-[10px] bg-[#bb3523] text-white font-semibold rounded-md hover:bg-red-700 text-center">
<Link href="/auth" className="w-full lg:w-max px-4 py-1 bg-[#bb3523] text-white font-semibold rounded-md hover:bg-red-700 text-center">
{t("logIn")}
</Link>
<Dialog>
<DialogTrigger asChild>
<Button className="w-full lg:w-max px-4 py-1 bg-[#bb3523] text-white font-semibold rounded-md hover:bg-red-700 text-center">{t("register")}</Button>
<Button className="w-full lg:w-fit px-4 h-8 border bg-white border-[#bb3523] text-[#bb3523] font-semibold rounded-md hover:bg-[#bb3523] hover:text-white">{t("register")}</Button>
</DialogTrigger>
<DialogContent size="sm" className="sm:max-w-[425px]">
<div className="flex flex-col w-full gap-1">

View File

@ -11,24 +11,31 @@ import { Link } from "@/i18n/routing";
import { Reveal } from "./Reveal";
import { useTranslations } from "next-intl";
const NewContent = (props: { type: string }) => {
const NewContent = (props: { group: string, type: string }) => {
const [newContent, setNewContent] = useState<any>();
const [selectedTab, setSelectedTab] = useState("image");
const router = useRouter();
const pathname = usePathname();
const params = useParams();
const locale = params?.locale;
const poldaName = params?.polda_name;
const satkerName = params?.satker_name;
const t = useTranslations("LandingPage");
useEffect(() => {
initFetch();
}, [selectedTab]);
const initFetch = async () => {
console.log("Satker Name : ", satkerName);
const request = {
title: "",
page: 0,
size: 5,
sortBy: props.type == "popular" ? "clickCount" : "createdAt",
contentTypeId: selectedTab == "image" ? "1" : selectedTab == "video" ? "2" : selectedTab == "text" ? "3" : selectedTab == "audio" ? "4" : "",
group: props.group == "mabes" ? "" :
props.group == "polda" && poldaName && String(poldaName)?.length > 1 ? poldaName :
props.group == "satker" && satkerName && String(satkerName)?.length > 1 ? "satker-"+satkerName : "",
};
const response = await getListContent(request);
console.log("category", response);

View File

@ -10,9 +10,7 @@ export async function getCategoryData() {
export async function getListContent(props: any) {
return await httpGetInterceptor(
`media/public/list?enablePage=1&sort=desc&sortBy=${props.sortBy}&size=${props.size}&page=${props.page}&typeId=${props.contentTypeId}&title=${
props.title ? props.title : ""
}&categoryId=&fileFormats=&tags=&group=&startDate=&endDate=&month=&year=`
`media/public/list?enablePage=1&sort=desc&sortBy=${props.sortBy}&size=${props.size}&page=${props.page}&typeId=${props.contentTypeId}&title=${props.title}&group=${props.group}&categoryId=&fileFormats=&tags=&startDate=&endDate=&month=&year=`
);
}