This commit is contained in:
Anang Yusman 2025-06-24 16:20:13 +08:00
commit a98d16008c
37 changed files with 2792 additions and 1384 deletions

View File

@ -58,16 +58,6 @@ const columns: ColumnDef<any>[] = [
<span>{formatDateToIndonesian(row.getValue("createdAt"))}</span>
),
},
{
accessorKey: "isStaticBanner",
header: "Static Banner",
cell: ({ row }) => (
<StaticToogle
id={row.original.id}
initChecked={row.original.isStaticBanner}
/>
),
},
{
accessorKey: "statusName",
header: "Status Banner",
@ -75,7 +65,6 @@ const columns: ColumnDef<any>[] = [
<StatusToogle id={row.original.id} initChecked={row.original.isBanner} />
),
},
{
id: "actions",
accessorKey: "action",

View File

@ -94,9 +94,7 @@ const ContentListBanner = () => {
const [selectedItems, setSelectedItems] = React.useState<number[]>([]);
const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1);
let typingTimer: any;
const doneTypingInterval = 1500;
const [searchQuery, setSearchQuery] = React.useState("");
const handleKeyUp = () => {
clearTimeout(typingTimer);
@ -108,6 +106,21 @@ const ContentListBanner = () => {
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
let typingTimer: NodeJS.Timeout;
const doneTypingInterval = 1500;
const handleTyping = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(() => {
setPage(1);
fetchData();
}, doneTypingInterval);
};
React.useEffect(() => {
fetchData();
}, [categoryFilter, statusFilter]);
async function doneTyping() {
fetchData();
}
@ -133,10 +146,11 @@ const ContentListBanner = () => {
const res = await listDataMedia(
page - 1,
showData,
"",
searchQuery,
categoryFilter?.sort().join(","),
statusFilter?.sort().join(",")
);
const data = res?.data?.data;
const contentData = data?.content;
contentData.forEach((item: any, index: number) => {
@ -224,10 +238,19 @@ const ContentListBanner = () => {
<Input
type="text"
placeholder="Search"
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
value={searchQuery}
onChange={(e) => {
setSearchQuery(e.target.value);
handleTyping();
}}
className="max-w-[300px]"
onKeyDown={(e) => {
if (e.key === "Enter") {
fetchData();
}
}}
/>
{/* <div className="flex flex-row gap-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
@ -393,9 +416,12 @@ const ContentListBanner = () => {
alt={item.title}
className="w-full h-48 object-cover"
/>
<div className="p-3">
<h4 className="font-semibold text-sm truncate">{item.title}</h4>
</div>
<Link
href={`/contributor/content/image/detail/${item?.id}`}
className="p-3"
>
<h4 className="font-semibold text-sm">{item.title}</h4>
</Link>
</div>
))}
</div>

View File

@ -28,7 +28,7 @@ export default function AdminBanner() {
: "bg-white text-black "
}`}
>
Kontent
Konten
</Button>
<Button
rounded="md"

View File

@ -58,24 +58,13 @@ const columns: ColumnDef<any>[] = [
<span>{formatDateToIndonesian(row.getValue("createdAt"))}</span>
),
},
{
accessorKey: "isStaticBanner",
header: "Static Banner",
cell: ({ row }) => (
<StaticToogle
id={row.original.id}
initChecked={row.original.isStaticBanner}
/>
),
},
{
accessorKey: "statusName",
header: "Status Banner",
header: "Status Pop Up",
cell: ({ row }) => (
<StatusToogle id={row.original.id} initChecked={row.original.isBanner} />
<StatusToogle id={row.original.id} initChecked={row.original.isInterstitial} />
),
},
{
id: "actions",
accessorKey: "action",

View File

@ -98,16 +98,21 @@ const PopUpListTable = () => {
loading();
let temp: any;
const response = await listDataPopUp(
page - 1,
showData,
"",
categoryFilter?.sort().join(","),
statusFilter?.sort().join(",")
);
const data = response?.data?.data?.content;
console.log("banner", data);
setGetData(data);
// const response = await listDataPopUp(
// page - 1,
// showData,
// "",
// categoryFilter?.sort().join(","),
// statusFilter?.sort().join(",")
// );
// const data = response?.data?.data?.content;
// console.log("banner", data);
// setGetData(data);
const response = await listBanner();
const data = response?.data?.data?.content;
console.log("banner", data);
setGetData(data);
close();
}

View File

@ -1,7 +1,7 @@
import { Switch } from "@/components/ui/switch";
import { useToast } from "@/components/ui/use-toast";
import { useRouter } from "@/i18n/routing";
import { setBanner } from "@/service/settings/settings";
import { setBanner, setPopUp } from "@/service/settings/settings";
export default function StatusToogle(props: {
id: number;
@ -12,7 +12,7 @@ export default function StatusToogle(props: {
const router = useRouter();
const disableBanner = async () => {
const response = await setBanner(id, false);
const response = await setPopUp(id, false);
if (response?.error) {
toast({
@ -25,7 +25,7 @@ export default function StatusToogle(props: {
toast({
title: "Success ",
});
router.push("/admin/settings/banner?dataChange=true");
router.push("/admin/settings/popup?dataChange=true");
};
return (
<Switch

View File

@ -94,9 +94,10 @@ const ContentListPopUp = () => {
const [selectedItems, setSelectedItems] = React.useState<number[]>([]);
const [page, setPage] = React.useState(1);
const [totalPage, setTotalPage] = React.useState(1);
const [searchQuery, setSearchQuery] = React.useState("");
let typingTimer: any;
const doneTypingInterval = 1500;
let typingTimer: NodeJS.Timeout;
const doneTypingInterval = 2000;
const handleKeyUp = () => {
clearTimeout(typingTimer);
@ -105,9 +106,16 @@ const ContentListPopUp = () => {
const handleKeyDown = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
typingTimer = setTimeout(() => {
setPage(1);
fetchData();
}, doneTypingInterval);
};
React.useEffect(() => {
fetchData();
}, [categoryFilter, statusFilter]);
async function doneTyping() {
fetchData();
}
@ -127,13 +135,40 @@ const ContentListPopUp = () => {
});
}, [page, showData]);
// async function fetchData() {
// try {
// loading();
// const res = await listDataPopUp(
// page - 1,
// showData,
// "",
// categoryFilter?.sort().join(","),
// statusFilter?.sort().join(",")
// );
// const data = res?.data?.data;
// const contentData = data?.content;
// contentData.forEach((item: any, index: number) => {
// item.no = (page - 1) * Number(showData) + index + 1;
// });
// console.log("contentData : ", data);
// setData(contentData);
// setTotalData(data?.totalElements);
// setTotalPage(data?.totalPages);
// close();
// } catch (error) {
// console.error("Error fetching tasks:", error);
// }
// }
async function fetchData() {
try {
loading();
const res = await listDataPopUp(
const res = await listDataMedia(
page - 1,
showData,
"",
searchQuery, // <-- gunakan nilai pencarian
categoryFilter?.sort().join(","),
statusFilter?.sort().join(",")
);
@ -224,10 +259,19 @@ const ContentListPopUp = () => {
<Input
type="text"
placeholder="Search"
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
value={searchQuery}
onChange={(e) => {
setSearchQuery(e.target.value);
handleKeyDown();
}}
onKeyDown={(e) => {
if (e.key === "Enter") {
fetchData();
}
}}
className="max-w-[300px]"
/>
{/* <div className="flex flex-row gap-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
@ -393,9 +437,12 @@ const ContentListPopUp = () => {
alt={item.title}
className="w-full h-48 object-cover"
/>
<div className="p-3">
<h4 className="font-semibold text-sm truncate">{item.title}</h4>
</div>
<Link
href={`/contributor/content/image/detail/${item?.id}`}
className="p-3"
>
<h4 className="font-semibold text-sm">{item.title}</h4>
</Link>
</div>
))}
</div>

View File

@ -28,7 +28,7 @@ export default function AdminPopup() {
: "bg-white text-black "
}`}
>
Kontent
Konten
</Button>
<Button
rounded="md"

View File

@ -23,7 +23,7 @@ import { Link, useRouter } from "@/components/navigation";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { deleteBlog } from "@/service/blog/blog";
import { error, loading } from "@/lib/swal";
import { error, loading, close } from "@/lib/swal";
import { useTranslations } from "next-intl";
import axios from "axios";
@ -126,6 +126,7 @@ const useTableColumns = ({
const handleDownload = async (id: string) => {
try {
loading();
const response = await axios.get(
`https://netidhub.com/api/media/report/download?id=${id}`,
{
@ -140,6 +141,7 @@ const useTableColumns = ({
document.body.appendChild(link);
link.click();
link.remove();
close();
} catch (error) {
console.error("Download failed", error);
MySwal.fire({

View File

@ -37,6 +37,7 @@ export default function ExecutiveDataDashboard() {
const state = Cookies.get("state");
const provState = Cookies.get("state-prov");
const t = useTranslations("AnalyticsDashboard");
const [refreshTicket, setRefreshTicket] = useState(true);
const [ticket1, setTicket1] = useState("");
const [ticket2, setTicket2] = useState("");
@ -44,6 +45,7 @@ export default function ExecutiveDataDashboard() {
const [ticket4, setTicket4] = useState("");
const [ticket5, setTicket5] = useState("");
const [ticket6, setTicket6] = useState("");
const [ticket7, setTicket7] = useState("");
const [isInternational, setIsInternational] = useState([false, false, false]);
const baseUrl = "https://analytic.sitani.info/";
@ -54,13 +56,40 @@ export default function ExecutiveDataDashboard() {
const view1 =
levelName == "MABES POLRI"
? isInternational[0]
? "views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue-executive?"
: "views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue-executive?"
? "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-per-polda-new?polda-selected=ALL"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-per-polda-new?polda-selected=ALL"
: safeLevelName.includes("POLDA")
? `views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue?provinsi-polda=${state}&`
: `views/2023_08_MediaHUB-KtnMgt_Rev100/db-emg-issue?provinsi-polda=${state}&`;
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-per-polda-new?polda-selected=${state}&`
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-per-polda-new?polda-selected=${state}&`;
const view2 =
levelName == "MABES POLRI"
? isInternational[1]
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-content-interaction-per-satker?polda-selected=ALL"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-per-satker?polda-selected=ALL"
: safeLevelName.includes("POLDA")
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-per-satker?polda-selected=SATKER POLRI&`
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-per-satker?polda-selected=SATKER POLRI&`;
const view3 =
levelName == "MABES POLRI"
? isInternational[2]
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-content-category-per-polda-new?polda-selected=ALL"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-category-per-polda-new?polda-selected=ALL"
: safeLevelName.includes("POLDA")
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-category-per-polda-new?polda-selected=${state}&`
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-category-per-polda-new?polda-selected=${state}&`;
const view4 =
levelName == "MABES POLRI"
? isInternational[1]
? "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-category-per-satker?polda-selected=ALL"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-category-per-satker?polda-selected=ALL"
: safeLevelName.includes("POLDA")
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-category-per-satker?polda-selected=SATKER POLRI&`
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-category-per-satker?polda-selected=SATKER POLRI&`;
const view5 =
levelName == "MABES POLRI"
? isInternational[1]
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-published-produksi?"
@ -69,7 +98,7 @@ export default function ExecutiveDataDashboard() {
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-published-produksi-polda-executive?polda-selected=${state}&`
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-published-produksi-polda-executive?polda-selected=${state}&`;
const view3 =
const view6 =
levelName == "MABES POLRI"
? isInternational[2]
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-waktu-akses-pengguna?"
@ -78,59 +107,14 @@ export default function ExecutiveDataDashboard() {
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-waktu-akses-pengguna-polda-executive?polda-selected=${state}&`
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-waktu-akses-pengguna-polda-executive?polda-selected=${state}&`;
const view4 =
levelName == "MABES POLRI"
? isInternational[1]
? "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polda?"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polda?"
: safeLevelName.includes("POLDA")
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-per-polda-new?polda-selected=${state}&`
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-per-polda-new?polda-selected=${state}&`;
const view5 =
levelName == "MABES POLRI"
? isInternational[1]
? "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polres?"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polres?"
: safeLevelName.includes("POLDA")
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polres?provinsi-polda=${state}&`
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polres?provinsi-polda=${state}&`;
const view6 =
levelName == "MABES POLRI"
? isInternational[1]
? "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-satker?"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-satker?"
: safeLevelName.includes("POLDA")
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-satker?satker-selected=${state}&`
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-satker?satker-selected=${state}&`;
const view7 =
levelName == "MABES POLRI"
? isInternational[2]
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-penugasan?"
: "views/2023_09_db-penugasan_rev100/db-penugasan?"
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-penugasan-vertical-bar?"
: "views/2023_09_db-penugasan_rev100/db-penugasan-vertical-bar?"
: safeLevelName.includes("POLDA")
? `views/2023_09_db-penugasan_rev100/db-penugasan?polda-selected=${state}&`
: `views/2023_09_db-penugasan_rev100/db-penugasan?polda-selected=${state}&`;
const view8 =
levelName == "MABES POLRI"
? isInternational[2]
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-konten-kategori-top10?"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori-top10?"
: safeLevelName.includes("POLDA")
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polda-new?polda-selected=${state}&`
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-content-interaction-polda-new?polda-selected=${state}&`;
const view9 =
levelName == "MABES POLRI"
? isInternational[3]
? "views/2023_04_MediaHUB-Viz_INTL_Rev202/db-konten-kategori?"
: "views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori?"
: safeLevelName.includes("POLDA")
? `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori-polda?polda-selected=${state}&`
: `views/2023_04_MediaHUB-Viz-POLDA_Rev200/db-konten-kategori-polda?polda-selected=${state}&`;
? `views/2023_09_db-penugasan_rev100/db-penugasan-vertical-bar?polda-selected=${state}&`
: `views/2023_09_db-penugasan_rev100/db-penugasan-vertical-bar?polda-selected=${state}&`;
const param = ":embed=yes&:toolbar=no&:iframeSizedToWindow=true";
@ -153,10 +137,13 @@ export default function ExecutiveDataDashboard() {
const response6 = await generateTicket();
setTicket6(response6?.data?.data);
const response7 = await generateTicket();
setTicket7(response7?.data?.data);
}
initState();
}, [isInternational]);
}, [isInternational, refreshTicket]);
// Hooks
useEffect(() => {
@ -178,7 +165,7 @@ export default function ExecutiveDataDashboard() {
<SiteBreadcrumb />
<div>
<div className="my-3">
<Tabs defaultValue="content-publish" className="w-full">
<Tabs defaultValue="content-publish" className="w-full" onValueChange={() => setRefreshTicket(!refreshTicket)}>
<TabsList className="flex-wrap bg-black">
<TabsTrigger
value="content-publish"
@ -217,19 +204,17 @@ export default function ExecutiveDataDashboard() {
{/* Polda */}
{(levelNumber === "1" || levelNumber === "2") && (
<Card
className={`rounded-sm p-3 ${
levelNumber === "2" ? "w-full" : "w-full"
}`}
className={`rounded-sm p-3 w-full`}
>
<div className="flex flex-row justify-between">
{/* <div className="flex flex-row justify-between">
<p className="text-base font-semibold">
Upload konten hari ini Polda
</p>
</div>
</div> */}
<div className="my-5">
{ticket1 == "w-full" ? (
<iframe
src={`${baseUrl + view4 + param}`}
src={`${baseUrl + view1 + param}`}
width="100%"
height="750"
className="w-full"
@ -237,7 +222,7 @@ export default function ExecutiveDataDashboard() {
/>
) : (
<iframe
src={`${url + ticket1}/${view4}${param}`}
src={`${url + ticket1}/${view1}${param}`}
width="100%"
height="750"
frameBorder="0"
@ -248,60 +233,26 @@ export default function ExecutiveDataDashboard() {
)}
{/* Satker */}
{(levelNumber === "1" || levelNumber === "3") && (
{(levelNumber === "1") && (
<Card
className={`rounded-sm p-3 ${
levelNumber === "3" ? "w-full" : "w-full"
}`}
className={`rounded-sm p-3 w-full`}
>
<div className="flex flex-row justify-between">
{/* <div className="flex flex-row justify-between">
<p className="text-base font-semibold">
Upload konten hari ini Satker
</p>
</div>
</div> */}
<div className="my-5">
{ticket2 == "" ? (
<iframe
src={`${baseUrl + view6 + param}`}
src={`${baseUrl + view2 + param}`}
width="100%"
height="750"
frameBorder="0"
/>
) : (
<iframe
src={`${url + ticket2}/${view6}${param}`}
width="100%"
height="750"
frameBorder="0"
/>
)}
</div>
</Card>
)}
{/* Polres */}
{(levelNumber === "1" || levelNumber === "2") && (
<Card
className={`rounded-sm p-3 ${
levelNumber === "2" ? "w-full" : "w-full"
}`}
>
<div className="flex flex-row justify-between">
<p className="text-base font-semibold">
Upload konten hari ini Polres
</p>
</div>
<div className="my-5">
{ticket3 == "" ? (
<iframe
src={`${baseUrl + view5 + param}`}
width="100%"
height="750"
frameBorder="0"
/>
) : (
<iframe
src={`${url + ticket3}/${view5}${param}`}
src={`${url + ticket2}/${view2}${param}`}
width="100%"
height="750"
frameBorder="0"
@ -313,82 +264,63 @@ export default function ExecutiveDataDashboard() {
</div>
</TabsContent>
<TabsContent value="content-category">
<Card className="px-3 py-3">
<p className="text-lg">
<b>
{isInternational[2]
? "INTERACTION OF THE MOST POPULAR CATEGORIES AND TITLES"
: "INTERAKSI KATEGORI DAN JUDUL TERPOPULER"}
</b>
</p>
{levelName === "MABES POLRI" ? (
<div className="flex flex-col gap-1">
<p>{t("choose_category")}</p>
<div className="flex flex-row gap-1 border-2 w-fit">
<Button
onClick={() => handleInternational(2, false)}
className={` hover:text-white rounded-none
${
isInternational[2]
? "bg-white text-black "
: "bg-black text-white "
}`}
>
Indonesia
</Button>
<Button
onClick={() => handleInternational(2, true)}
className={`hover:text-white rounded-none ${
isInternational[1]
? "bg-black text-white "
: "bg-white text-black "
}
`}
>
{t("international")}
</Button>
{(levelNumber === "1" || levelNumber === "2") && (
<Card className="px-3 py-3">
<div className="my-5">
{ticket3 == "" ? (
<iframe
src={`${baseUrl + view3 + param}`}
width="100%"
height="750"
frameBorder="0"
/>
) : (
<iframe
src={`${`${url + ticket3}/${view3}${param}`}`}
width="100%"
height="750"
frameBorder="0"
/>
)}
</div>
</div>
) : (
""
)}
<div className="my-5">
{ticket3 == "" ? (
<iframe
src={`${baseUrl + view8 + param}`}
width="100%"
height="750"
frameBorder="0"
/>
) : (
<iframe
src={`${`${url + ticket3}/${view8}${param}`}`}
width="100%"
height="750"
frameBorder="0"
/>
)}
</div>
</Card>
</Card>
)}
{levelNumber === "1" && (
<Card className="px-3 py-3">
<div className="my-5">
{ticket4 == "" ? (
<iframe
src={`${baseUrl + view4 + param}`}
width="100%"
height="750"
frameBorder="0"
/>
) : (
<iframe
src={`${`${url + ticket4}/${view4}${param}`}`}
width="100%"
height="750"
frameBorder="0"
/>
)}
</div>
</Card>
)}
</TabsContent>
<TabsContent value="popular-content">
<Card className="rounded-sm p-3 h-[750px]">
<div className="flex flex-row justify-between">
<p className="text-base font-semibold">
Konten Paling Populer
</p>
</div>
<div className="my-5">
{ticket4 == "" ? (
{ticket5 == "" ? (
<iframe
src={`${baseUrl + view2 + param}`}
src={`${baseUrl + view5 + param}`}
width="100%"
height="650"
frameBorder="0"
/>
) : (
<iframe
src={`${`${url + ticket4}/${view2}${param}`}`}
src={`${`${url + ticket5}/${view5}${param}`}`}
width="100%"
height="650"
frameBorder="0"
@ -400,22 +332,17 @@ export default function ExecutiveDataDashboard() {
<TabsContent value="heatmap">
<Card className="rounded-sm p-3 h-[750px]">
<div className="flex flex-row justify-between mx-3">
<p className="text-base font-semibold">
Heatmap Konten dan Kategori dengan Interaksi
</p>
</div>
<div className="my-5">
{ticket5 == "" ? (
{ticket6 == "" ? (
<iframe
src={`${baseUrl + view3 + param}`}
src={`${baseUrl + view6 + param}`}
width="100%"
height="600"
frameBorder="0"
/>
) : (
<iframe
src={`${`${url + ticket5}/${view3}${param}`}`}
src={`${`${url + ticket6}/${view6}${param}`}`}
width="100%"
height="600"
frameBorder="0"
@ -425,60 +352,25 @@ export default function ExecutiveDataDashboard() {
</Card>
</TabsContent>
<TabsContent value="task">
<div className="grid grid-cols-12 gap-5">
<div className="lg:col-span-12 col-span-12">
<Card className="px-3 py-3">
{levelName === "MABES POLRI" ? (
<div className="flex flex-col gap-1">
<p>{t("choose_category")}</p>
<div className="flex flex-row gap-1 border-2 w-fit">
<Button
onClick={() => handleInternational(2, false)}
className={` hover:text-white rounded-none
${
isInternational[2]
? "bg-white text-black "
: "bg-black text-white "
}`}
>
Indonesia
</Button>
<Button
onClick={() => handleInternational(2, true)}
className={`hover:text-white rounded-none ${
isInternational[1]
? "bg-black text-white "
: "bg-white text-black "
}
`}
>
{t("international")}
</Button>
</div>
</div>
) : (
""
)}
<div className="my-5">
{ticket3 == "" ? (
<iframe
src={`${baseUrl + view7 + param}`}
width="100%"
height="750"
frameBorder="0"
/>
) : (
<iframe
src={`${`${url + ticket3}/${view7}${param}`}`}
width="100%"
height="750"
frameBorder="0"
/>
)}
</div>
</Card>
<Card className="rounded-sm p-3 h-[750px]">
<div className="my-5">
{ticket7 == "" ? (
<iframe
src={`${baseUrl + view7 + param}`}
width="100%"
height="750"
frameBorder="0"
/>
) : (
<iframe
src={`${`${url + ticket7}/${view7}${param}`}`}
width="100%"
height="750"
frameBorder="0"
/>
)}
</div>
</div>
</Card>
</TabsContent>
</Tabs>
</div>

View File

@ -13,21 +13,81 @@ import {
deleteKnowledgeBase,
getKnowledgeBaseCategoryList,
getKnowledgeBaseList,
saveKnowledgeBase,
saveKnowledgeBaseCategory,
} from "@/service/master/knowledge-base";
import React from "react";
import { Plus, Trash, Trash2 } from "lucide-react";
import React, { useEffect, useState } from "react";
import { Edit2Icon, Plus, Trash, Trash2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import CreateCategory from "./create-category";
import withReactContent from "sweetalert2-react-content";
import Swal from "sweetalert2";
import { deleteMedia } from "@/service/content/content";
import { error } from "@/lib/swal";
import { close, loading } from "@/config/swal";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { toast } from "@/components/ui/use-toast";
import { Textarea } from "@/components/ui/textarea";
const FormSchema = z.object({
name: z.string().min(2, {
message: "Name must be at least 2 characters.",
}),
});
const FormSchemaCreate = z.object({
title: z.string().min(2, {
message: "Judul minimal 2 karakter.",
}),
question: z.string().min(2, {
message: "Pertanyaan minimal 2 karakter.",
}),
answer: z.string().min(2, {
message: "Jawaban minimal 2 karakter.",
}),
});
const KnowledgeBase = () => {
const MySwal = withReactContent(Swal);
const [categories, setCategories] = React.useState<any>([]);
const [questions, setQuestions] = React.useState<any>([]);
const [categories, setCategories] = useState<any>([]);
const [questions, setQuestions] = useState<any>([]);
const [selectedCategoryId, setSelectedCategoryId] = useState(0);
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(false);
const [viewCreate, setViewCreate] = useState(false);
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
name: "",
},
});
const formCreate = useForm<z.infer<typeof FormSchemaCreate>>({
resolver: zodResolver(FormSchemaCreate),
defaultValues: {
title: "",
question: "",
answer: "",
},
});
React.useEffect(() => {
fetchCategoryList();
}, []);
@ -37,7 +97,9 @@ const KnowledgeBase = () => {
const data = response?.data?.data;
if (data) {
setCategories(data);
fetchQuestions(data[0]?.id);
fetchQuestions(
selectedCategoryId === 0 ? data[0]?.id : selectedCategoryId
);
}
}
@ -50,7 +112,7 @@ const KnowledgeBase = () => {
};
async function doDelete(id: any) {
// loading();
loading();
const data = {
id,
};
@ -61,6 +123,7 @@ const KnowledgeBase = () => {
error(response.message);
return false;
}
close();
success();
}
@ -72,7 +135,7 @@ const KnowledgeBase = () => {
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
window.location.reload();
fetchQuestions(selectedCategoryId);
}
});
}
@ -93,6 +156,50 @@ const KnowledgeBase = () => {
});
};
function onSubmit(data: z.infer<typeof FormSchema>) {
save(data);
setIsDialogOpen(false);
}
useEffect(() => {
formCreate.reset();
}, [viewCreate]);
async function save(data: any) {
loading();
const reqData = {
id: selectedCategoryId,
name: data.name,
};
const response = await saveKnowledgeBaseCategory(reqData);
if (response?.error) {
return false;
}
close();
toast({
title: "Task created successfully.",
});
fetchCategoryList();
}
async function onSubmitCreate(data: z.infer<typeof FormSchemaCreate>) {
const reqData = {
title: data.title,
categoryId:
selectedCategoryId === 0 ? categories[0]?.id : selectedCategoryId,
question: data.question,
answer: data.answer,
};
const response = await saveKnowledgeBase(reqData);
if (response?.error) {
error(response.message);
return false;
}
fetchQuestions(
selectedCategoryId === 0 ? categories[0]?.id : selectedCategoryId
);
}
return (
<div>
<SiteBreadcrumb />
@ -108,6 +215,7 @@ const KnowledgeBase = () => {
value={`category-${index}`}
onClick={() => {
fetchQuestions(category?.id);
setSelectedCategoryId(category?.id);
}}
className="group data-[state=active]:bg-secondary data-[state=active]:text-default rounded-md px-6 py-3 w-full justify-between flex items-center"
>
@ -116,7 +224,57 @@ const KnowledgeBase = () => {
className="right-2 top-2 hidden group-hover:inline-flex"
// onClick={() => deleteCategory(category?.id)}
>
<Trash size={20} className="text-red-500" />
<div className="flex flex-row gap-2 items-center">
<Dialog
open={isDialogOpen}
onOpenChange={setIsDialogOpen}
>
<DialogTrigger asChild>
<a
className="dark:bg-background dark:text-foreground"
onClick={() =>
form.setValue("name", category.name)
}
>
<Edit2Icon size={18} />
</a>
</DialogTrigger>
<DialogContent>
<DialogHeader className="mb-4">
<DialogTitle>Edit Category</DialogTitle>
</DialogHeader>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-4"
>
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel className="text-default-700">
Name
</FormLabel>
<FormControl>
<Input
placeholder="Enter Title"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="flex justify-end">
<Button type="submit">Submit</Button>
</div>
</form>
</Form>
</DialogContent>
</Dialog>
<Trash size={20} className="text-red-500" />
</div>
</div>
</TabsTrigger>
))}
@ -145,6 +303,7 @@ const KnowledgeBase = () => {
}}
className="text-left"
/>
<Trash
size={20}
className="text-red-500 hover:cursor-pointer"
@ -158,16 +317,86 @@ const KnowledgeBase = () => {
</AccordionItem>
))}
</Accordion>
{questions?.length > 0 && (
<div className="flex gap-3">
<Button fullWidth size="md" variant="outline">
<Plus className="w-6 h-6 me-1.5" />
Import
</Button>
<Button fullWidth size="md">
<Plus className="w-6 h-6 me-1.5" />
Add Question
</Button>
<div className="flex gap-3">
<Button fullWidth size="md" variant="outline">
<Plus className="w-6 h-6 me-1.5" />
Import
</Button>
<Button
fullWidth
size="md"
onClick={() => setViewCreate(!viewCreate)}
>
<Plus className="w-6 h-6 me-1.5" />
Add Question
</Button>
</div>
{viewCreate && (
<div className="flex flex-col gap-2 py-6">
<Form {...formCreate}>
<form
onSubmit={formCreate.handleSubmit(onSubmitCreate)}
className="space-y-4"
>
<FormField
control={formCreate.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel className="text-default-700">
Judul
</FormLabel>
<FormControl>
<Input
placeholder="Masukkan Judul"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={formCreate.control}
name="question"
render={({ field }) => (
<FormItem>
<FormLabel className="text-default-700">
Pertanyaan
</FormLabel>
<FormControl>
<Textarea
placeholder="Masukkan Pertanyaan"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={formCreate.control}
name="answer"
render={({ field }) => (
<FormItem>
<FormLabel className="text-default-700">
Jawaban
</FormLabel>
<FormControl>
<Textarea
placeholder="Masukkan Jawaban"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="flex justify-end">
<Button type="submit">Submit</Button>
</div>
</form>
</Form>
</div>
)}
</TabsContent>

View File

@ -1,26 +1,80 @@
"use client";
import { useState } from "react";
import { use, useEffect, useState } from "react";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { MoreVertical } from "lucide-react";
import { MoreVertical, Trash2 } from "lucide-react";
import {
deleteDataFeedback,
getListFeedback,
postDataFeedback,
} from "@/service/settings/settings";
import { close, loading } from "@/config/swal";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
const FeedbackForm = () => {
const [region, setRegion] = useState("nasional");
const [feedbackList, setFeedbackList] = useState<string[]>([
"Silahkan berikan rating Anda terkait dengan kemudahan akses website MediaHUB Polri",
"Silahkan berikan rating Anda terkait dengan konten MediaHUB Polri",
"Silahkan berikan rating Anda terkait dengan tampilan MediaHUB Polri",
]);
const [feedbackList, setFeedbackList] = useState<any>([]);
const [newFeedback, setNewFeedback] = useState("");
const [selectedFeedback, setSetSelectedFeedback] = useState(0);
const handleAddFeedback = () => {
useEffect(() => {
fetchFeedback();
}, []);
const fetchFeedback = async () => {
loading();
const res = await getListFeedback();
setFeedbackList(res?.data?.data);
close();
};
const handleAddFeedback = async () => {
if (newFeedback.trim()) {
setFeedbackList([...feedbackList, newFeedback.trim()]);
const req = {
question: newFeedback.trim(),
isInternational: region === "internasional",
};
loading();
const res = await postDataFeedback(req);
close();
fetchFeedback();
}
};
const handleEditFeedback = async () => {
if (newFeedback.trim()) {
const req = {
id: selectedFeedback,
question: newFeedback.trim(),
isInternational: region === "internasional",
};
loading();
const res = await postDataFeedback(req);
close();
fetchFeedback();
setSetSelectedFeedback(0);
setNewFeedback("");
}
};
const deleteFeedback = async (id: number) => {
loading();
const res = await deleteDataFeedback(id);
console.log(res);
close();
fetchFeedback();
};
const doEdit = (id: number, question: string) => {
setSetSelectedFeedback(id);
setNewFeedback(question);
};
return (
<div className="space-y-4">
<div className="bg-white rounded-md p-6 flex flex-col md:flex-row gap-6">
@ -52,23 +106,57 @@ const FeedbackForm = () => {
className="min-h-[100px]"
/>
</div>
<Button
onClick={handleAddFeedback}
className="bg-blue-600 text-white"
>
Tambah Feedback
</Button>
<div className="flex flex-row gap-1">
{selectedFeedback !== 0 && (
<Button
variant="outline"
onClick={handleEditFeedback}
className="bg-white text-blue-600 border border-bue-600"
>
Ubah Feedback
</Button>
)}
<Button
onClick={handleAddFeedback}
className="bg-blue-600 text-white"
>
Tambah Feedback
</Button>
</div>
</div>
<div className="md:w-1/2">
<h3 className="font-semibold mb-2 border-b pb-1">Feedback</h3>
<p className="font-medium mb-4">Pertanyaan</p>
<ul className="space-y-3">
{feedbackList.map((item, index) => (
<li key={index} className="flex justify-between items-start">
<span>{item}</span>
<MoreVertical className="w-4 h-4 text-gray-600 cursor-pointer" />
{feedbackList.map((item: any) => (
<li key={item.id} className="flex justify-between items-center">
<span>{item.question}</span>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
className="bg-transparent ring-offset-transparent hover:bg-transparent hover:ring-0 hover:ring-transparent"
>
<span className="sr-only">Open menu</span>
<MoreVertical className="h-4 w-4 text-default-800" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0" align="end">
<DropdownMenuItem
onClick={() => doEdit(item.id, item.question)}
className="p-2 border-b rounded-none focus:bg-slate-300"
>
Edit
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => deleteFeedback(item.id)}
className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none"
>
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</li>
))}
</ul>

View File

@ -107,8 +107,9 @@ const FilterPage = () => {
let prefixPath = poldaName ? `/polda/${poldaName}` : "/";
const t = useTranslations("FilterPage");
const satkerName = params?.satker_name;
const [isLoading, setIsLoading] = useState<any>(true);
const [categoryPage, setCategoryPage] = useState(1);
const [categoryTotalPages, setCategoryTotalPages] = useState(1);
// const [startDate, endDate] = dateRange;
@ -119,6 +120,7 @@ const FilterPage = () => {
return () => clearTimeout(timer);
}, []);
React.useEffect(() => {
const pageFromUrl = searchParams?.get("page");
if (pageFromUrl) {
@ -185,23 +187,51 @@ const FilterPage = () => {
// setCategories(resCategory);
// }
// useEffect(() => {
// initFetch();
// }, []);
// const initFetch = async () => {
// const response = await getPublicCategoryData(
// poldaName && String(poldaName)?.length > 1
// ? poldaName
// : satkerName && String(satkerName)?.length > 1
// ? "satker-" + satkerName
// : "",
// "",
// locale == "en" ? true : false
// );
// console.log("category", response);
// setCategories(response?.data?.data?.content);
// };
useEffect(() => {
initFetch();
}, []);
const initFetch = async () => {
const response = await getPublicCategoryData(
poldaName && String(poldaName)?.length > 1
fetchCategories(categoryPage);
}, [categoryPage]);
const fetchCategories = async (pageNumber: number) => {
const groupParam =
poldaName && poldaName.length > 1
? poldaName
: satkerName && String(satkerName)?.length > 1
: satkerName && satkerName.length > 1
? "satker-" + satkerName
: "",
: "";
const isInt = locale === "en";
const response = await getPublicCategoryData(
groupParam,
"",
locale == "en" ? true : false
isInt,
pageNumber
);
console.log("category", response);
setCategories(response?.data?.data?.content);
const content = response?.data?.data?.content || [];
const total = response?.data?.data?.totalPages || 1;
setCategories(content);
setCategoryTotalPages(total);
};
useEffect(() => {
function initState() {
if (dateRange[0] != null && dateRange[1] != null) {
@ -293,10 +323,11 @@ const FilterPage = () => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
filter = [...filter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
filter = filter.filter((item) => item !== String(id));
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
@ -560,6 +591,64 @@ const FilterPage = () => {
</label>
</li>
))}
<div className="mt-4 flex gap-2 justify-center items-center">
{/* Tombol Prev */}
<button
onClick={() =>
setCategoryPage((prev) => Math.max(prev - 1, 1))
}
disabled={categoryPage === 1}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="m13.15 16.15l-3.625-3.625q-.125-.125-.175-.25T9.3 12t.05-.275t.175-.25L13.15 7.85q.075-.075.163-.112T13.5 7.7q.2 0 .35.138T14 8.2v7.6q0 .225-.15.363t-.35.137q-.05 0-.35-.15"
/>
</svg>
</button>
{/* Nomor Halaman */}
{Array.from({ length: categoryTotalPages }, (_, i) => (
<button
key={i}
onClick={() => setCategoryPage(i + 1)}
className={`px-3 py-1 border rounded ${
categoryPage === i + 1 ? "bg-[#bb3523] text-white" : ""
}`}
>
{i + 1}
</button>
))}
{/* Tombol Next */}
<button
onClick={() =>
setCategoryPage((prev) =>
Math.min(prev + 1, categoryTotalPages)
)
}
disabled={categoryPage === categoryTotalPages}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M10.5 16.3q-.2 0-.35-.137T10 15.8V8.2q0-.225.15-.362t.35-.138q.05 0 .35.15l3.625 3.625q.125.125.175.25t.05.275t-.05.275t-.175.25L10.85 16.15q-.075.075-.162.113t-.188.037"
/>
</svg>
</button>
</div>
</ul>
</div>
{/* Garis */}

View File

@ -100,6 +100,8 @@ const FilterPage = () => {
let prefixPath = poldaName ? `/polda/${poldaName}` : "/";
const [isLoading, setIsLoading] = useState<any>(true);
const satkerName = params?.satker_name;
const [categoryPage, setCategoryPage] = useState(1);
const [categoryTotalPages, setCategoryTotalPages] = useState(1);
// const [startDate, endDate] = dateRange;
@ -176,21 +178,49 @@ const FilterPage = () => {
// setCategories(resCategory);
// }
// useEffect(() => {
// initFetch();
// }, []);
// const initFetch = async () => {
// const response = await getPublicCategoryData(
// poldaName && String(poldaName)?.length > 1
// ? poldaName
// : satkerName && String(satkerName)?.length > 1
// ? "satker-" + satkerName
// : "",
// "",
// locale == "en" ? true : false
// );
// console.log("category", response);
// setCategories(response?.data?.data?.content);
// };
useEffect(() => {
initFetch();
}, []);
const initFetch = async () => {
const response = await getPublicCategoryData(
poldaName && String(poldaName)?.length > 1
fetchCategories(categoryPage);
}, [categoryPage]);
const fetchCategories = async (pageNumber: number) => {
const groupParam =
poldaName && poldaName.length > 1
? poldaName
: satkerName && String(satkerName)?.length > 1
: satkerName && satkerName.length > 1
? "satker-" + satkerName
: "",
: "";
const isInt = locale === "en";
const response = await getPublicCategoryData(
groupParam,
"",
locale == "en" ? true : false
isInt,
pageNumber
);
console.log("category", response);
setCategories(response?.data?.data?.content);
const content = response?.data?.data?.content || [];
const total = response?.data?.data?.totalPages || 1;
setCategories(content);
setCategoryTotalPages(total);
};
useEffect(() => {
@ -290,10 +320,11 @@ const FilterPage = () => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
filter = [...filter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
filter = filter.filter((item) => item !== String(id));
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
@ -576,6 +607,64 @@ const FilterPage = () => {
</label>
</li>
))}
<div className="mt-4 flex gap-2 justify-center items-center">
{/* Tombol Prev */}
<button
onClick={() =>
setCategoryPage((prev) => Math.max(prev - 1, 1))
}
disabled={categoryPage === 1}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="m13.15 16.15l-3.625-3.625q-.125-.125-.175-.25T9.3 12t.05-.275t.175-.25L13.15 7.85q.075-.075.163-.112T13.5 7.7q.2 0 .35.138T14 8.2v7.6q0 .225-.15.363t-.35.137q-.05 0-.35-.15"
/>
</svg>
</button>
{/* Nomor Halaman */}
{Array.from({ length: categoryTotalPages }, (_, i) => (
<button
key={i}
onClick={() => setCategoryPage(i + 1)}
className={`px-3 py-1 border rounded ${
categoryPage === i + 1 ? "bg-[#bb3523] text-white" : ""
}`}
>
{i + 1}
</button>
))}
{/* Tombol Next */}
<button
onClick={() =>
setCategoryPage((prev) =>
Math.min(prev + 1, categoryTotalPages)
)
}
disabled={categoryPage === categoryTotalPages}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M10.5 16.3q-.2 0-.35-.137T10 15.8V8.2q0-.225.15-.362t.35-.138q.05 0 .35.15l3.625 3.625q.125.125.175.25t.05.275t-.05.275t-.175.25L10.85 16.15q-.075.075-.162.113t-.188.037"
/>
</svg>
</button>
</div>
</ul>
</div>
{/* Garis */}

View File

@ -103,6 +103,8 @@ const FilterPage = () => {
const satkerName = params?.satker_name;
let prefixPath = poldaName ? `/polda/${poldaName}` : "/";
const [isLoading, setIsLoading] = useState<any>(true);
const [categoryPage, setCategoryPage] = useState(1);
const [categoryTotalPages, setCategoryTotalPages] = useState(1);
const t = useTranslations("FilterPage");
@ -182,22 +184,51 @@ const FilterPage = () => {
// setCategories(resCategory);
// }
// useEffect(() => {
// initFetch();
// }, []);
// const initFetch = async () => {
// const response = await getPublicCategoryData(
// poldaName && String(poldaName)?.length > 1
// ? poldaName
// : satkerName && String(satkerName)?.length > 1
// ? "satker-" + satkerName
// : "",
// "",
// locale == "en" ? true : false
// );
// console.log("category", response);
// setCategories(response?.data?.data?.content);
// };
useEffect(() => {
initFetch();
}, []);
const initFetch = async () => {
const response = await getPublicCategoryData(
poldaName && String(poldaName)?.length > 1
fetchCategories(categoryPage);
}, [categoryPage]);
const fetchCategories = async (pageNumber: number) => {
const groupParam =
poldaName && poldaName.length > 1
? poldaName
: satkerName && String(satkerName)?.length > 1
: satkerName && satkerName.length > 1
? "satker-" + satkerName
: "",
: "";
const isInt = locale === "en";
const response = await getPublicCategoryData(
groupParam,
"",
locale == "en" ? true : false
);
console.log("category", response);
setCategories(response?.data?.data?.content);
isInt,
pageNumber
); // halaman 1-based
const content = response?.data?.data?.content || [];
const total = response?.data?.data?.totalPages || 1;
setCategories(content);
setCategoryTotalPages(total);
};
useEffect(() => {
function initState() {
if (dateRange[0] != null && dateRange[1] != null) {
@ -289,10 +320,11 @@ const FilterPage = () => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
filter = [...filter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
filter = filter.filter((item) => item !== String(id));
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
@ -586,6 +618,66 @@ const FilterPage = () => {
</label>
</li>
))}
<div className="mt-4 flex gap-2 justify-center items-center">
{/* Tombol Prev */}
<button
onClick={() =>
setCategoryPage((prev) => Math.max(prev - 1, 1))
}
disabled={categoryPage === 1}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="m13.15 16.15l-3.625-3.625q-.125-.125-.175-.25T9.3 12t.05-.275t.175-.25L13.15 7.85q.075-.075.163-.112T13.5 7.7q.2 0 .35.138T14 8.2v7.6q0 .225-.15.363t-.35.137q-.05 0-.35-.15"
/>
</svg>
</button>
{/* Nomor Halaman */}
{Array.from({ length: categoryTotalPages }, (_, i) => (
<button
key={i}
onClick={() => setCategoryPage(i + 1)}
className={`px-3 py-1 border rounded ${
categoryPage === i + 1
? "bg-[#bb3523] text-white"
: ""
}`}
>
{i + 1}
</button>
))}
{/* Tombol Next */}
<button
onClick={() =>
setCategoryPage((prev) =>
Math.min(prev + 1, categoryTotalPages)
)
}
disabled={categoryPage === categoryTotalPages}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M10.5 16.3q-.2 0-.35-.137T10 15.8V8.2q0-.225.15-.362t.35-.138q.05 0 .35.15l3.625 3.625q.125.125.175.25t.05.275t-.05.275t-.175.25L10.85 16.15q-.075.075-.162.113t-.188.037"
/>
</svg>
</button>
</div>
</ul>
</div>
{/* Garis */}

View File

@ -100,6 +100,8 @@ const FilterPage = () => {
const t = useTranslations("FilterPage");
const [isLoading, setIsLoading] = useState<any>(true);
const satkerName = params?.satker_name;
const [categoryPage, setCategoryPage] = useState(1);
const [categoryTotalPages, setCategoryTotalPages] = useState(1);
// const [startDate, endDate] = dateRange;
@ -176,22 +178,51 @@ const FilterPage = () => {
// setCategories(resCategory);
// }
// useEffect(() => {
// initFetch();
// }, []);
// const initFetch = async () => {
// const response = await getPublicCategoryData(
// poldaName && String(poldaName)?.length > 1
// ? poldaName
// : satkerName && String(satkerName)?.length > 1
// ? "satker-" + satkerName
// : "",
// "",
// locale == "en" ? true : false
// );
// console.log("category", response);
// setCategories(response?.data?.data?.content);
// };
useEffect(() => {
initFetch();
}, []);
const initFetch = async () => {
const response = await getPublicCategoryData(
poldaName && String(poldaName)?.length > 1
fetchCategories(categoryPage);
}, [categoryPage]);
const fetchCategories = async (pageNumber: number) => {
const groupParam =
poldaName && poldaName.length > 1
? poldaName
: satkerName && String(satkerName)?.length > 1
: satkerName && satkerName.length > 1
? "satker-" + satkerName
: "",
: "";
const isInt = locale === "en";
const response = await getPublicCategoryData(
groupParam,
"",
locale == "en" ? true : false
);
console.log("category", response);
setCategories(response?.data?.data?.content);
isInt,
pageNumber
); // halaman 1-based
const content = response?.data?.data?.content || [];
const total = response?.data?.data?.totalPages || 1;
setCategories(content);
setCategoryTotalPages(total);
};
useEffect(() => {
function initState() {
if (dateRange[0] != null && dateRange[1] != null) {
@ -283,10 +314,11 @@ const FilterPage = () => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
filter = [...filter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
filter = filter.filter((item) => item !== String(id));
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
@ -572,6 +604,64 @@ const FilterPage = () => {
</label>
</li>
))}
<div className="mt-4 flex gap-2 justify-center items-center">
{/* Tombol Prev */}
<button
onClick={() =>
setCategoryPage((prev) => Math.max(prev - 1, 1))
}
disabled={categoryPage === 1}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="m13.15 16.15l-3.625-3.625q-.125-.125-.175-.25T9.3 12t.05-.275t.175-.25L13.15 7.85q.075-.075.163-.112T13.5 7.7q.2 0 .35.138T14 8.2v7.6q0 .225-.15.363t-.35.137q-.05 0-.35-.15"
/>
</svg>
</button>
{/* Nomor Halaman */}
{Array.from({ length: categoryTotalPages }, (_, i) => (
<button
key={i}
onClick={() => setCategoryPage(i + 1)}
className={`px-3 py-1 border rounded ${
categoryPage === i + 1 ? "bg-[#bb3523] text-white" : ""
}`}
>
{i + 1}
</button>
))}
{/* Tombol Next */}
<button
onClick={() =>
setCategoryPage((prev) =>
Math.min(prev + 1, categoryTotalPages)
)
}
disabled={categoryPage === categoryTotalPages}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M10.5 16.3q-.2 0-.35-.137T10 15.8V8.2q0-.225.15-.362t.35-.138q.05 0 .35.15l3.625 3.625q.125.125.175.25t.05.275t-.05.275t-.175.25L10.85 16.15q-.075.075-.162.113t-.188.037"
/>
</svg>
</button>
</div>
</ul>
</div>
{/* Garis */}

View File

@ -7,6 +7,7 @@ import {
formatDateToIndonesian,
getOnlyDate,
getOnlyMonthAndYear,
secondToTimes,
} from "@/utils/globals";
import { useParams, usePathname, useSearchParams } from "next/navigation";
import {
@ -43,6 +44,7 @@ import {
CarouselPrevious,
} from "@/components/ui/carousel";
import { useTranslations } from "next-intl";
import { Skeleton } from "@/components/ui/skeleton";
const columns: ColumnDef<any>[] = [
{
@ -105,6 +107,17 @@ const FilterPage = () => {
const satkerName = params?.satker_name;
let prefixPath = satkerName ? `/satker/${satkerName}` : "/";
const t = useTranslations("FilterPage");
const [isLoading, setIsLoading] = useState<any>(true);
const [categoryPage, setCategoryPage] = useState(1);
const [categoryTotalPages, setCategoryTotalPages] = useState(1);
useEffect(() => {
const timer = setTimeout(() => {
setIsLoading(false);
}, 3000);
return () => clearTimeout(timer);
}, []);
// const [startDate, endDate] = dateRange;
@ -174,21 +187,49 @@ const FilterPage = () => {
// setCategories(resCategory);
// }
// useEffect(() => {
// initFetch();
// }, []);
// const initFetch = async () => {
// const response = await getPublicCategoryData(
// poldaName && String(poldaName)?.length > 1
// ? poldaName
// : satkerName && String(satkerName)?.length > 1
// ? "satker-" + satkerName
// : "",
// "",
// locale == "en" ? true : false
// );
// console.log("category", response);
// setCategories(response?.data?.data?.content);
// };
useEffect(() => {
initFetch();
}, []);
const initFetch = async () => {
const response = await getPublicCategoryData(
poldaName && String(poldaName)?.length > 1
fetchCategories(categoryPage);
}, [categoryPage]);
const fetchCategories = async (pageNumber: number) => {
const groupParam =
poldaName && poldaName.length > 1
? poldaName
: satkerName && String(satkerName)?.length > 1
: satkerName && satkerName.length > 1
? "satker-" + satkerName
: "",
: "";
const isInt = locale === "en";
const response = await getPublicCategoryData(
groupParam,
"",
locale == "en" ? true : false
isInt,
pageNumber
);
console.log("category", response);
setCategories(response?.data?.data?.content);
const content = response?.data?.data?.content || [];
const total = response?.data?.data?.totalPages || 1;
setCategories(content);
setCategoryTotalPages(total);
};
useEffect(() => {
@ -278,14 +319,15 @@ const FilterPage = () => {
}
}
const handleCategoryFilter = (e: boolean, id: string) => {
const handleCategoryFilter = (e: boolean, id: string) => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
filter = [...filter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
filter = filter.filter((item) => item !== String(id));
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
@ -441,7 +483,7 @@ const FilterPage = () => {
<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">
<div className="flex flex-col md:flex-row items-start gap-5 py-10 px-4 lg:px-20 bg-[#f7f7f7] dark:bg-black">
<p>
{" "}
Audio {">"} <span className="font-bold">{t("allAudio")}</span>
@ -451,8 +493,8 @@ const FilterPage = () => {
</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">
<div className="flex flex-col lg:flex-row gap-6 pl-4 lg:pl-20 py-4">
<div className="h-fit min-w-full lg:min-w-[280px] max-w-full lg:max-w-[300px] 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
@ -529,7 +571,7 @@ const FilterPage = () => {
{t("categories")}
</h3>
<ul className="mt-2 space-y-2">
{categories.map((category: any) => (
{categories?.map((category: any) => (
<li key={category?.id}>
<label
className="inline-flex items-center"
@ -549,6 +591,64 @@ const FilterPage = () => {
</label>
</li>
))}
<div className="mt-4 flex gap-2 justify-center items-center">
{/* Tombol Prev */}
<button
onClick={() =>
setCategoryPage((prev) => Math.max(prev - 1, 1))
}
disabled={categoryPage === 1}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="m13.15 16.15l-3.625-3.625q-.125-.125-.175-.25T9.3 12t.05-.275t.175-.25L13.15 7.85q.075-.075.163-.112T13.5 7.7q.2 0 .35.138T14 8.2v7.6q0 .225-.15.363t-.35.137q-.05 0-.35-.15"
/>
</svg>
</button>
{/* Nomor Halaman */}
{Array.from({ length: categoryTotalPages }, (_, i) => (
<button
key={i}
onClick={() => setCategoryPage(i + 1)}
className={`px-3 py-1 border rounded ${
categoryPage === i + 1 ? "bg-[#bb3523] text-white" : ""
}`}
>
{i + 1}
</button>
))}
{/* Tombol Next */}
<button
onClick={() =>
setCategoryPage((prev) =>
Math.min(prev + 1, categoryTotalPages)
)
}
disabled={categoryPage === categoryTotalPages}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M10.5 16.3q-.2 0-.35-.137T10 15.8V8.2q0-.225.15-.362t.35-.138q.05 0 .35.15l3.625 3.625q.125.125.175.25t.05.275t-.05.275t-.175.25L10.85 16.15q-.075.075-.162.113t-.188.037"
/>
</svg>
</button>
</div>
</ul>
</div>
{/* Garis */}
@ -603,9 +703,9 @@ const FilterPage = () => {
</div>
</div>
{/* Konten Kanan */}
<Reveal>
<div className="flex-1">
{/* Right */}
<div className="w-full pr-4 lg:pr-16 pb-4">
<Reveal>
<div className="flex flex-col items-end mb-4">
<h2 className="text-lg font-semibold">{t("sortBy")} </h2>
<select
@ -618,22 +718,35 @@ const FilterPage = () => {
</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"
{isLoading ? (
<>
<div className="flex flex-col space-y-3 w-full justify-center items-center gap-3">
<Skeleton className="h-[100px] w-full rounded-xl" />
<Skeleton className="h-[100px] w-full rounded-xl" />
<Skeleton className="h-[100px] w-full rounded-xl" />
<Skeleton className="h-[100px] w-full rounded-xl" />
<Skeleton className="h-[100px] w-full rounded-xl" />
<Skeleton className="h-[100px] w-full rounded-xl" />
</div>
</>
) : (
<>
{audioData?.length > 0 ? (
<div className="flex flex-col gap-3">
{audioData?.map((audio: any) => (
<div key={audio?.id}>
<div
// href={`/audio/detail/${audio?.slug}`}
onClick={() =>
router.push(
prefixPath + `/audio/detail/${audio?.slug}`
)
}
className="flex flex-row items-center bg-white dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full"
>
<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">
<div className="flex justify-center lg:justify-between w-full gap-2 lg:gap-6">
<div className="flex flex-row gap-3">
<div className="flex items-center justify-center bg-red-500 text-white rounded-lg w-16 h-full lg:h-16">
<svg
width="32"
height="34"
@ -647,42 +760,76 @@ const FilterPage = () => {
/>
</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 className="flex flex-col">
<div className="flex flex-col lg:flex-row lg:gap-2">
<div className="text-gray-500 dark:text-gray-400 flex flex-row text-sm items-center">
{formatDateToIndonesian(
new Date(audio?.createdAt)
)}{" "}
{audio?.timezone ? audio?.timezone : "WIB"}
</div>
<div className="text-gray-500 dark:text-gray-400 flex flex-row text-sm items-center">
|&nbsp;
<Icon
icon="formkit:eye"
width="15"
height="15"
/>
&nbsp;{audio?.clickCount}{" "}
</div>
</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>
<div className="flex flex-col lg:flex-row items-center place-content-end gap-3">
<img
src="/assets/wave.svg"
alt="wave"
className="h-5 lg:h-16"
/>
<svg
width="17"
height="20"
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="black"
/>
</svg>
<p className="text-xs lg:text-sm text-center">
{" "}
{audio?.duration
? secondToTimes(Number(audio?.duration))
: "00:00:00"}
</p>
<Icon
icon="ph:download-fill"
className="text-red-500"
fontSize={20}
/>
</div>
</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>
</div>
</div>
))}
</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
@ -690,8 +837,8 @@ const FilterPage = () => {
totalData={totalData}
totalPage={totalPage}
/>
</div>
</Reveal>
</Reveal>
</div>
</div>
</div>
);

View File

@ -98,6 +98,8 @@ const FilterPage = () => {
const poldaName = params?.polda_name;
const satkerName = params?.satker_name;
let prefixPath = satkerName ? `/satker/${satkerName}` : "/";
const [categoryPage, setCategoryPage] = useState(1);
const [categoryTotalPages, setCategoryTotalPages] = useState(1);
// const [startDate, endDate] = dateRange;
@ -167,22 +169,51 @@ const FilterPage = () => {
// setCategories(resCategory);
// }
// useEffect(() => {
// fetchContent();
// }, []);
// const fetchContent = async () => {
// const response = await getPublicCategoryData(
// poldaName && String(poldaName)?.length > 1
// ? poldaName
// : satkerName && String(satkerName)?.length > 1
// ? "satker-" + satkerName
// : "",
// "",
// locale == "en" ? true : false
// );
// console.log("category", response);
// setCategories(response?.data?.data?.content);
// };
useEffect(() => {
fetchContent();
}, []);
const fetchContent = async () => {
const response = await getPublicCategoryData(
poldaName && String(poldaName)?.length > 1
fetchCategories(categoryPage);
}, [categoryPage]);
const fetchCategories = async (pageNumber: number) => {
const groupParam =
poldaName && poldaName.length > 1
? poldaName
: satkerName && String(satkerName)?.length > 1
: satkerName && satkerName.length > 1
? "satker-" + satkerName
: "",
: "";
const isInt = locale === "en";
const response = await getPublicCategoryData(
groupParam,
"",
locale == "en" ? true : false
isInt,
pageNumber
);
console.log("category", response);
setCategories(response?.data?.data?.content);
const content = response?.data?.data?.content || [];
const total = response?.data?.data?.totalPages || 1;
setCategories(content);
setCategoryTotalPages(total);
};
useEffect(() => {
function initState() {
if (dateRange[0] != null && dateRange[1] != null) {
@ -280,10 +311,11 @@ const FilterPage = () => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
filter = [...filter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
filter = filter.filter((item) => item !== String(id));
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
@ -457,8 +489,7 @@ const FilterPage = () => {
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">
<div className="flex flex-row md:flex-row items-start gap-3 py-10 px-4 lg:px-20 bg-[#f7f7f7] dark:bg-black">
<p>
{" "}
{t("text")} {">"} <span className="font-bold">{t("allText")}</span>
@ -468,7 +499,7 @@ const FilterPage = () => {
</div>
{/* Left */}
<div className="flex flex-col lg:flex-row gap-6 p-4">
<div className="flex flex-col lg:flex-row gap-6 pl-4 lg:pl-20 py-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} />
@ -566,6 +597,64 @@ const FilterPage = () => {
</label>
</li>
))}
<div className="mt-4 flex gap-2 justify-center items-center">
{/* Tombol Prev */}
<button
onClick={() =>
setCategoryPage((prev) => Math.max(prev - 1, 1))
}
disabled={categoryPage === 1}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="m13.15 16.15l-3.625-3.625q-.125-.125-.175-.25T9.3 12t.05-.275t.175-.25L13.15 7.85q.075-.075.163-.112T13.5 7.7q.2 0 .35.138T14 8.2v7.6q0 .225-.15.363t-.35.137q-.05 0-.35-.15"
/>
</svg>
</button>
{/* Nomor Halaman */}
{Array.from({ length: categoryTotalPages }, (_, i) => (
<button
key={i}
onClick={() => setCategoryPage(i + 1)}
className={`px-3 py-1 border rounded ${
categoryPage === i + 1 ? "bg-[#bb3523] text-white" : ""
}`}
>
{i + 1}
</button>
))}
{/* Tombol Next */}
<button
onClick={() =>
setCategoryPage((prev) =>
Math.min(prev + 1, categoryTotalPages)
)
}
disabled={categoryPage === categoryTotalPages}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M10.5 16.3q-.2 0-.35-.137T10 15.8V8.2q0-.225.15-.362t.35-.138q.05 0 .35.15l3.625 3.625q.125.125.175.25t.05.275t-.05.275t-.175.25L10.85 16.15q-.075.075-.162.113t-.188.037"
/>
</svg>
</button>
</div>
</ul>
</div>
{/* Garis */}
@ -666,86 +755,90 @@ const FilterPage = () => {
</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>
<div className="w-full pr-4 lg:pr-16 pb-4">
<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">
{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"
width="1em"
height="1em"
viewBox="0 0 512 512"
>
<path
fill="#f00"
d="M224 30v256h-64l96 128l96-128h-64V30zM32 434v48h448v-48z"
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>
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>
)}
<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>
<LandingPagination
table={table}
totalData={totalData}
totalPage={totalPage}
/>
</div>
</Reveal>
</div>
</div>
</div>
);

View File

@ -100,6 +100,8 @@ const FilterPage = () => {
const satkerName = params?.satker_name;
let prefixPath = satkerName ? `/satker/${satkerName}` : "/";
const t = useTranslations("FilterPage");
const [categoryPage, setCategoryPage] = useState(1);
const [categoryTotalPages, setCategoryTotalPages] = useState(1);
// const [startDate, endDate] = dateRange;
@ -169,21 +171,49 @@ const FilterPage = () => {
// setCategories(resCategory);
// }
// useEffect(() => {
// fetchContent();
// }, []);
// const fetchContent = async () => {
// const response = await getPublicCategoryData(
// poldaName && String(poldaName)?.length > 1
// ? poldaName
// : satkerName && String(satkerName)?.length > 1
// ? "satker-" + satkerName
// : "",
// "",
// locale == "en" ? true : false
// );
// console.log("category", response);
// setCategories(response?.data?.data?.content);
// };
useEffect(() => {
fetchContent();
}, []);
const fetchContent = async () => {
const response = await getPublicCategoryData(
poldaName && String(poldaName)?.length > 1
fetchCategories(categoryPage);
}, [categoryPage]);
const fetchCategories = async (pageNumber: number) => {
const groupParam =
poldaName && poldaName.length > 1
? poldaName
: satkerName && String(satkerName)?.length > 1
: satkerName && satkerName.length > 1
? "satker-" + satkerName
: "",
: "";
const isInt = locale === "en";
const response = await getPublicCategoryData(
groupParam,
"",
locale == "en" ? true : false
isInt,
pageNumber
);
console.log("category", response);
setCategories(response?.data?.data?.content);
const content = response?.data?.data?.content || [];
const total = response?.data?.data?.totalPages || 1;
setCategories(content);
setCategoryTotalPages(total);
};
useEffect(() => {
@ -273,14 +303,15 @@ const FilterPage = () => {
}
}
const handleCategoryFilter = (e: boolean, id: string) => {
const handleCategoryFilter = (e: boolean, id: string) => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
filter = [...filter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
filter = filter.filter((item) => item !== String(id));
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
@ -435,8 +466,7 @@ const FilterPage = () => {
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">
<div className="flex flex-row md:flex-row items-start gap-3 py-10 px-4 lg:px-20 bg-[#f7f7f7] dark:bg-black">
<p>
{" "}
{t("image")} {">"} <span className="font-bold">{t("allImage")}</span>
@ -446,7 +476,7 @@ const FilterPage = () => {
</div>
{/* Left */}
<div className="flex w-fit flex-col lg:flex-row gap-6 p-4">
<div className="flex flex-col lg:flex-row gap-6 pl-4 lg:pl-20 py-4">
<div className="lg:hidden flex justify-end mb-2">
<button
onClick={() => setIsFilterOpen(!isFilterOpen)}
@ -553,6 +583,66 @@ const FilterPage = () => {
</label>
</li>
))}
<div className="mt-4 flex gap-2 justify-center items-center">
{/* Tombol Prev */}
<button
onClick={() =>
setCategoryPage((prev) => Math.max(prev - 1, 1))
}
disabled={categoryPage === 1}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="m13.15 16.15l-3.625-3.625q-.125-.125-.175-.25T9.3 12t.05-.275t.175-.25L13.15 7.85q.075-.075.163-.112T13.5 7.7q.2 0 .35.138T14 8.2v7.6q0 .225-.15.363t-.35.137q-.05 0-.35-.15"
/>
</svg>
</button>
{/* Nomor Halaman */}
{Array.from({ length: categoryTotalPages }, (_, i) => (
<button
key={i}
onClick={() => setCategoryPage(i + 1)}
className={`px-3 py-1 border rounded ${
categoryPage === i + 1
? "bg-[#bb3523] text-white"
: ""
}`}
>
{i + 1}
</button>
))}
{/* Tombol Next */}
<button
onClick={() =>
setCategoryPage((prev) =>
Math.min(prev + 1, categoryTotalPages)
)
}
disabled={categoryPage === categoryTotalPages}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M10.5 16.3q-.2 0-.35-.137T10 15.8V8.2q0-.225.15-.362t.35-.138q.05 0 .35.15l3.625 3.625q.125.125.175.25t.05.275t-.05.275t-.175.25L10.85 16.15q-.075.075-.162.113t-.188.037"
/>
</svg>
</button>
</div>
</ul>
</div>
{/* Garis */}
@ -624,75 +714,79 @@ const FilterPage = () => {
)}
{/* 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 className="w-full pr-4 lg:pr-16 pb-4">
<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>
) : (
<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>
{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>
</div>
);

View File

@ -96,6 +96,8 @@ const FilterPage = () => {
const satkerName = params?.satker_name;
let prefixPath = satkerName ? `/satker/${satkerName}` : "/";
const t = useTranslations("FilterPage");
const [categoryPage, setCategoryPage] = useState(1);
const [categoryTotalPages, setCategoryTotalPages] = useState(1);
// const [startDate, endDate] = dateRange;
@ -165,21 +167,49 @@ const FilterPage = () => {
// setCategories(resCategory);
// }
// useEffect(() => {
// fetchContent();
// }, []);
// const fetchContent = async () => {
// const response = await getPublicCategoryData(
// poldaName && String(poldaName)?.length > 1
// ? poldaName
// : satkerName && String(satkerName)?.length > 1
// ? "satker-" + satkerName
// : "",
// "",
// locale == "en" ? true : false
// );
// console.log("category", response);
// setCategories(response?.data?.data?.content);
// };
useEffect(() => {
fetchContent();
}, []);
const fetchContent = async () => {
const response = await getPublicCategoryData(
poldaName && String(poldaName)?.length > 1
fetchCategories(categoryPage);
}, [categoryPage]);
const fetchCategories = async (pageNumber: number) => {
const groupParam =
poldaName && poldaName.length > 1
? poldaName
: satkerName && String(satkerName)?.length > 1
: satkerName && satkerName.length > 1
? "satker-" + satkerName
: "",
: "";
const isInt = locale === "en";
const response = await getPublicCategoryData(
groupParam,
"",
locale == "en" ? true : false
isInt,
pageNumber
);
console.log("category", response);
setCategories(response?.data?.data?.content);
const content = response?.data?.data?.content || [];
const total = response?.data?.data?.totalPages || 1;
setCategories(content);
setCategoryTotalPages(total);
};
useEffect(() => {
@ -273,10 +303,11 @@ const FilterPage = () => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
filter = [...filter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
filter = filter.filter((item) => item !== String(id));
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
@ -452,8 +483,7 @@ const FilterPage = () => {
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">
<div className="flex flex-row md:flex-row items-start gap-3 py-10 px-4 lg:px-20 bg-[#f7f7f7] dark:bg-black">
<p>
{" "}
{t("video")}&nbsp;
@ -464,11 +494,11 @@ const FilterPage = () => {
</div>
{/* Left */}
<div className="flex flex-col lg:flex-row gap-6 p-4">
<div className="flex flex-col lg:flex-row gap-6 pl-4 lg:pl-20 py-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
Filter
</h2>
<div className="border-t border-black my-4 dark:border-white"></div>
<div className="space-y-6">
@ -562,6 +592,64 @@ const FilterPage = () => {
</label>
</li>
))}
<div className="mt-4 flex gap-2 justify-center items-center">
{/* Tombol Prev */}
<button
onClick={() =>
setCategoryPage((prev) => Math.max(prev - 1, 1))
}
disabled={categoryPage === 1}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="m13.15 16.15l-3.625-3.625q-.125-.125-.175-.25T9.3 12t.05-.275t.175-.25L13.15 7.85q.075-.075.163-.112T13.5 7.7q.2 0 .35.138T14 8.2v7.6q0 .225-.15.363t-.35.137q-.05 0-.35-.15"
/>
</svg>
</button>
{/* Nomor Halaman */}
{Array.from({ length: categoryTotalPages }, (_, i) => (
<button
key={i}
onClick={() => setCategoryPage(i + 1)}
className={`px-3 py-1 border rounded ${
categoryPage === i + 1 ? "bg-[#bb3523] text-white" : ""
}`}
>
{i + 1}
</button>
))}
{/* Tombol Next */}
<button
onClick={() =>
setCategoryPage((prev) =>
Math.min(prev + 1, categoryTotalPages)
)
}
disabled={categoryPage === categoryTotalPages}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M10.5 16.3q-.2 0-.35-.137T10 15.8V8.2q0-.225.15-.362t.35-.138q.05 0 .35.15l3.625 3.625q.125.125.175.25t.05.275t-.05.275t-.175.25L10.85 16.15q-.075.075-.162.113t-.188.037"
/>
</svg>
</button>
</div>
</ul>
</div>
{/* Garis */}
@ -662,75 +750,79 @@ const FilterPage = () => {
</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 className="w-full pr-4 lg:pr-16 pb-4">
<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>
) : (
<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>
{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>
</div>
);

View File

@ -99,7 +99,8 @@ const FilterPage = () => {
const [isLoading, setIsLoading] = useState<any>(true);
const poldaName = params?.polda_name;
const satkerName = params?.satker_name;
const [categoryPage, setCategoryPage] = useState(1);
const [categoryTotalPages, setCategoryTotalPages] = useState(1);
// const [startDate, endDate] = dateRange;
useEffect(() => {
@ -176,21 +177,49 @@ const FilterPage = () => {
// setCategories(resCategory);
// }
// useEffect(() => {
// initFetch();
// }, []);
// const initFetch = async () => {
// const response = await getPublicCategoryData(
// poldaName && String(poldaName)?.length > 1
// ? poldaName
// : satkerName && String(satkerName)?.length > 1
// ? "satker-" + satkerName
// : "",
// "",
// locale == "en" ? true : false
// );
// console.log("category", response);
// setCategories(response?.data?.data?.content);
// };
useEffect(() => {
initFetch();
}, []);
const initFetch = async () => {
const response = await getPublicCategoryData(
poldaName && String(poldaName)?.length > 1
fetchCategories(categoryPage);
}, [categoryPage]);
const fetchCategories = async (pageNumber: number) => {
const groupParam =
poldaName && poldaName.length > 1
? poldaName
: satkerName && String(satkerName)?.length > 1
: satkerName && satkerName.length > 1
? "satker-" + satkerName
: "",
: "";
const isInt = locale === "en";
const response = await getPublicCategoryData(
groupParam,
"",
locale == "en" ? true : false
);
console.log("category", response);
setCategories(response?.data?.data?.content);
isInt,
pageNumber
); // halaman 1-based
const content = response?.data?.data?.content || [];
const total = response?.data?.data?.totalPages || 1;
setCategories(content);
setCategoryTotalPages(total);
};
useEffect(() => {
@ -296,10 +325,11 @@ const FilterPage = () => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
filter = [...filter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
filter = filter.filter((item) => item !== String(id));
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
@ -563,6 +593,64 @@ const FilterPage = () => {
</label>
</li>
))}
<div className="mt-4 flex gap-2 justify-center items-center">
{/* Tombol Prev */}
<button
onClick={() =>
setCategoryPage((prev) => Math.max(prev - 1, 1))
}
disabled={categoryPage === 1}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="m13.15 16.15l-3.625-3.625q-.125-.125-.175-.25T9.3 12t.05-.275t.175-.25L13.15 7.85q.075-.075.163-.112T13.5 7.7q.2 0 .35.138T14 8.2v7.6q0 .225-.15.363t-.35.137q-.05 0-.35-.15"
/>
</svg>
</button>
{/* Nomor Halaman */}
{Array.from({ length: categoryTotalPages }, (_, i) => (
<button
key={i}
onClick={() => setCategoryPage(i + 1)}
className={`px-3 py-1 border rounded ${
categoryPage === i + 1 ? "bg-[#bb3523] text-white" : ""
}`}
>
{i + 1}
</button>
))}
{/* Tombol Next */}
<button
onClick={() =>
setCategoryPage((prev) =>
Math.min(prev + 1, categoryTotalPages)
)
}
disabled={categoryPage === categoryTotalPages}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M10.5 16.3q-.2 0-.35-.137T10 15.8V8.2q0-.225.15-.362t.35-.138q.05 0 .35.15l3.625 3.625q.125.125.175.25t.05.275t-.05.275t-.175.25L10.85 16.15q-.075.075-.162.113t-.188.037"
/>
</svg>
</button>
</div>
</ul>
</div>
{/* Garis */}

View File

@ -99,6 +99,8 @@ const FilterPage = () => {
const [isLoading, setIsLoading] = useState<any>(true);
const poldaName = params?.polda_name;
const satkerName = params?.satker_name;
const [categoryPage, setCategoryPage] = useState(1);
const [categoryTotalPages, setCategoryTotalPages] = useState(1);
// const [startDate, endDate] = dateRange;
@ -176,21 +178,49 @@ const FilterPage = () => {
// setCategories(resCategory);
// }
// useEffect(() => {
// initFetch();
// }, []);
// const initFetch = async () => {
// const response = await getPublicCategoryData(
// poldaName && String(poldaName)?.length > 1
// ? poldaName
// : satkerName && String(satkerName)?.length > 1
// ? "satker-" + satkerName
// : "",
// "",
// locale == "en" ? true : false
// );
// console.log("category", response);
// setCategories(response?.data?.data?.content);
// };
useEffect(() => {
initFetch();
}, []);
const initFetch = async () => {
const response = await getPublicCategoryData(
poldaName && String(poldaName)?.length > 1
fetchCategories(categoryPage);
}, [categoryPage]);
const fetchCategories = async (pageNumber: number) => {
const groupParam =
poldaName && poldaName.length > 1
? poldaName
: satkerName && String(satkerName)?.length > 1
: satkerName && satkerName.length > 1
? "satker-" + satkerName
: "",
: "";
const isInt = locale === "en";
const response = await getPublicCategoryData(
groupParam,
"",
locale == "en" ? true : false
);
console.log("category", response);
setCategories(response?.data?.data?.content);
isInt,
pageNumber
); // halaman 1-based
const content = response?.data?.data?.content || [];
const total = response?.data?.data?.totalPages || 1;
setCategories(content);
setCategoryTotalPages(total);
};
useEffect(() => {
@ -296,10 +326,11 @@ const FilterPage = () => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
filter = [...filter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
filter = filter.filter((item) => item !== String(id));
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
@ -578,6 +609,64 @@ const FilterPage = () => {
</label>
</li>
))}
<div className="mt-4 flex gap-2 justify-center items-center">
{/* Tombol Prev */}
<button
onClick={() =>
setCategoryPage((prev) => Math.max(prev - 1, 1))
}
disabled={categoryPage === 1}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="m13.15 16.15l-3.625-3.625q-.125-.125-.175-.25T9.3 12t.05-.275t.175-.25L13.15 7.85q.075-.075.163-.112T13.5 7.7q.2 0 .35.138T14 8.2v7.6q0 .225-.15.363t-.35.137q-.05 0-.35-.15"
/>
</svg>
</button>
{/* Nomor Halaman */}
{Array.from({ length: categoryTotalPages }, (_, i) => (
<button
key={i}
onClick={() => setCategoryPage(i + 1)}
className={`px-3 py-1 border rounded ${
categoryPage === i + 1 ? "bg-[#bb3523] text-white" : ""
}`}
>
{i + 1}
</button>
))}
{/* Tombol Next */}
<button
onClick={() =>
setCategoryPage((prev) =>
Math.min(prev + 1, categoryTotalPages)
)
}
disabled={categoryPage === categoryTotalPages}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M10.5 16.3q-.2 0-.35-.137T10 15.8V8.2q0-.225.15-.362t.35-.138q.05 0 .35.15l3.625 3.625q.125.125.175.25t.05.275t-.05.275t-.175.25L10.85 16.15q-.075.075-.162.113t-.188.037"
/>
</svg>
</button>
</div>
</ul>
</div>
{/* Garis */}

View File

@ -102,6 +102,8 @@ const FilterPage = () => {
const [isFilterOpen, setIsFilterOpen] = useState(true);
const poldaName = params?.polda_name;
const satkerName = params?.satker_name;
const [categoryPage, setCategoryPage] = useState(1);
const [categoryTotalPages, setCategoryTotalPages] = useState(1);
// const [startDate, endDate] = dateRange;
@ -179,19 +181,50 @@ const FilterPage = () => {
// setCategories(resCategory);
// }
useEffect(() => {
initFetch();
}, []);
const initFetch = async () => {
const response = await getPublicCategoryData(
poldaName && String(poldaName)?.length > 1 ? poldaName : satkerName && String(satkerName)?.length > 1 ? "satker-" + satkerName : "",
"",
locale == "en" ? true : false
);
console.log("category", response);
setCategories(response?.data?.data?.content);
};
// useEffect(() => {
// initFetch();
// }, []);
// const initFetch = async () => {
// const response = await getPublicCategoryData(
// poldaName && String(poldaName)?.length > 1
// ? poldaName
// : satkerName && String(satkerName)?.length > 1
// ? "satker-" + satkerName
// : "",
// "",
// locale == "en" ? true : false
// );
// console.log("category", response);
// setCategories(response?.data?.data?.content);
// };
useEffect(() => {
fetchCategories(categoryPage);
}, [categoryPage]);
const fetchCategories = async (pageNumber: number) => {
const groupParam =
poldaName && poldaName.length > 1
? poldaName
: satkerName && satkerName.length > 1
? "satker-" + satkerName
: "";
const isInt = locale === "en";
const response = await getPublicCategoryData(
groupParam,
"",
isInt,
pageNumber
);
const content = response?.data?.data?.content || [];
const total = response?.data?.data?.totalPages || 1;
setCategories(content);
setCategoryTotalPages(total);
};
useEffect(() => {
function initState() {
@ -296,10 +329,11 @@ const FilterPage = () => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
filter = [...filter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
filter = filter.filter((item) => item !== String(id));
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
@ -593,6 +627,66 @@ const FilterPage = () => {
</label>
</li>
))}
<div className="mt-4 flex gap-2 justify-center items-center">
{/* Tombol Prev */}
<button
onClick={() =>
setCategoryPage((prev) => Math.max(prev - 1, 1))
}
disabled={categoryPage === 1}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="m13.15 16.15l-3.625-3.625q-.125-.125-.175-.25T9.3 12t.05-.275t.175-.25L13.15 7.85q.075-.075.163-.112T13.5 7.7q.2 0 .35.138T14 8.2v7.6q0 .225-.15.363t-.35.137q-.05 0-.35-.15"
/>
</svg>
</button>
{/* Nomor Halaman */}
{Array.from({ length: categoryTotalPages }, (_, i) => (
<button
key={i}
onClick={() => setCategoryPage(i + 1)}
className={`px-3 py-1 border rounded ${
categoryPage === i + 1
? "bg-[#bb3523] text-white"
: ""
}`}
>
{i + 1}
</button>
))}
{/* Tombol Next */}
<button
onClick={() =>
setCategoryPage((prev) =>
Math.min(prev + 1, categoryTotalPages)
)
}
disabled={categoryPage === categoryTotalPages}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M10.5 16.3q-.2 0-.35-.137T10 15.8V8.2q0-.225.15-.362t.35-.138q.05 0 .35.15l3.625 3.625q.125.125.175.25t.05.275t-.05.275t-.175.25L10.85 16.15q-.075.075-.162.113t-.188.037"
/>
</svg>
</button>
</div>
</ul>
</div>
{/* Garis */}

View File

@ -99,7 +99,8 @@ const FilterPage = () => {
const [userLevels, setUserLevels] = useState([]);
const t = useTranslations("FilterPage");
const [isLoading, setIsLoading] = useState<any>(true);
const [categoryPage, setCategoryPage] = useState(1);
const [categoryTotalPages, setCategoryTotalPages] = useState(1);
// const [startDate, endDate] = dateRange;
useEffect(() => {
@ -171,21 +172,49 @@ const FilterPage = () => {
// setCategories(resCategory);
// }
// useEffect(() => {
// initFetch();
// }, []);
// const initFetch = async () => {
// const response = await getPublicCategoryData(
// poldaName && String(poldaName)?.length > 1
// ? poldaName
// : satkerName && String(satkerName)?.length > 1
// ? "satker-" + satkerName
// : "",
// "",
// locale == "en" ? true : false
// );
// console.log("category", response);
// setCategories(response?.data?.data?.content);
// };
useEffect(() => {
initFetch();
}, []);
const initFetch = async () => {
const response = await getPublicCategoryData(
poldaName && String(poldaName)?.length > 1
fetchCategories(categoryPage);
}, [categoryPage]);
const fetchCategories = async (pageNumber: number) => {
const groupParam =
poldaName && poldaName.length > 1
? poldaName
: satkerName && String(satkerName)?.length > 1
: satkerName && satkerName.length > 1
? "satker-" + satkerName
: "",
: "";
const isInt = locale === "en";
const response = await getPublicCategoryData(
groupParam,
"",
locale == "en" ? true : false
);
console.log("category", response);
setCategories(response?.data?.data?.content);
isInt,
pageNumber
); // halaman 1-based
const content = response?.data?.data?.content || [];
const total = response?.data?.data?.totalPages || 1;
setCategories(content);
setCategoryTotalPages(total);
};
useEffect(() => {
@ -290,15 +319,16 @@ const FilterPage = () => {
let filter = [...categoryFilter];
if (e) {
filter = [...categoryFilter, String(id)];
filter = [...filter, String(id)];
} else {
filter.splice(categoryFilter.indexOf(id), 1);
filter = filter.filter((item) => item !== String(id));
}
console.log("checkbox filter", filter);
setCategoryFilter(filter);
router.push(`?category=${filter.join("&")}`);
};
const handleFormatFilter = (e: boolean, id: string) => {
let filter = [...formatFilter];
@ -580,6 +610,64 @@ const FilterPage = () => {
</label>
</li>
))}
<div className="mt-4 flex gap-2 justify-center items-center">
{/* Tombol Prev */}
<button
onClick={() =>
setCategoryPage((prev) => Math.max(prev - 1, 1))
}
disabled={categoryPage === 1}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="m13.15 16.15l-3.625-3.625q-.125-.125-.175-.25T9.3 12t.05-.275t.175-.25L13.15 7.85q.075-.075.163-.112T13.5 7.7q.2 0 .35.138T14 8.2v7.6q0 .225-.15.363t-.35.137q-.05 0-.35-.15"
/>
</svg>
</button>
{/* Nomor Halaman */}
{Array.from({ length: categoryTotalPages }, (_, i) => (
<button
key={i}
onClick={() => setCategoryPage(i + 1)}
className={`px-3 py-1 border rounded ${
categoryPage === i + 1 ? "bg-[#bb3523] text-white" : ""
}`}
>
{i + 1}
</button>
))}
{/* Tombol Next */}
<button
onClick={() =>
setCategoryPage((prev) =>
Math.min(prev + 1, categoryTotalPages)
)
}
disabled={categoryPage === categoryTotalPages}
className="px-3 py-1 border rounded disabled:opacity-50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M10.5 16.3q-.2 0-.35-.137T10 15.8V8.2q0-.225.15-.362t.35-.138q.05 0 .35.15l3.625 3.625q.125.125.175.25t.05.275t-.05.275t-.175.25L10.85 16.15q-.075.075-.162.113t-.188.037"
/>
</svg>
</button>
</div>
</ul>
</div>
{/* Garis */}

View File

@ -14,11 +14,14 @@ import Image from "next/image";
import Coverage from "./coverage";
import Division from "./division";
import { useTranslations } from "next-intl";
import { useParams } from "next/navigation";
const AreaCoverageWorkUnits = () => {
const [openPolda, setOpenPolda] = useState(false);
const [openSatker, setOpenSatker] = useState(false);
const t = useTranslations("LandingPage");
const params = useParams();
const locale = params?.locale;
useEffect(() => {
if (openPolda || openSatker) {
@ -31,90 +34,97 @@ const AreaCoverageWorkUnits = () => {
document.body.classList.remove("overflow-hidden");
};
}, [openPolda, openSatker]);
return (
<div className="mx-auto px-4 lg:px-0 py-6">
<h2 className="text-start text-lg md:text-xl font-bold text-[#bb3523] border-b-2 border-[#bb3523] mb-4 uppercase">
{t("areaCoverage")}
</h2>
<div className="flex flex-col justify-center lg:flex-row gap-8 ">
{/* POLDA */}
<Dialog open={openPolda} onOpenChange={setOpenPolda}>
<DialogTrigger asChild>
<button
onClick={() => setOpenPolda(true)}
className="flex flex-col gap-2 justify-center items-center shadow-lg group rounded-xl py-5 w-full border-2 border-transparent hover:border-[#bb3523] transition-all duration-300"
>
<Image
width={1920}
height={1080}
alt="indo"
src="/assets/indo.png"
className="h-32 w-32 group-hover:scale-110 group-hover:border-[#bb3523] "
/>
<p className="text-base font-bold">{t("regionalPolice")}</p>
</button>
</DialogTrigger>
<DialogContent
size="md"
className="max-h-[90vh] overflow-hidden flex flex-col"
data-lenis-prevent
>
<DialogHeader className="flex flex-col justify-center">
<DialogTitle>
<p className="text-center">{t("regionalPolice")}</p>
</DialogTitle>
<DialogTitle>
<div className="h-1 w-[150px] bg-[#bb3523] mx-auto mb-6 rounded"></div>
</DialogTitle>
</DialogHeader>
<div className="overflow-y-auto px-1 flex-1">
<Coverage />
</div>
<div className="text-right mt-4">
<DialogClose asChild>
<button className="text-[#bb3523] font-bold">
{t("close")}
</button>
</DialogClose>
</div>
</DialogContent>
</Dialog>
{/* SATKER */}
<Dialog open={openSatker} onOpenChange={setOpenSatker}>
<DialogTrigger asChild>
<button className="flex flex-col gap-2 justify-center items-center shadow-lg group rounded-xl py-5 w-full border-2 border-transparent hover:border-[#bb3523] transition-all duration-300">
<Image
width={1920}
height={1080}
alt="polri"
src="/assets/logo-polri.png"
className="h-32 w-32 group-hover:scale-110 group-hover:border-[#bb3523] transition-transform duration-300"
/>
<p className="text-base font-bold">{t("policeDivision")}</p>
</button>
</DialogTrigger>
<DialogContent size="md" data-lenis-prevent>
<DialogHeader className="flex flex-col justify-center">
<DialogTitle>
<p className="text-center">{t("policeDivision")}</p>
</DialogTitle>
<DialogTitle>
<div className="h-1 w-[150px] bg-[#bb3523] mx-auto mb-6 rounded"></div>
</DialogTitle>
</DialogHeader>
<Division />
<div className="text-right mt-4">
<DialogClose asChild>
<button className="text-[#bb3523] font-bold">
{t("close")}
return (
<>
{locale === "in" && (
<div className="mx-auto px-4 lg:px-0 py-6">
<h2 className="text-start text-lg md:text-xl font-bold text-[#bb3523] border-b-2 border-[#bb3523] mb-4 uppercase">
{t("areaCoverage")}
</h2>
<div className="flex flex-col justify-center lg:flex-row gap-8 ">
{/* POLDA */}
<Dialog open={openPolda} onOpenChange={setOpenPolda}>
<DialogTrigger asChild>
<button
onClick={() => setOpenPolda(true)}
className="flex flex-col gap-2 justify-center items-center shadow-lg group rounded-xl py-5 w-full border-2 border-transparent hover:border-[#bb3523] transition-all duration-300"
>
<Image
width={1920}
height={1080}
alt="indo"
src="/assets/indo.png"
className="h-32 w-32 group-hover:scale-110 group-hover:border-[#bb3523] "
/>
<p className="text-base font-bold">{t("regionalPolice")}</p>
</button>
</DialogClose>
</div>
</DialogContent>
</Dialog>
</div>
</div>
</DialogTrigger>
<DialogContent
size="md"
className="max-h-[90vh] overflow-hidden flex flex-col"
data-lenis-prevent
>
<DialogHeader className="flex flex-col justify-center">
<DialogTitle>
<p className="text-center">{t("regionalPolice")}</p>
</DialogTitle>
<DialogTitle>
<div className="h-1 w-[150px] bg-[#bb3523] mx-auto mb-6 rounded"></div>
</DialogTitle>
</DialogHeader>
<div className="overflow-y-auto px-1 flex-1">
<Coverage />
</div>
<div className="text-right mt-4">
<DialogClose asChild>
<button className="text-[#bb3523] font-bold">
{t("close")}
</button>
</DialogClose>
</div>
</DialogContent>
</Dialog>
{/* SATKER */}
<Dialog open={openSatker} onOpenChange={setOpenSatker}>
<DialogTrigger asChild>
<button className="flex flex-col gap-2 justify-center items-center shadow-lg group rounded-xl py-5 w-full border-2 border-transparent hover:border-[#bb3523] transition-all duration-300">
<Image
width={1920}
height={1080}
alt="polri"
src="/assets/logo-polri.png"
className="h-32 w-32 group-hover:scale-110 group-hover:border-[#bb3523] transition-transform duration-300"
/>
<p className="text-base font-bold">{t("policeDivision")}</p>
</button>
</DialogTrigger>
<DialogContent size="md" data-lenis-prevent>
<DialogHeader className="flex flex-col justify-center">
<DialogTitle>
<p className="text-center">{t("policeDivision")}</p>
</DialogTitle>
<DialogTitle>
<div className="h-1 w-[150px] bg-[#bb3523] mx-auto mb-6 rounded"></div>
</DialogTitle>
</DialogHeader>
<Division />
<div className="text-right mt-4">
<DialogClose asChild>
<button className="text-[#bb3523] font-bold">
{t("close")}
</button>
</DialogClose>
</div>
</DialogContent>
</Dialog>
</div>
</div>
)}
</>
);
};

View File

@ -10,7 +10,7 @@ import Image from "next/image";
const regions = [
{ name: "ITWASUM POLRI", slug: "itwasum", logo: "/logo/satker/ITWASUM.png" },
{
name: "BAINTELKAM POLRI",
name: "BAINTELKAM POLRI ",
slug: "baintelkam",
logo: "/logo/satker/BAINTELKAM.png",
},

View File

@ -1,6 +1,7 @@
import { getCalendarPagination } from "@/service/schedule/schedule";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { useTranslations } from "next-intl";
import { useParams } from "next/navigation";
import React, { useEffect, useState } from "react";
interface CalendarItem {
@ -24,12 +25,13 @@ const EventCalender = () => {
const currentMonth = today.getMonth();
const currentYear = today.getFullYear();
const currentDate = today.getDate();
const [events, setEvents] = useState<CalendarItem[]>([]);
const [selectedEvent, setSelectedEvent] = useState<CalendarItem | null>(null);
const [month, setMonth] = useState(currentMonth);
const [year, setYear] = useState(currentYear);
const t = useTranslations("LandingPage");
const params = useParams();
const locale = params?.locale;
const monthNames = [
t("january"),
@ -164,169 +166,174 @@ const EventCalender = () => {
};
return (
<div className="mt-8 rounded-lg bg-white dark:bg-zinc-900 p-4 shadow">
<h2 className="text-lg font-bold text-red-600 border-b border-red-600 mb-4 pb-2">
{t("calendar")}
</h2>
<div className="flex flex-col lg:flex-row gap-6">
<div className="w-full lg:w-1/2">
<div className="bg-gray-100 dark:bg-zinc-800 p-4 rounded-md mb-4">
<div className="flex items-center justify-between mb-2">
<button onClick={handlePreviousMonth}>
<ChevronLeft className="w-4 h-4" />
</button>
<div className="text-center font-semibold">
{monthNames[month]} {year}
</div>
<button onClick={handleNextMonth}>
<ChevronRight className="w-4 h-4" />
</button>
</div>
<div className="grid grid-cols-7 gap-1 text-sm text-center">
{["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"].map((d) => (
<div key={d} className="font-medium p-1">
{d}
<>
{locale === "in" && (
<div className="mt-8 rounded-lg bg-white dark:bg-zinc-900 p-4 shadow">
<h2 className="text-lg font-bold text-red-600 border-b border-red-600 mb-4 pb-2">
{t("calendar")}
</h2>
<div className="flex flex-col lg:flex-row gap-6">
<div className="w-full lg:w-1/2">
<div className="bg-gray-100 dark:bg-zinc-800 p-4 rounded-md mb-4">
<div className="flex items-center justify-between mb-2">
<button onClick={handlePreviousMonth}>
<ChevronLeft className="w-4 h-4" />
</button>
<div className="text-center font-semibold">
{monthNames[month]} {year}
</div>
<button onClick={handleNextMonth}>
<ChevronRight className="w-4 h-4" />
</button>
</div>
))}
{calendarDays.map((day, index) => {
const isToday =
day === currentDate &&
month === currentMonth &&
year === currentYear;
<div className="grid grid-cols-7 gap-1 text-sm text-center">
{["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"].map((d) => (
<div key={d} className="font-medium p-1">
{d}
</div>
))}
{calendarDays.map((day, index) => {
const isToday =
day === currentDate &&
month === currentMonth &&
year === currentYear;
return (
<div
key={index}
className={`p-1 rounded min-h-[24px] flex items-center justify-center text-xs ${
day === null
? ""
: eventDates.includes(day)
? "bg-red-600 text-white font-semibold"
: isToday
? "bg-blue-500 text-white font-semibold"
: "hover:bg-gray-200 dark:hover:bg-zinc-700"
}`}
>
{day}
</div>
);
})}
</div>
</div>
<div
className="space-y-3 max-h-[230px] overflow-y-auto pr-5"
data-lenis-prevent
>
<h3 className="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-3">
{t("eventList")}
</h3>
{events?.length === 0 ? (
<div className="text-center text-gray-500 dark:text-gray-400 py-8">
{t("noEvent")}
return (
<div
key={index}
className={`p-1 rounded min-h-[24px] flex items-center justify-center text-xs ${
day === null
? ""
: eventDates.includes(day)
? "bg-red-600 text-white font-semibold"
: isToday
? "bg-blue-500 text-white font-semibold"
: "hover:bg-gray-200 dark:hover:bg-zinc-700"
}`}
>
{day}
</div>
);
})}
</div>
</div>
) : (
events.map((event) => (
<div
key={event.id}
onClick={() => setSelectedEvent(event)}
className={`flex items-center rounded-xl shadow-sm p-3 cursor-pointer transition-all duration-200 hover:shadow-md ${
selectedEvent?.id === event.id
? "bg-red-100 dark:bg-red-900/20 border-2 border-red-500"
: "bg-gray-200 dark:bg-zinc-800 hover:bg-gray-300 dark:hover:bg-zinc-700"
}`}
>
<img
src={event.thumbnailUrl || "/images/default-event.png"}
alt={event.title}
className="w-16 h-12 object-cover rounded flex-shrink-0"
onError={(e) => {
const target = e.target as HTMLImageElement;
target.src = "/images/default-event.png";
}}
/>
<div className="ml-3 flex-1 min-w-0">
<div className="text-sm font-semibold text-gray-800 dark:text-gray-200 line-clamp-2">
{event.title}
</div>
<div className="text-xs text-gray-600 dark:text-gray-400 mt-1">
{formatDateRange(event.startDate, event.endDate)}
</div>
<div
className="space-y-3 max-h-[230px] overflow-y-auto pr-5"
data-lenis-prevent
>
<h3 className="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-3">
{t("eventList")}
</h3>
{events?.length === 0 ? (
<div className="text-center text-gray-500 dark:text-gray-400 py-8">
{t("noEvent")}
</div>
<div className="ml-2 flex-shrink-0">
) : (
events.map((event) => (
<div
className={`w-2 h-2 rounded-full ${
event.isActive ? "bg-green-500" : "bg-red-500"
key={event.id}
onClick={() => setSelectedEvent(event)}
className={`flex items-center rounded-xl shadow-sm p-3 cursor-pointer transition-all duration-200 hover:shadow-md ${
selectedEvent?.id === event.id
? "bg-red-100 dark:bg-red-900/20 border-2 border-red-500"
: "bg-gray-200 dark:bg-zinc-800 hover:bg-gray-300 dark:hover:bg-zinc-700"
}`}
></div>
</div>
</div>
))
)}
</div>
</div>
<div className="w-full lg:w-1/2">
<div className="bg-gray-100 dark:bg-zinc-800 rounded-lg p-4 sticky top-4">
<h3 className="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">
{t("eventDetails")}
</h3>
{selectedEvent ? (
<div className="space-y-4">
<img
src={
selectedEvent.thumbnailUrl || "/images/default-event.png"
}
alt={selectedEvent.title}
className="w-full h-48 object-cover rounded-lg"
onError={(e) => {
const target = e.target as HTMLImageElement;
target.src = "/images/default-event.png";
}}
/>
<div>
<h4 className="text-lg font-bold text-gray-800 dark:text-gray-200 mb-2">
{selectedEvent.title}
</h4>
<div className="grid grid-cols-1 gap-3 mb-4">
<div className="flex items-start text-sm text-gray-600 dark:text-gray-400">
<span className="w-20 font-semibold flex-shrink-0">
{t("date")}
</span>
<span>
{formatDateRange(
selectedEvent.startDate,
selectedEvent.endDate
)}
</span>
>
<img
src={event.thumbnailUrl || "/images/default-event.png"}
alt={event.title}
className="w-16 h-12 object-cover rounded flex-shrink-0"
onError={(e) => {
const target = e.target as HTMLImageElement;
target.src = "/images/default-event.png";
}}
/>
<div className="ml-3 flex-1 min-w-0">
<div className="text-sm font-semibold text-gray-800 dark:text-gray-200 line-clamp-2">
{event.title}
</div>
<div className="text-xs text-gray-600 dark:text-gray-400 mt-1">
{formatDateRange(event.startDate, event.endDate)}
</div>
</div>
<div className="ml-2 flex-shrink-0">
<div
className={`w-2 h-2 rounded-full ${
event.isActive ? "bg-green-500" : "bg-red-500"
}`}
></div>
</div>
</div>
<div className="mb-4">
<p className="text-sm text-gray-600 dark:text-gray-400 leading-relaxed">
{selectedEvent.description ||
"Tidak ada deskripsi tersedia."}
</p>
))
)}
</div>
</div>
<div className="w-full lg:w-1/2">
<div className="bg-gray-100 dark:bg-zinc-800 rounded-lg p-4 sticky top-4">
<h3 className="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">
{t("eventDetails")}
</h3>
{selectedEvent ? (
<div className="space-y-4">
<img
src={
selectedEvent.thumbnailUrl ||
"/images/default-event.png"
}
alt={selectedEvent.title}
className="w-full h-48 object-cover rounded-lg"
onError={(e) => {
const target = e.target as HTMLImageElement;
target.src = "/images/default-event.png";
}}
/>
<div>
<h4 className="text-lg font-bold text-gray-800 dark:text-gray-200 mb-2">
{selectedEvent.title}
</h4>
<div className="grid grid-cols-1 gap-3 mb-4">
<div className="flex items-start text-sm text-gray-600 dark:text-gray-400">
<span className="w-20 font-semibold flex-shrink-0">
{t("date")}
</span>
<span>
{formatDateRange(
selectedEvent.startDate,
selectedEvent.endDate
)}
</span>
</div>
<div className="mb-4">
<p className="text-sm text-gray-600 dark:text-gray-400 leading-relaxed">
{selectedEvent.description ||
"Tidak ada deskripsi tersedia."}
</p>
</div>
</div>
<div className="flex gap-2">
<button className="px-4 py-2 bg-gray-300 dark:bg-zinc-700 text-gray-700 dark:text-gray-300 text-sm font-medium rounded-lg hover:bg-gray-400 dark:hover:bg-zinc-600 transition-colors">
{t("share")}
</button>
</div>
</div>
</div>
<div className="flex gap-2">
<button className="px-4 py-2 bg-gray-300 dark:bg-zinc-700 text-gray-700 dark:text-gray-300 text-sm font-medium rounded-lg hover:bg-gray-400 dark:hover:bg-zinc-600 transition-colors">
{t("share")}
</button>
) : (
<div className="text-center text-gray-500 dark:text-gray-400 py-8">
{t("selectEvent")}
</div>
</div>
)}
</div>
) : (
<div className="text-center text-gray-500 dark:text-gray-400 py-8">
{t("selectEvent")}
</div>
)}
</div>
</div>
</div>
</div>
</div>
)}
</>
);
};

View File

@ -66,6 +66,11 @@ const HeroModal = ({
const params = useParams();
const locale = params?.locale;
const swiperRef = useRef<SwiperClass | null>(null);
const pathname = usePathname();
if (pathname?.includes("/polda") || pathname?.includes("/satker")) {
return null;
}
let prefixPath = poldaName
? `/polda/${poldaName}`
@ -462,7 +467,7 @@ const HeroNew = (props: { group?: string }) => {
</Carousel>
<div className="hidden lg:flex flex-col gap-3 absolute bottom-4 right-4 w-[520px] bg-black/40 p-4 rounded-lg z-10">
{newContent?.slice(0, 3).map((item: any) => (
{content?.slice(0, 3).map((item: any) => (
<li key={item?.id} className="flex gap-4 flex-row lg:w-full mx-2">
<div className="flex-shrink-0 w-32 rounded-lg">
<Image
@ -476,7 +481,7 @@ const HeroNew = (props: { group?: string }) => {
className="w-full h-[100px] object-cover rounded-lg"
/>
</div>
<div className="w-[280px] lg:w-[200px]">
<div className="w-[280px] lg:w-full">
<Link
href={
Number(item?.fileTypeId) == 1
@ -487,14 +492,15 @@ const HeroNew = (props: { group?: string }) => {
? `${prefixPath}/document/detail/${item?.slug}`
: `${prefixPath}/audio/detail/${item?.slug}`
}
className="flex flex-col justify-between"
>
<span className="py-1 rounded-lg flex text-red-600 font-bold uppercase w-fit">
<p className="rounded-lg flex text-red-600 font-bold uppercase w-fit">
{item?.categoryName}
</span>
<h3 className="text-base text-white font-bold h-6 hover:h-auto truncate hover:whitespace-normal hover:overflow-visible">
</p>
<h3 className="text-base text-white font-bold">
{item?.title}
</h3>
<p className="text-[10px] flex flex-row items-center gap-1 text-white mt-1">
<p className="text-[10px] flex flex-row items-center gap-1 text-white mt-2">
{formatDateToIndonesian(new Date(item?.createdAt))}{" "}
{item?.timezone || "WIB"} |{" "}
<svg

View File

@ -49,9 +49,9 @@ import {
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import PoldaLogo from "./polda-logo";
import { DynamicLogoPolda } from "./dynamic-logo-polda";
import { DynamicLogoSatker } from "./dynamic-logo-satker";
import { ChevronDown, ChevronUp } from "lucide-react";
type Detail = {
id: number;
@ -93,6 +93,8 @@ const Navbar = () => {
const [notificationsUpdate, setNotificationsUpdate] = useState([]);
const [selectedTab, setSelectedTab] = useState("image");
const isHidden = pathname?.includes("polda") || pathname?.includes("satker");
let prefixPath = poldaName
? `/polda/${poldaName}`
: satkerName
@ -109,19 +111,18 @@ const Navbar = () => {
window.location.href = "/";
};
// useEffect(() => {
// if (!roleId) {
// router.replace("/"); // Kalau roleId-nya belum tersedia, redirect juga (opsional)
// return;
// }
// useEffect(() => {
// if (!roleId) {
// router.replace("/"); // Kalau roleId-nya belum tersedia, redirect juga (opsional)
// return;
// }
// const token = Cookies.get(roleId);
// if (!token) {
// router.replace("/"); // Redirect ke halaman home jika tidak ada token
// }
// }, []);
// const token = Cookies.get(roleId);
// if (!token) {
// router.replace("/"); // Redirect ke halaman home jika tidak ada token
// }
// }, []);
// const profilePicture = Cookies.get("profile_picture");
const fullName = getCookiesDecrypt("ufne");
@ -202,6 +203,8 @@ const Navbar = () => {
return () => clearTimeout(handler);
}, [onSearch]);
const [open, setOpen] = useState(false);
return (
<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-16 py-2 gap-3">
@ -218,132 +221,114 @@ const Navbar = () => {
<DynamicLogoPolda /> <DynamicLogoSatker />
{/* Nav Menu */}
<div className="hidden custom-lg-button:flex items-center gap-5">
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuTrigger>
<a className="dark:text-white text-black flex flex-row justify-center items-center cursor-pointer ">
<svg
className="mx-2 dark:"
width="25"
height="24"
viewBox="0 0 25 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M20 7.5H5C4.6023 7.5004 4.221 7.65856 3.93978 7.93978C3.65856 8.221 3.5004 8.6023 3.5 9V19.5C3.5004 19.8977 3.65856 20.279 3.93978 20.5602C4.221 20.8414 4.6023 20.9996 5 21H20C20.3977 20.9996 20.779 20.8414 21.0602 20.5602C21.3414 20.279 21.4996 19.8977 21.5 19.5V9C21.4996 8.6023 21.3414 8.221 21.0602 7.93978C20.779 7.65856 20.3977 7.5004 20 7.5ZM10.25 17.25V11.25L15.5 14.25L10.25 17.25ZM5 4.5H20V6H5V4.5ZM6.5 1.5H18.5V3H6.5V1.5Z"
fill="currentColor"
/>
</svg>
{t("content")}
</a>
</NavigationMenuTrigger>
<NavigationMenuContent className="flex flex-col place-content-start rounded-md overflow-hidden ">
<NavigationMenuLink
onClick={() => router.push(prefixPath + "/image/filter")}
className="flex place-items-start gap-1 px-3 py-1 w-36"
<div className="flex gap-4 items-center">
{/* Popover Utama */}
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<button className="dark:text-white text-black flex items-center gap-3 cursor-pointer bg-transparent border-none focus:outline-none">
<svg
className="mx-1"
width="25"
height="24"
viewBox="0 0 25 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<p className="text-slate-600 dark:text-white hover:text-[#bb3523] flex flex-row items-center py-1 cursor-pointer">
<FiImage className="mr-2" />
{t("image")}
</p>
</NavigationMenuLink>
<NavigationMenuLink
onClick={() => router.push(prefixPath + "/video/filter")}
className="flex items-start gap-1 py-1 px-3 "
<path
d="M20 7.5H5C4.6023 7.5004 4.221 7.65856 3.93978 7.93978C3.65856 8.221 3.5004 8.6023 3.5 9V19.5C3.5004 19.8977 3.65856 20.279 3.93978 20.5602C4.221 20.8414 4.6023 20.9996 5 21H20C20.3977 20.9996 20.779 20.8414 21.0602 20.5602C21.3414 20.279 21.4996 19.8977 21.5 19.5V9C21.4996 8.6023 21.3414 8.221 21.0602 7.93978C20.779 7.65856 20.3977 7.5004 20 7.5ZM10.25 17.25V11.25L15.5 14.25L10.25 17.25ZM5 4.5H20V6H5V4.5ZM6.5 1.5H18.5V3H6.5V1.5Z"
fill="currentColor"
/>
</svg>
<span>{t("content")}</span>
{open ? (
<ChevronUp className="w-4 h-4 transition-all" />
) : (
<ChevronDown className="w-4 h-4 transition-all" />
)}
</button>
</PopoverTrigger>
<PopoverContent className="flex flex-col gap-2 w-fit px-6 py-4 rounded-md shadow-md bg-white dark:bg-black">
<button
onClick={() => router.push(prefixPath + "/image/filter")}
className="flex items-center text-slate-600 dark:text-white hover:text-[#bb3523]"
>
<FiImage className="mr-2" />
{t("image")}
</button>
<button
onClick={() => router.push(prefixPath + "/video/filter")}
className="flex items-center text-slate-600 dark:text-white hover:text-[#bb3523]"
>
<FiYoutube className="mr-2" />
{t("video")}
</button>
<button
onClick={() => router.push(prefixPath + "/document/filter")}
className="flex items-center text-slate-600 dark:text-white hover:text-[#bb3523]"
>
<FiFile className="mr-2" />
{t("text")}
</button>
<button
onClick={() => router.push(prefixPath + "/audio/filter")}
className="flex items-center text-slate-600 dark:text-white hover:text-[#bb3523]"
>
<FiMusic className="mr-2" />
{t("audio")}
</button>
</PopoverContent>
</Popover>
{/* Link Schedule */}
<Link href={prefixPath + "/schedule"} passHref legacyBehavior>
<a className="group inline-flex h-10 items-center text-center justify-center rounded-md bg-[#f7f7f7] dark:bg-black xl:px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:outline-none">
<span>
<svg
className="mr-2"
width="25"
height="24"
viewBox="0 0 25 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
{pathname?.split("/")[1] == "in" ? (
<>
<p className="text-slate-600 text-left dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center py-1 cursor-pointer">
<FiYoutube className="mr-2" />
{t("video")}
</p>
</>
) : (
<>
<p className="text-slate-600 text-left dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center py-1 cursor-pointer">
<FiYoutube className="mr-2" />
{t("video")}
</p>
</>
)}
</NavigationMenuLink>
<NavigationMenuLink
onClick={() =>
router.push(prefixPath + "/document/filter")
}
className="flex place-items-start gap-1 py-1 px-3"
<path
d="M19.5 4H18.5V3C18.5 2.4 18.1 2 17.5 2C16.9 2 16.5 2.4 16.5 3V4H8.5V3C8.5 2.4 8.1 2 7.5 2C6.9 2 6.5 2.4 6.5 3V4H5.5C3.8 4 2.5 5.3 2.5 7V8H22.5V7C22.5 5.3 21.2 4 19.5 4ZM2.5 19C2.5 20.7 3.8 22 5.5 22H19.5C21.2 22 22.5 20.7 22.5 19V10H2.5V19ZM17.5 12C18.1 12 18.5 12.4 18.5 13C18.5 13.6 18.1 14 17.5 14C16.9 14 16.5 13.6 16.5 13C16.5 12.4 16.9 12 17.5 12ZM17.5 16C18.1 16 18.5 16.4 18.5 17C18.5 17.6 18.1 18 17.5 18C16.9 18 16.5 17.6 16.5 17C16.5 16.4 16.9 16 17.5 16ZM12.5 12C13.1 12 13.5 12.4 13.5 13C13.5 13.6 13.1 14 12.5 14C11.9 14 11.5 13.6 11.5 13C11.5 12.4 11.9 12 12.5 12ZM12.5 16C13.1 16 13.5 16.4 13.5 17C13.5 17.6 13.1 18 12.5 18C11.9 18 11.5 17.6 11.5 17C11.5 16.4 11.9 16 12.5 16ZM7.5 12C8.1 12 8.5 12.4 8.5 13C8.5 13.6 8.1 14 7.5 14C6.9 14 6.5 13.6 6.5 13C6.5 12.4 6.9 12 7.5 12ZM7.5 16C8.1 16 8.5 16.4 8.5 17C8.5 17.6 8.1 18 7.5 18C6.9 18 6.5 17.6 6.5 17C6.5 16.4 6.9 16 7.5 16Z"
fill="currentColor"
/>
</svg>
</span>
{t("schedule")}
</a>
</Link>
{/* Link Index */}
<Link href={prefixPath + "/indeks"} passHref legacyBehavior>
<a className="group inline-flex h-10 items-center justify-center text-center rounded-md bg-[#f7f7f7] dark:bg-black xl:px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:outline-none">
<span>
<svg
className="mr-2"
width="25"
height="24"
viewBox="0 0 25 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<p className="text-slate-600 text-left dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center py-1 cursor-pointer">
<FiFile className="mr-2" />
{t("text")}
</p>
</NavigationMenuLink>
<NavigationMenuLink
onClick={() => router.push(prefixPath + "/audio/filter")}
className="flex place-items-start gap-1 py-1 px-3 "
>
<p className="text-slate-600 text-left dark:text-white hover:text-[#bb3523] flex flex-row justify-center items-center py-1 cursor-pointer">
<FiMusic className="mr-2" />
{t("audio")}{" "}
</p>
</NavigationMenuLink>
</NavigationMenuContent>
</NavigationMenuItem>
<NavigationMenuItem>
<Link href={prefixPath + "/schedule"} legacyBehavior passHref>
<NavigationMenuLink className="group inline-flex h-10 w-max items-center justify-center rounded-md bg-[#f7f7f7] dark:bg-black xl:px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50">
<span>
<svg
className="mr-2"
width="25"
height="24"
viewBox="0 0 25 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19.5 4H18.5V3C18.5 2.4 18.1 2 17.5 2C16.9 2 16.5 2.4 16.5 3V4H8.5V3C8.5 2.4 8.1 2 7.5 2C6.9 2 6.5 2.4 6.5 3V4H5.5C3.8 4 2.5 5.3 2.5 7V8H22.5V7C22.5 5.3 21.2 4 19.5 4ZM2.5 19C2.5 20.7 3.8 22 5.5 22H19.5C21.2 22 22.5 20.7 22.5 19V10H2.5V19ZM17.5 12C18.1 12 18.5 12.4 18.5 13C18.5 13.6 18.1 14 17.5 14C16.9 14 16.5 13.6 16.5 13C16.5 12.4 16.9 12 17.5 12ZM17.5 16C18.1 16 18.5 16.4 18.5 17C18.5 17.6 18.1 18 17.5 18C16.9 18 16.5 17.6 16.5 17C16.5 16.4 16.9 16 17.5 16ZM12.5 12C13.1 12 13.5 12.4 13.5 13C13.5 13.6 13.1 14 12.5 14C11.9 14 11.5 13.6 11.5 13C11.5 12.4 11.9 12 12.5 12ZM12.5 16C13.1 16 13.5 16.4 13.5 17C13.5 17.6 13.1 18 12.5 18C11.9 18 11.5 17.6 11.5 17C11.5 16.4 11.9 16 12.5 16ZM7.5 12C8.1 12 8.5 12.4 8.5 13C8.5 13.6 8.1 14 7.5 14C6.9 14 6.5 13.6 6.5 13C6.5 12.4 6.9 12 7.5 12ZM7.5 16C8.1 16 8.5 16.4 8.5 17C8.5 17.6 8.1 18 7.5 18C6.9 18 6.5 17.6 6.5 17C6.5 16.4 6.9 16 7.5 16Z"
fill="currentColor"
/>
</svg>
</span>
{t("schedule")}
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
<NavigationMenuItem>
<Link href={prefixPath + "/indeks"} legacyBehavior passHref>
<NavigationMenuLink className="group inline-flex h-10 w-max items-center justify-center rounded-md bg-[#f7f7f7] dark:bg-black xl:px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50">
<span>
<svg
className="mr-2"
width="25"
height="24"
viewBox="0 0 25 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M2.5 6C2.5 5.20435 2.81607 4.44129 3.37868 3.87868C3.94129 3.31607 4.70435 3 5.5 3H19.5C20.2956 3 21.0587 3.31607 21.6213 3.87868C22.1839 4.44129 22.5 5.20435 22.5 6V18C22.5 18.7956 22.1839 19.5587 21.6213 20.1213C21.0587 20.6839 20.2956 21 19.5 21H5.5C4.70435 21 3.94129 20.6839 3.37868 20.1213C2.81607 19.5587 2.5 18.7956 2.5 18V6ZM7.5 7C7.23478 7 6.98043 7.10536 6.79289 7.29289C6.60536 7.48043 6.5 7.73478 6.5 8V12C6.5 12.2652 6.60536 12.5196 6.79289 12.7071C6.98043 12.8946 7.23478 13 7.5 13H11.5C11.7652 13 12.0196 12.8946 12.2071 12.7071C12.3946 12.5196 12.5 12.2652 12.5 12V8C12.5 7.73478 12.3946 7.48043 12.2071 7.29289C12.0196 7.10536 11.7652 7 11.5 7H7.5ZM8.5 11V9H10.5V11H8.5ZM15.5 7C15.2348 7 14.9804 7.10536 14.7929 7.29289C14.6054 7.48043 14.5 7.73478 14.5 8C14.5 8.26522 14.6054 8.51957 14.7929 8.70711C14.9804 8.89464 15.2348 9 15.5 9H17.5C17.7652 9 18.0196 8.89464 18.2071 8.70711C18.3946 8.51957 18.5 8.26522 18.5 8C18.5 7.73478 18.3946 7.48043 18.2071 7.29289C18.0196 7.10536 17.7652 7 17.5 7H15.5ZM15.5 11C15.2348 11 14.9804 11.1054 14.7929 11.2929C14.6054 11.4804 14.5 11.7348 14.5 12C14.5 12.2652 14.6054 12.5196 14.7929 12.7071C14.9804 12.8946 15.2348 13 15.5 13H17.5C17.7652 13 18.0196 12.8946 18.2071 12.7071C18.3946 12.5196 18.5 12.2652 18.5 12C18.5 11.7348 18.3946 11.4804 18.2071 11.2929C18.0196 11.1054 17.7652 11 17.5 11H15.5ZM7.5 15C7.23478 15 6.98043 15.1054 6.79289 15.2929C6.60536 15.4804 6.5 15.7348 6.5 16C6.5 16.2652 6.60536 16.5196 6.79289 16.7071C6.98043 16.8946 7.23478 17 7.5 17H17.5C17.7652 17 18.0196 16.8946 18.2071 16.7071C18.3946 16.5196 18.5 16.2652 18.5 16C18.5 15.7348 18.3946 15.4804 18.2071 15.2929C18.0196 15.1054 17.7652 15 17.5 15H7.5Z"
fill="currentColor"
/>
</svg>
</span>
{t("index")}
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M2.5 6C2.5 5.20435 2.81607 4.44129 3.37868 3.87868C3.94129 3.31607 4.70435 3 5.5 3H19.5C20.2956 3 21.0587 3.31607 21.6213 3.87868C22.1839 4.44129 22.5 5.20435 22.5 6V18C22.5 18.7956 22.1839 19.5587 21.6213 20.1213C21.0587 20.6839 20.2956 21 19.5 21H5.5C4.70435 21 3.94129 20.6839 3.37868 20.1213C2.81607 19.5587 2.5 18.7956 2.5 18V6ZM7.5 7C7.23478 7 6.98043 7.10536 6.79289 7.29289C6.60536 7.48043 6.5 7.73478 6.5 8V12C6.5 12.2652 6.60536 12.5196 6.79289 12.7071C6.98043 12.8946 7.23478 13 7.5 13H11.5C11.7652 13 12.0196 12.8946 12.2071 12.7071C12.3946 12.5196 12.5 12.2652 12.5 12V8C12.5 7.73478 12.3946 7.48043 12.2071 7.29289C12.0196 7.10536 11.7652 7 11.5 7H7.5ZM8.5 11V9H10.5V11H8.5ZM15.5 7C15.2348 7 14.9804 7.10536 14.7929 7.29289C14.6054 7.48043 14.5 7.73478 14.5 8C14.5 8.26522 14.6054 8.51957 14.7929 8.70711C14.9804 8.89464 15.2348 9 15.5 9H17.5C17.7652 9 18.0196 8.89464 18.2071 8.70711C18.3946 8.51957 18.5 8.26522 18.5 8C18.5 7.73478 18.3946 7.48043 18.2071 7.29289C18.0196 7.10536 17.7652 7 17.5 7H15.5ZM15.5 11C15.2348 11 14.9804 11.1054 14.7929 11.2929C14.6054 11.4804 14.5 11.7348 14.5 12C14.5 12.2652 14.6054 12.5196 14.7929 12.7071C14.9804 12.8946 15.2348 13 15.5 13H17.5C17.7652 13 18.0196 12.8946 18.2071 12.7071C18.3946 12.5196 18.5 12.2652 18.5 12C18.5 11.7348 18.3946 11.4804 18.2071 11.2929C18.0196 11.1054 17.7652 11 17.5 11H15.5ZM7.5 15C7.23478 15 6.98043 15.1054 6.79289 15.2929C6.60536 15.4804 6.5 15.7348 6.5 16C6.5 16.2652 6.60536 16.5196 6.79289 16.7071C6.98043 16.8946 7.23478 17 7.5 17H17.5C17.7652 17 18.0196 16.8946 18.2071 16.7071C18.3946 16.5196 18.5 16.2652 18.5 16C18.5 15.7348 18.3946 15.4804 18.2071 15.2929C18.0196 15.1054 17.7652 15 17.5 15H7.5Z"
fill="currentColor"
/>
</svg>
</span>
{t("index")}
</a>
</Link>
</div>
</div>
</div>
{/* Mobile Menu Toggle */}
{/* Desktop Navigation */}
<div className="hidden custom-lg-button:flex lg:flex items-center xl:gap-5">
{roleId == undefined ? (
@ -399,7 +384,7 @@ const Navbar = () => {
</div>
{/* Languange */}
<div className="hidden custom-lg-button:flex relative text-left">
<div className={`${isHidden ? "hidden" : "custom-lg-button:flex"} relative text-left`}>
<LocalSwitcher />
</div>
@ -1030,7 +1015,7 @@ const Navbar = () => {
{/* Mobile Menu */}
{menuOpen && (
<div className="custom-lg-button:hidden absolute bg-[#f7f7f7] dark:bg-slate-600 px-4 py-3 w-full space-y-3 z-50 ">
<div className="flex flex-row pl-5 ">
<div className="flex flex-col gap-2">
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
@ -1242,13 +1227,13 @@ const Navbar = () => {
</div>
</div>
<div className=" py-1 flex items-center mx-3">
<div className="py-1 flex items-center mx-3">
<input
value={onSearch}
onChange={(e) => setOnSearch(e.target.value)}
type="text"
placeholder="Search..."
className="pl-8 pr-4 py-1 w-28 text-[13px] border rounded-full focus:outline-none dark:text-white"
className="pl-8 pr-4 py-1 text-[13px] border rounded-full focus:outline-none dark:text-white w-full "
/>{" "}
</div>
@ -1793,7 +1778,7 @@ const Navbar = () => {
<div className="flex justify-center items-center mx-3 gap-1 lg:gap-1 xl:gap-5">
<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"
className="w-[50%] px-4 py-1 bg-[#bb3523] text-white font-semibold rounded-md hover:bg-red-700 text-center"
>
{t("logIn")}
</Link>
@ -1801,7 +1786,7 @@ const Navbar = () => {
<DialogTrigger asChild>
<Button
size="sm"
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"
className="w-[50%] px-4 h-8 border bg-white border-[#bb3523] text-[#bb3523] font-semibold rounded-md hover:bg-[#bb3523] hover:text-white"
>
{t("register")}
</Button>

View File

@ -243,20 +243,29 @@ const NewContent = (props: { group: string; type: string }) => {
</div>
{/* Caption section */}
<div className="p-4">
<p className="text-[12px] font-bold text-[#bb3523] uppercase mb-1">
{image?.categoryName?.toUpperCase() ??
"Giat Pimpinan"}
</p>
<p className="text-lg font-semibold text-black truncate">
{image?.title}
</p>
{/* <p className="flex flex-row items-center text-[10px] gap-1 mt-2 text-gray-600">
{formatDateToIndonesian(new Date(image?.createdAt))} {image?.timezone ?? "WIB"} |
<Icon icon="formkit:eye" width="15" height="15" /> {image.clickCount}
</p> */}
<div className="p-4 h-full flex flex-col justify-between">
<div className="flex flex-col gap-1 flex-grow">
<p className="text-[10px] font-bold text-[#bb3523] uppercase">
{image?.categoryName?.toUpperCase() ??
"Giat Pimpinan"}
</p>
<p
className="
text-sm lg:text-base font-semibold text-black
line-clamp-4 /* LIMIT to 2 lines if plugin available */
/* or use min-h-[3rem] as fallback */
"
>
{image?.title}
</p>
</div>
{/* Optional metadata area (uncomment if needed) */}
{/*
<p className="flex flex-row items-center text-[10px] gap-1 mt-2 text-gray-600">
{formatDateToIndonesian(new Date(image?.createdAt))} {image?.timezone ?? "WIB"} |
<Icon icon="formkit:eye" width="15" height="15" /> {image.clickCount}
</p>
*/}
</div>
</div>
</CarouselItem>
@ -329,7 +338,7 @@ const NewContent = (props: { group: string; type: string }) => {
"GIAT PIMPINAN"}
</p>
<p className="text-xl font-semibold text-black truncate">
<p className="text-xl font-semibold text-black line-clamp-4">
{audio?.title}
</p>
@ -427,19 +436,24 @@ const NewContent = (props: { group: string; type: string }) => {
</div>
{/* Caption section */}
<div className="p-4">
<p className="text-[12px] font-bold text-[#bb3523] uppercase mb-1">
{video?.categoryName?.toUpperCase() ?? "Video"}
</p>
<p className="text-lg font-semibold text-black truncate">
{video?.title}
</p>
{/* <p className="flex flex-row items-center text-[10px] gap-1 mt-2 text-gray-600">
{formatDateToIndonesian(new Date(video?.createdAt))} {video?.timezone ?? "WIB"} |
<Icon icon="formkit:eye" width="15" height="15" /> {video?.clickCount}
</p> */}
<div className="p-4 h-full flex flex-col justify-between">
<div className="flex flex-col gap-1 flex-grow">
<p className="text-[10px] font-bold text-[#bb3523] uppercase">
{video?.categoryName?.toUpperCase() ??
"Giat Pimpinan"}
</p>
<p
className="text-sm lg:text-base font-semibold text-black line-clamp-5">
{video?.title}
</p>
</div>
{/* Optional metadata area (uncomment if needed) */}
{/*
<p className="flex flex-row items-center text-[10px] gap-1 mt-2 text-gray-600">
{formatDateToIndonesian(new Date(image?.createdAt))} {image?.timezone ?? "WIB"} |
<Icon icon="formkit:eye" width="15" height="15" /> {image.clickCount}
</p>
*/}
</div>
</div>
</CarouselItem>
@ -513,7 +527,7 @@ const NewContent = (props: { group: string; type: string }) => {
</div>
{/* Judul */}
<div className="font-semibold text-gray-900 text-xl leading-snug line-clamp-2">
<div className="font-semibold text-gray-900 text-xl leading-snug line-clamp-4">
{text?.title}
</div>

View File

@ -69,6 +69,7 @@ const ScrollableContent = () => {
setContentPolda(data);
};
const initFetchSatker = async () => {
const response = await getHeroData(locale == "en", "satker");
console.log(response);
@ -79,8 +80,8 @@ const ScrollableContent = () => {
return (
<>
<div className="">
<h1 className="text-2xl md:text-3xl font-bold text-gray-800 dark:text-white">
<div className="w-full">
<h1 className="text-[18px] md:text-3xl font-bold text-gray-800 dark:text-white">
<span className="text-[#c03724] dark:text-white">
{t("exploration")}&nbsp;{t("and")}
</span>
@ -151,7 +152,7 @@ const ScrollableContent = () => {
onClick={() =>
router.push(`/${contentType}/filter?title=${search}`)
}
className="flex justify-center items-center px-6 w-full lg:w-[20%] py-4 bg-[#bb3523] gap-2 text-white rounded-lg hover:bg-red-700"
className="flex justify-center items-center px-6 w-full lg:w-[20%] py-4 bg-[#bb3523] gap-2 text-white rounded-lg hover:bg-red-700 text-[14px]"
>
{t("searchCoverage")}
<Icon icon="ri:arrow-right-s-line" fontSize={20} />
@ -160,15 +161,18 @@ const ScrollableContent = () => {
</div>
<div className="flex flex-col md:flex-row gap-6 py-8">
{/* Berita Polda */}
<div className="w-full md:w-1/2 px-4">
<h2 className="text-lg md:text-xl font-bold text-[#bb3523] mb-2 uppercase">
{t("regionNews")}
</h2>
<div className="w-[10%] h-1 bg-[#bb3523]"></div>
<div className="w-full h-1 bg-[#bb3523] mx-auto mb-4"></div>
<div className="grid gap-4">
{(seeAllValuePolda ? contentPolda : contentPolda?.slice(0, 3))?.map(
(item: any, index: number) => (
{locale === "in" && (
<div className="w-full md:w-1/2 px-0 lg:px-4">
<h2 className="text-lg md:text-xl font-bold text-[#bb3523] mb-2 uppercase">
{t("regionNews")}
</h2>
<div className="w-[10%] h-1 bg-[#bb3523]"></div>
<div className="w-full h-1 bg-[#bb3523] mx-auto mb-4"></div>
<div className="grid gap-4">
{(seeAllValuePolda
? contentPolda
: contentPolda?.slice(0, 3)
)?.map((item: any, index: number) => (
<div
key={index}
className={`bg-white rounded-lg shadow-md overflow-hidden ${
@ -215,17 +219,6 @@ const ScrollableContent = () => {
</div>
</Link>
</div>
{/* <Link
href={
Number(item?.fileTypeId) == 1
? `${prefixPath}/image/detail/${item?.slug}`
: Number(item?.fileTypeId) == 2
? `${prefixPath}/video/detail/${item?.slug}`
: Number(item?.fileTypeId) == 3
? `${prefixPath}/document/detail/${item?.slug}`
: `${prefixPath}/audio/detail/${item?.slug}`
}
> */}
<Link
href={
Number(item?.fileTypeId) == 1
@ -240,63 +233,73 @@ const ScrollableContent = () => {
index === 0 ? "p-4" : "p-3 w-[50%] cursor-pointer"
}`}
>
<p className="text-sm text-[#bb3523] font-bold mb-1">
<p className="text-sm text-[#bb3523] font-bold mb-1 px-2">
{item.categoryName}
</p>
<h3 className="text-sm font-semibold text-gray-800">
<h3 className="text-sm font-semibold text-gray-800 px-2">
{item.title}
</h3>
<p className="text-xs text-gray-500 mt-1 truncate">
<p className="text-xs text-gray-500 mt-1 truncate px-2">
{htmlToString(item.description)}
</p>
</Link>
</div>
)
)}
{contentPolda?.length > 3 && (
<div className="flex items-center flex-row justify-start mt-6">
<Button
onClick={() => setSeeAllValuePolda(!seeAllValuePolda)}
className="bg-white hover:bg-[#bb3523] text-[#bb3523] hover:text-white border-2 border-[#bb3523]"
>
{seeAllValuePolda ? t("seeLess") : t("seeMore")}{" "}
<span className="text-[#bb3523] hover:text-white">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2.5"
d="m10 17l5-5m0 0l-5-5"
/>
</svg>
</span>
</Button>
</div>
)}
))}
{contentPolda?.length > 3 && (
<div className="flex items-center flex-row justify-start mt-6">
<Button
onClick={() => setSeeAllValuePolda(!seeAllValuePolda)}
className="bg-white hover:bg-[#bb3523] text-[#bb3523] hover:text-white border-2 border-[#bb3523]"
>
{seeAllValuePolda ? t("seeLess") : t("seeMore")}{" "}
<span className="text-[#bb3523] hover:text-white">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2.5"
d="m10 17l5-5m0 0l-5-5"
/>
</svg>
</span>
</Button>
</div>
)}
</div>
</div>
</div>
)}
{/* Berita SATKER */}
<div className="w-full md:w-1/2 px-4">
<h2 className="text-lg md:text-xl font-bold mb-2 text-[#bb3523] uppercase">
{t("divisionNews")}
</h2>
<div className="w-[10%] h-1 bg-[#bb3523]"></div>
<div className="w-full h-1 bg-[#bb3523] mx-auto mb-4"></div>
<div className="grid gap-4">
{/* {seeAllValue ? content : content
?.filter((item: any) => item.isPublishOnPolda === true)
.slice(0, 3)
.map((item: any, index: number) => (
<div key={index} className={`bg-white rounded-lg shadow-md overflow-hidden ${index === 0 ? "" : "flex"}`}>
<div className={`relative ${index === 0 ? "w-full h-48" : " w-1/2 h-[150px]"}`}>
{locale === "in" && (
<div className="w-full md:w-1/2 px-0 lg:px-4">
<h2 className="text-lg md:text-xl font-bold mb-2 text-[#bb3523] uppercase">
{t("divisionNews")}
</h2>
<div className="w-[10%] h-1 bg-[#bb3523]"></div>
<div className="w-full h-1 bg-[#bb3523] mx-auto mb-4"></div>
<div className="grid gap-4">
{(seeAllValueSatker
? contentSatker
: contentSatker?.slice(0, 3)
)?.map((item: any, index: number) => (
<div
key={index}
className={`bg-white rounded-lg shadow-md overflow-hidden ${
index === 0 ? "" : "flex"
}`}
>
<div
className={`relative ${
index === 0 ? "w-full h-48" : " w-1/2 h-[150px]"
}`}
>
<Link
href={
Number(item?.fileTypeId) == 1
@ -308,9 +311,19 @@ const ScrollableContent = () => {
: `${prefixPath}/audio/detail/${item?.slug}`
}
>
<Image src={item.thumbnailLink} alt={item.title} layout="fill" objectFit="cover" />
<Image
src={item?.smallThumbnailLink}
alt={item?.title}
layout="fill"
objectFit="cover"
/>
<div className="absolute top-2 right-2 bg-[#c03724] rounded-full p-1 shadow">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<g fill="none">
<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
@ -322,36 +335,6 @@ const ScrollableContent = () => {
</div>
</Link>
</div>
<div className={`${index === 0 ? "p-4" : "p-3 w-[50%]"}`}>
<p className="text-sm text-[#bb3523] font-bold mb-1">{item.categoryName}</p>
<h3 className="text-sm font-semibold text-gray-800">{item.title}</h3>
<p className="text-xs text-gray-500 mt-1 truncate">{htmlToString(item.description)}</p>
</div>
</div>
))} */}
{/* <button className="w-fit px-2 mt-2 py-2 border flex flex-row gap-2 border-[#bb3523] bg-white text-[#bb3523] rounded-lg font-semibold hover:bg-[#bb3523] hover:text-white transition">
LEBIH SEDIKIT
<span className="text-[#bb3523] hover:text-white">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2.5" d="m10 17l5-5m0 0l-5-5" />
</svg>
</span>
</button> */}
{(seeAllValueSatker
? contentSatker
: contentSatker?.slice(0, 3)
)?.map((item: any, index: number) => (
<div
key={index}
className={`bg-white rounded-lg shadow-md overflow-hidden ${
index === 0 ? "" : "flex"
}`}
>
<div
className={`relative ${
index === 0 ? "w-full h-48" : " w-1/2 h-[150px]"
}`}
>
<Link
href={
Number(item?.fileTypeId) == 1
@ -362,96 +345,51 @@ const ScrollableContent = () => {
? `${prefixPath}/document/detail/${item?.slug}`
: `${prefixPath}/audio/detail/${item?.slug}`
}
className={`${index === 0 ? "p-4" : "p-3 w-[50%]"}`}
>
<Image
src={item?.smallThumbnailLink}
alt={item?.title}
layout="fill"
objectFit="cover"
/>
<div className="absolute top-2 right-2 bg-[#c03724] rounded-full p-1 shadow">
<p className="text-sm text-[#bb3523] font-bold mb-1 px-2">
{item.categoryName}
</p>
<h3 className="text-sm font-semibold text-gray-800 px-2">
{item.title}
</h3>
<p className="text-xs text-gray-500 mt-1 truncate px-2">
{htmlToString(item.description)}
</p>
</Link>
</div>
))}
{/* Tombol See More / See Less */}
{contentSatker?.length > 3 && (
<div className="flex items-center flex-row justify-start mt-6">
<Button
onClick={() => setSeeAllValueSatker(!seeAllValueSatker)}
className="bg-white hover:bg-[#bb3523] text-[#bb3523] hover:text-white border-2 border-[#bb3523]"
>
{seeAllValueSatker ? t("seeLess") : t("seeMore")}{" "}
<span className="text-[#bb3523] hover:text-white">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<g fill="none">
<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="white"
d="M20 6a2 2 0 0 1 2 2v11.333a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2zm-8.268 7.944L7.136 18.54l-.066.06l-.07.054v.68h13v-.68l-.07-.053l-.066-.06l-2.24-2.24l-.353.354l.055.055a1 1 0 0 1-1.32 1.497l-.094-.083zM17 3a2 2 0 0 1 1.995 1.85L19 5H5a1 1 0 0 0-.993.883L4 6v12a2 2 0 0 1-1.995-1.85L2 16V6a3 3 0 0 1 2.824-2.995L5 3zm3 5H7v7.848L10.848 12a1.25 1.25 0 0 1 1.768 0l3.241 3.24l.884-.883a1.25 1.25 0 0 1 1.768 0L20 15.848zm-3.5 1.5a1.5 1.5 0 1 1 0 3a1.5 1.5 0 0 1 0-3"
/>
</g>
<path
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2.5"
d="m10 17l5-5m0 0l-5-5"
/>
</svg>
</div>
</Link>
</span>
</Button>
</div>
{/* <Link
href={
Number(item?.fileTypeId) == 1
? `${prefixPath}/image/detail/${item?.slug}`
: Number(item?.fileTypeId) == 2
? `${prefixPath}/video/detail/${item?.slug}`
: Number(item?.fileTypeId) == 3
? `${prefixPath}/document/detail/${item?.slug}`
: `${prefixPath}/audio/detail/${item?.slug}`
}
> */}
<Link
href={
Number(item?.fileTypeId) == 1
? `${prefixPath}/image/detail/${item?.slug}`
: Number(item?.fileTypeId) == 2
? `${prefixPath}/video/detail/${item?.slug}`
: Number(item?.fileTypeId) == 3
? `${prefixPath}/document/detail/${item?.slug}`
: `${prefixPath}/audio/detail/${item?.slug}`
}
className={`${index === 0 ? "p-4" : "p-3 w-[50%]"}`}
>
<p className="text-sm text-[#bb3523] font-bold mb-1">
{item.categoryName}
</p>
<h3 className="text-sm font-semibold text-gray-800">
{item.title}
</h3>
<p className="text-xs text-gray-500 mt-1 truncate">
{htmlToString(item.description)}
</p>
</Link>
</div>
))}
{/* Tombol See More / See Less */}
{contentSatker?.length > 3 && (
<div className="flex items-center flex-row justify-start mt-6">
<Button
onClick={() => setSeeAllValueSatker(!seeAllValueSatker)}
className="bg-white hover:bg-[#bb3523] text-[#bb3523] hover:text-white border-2 border-[#bb3523]"
>
{seeAllValueSatker ? t("seeLess") : t("seeMore")}{" "}
<span className="text-[#bb3523] hover:text-white">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2.5"
d="m10 17l5-5m0 0l-5-5"
/>
</svg>
</span>
</Button>
</div>
)}
)}
</div>
</div>
</div>
)}
</div>
</>
);

View File

@ -281,17 +281,17 @@ const LoginForm = () => {
return false;
}
const msg = response?.data?.message;
// onSubmit(data);
onSubmit(data);
if (msg == "Continue to setup email") {
setStep(2);
} else if (msg == "Email is valid and OTP has been sent") {
setStep(3);
} else if (msg == "Username & password valid") {
onSubmit(data);
} else {
error("Username / password incorrect");
}
// if (msg == "Continue to setup email") {
// setStep(2);
// } else if (msg == "Email is valid and OTP has been sent") {
// setStep(3);
// } else if (msg == "Username & password valid") {
// onSubmit(data);
// } else {
// error("Username / password incorrect");
// }
// else {
// setStep(1);
// }

View File

@ -71,22 +71,22 @@ export default function DashboardVisualization() {
setIsInternational(updatedIsInternational);
};
useEffect(() => {
async function fetchUrl() {
console.log("Fetch tableau");
const urlView = `${url + ticket1}/${view1}${param}`;
console.log("Fetch tableau ", urlView);
const urlRender = await fetch(urlView)
.then((response) => {
console.log("Tableau res : ", response);
})
.catch((error) => {
console.log("Tableau error: ", error);
});
}
// useEffect(() => {
// async function fetchUrl() {
// console.log("Fetch tableau");
// const urlView = `${url + ticket1}/${view1}${param}`;
// console.log("Fetch tableau ", urlView);
// const urlRender = await fetch(urlView)
// .then((response) => {
// console.log("Tableau res : ", response);
// })
// .catch((error) => {
// console.log("Tableau error: ", error);
// });
// }
fetchUrl();
}, [ticket1]);
// fetchUrl();
// }, [ticket1]);
return (
<div className="flex flex-col gap-2 bg-white rounded-lg p-3">

View File

@ -173,10 +173,10 @@ export async function createMedia(data: any) {
export async function uploadThumbnail(id: any, data: any) {
const url = `media/upload?id=${id}&operation=thumbnail`;
// const headers = {
// "Content-Type": "multipart/form-data",
// };
return httpPostInterceptor(url, data);
const headers = {
"Content-Type": "multipart/form-data",
};
return httpPostInterceptor(url, data, headers);
}
export async function detailSPIT(id: any) {

View File

@ -31,9 +31,15 @@ export async function getCsrfToken() {
// }
}
export async function getHeroData(isInt: Boolean = false, unitType= "") {
export async function getHeroData(isInt: Boolean = false, unitType = "") {
return await httpGetInterceptor(
`media/public/list?enablePage=1&sort=desc&sortBy=createdAt&size=5&page=0&typeId=1&title=&categoryId=&fileFormats=&tags=&group=&startDate=&endDate=&month=&year=&isInt=${isInt}${unitType==""?"":unitType=='polda'?'&isAllPolda=true':'&isAllSatker=true'}`
`media/public/list?enablePage=1&sort=desc&sortBy=createdAt&size=5&page=0&typeId=1&title=&categoryId=&fileFormats=&tags=&group=&startDate=&endDate=&month=&year=&isInt=${isInt}${
unitType == ""
? ""
: unitType == "polda"
? "&isAllPolda=true"
: "&isAllSatker=true"
}`
);
}
@ -41,7 +47,7 @@ export async function listStaticBanner(
group: any = "",
isInt: Boolean = false
) {
const url = `media/static-banner?group=${group}&isInt=${isInt}`;
const url = `media/public/banner?group=${group}&isInt=${isInt}`;
return httpGetInterceptor(url);
}
@ -53,10 +59,13 @@ export async function listPopUp(group: any = "", isInt: Boolean = false) {
export async function getPublicCategoryData(
group: any = "",
type: string = "",
isInt: Boolean = false
isInt: boolean = false,
page: number = 1
) {
return await httpGetInterceptor(
`media/categories/list/publish?enablePage=0&group=${group}&type=${type}&isInt=${isInt}`
`media/categories/list/publish?enablePage=1&size=12&sort=desc&sortBy=createdAt&page=${
page - 1
}&group=${group}&type=${type}&isInt=${isInt}`
);
}

View File

@ -23,3 +23,8 @@ export async function deleteKnowledgeBase(id: any) {
const url = `knowledge-base?id=${id}`;
return httpDeleteInterceptor(url);
}
export async function saveKnowledgeBase(data: any) {
const url = "knowledge-base";
return httpPostInterceptor(url, data);
}

View File

@ -11,7 +11,11 @@ export const generateLocalizedPath = (href: string, locale: string): string => {
return `/${locale}${href}`;
};
export function textEllipsis(str: string, maxLength: number, { side = "end", ellipsis = "..." } = {}) {
export function textEllipsis(
str: string,
maxLength: number,
{ side = "end", ellipsis = "..." } = {}
) {
if (str !== undefined && str?.length > maxLength) {
switch (side) {
case "start":
@ -78,7 +82,10 @@ export function getOnlyMonthAndYear(d: Date) {
export function getPublicLocaleTimestamp(d: any) {
const pad = (n: any, s = 2) => `${new Array(s).fill(0)}${n}`.slice(-s);
return `${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${pad(d.getFullYear(), 4)} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
return `${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${pad(
d.getFullYear(),
4
)} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
}
export function capitalize(s: any) {
@ -91,9 +98,13 @@ export function capitalize(s: any) {
}
export function getLocaleTimestamp(d: Date): string {
const pad = (n: number, s: number = 2): string => `${new Array(s).fill(0)}${n}`.slice(-s);
const pad = (n: number, s: number = 2): string =>
`${new Array(s).fill(0)}${n}`.slice(-s);
return `${pad(d.getDate())}-${pad(d.getMonth() + 1)}-${pad(d.getFullYear(), 4)} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
return `${pad(d.getDate())}-${pad(d.getMonth() + 1)}-${pad(
d.getFullYear(),
4
)} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
}
export function getLocaleTime(d: Date) {
@ -102,7 +113,9 @@ export function getLocaleTime(d: Date) {
}
export function getTimestamp(d: Date) {
const pad = (n: any, s = 2) => `${new Array(s).fill(0)}${n}`.slice(-s);
return `${pad(d.getFullYear(), 4)}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
return `${pad(d.getFullYear(), 4)}-${pad(d.getMonth() + 1)}-${pad(
d.getDate()
)} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
}
export function secondToTimes(sec: number) {
@ -116,7 +129,10 @@ export function secondToTimes(sec: number) {
export function checkMaliciousText(str: any) {
try {
const urlPattern = new RegExp("(https?:\\/\\/(?:www\\.|(?!www))[^\\s\\.]+\\.[^\\s]{2,}|www\\.[^\\s]+\\.[^\\s]{2,}|https?:\\/\\/[^\\s]+|\\b(?:https?|ftp):\\/\\/[^\\s/$.?#].[^\\s]*)", "gi");
const urlPattern = new RegExp(
"(https?:\\/\\/(?:www\\.|(?!www))[^\\s\\.]+\\.[^\\s]{2,}|www\\.[^\\s]+\\.[^\\s]{2,}|https?:\\/\\/[^\\s]+|\\b(?:https?|ftp):\\/\\/[^\\s/$.?#].[^\\s]*)",
"gi"
);
const isContainUrl = urlPattern.test(str);
if (isContainUrl) {
return "Message mengandung URL yang tidak diizinkan";
@ -155,20 +171,26 @@ export const shimmer = (w: number, h: number) => `
<animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
</svg>`;
export const toBase64 = (str: string) => (typeof window === "undefined" ? Buffer.from(str).toString("base64") : window.btoa(str));
export const toBase64 = (str: string) =>
typeof window === "undefined"
? Buffer.from(str).toString("base64")
: window.btoa(str);
const LoadScript = () => {
useEffect(() => {
const script = document.createElement("script");
script.src = "https://cdn.userway.org/widget.js";
script.setAttribute("data-account", "X36s1DpjqB");
script.setAttribute("data-position", "5");
script.async = true;
document.head.appendChild(script);
return () => {
// Cleanup if needed
document.head.removeChild(script);
};
}, []);
return null; // Tidak perlu merender apa-apa
return null;
};
export default LoadScript;