From 15a395ccd20b234a31e4d855c60718377d8c6f34 Mon Sep 17 00:00:00 2001 From: Sabda Yagra Date: Thu, 13 Nov 2025 17:46:23 +0700 Subject: [PATCH] fix: redirect /in and double // --- .../content/satker/components/columns.tsx | 422 +++++++++++++ .../satker/components/table-satker.tsx | 579 ++++++++++++++++++ .../content/satker/create/page.tsx | 15 + .../content/satker/detail/[id]/page.tsx | 16 + .../contributor/content/satker/layout.tsx | 11 + .../contributor/content/satker/page.tsx | 88 +++ .../content/satker/update-seo/[id]/page.tsx | 18 + .../content/satker/update/[id]/page.tsx | 17 + components/landing-page/navbar.tsx | 17 +- lib/menus.ts | 14 +- middleware.ts | 72 ++- service/content/content.ts | 33 + 12 files changed, 1276 insertions(+), 26 deletions(-) create mode 100644 app/[locale]/(protected)/contributor/content/satker/components/columns.tsx create mode 100644 app/[locale]/(protected)/contributor/content/satker/components/table-satker.tsx create mode 100644 app/[locale]/(protected)/contributor/content/satker/create/page.tsx create mode 100644 app/[locale]/(protected)/contributor/content/satker/detail/[id]/page.tsx create mode 100644 app/[locale]/(protected)/contributor/content/satker/layout.tsx create mode 100644 app/[locale]/(protected)/contributor/content/satker/page.tsx create mode 100644 app/[locale]/(protected)/contributor/content/satker/update-seo/[id]/page.tsx create mode 100644 app/[locale]/(protected)/contributor/content/satker/update/[id]/page.tsx diff --git a/app/[locale]/(protected)/contributor/content/satker/components/columns.tsx b/app/[locale]/(protected)/contributor/content/satker/components/columns.tsx new file mode 100644 index 00000000..436f8974 --- /dev/null +++ b/app/[locale]/(protected)/contributor/content/satker/components/columns.tsx @@ -0,0 +1,422 @@ +import * as React from "react"; +import { ColumnDef } from "@tanstack/react-table"; + +import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react"; +import { cn, getCookiesDecrypt } from "@/lib/utils"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuTrigger, + DropdownMenuItem, +} from "@/components/ui/dropdown-menu"; +import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/ui/badge"; +import { format } from "date-fns"; +import { Link } from "@/components/navigation"; +import { deleteMedia } from "@/service/content/content"; +import Swal from "sweetalert2"; +import withReactContent from "sweetalert2-react-content"; +import { error } from "@/lib/swal"; +import { useTranslations } from "next-intl"; +import { useRouter } from "@/i18n/routing"; + +const useTableColumns = () => { + const t = useTranslations("Table"); + const MySwal = withReactContent(Swal); + const userLevelId = getCookiesDecrypt("ulie"); + + const columns: ColumnDef[] = [ + { + accessorKey: "no", + header: t("no", { defaultValue: "No" }), + cell: ({ row }) => ( +
+
+

+ {row.getValue("no")} +

+
+
+ ), + }, + { + accessorKey: "title", + header: t("title", { defaultValue: "Title" }), + cell: ({ row }: { row: { getValue: (key: string) => string } }) => { + const title: string = row.getValue("title"); + return ( + + {title.length > 50 ? `${title.slice(0, 30)}...` : title} + + ); + }, + }, + { + accessorKey: "categoryName", + header: t("category-name", { defaultValue: "Category Name" }), + cell: ({ row }) => ( + + {row.getValue("categoryName")} + + ), + }, + { + accessorKey: "createdAt", + header: t("upload-date", { defaultValue: "Upload Date" }), + cell: ({ row }) => { + const createdAt = row.getValue("createdAt") as + | string + | number + | undefined; + + const formattedDate = + createdAt && !isNaN(new Date(createdAt).getTime()) + ? format(new Date(createdAt), "dd-MM-yyyy HH:mm:ss") + : "-"; + return {formattedDate}; + }, + }, + { + accessorKey: "creatorName", + header: t("creator-group", { defaultValue: "Creator Group" }), + cell: ({ row }) => ( + {row.getValue("creatorName")} + ), + }, + { + accessorKey: "creatorGroupLevelName", + header: t("source", { defaultValue: "Source" }), + cell: ({ row }) => ( + + {row.getValue("creatorGroupLevelName")} + + ), + }, + { + accessorKey: "publishedOn", + header: t("published", { defaultValue: "Published" }), + cell: ({ row }) => { + const isPublish = row.original.isPublish; + const isPublishOnPolda = row.original.isPublishOnPolda; + const creatorGroupParentLevelId = + row.original.creatorGroupParentLevelId; + + let displayText = "-"; + if (isPublish && !isPublishOnPolda) { + displayText = "Mabes"; + } else if (isPublish && isPublishOnPolda) { + if (Number(creatorGroupParentLevelId) == 761) { + displayText = "Mabes & Satker"; + } else { + displayText = "Mabes & Polda"; + } + } else if (!isPublish && isPublishOnPolda) { + if (Number(creatorGroupParentLevelId) == 761) { + displayText = "Satker"; + } else { + displayText = "Polda"; + } + } + + return ( +
+ {displayText} +
+ ); + }, + }, + + { + accessorKey: "statusName", + header: "Status", + cell: ({ row }) => { + const statusColors: Record = { + diterima: "bg-green-100 text-green-600", + "menunggu review": "bg-orange-100 text-orange-600", + }; + + const colors = [ + "bg-orange-100 text-orange-600", + "bg-orange-100 text-orange-600", + "bg-green-100 text-green-600", + "bg-blue-100 text-blue-600", + "bg-red-200 text-red-600", + ]; + + const status = + Number(row.original?.statusId) == 2 && + row.original?.reviewedAtLevel !== null && + !row.original?.reviewedAtLevel?.includes(`:${userLevelId}:`) && + Number(row.original?.creatorGroupLevelId) != Number(userLevelId) + ? "1" + : row.original?.statusId; + const statusStyles = + colors[Number(status)] || "bg-red-200 text-red-600"; + // const statusStyles = statusColors[status] || "bg-red-200 text-red-600"; + + return ( + + {(Number(row.original?.statusId) == 2 && + !row.original?.reviewedAtLevel !== null && + !row.original?.reviewedAtLevel?.includes( + `:${Number(userLevelId)}:` + ) && + Number(row.original?.creatorGroupLevelId) != + Number(userLevelId)) || + (Number(row.original?.statusId) == 1 && + Number(row.original?.needApprovalFromLevel) == + Number(userLevelId)) + ? "Menunggu Review" + : row.original?.statusName}{" "} + + ); + }, + }, + + { + id: "actions", + accessorKey: "action", + header: t("action", { defaultValue: "Action" }), + enableHiding: false, + cell: ({ row }) => { + const router = useRouter(); + const MySwal = withReactContent(Swal); + + async function doDelete(id: any) { + const data = { id }; + const response = await deleteMedia(data); + + if (response?.error) { + error(response.message); + return false; + } + success(); + } + + function success() { + MySwal.fire({ + title: "Sukses", + icon: "success", + confirmButtonColor: "#3085d6", + confirmButtonText: "OK", + }).then((result) => { + if (result.isConfirmed) { + window.location.reload(); + } + }); + } + + const handleDeleteMedia = (id: any) => { + MySwal.fire({ + title: "Hapus Data", + text: "", + icon: "warning", + showCancelButton: true, + cancelButtonColor: "#3085d6", + confirmButtonColor: "#d33", + confirmButtonText: "Hapus", + }).then((result) => { + if (result.isConfirmed) { + doDelete(id); + } + }); + }; + + const [isMabesApprover, setIsMabesApprover] = React.useState(false); + const userId = getCookiesDecrypt("uie"); + const userLevelId = getCookiesDecrypt("ulie"); + const roleId = Number(getCookiesDecrypt("urie")); // pastikan jadi number + + React.useEffect(() => { + if (userLevelId !== undefined && roleId !== undefined) { + setIsMabesApprover( + Number(userLevelId) === 216 && Number(roleId) === 3 + ); + } + }, [userLevelId, roleId]); + + const canEdit = + Number(row.original.uploadedById) === Number(userId) || + isMabesApprover || + roleId === 14; + + return ( + + + + + + + + + View + + + + {canEdit && ( + + + + Edit + + + )} + + handleDeleteMedia(row.original.id)} + className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none" + > + + Delete + + + + ); + }, + }, + // { + // id: "actions", + // accessorKey: "action", + // header: t("action", { defaultValue: "Action" }), + // enableHiding: false, + // cell: ({ row }) => { + // const MySwal = withReactContent(Swal); + + // async function doDelete(id: any) { + // // loading(); + // const data = { + // id, + // }; + + // const response = await deleteMedia(data); + + // if (response?.error) { + // error(response.message); + // return false; + // } + // success(); + // } + + // function success() { + // MySwal.fire({ + // title: "Sukses", + // icon: "success", + // confirmButtonColor: "#3085d6", + // confirmButtonText: "OK", + // }).then((result) => { + // if (result.isConfirmed) { + // window.location.reload(); + // } + // }); + // } + + // const handleDeleteMedia = (id: any) => { + // MySwal.fire({ + // title: "Hapus Data", + // text: "", + // icon: "warning", + // showCancelButton: true, + // cancelButtonColor: "#3085d6", + // confirmButtonColor: "#d33", + // confirmButtonText: "Hapus", + // }).then((result) => { + // if (result.isConfirmed) { + // doDelete(id); + // } + // }); + // }; + + // const [isMabesApprover, setIsMabesApprover] = React.useState(false); + // const userId = getCookiesDecrypt("uie"); + // const userLevelId = getCookiesDecrypt("ulie"); + // const roleId = getCookiesDecrypt("urie"); + + // React.useEffect(() => { + // if (userLevelId !== undefined && roleId !== undefined) { + // setIsMabesApprover( + // Number(userLevelId) == 216 && Number(roleId) == 3 + // ); + // } + // }, [userLevelId, roleId]); + + // return ( + // + // + // + // + // + // + // + // + // View + // + // + // {/* + // + // + // Edit + // + // */} + // {(Number(row.original.uploadedById) === Number(userId) || + // isMabesApprover) && ( + // + // + // + // Edit + // + // + // )} + // handleDeleteMedia(row.original.id)} + // className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none" + // > + // + // Delete + // + // {/* {(row.original.uploadedById === userId || isMabesApprover) && ( + // handleDeleteMedia(row.original.id)} + // className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none" + // > + // + // Hapus + // + // )} */} + // + // + // ); + // }, + // }, + ]; + + return columns; +}; + +export default useTableColumns; diff --git a/app/[locale]/(protected)/contributor/content/satker/components/table-satker.tsx b/app/[locale]/(protected)/contributor/content/satker/components/table-satker.tsx new file mode 100644 index 00000000..febde085 --- /dev/null +++ b/app/[locale]/(protected)/contributor/content/satker/components/table-satker.tsx @@ -0,0 +1,579 @@ +"use client"; + +import * as React from "react"; +import { + ColumnFiltersState, + PaginationState, + SortingState, + VisibilityState, + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from "@tanstack/react-table"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { ChevronDown, Search } from "lucide-react"; +import { getCookiesDecrypt } from "@/lib/utils"; +import { Input } from "@/components/ui/input"; +import { InputGroup, InputGroupText } from "@/components/ui/input-group"; +import TablePagination from "@/components/table/table-pagination"; +import { listDataAll, listDataSatker, listEnableCategory } from "@/service/content/content"; +import Swal from "sweetalert2"; +import withReactContent from "sweetalert2-react-content"; +import { format } from "date-fns"; +import useTableColumns from "./columns"; +import { Label } from "@/components/ui/label"; +import { useRouter } from "@/i18n/routing"; +import { useSearchParams } from "next/navigation"; + +const TableSatker = () => { + const router = useRouter(); + const searchParams = useSearchParams(); + const MySwal = withReactContent(Swal); + + const [dataTable, setDataTable] = React.useState([]); + const [totalData, setTotalData] = React.useState(1); + const [totalPage, setTotalPage] = React.useState(1); + + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = React.useState( + [] + ); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + const [rowSelection, setRowSelection] = React.useState({}); + + const [showData, setShowData] = React.useState("10"); + const [page, setPage] = React.useState(1); + const [search, setSearch] = React.useState(""); + + // gunakan ref untuk debounce search + const searchTimeoutRef = React.useRef(null); + + // === FILTER STATES === + const [categories, setCategories] = React.useState([]); + const [selectedCategories, setSelectedCategories] = React.useState( + [] + ); + const [categoryFilter, setCategoryFilter] = React.useState(""); + const [statusFilter, setStatusFilter] = React.useState([]); + const [startDate, setStartDate] = React.useState(""); + const [endDate, setEndDate] = React.useState(""); + const [filterByCreator, setFilterByCreator] = React.useState(""); + const [filterBySource, setFilterBySource] = React.useState(""); + const [filterByCreatorGroup, setFilterByCreatorGroup] = React.useState(""); + const [typeId, setTypeId] = React.useState(""); // 1=image, 2=video, 3=text, 4=audio + + const userLevelId = getCookiesDecrypt("ulie"); + const roleId = getCookiesDecrypt("urie"); + + const columns = useTableColumns(); + + const [pagination, setPagination] = React.useState({ + pageIndex: 0, + pageSize: Number(showData), + }); + + const table = useReactTable({ + data: dataTable, + columns, + onSortingChange: setSorting, + onColumnFiltersChange: setColumnFilters, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFilteredRowModel: getFilteredRowModel(), + onColumnVisibilityChange: setColumnVisibility, + onRowSelectionChange: setRowSelection, + onPaginationChange: setPagination, + state: { + sorting, + columnFilters, + columnVisibility, + rowSelection, + pagination, + }, + }); + + // Sync page dari URL (?page=) + React.useEffect(() => { + const pageFromUrl = searchParams?.get("page"); + if (pageFromUrl) setPage(Number(pageFromUrl)); + }, [searchParams]); + + // Ambil kategori sekali di awal + React.useEffect(() => { + getCategories(); + }, []); + + // Fetch data ketika filter/pagination berubah + React.useEffect(() => { + fetchData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + categoryFilter, + statusFilter, + startDate, + endDate, + showData, + page, + typeId, + filterByCreatorGroup, + ]); + + async function getCategories() { + try { + // ini mengikuti kode awal, type "2" (misal: kategori video) + const category = await listEnableCategory("2"); + const resCategory = category?.data?.data?.content; + setCategories(resCategory || []); + } catch (error) { + console.error("Error fetching categories:", error); + } + } + + async function fetchData(showLoader = false, customSearch?: string) { + const formattedStartDate = startDate + ? format(new Date(startDate), "yyyy-MM-dd") + : ""; + const formattedEndDate = endDate + ? format(new Date(endDate), "yyyy-MM-dd") + : ""; + + try { + if (showLoader) { + MySwal.fire({ + title: "Memuat data...", + html: "Mohon tunggu sebentar", + allowOutsideClick: false, + didOpen: () => MySwal.showLoading(), + }); + } + + const isForSelf = Number(roleId) === 4; + const isApproval = !isForSelf; + + const res = await listDataSatker( + isForSelf, + isApproval, + page - 1, // page (0-based) + parseInt(showData) || 10, // limit + search, // search (kalau diperlukan di backend) + typeId, // typeId: 1=image, 2=video, 3=text, 4=audio + statusFilter?.join(","), // statusId + statusFilter?.join(",")?.includes("1") ? userLevelId : "", + filterByCreator, + filterBySource, + formattedStartDate, + formattedEndDate, + customSearch ?? search // title + ); + + const data = res?.data?.data; + const contentData = data?.content || []; + const newData = contentData.map((item: any, index: number) => ({ + ...item, + no: (page - 1) * Number(showData) + index + 1, + })); + + setDataTable([...newData]); + setTotalData(data?.totalElements || 0); + setTotalPage(data?.totalPages || 1); + } catch (error) { + console.error("Error fetching data:", error); + } finally { + MySwal.close(); + } + } + + // === HANDLERS === + const handleCheckboxChange = (categoryId: number) => { + setSelectedCategories((prev) => + prev.includes(categoryId) + ? prev.filter((id) => id !== categoryId) + : [...prev, categoryId] + ); + + setCategoryFilter((prev) => { + const updated = prev.split(",").filter(Boolean).map(Number); + const newList = updated.includes(categoryId) + ? updated.filter((id) => id !== categoryId) + : [...updated, categoryId]; + return newList.join(","); + }); + }; + + function handleStatusCheckboxChange(value: any) { + setStatusFilter((prev: any) => + prev.includes(value) + ? prev.filter((status: any) => status !== value) + : [...prev, value] + ); + } + + // Search dengan debounce 2 detik + const handleSearch = (e: React.ChangeEvent) => { + const value = e.target.value; + setSearch(value); + + if (searchTimeoutRef.current) { + clearTimeout(searchTimeoutRef.current); + } + + searchTimeoutRef.current = setTimeout(() => { + // setiap kali search, reset ke page 1 + setPage(1); + fetchData(true, value); + }, 2000); + }; + + const handleSearchFilterByCreator = ( + e: React.ChangeEvent + ) => { + const value = e.target.value; + setFilterByCreator(value); + setPage(1); + fetchData(true); + }; + + const handleSearchFilterBySource = ( + e: React.ChangeEvent + ) => { + const value = e.target.value; + setFilterBySource(value); + setPage(1); + fetchData(true); + }; + + // ๐Ÿงน Reset semua filter + const handleResetFilters = () => { + setSelectedCategories([]); + setCategoryFilter(""); + setStatusFilter([]); + setStartDate(""); + setEndDate(""); + setFilterByCreator(""); + setFilterBySource(""); + setFilterByCreatorGroup(""); + setTypeId(""); + setPage(1); + fetchData(true); + }; + + return ( +
+
+ {/* ๐Ÿ” Search bar */} +
+ + + + + + +
+ + {/* Filter & Columns & ShowData */} +
+ {/* Show Data Dropdown */} +
+ + + + + + { + setShowData(value); + setPagination((prev) => ({ + ...prev, + pageSize: Number(value), + pageIndex: 0, + })); + setPage(1); + }} + > + + 10 Data + + + 50 Data + + + 100 Data + + + 250 Data + + + + +
+ + {/* Jenis Konten (typeId) */} + + + + + + { + setTypeId(value); + setPage(1); + fetchData(true); + }} + > + + Semua Jenis + + + Gambar / Image + + + Video + + + Teks / Artikel + + + Audio + + + + + + {/* Filter Dropdown */} + + + + + + {/* ๐Ÿงน Reset All Button */} +
+ +
+ + + {categories.length > 0 ? ( + categories.map((category) => ( +
+ handleCheckboxChange(category.id)} + /> + +
+ )) + ) : ( +

+ Tidak ada kategori. +

+ )} + +
+ + { + setStartDate(e.target.value); + setPage(1); + }} + /> +
+
+ + { + setEndDate(e.target.value); + setPage(1); + }} + /> +
+ +
+ + +
+ +
+ + +
+ + + {[1, 2, 3, 4].map((id) => { + const label = + id === 1 + ? "Menunggu Review" + : id === 2 + ? "Diterima" + : id === 3 + ? "Minta Update" + : "Ditolak"; + return ( +
+ handleStatusCheckboxChange(id)} + /> + +
+ ); + })} +
+
+ + {/* Columns Toggle */} + + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ))} + + +
+
+ + {/* === TABLE === */} + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ))} + + ))} + + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + Tidak ada hasil ditemukan. + + + )} + +
+ + +
+ ); +}; + +export default TableSatker; diff --git a/app/[locale]/(protected)/contributor/content/satker/create/page.tsx b/app/[locale]/(protected)/contributor/content/satker/create/page.tsx new file mode 100644 index 00000000..089cc55f --- /dev/null +++ b/app/[locale]/(protected)/contributor/content/satker/create/page.tsx @@ -0,0 +1,15 @@ +import FormVideo from "@/components/form/content/video-form"; +import SiteBreadcrumb from "@/components/site-breadcrumb"; + +const VideoCreatePage = async () => { + return ( +
+ +
+ +
+
+ ); +}; + +export default VideoCreatePage; diff --git a/app/[locale]/(protected)/contributor/content/satker/detail/[id]/page.tsx b/app/[locale]/(protected)/contributor/content/satker/detail/[id]/page.tsx new file mode 100644 index 00000000..377c0a87 --- /dev/null +++ b/app/[locale]/(protected)/contributor/content/satker/detail/[id]/page.tsx @@ -0,0 +1,16 @@ +import SiteBreadcrumb from "@/components/site-breadcrumb"; +import FormImageDetail from "@/components/form/content/image-detail-form"; +import FormVideoDetail from "@/components/form/content/video-detail-form"; + +const VideoDetailPage = async () => { + return ( +
+ +
+ +
+
+ ); +}; + +export default VideoDetailPage; diff --git a/app/[locale]/(protected)/contributor/content/satker/layout.tsx b/app/[locale]/(protected)/contributor/content/satker/layout.tsx new file mode 100644 index 00000000..6e956709 --- /dev/null +++ b/app/[locale]/(protected)/contributor/content/satker/layout.tsx @@ -0,0 +1,11 @@ +import { Metadata } from "next"; + +export const metadata: Metadata = { + title: "Media Hub | POLRI", + description: "Media Hub merupakan situs resmi milik Divisi Humas Polri di mana di dalamnya berisi konten-konten yang dapat diakses secara gratis oleh Internal Polri, Jurnalis, Masyarakat Umum, dan KSP.", +}; +const Layout = ({ children }: { children: React.ReactNode }) => { + return <>{children}; +}; + +export default Layout; diff --git a/app/[locale]/(protected)/contributor/content/satker/page.tsx b/app/[locale]/(protected)/contributor/content/satker/page.tsx new file mode 100644 index 00000000..e419b0e2 --- /dev/null +++ b/app/[locale]/(protected)/contributor/content/satker/page.tsx @@ -0,0 +1,88 @@ +"use client"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import SiteBreadcrumb from "@/components/site-breadcrumb"; +import TableImage from "./components/table-satker"; +import { UploadIcon } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { Icon } from "@iconify/react/dist/iconify.js"; +import TableVideo from "./components/table-satker"; +import { Link } from "@/components/navigation"; +import { useTranslations } from "next-intl"; + +const ReactTableVideoPage = () => { + const t = useTranslations("AnalyticsDashboard"); + return ( +
+ +
+ +
+
+
+ +
+
+

+ {t("average", { defaultValue: "Average" })} :0 +

+

+ {t("Hasil_unggah_disetujui_hari_ini", { defaultValue: "Hasil Unggah Disetujui Hari Ini" })} +

+
+
+
+
+ +
+
+

+ {t("average", { defaultValue: "Average" })} :0 +

+

{t("Hasil_unggah_direvisi_hari_ini", { defaultValue: "Hasil Unggah Direvisi Hari Ini" })}

+
+
+
+
+ +
+
+

+ {t("average", { defaultValue: "Average" })} :0 +

+

{t("Hasil_unggah_ditolak_hari_ini", { defaultValue: "Hasil Unggah Ditolak Hari Ini" })}

+
+
+
+
+ + + +
+
+ {t("video", { defaultValue: "Video" })} +
+
+ + + + {/* */} +
+
+
+
+ + + +
+
+
+ ); +}; + +export default ReactTableVideoPage; diff --git a/app/[locale]/(protected)/contributor/content/satker/update-seo/[id]/page.tsx b/app/[locale]/(protected)/contributor/content/satker/update-seo/[id]/page.tsx new file mode 100644 index 00000000..260e06f2 --- /dev/null +++ b/app/[locale]/(protected)/contributor/content/satker/update-seo/[id]/page.tsx @@ -0,0 +1,18 @@ +import SiteBreadcrumb from "@/components/site-breadcrumb"; +import FormImageDetail from "@/components/form/content/image-detail-form"; +import FormImageUpdate from "@/components/form/content/image-update-form"; +import FormVideoUpdate from "@/components/form/content/video-update-form"; +import FormVideoSeo from "@/components/form/content/video-update-seo"; + +const VideoUpdatePage = async () => { + return ( +
+ +
+ +
+
+ ); +}; + +export default VideoUpdatePage; diff --git a/app/[locale]/(protected)/contributor/content/satker/update/[id]/page.tsx b/app/[locale]/(protected)/contributor/content/satker/update/[id]/page.tsx new file mode 100644 index 00000000..1b0a481c --- /dev/null +++ b/app/[locale]/(protected)/contributor/content/satker/update/[id]/page.tsx @@ -0,0 +1,17 @@ +import SiteBreadcrumb from "@/components/site-breadcrumb"; +import FormImageDetail from "@/components/form/content/image-detail-form"; +import FormImageUpdate from "@/components/form/content/image-update-form"; +import FormVideoUpdate from "@/components/form/content/video-update-form"; + +const VideoUpdatePage = async () => { + return ( +
+ +
+ +
+
+ ); +}; + +export default VideoUpdatePage; diff --git a/components/landing-page/navbar.tsx b/components/landing-page/navbar.tsx index 28998ddd..57e717c2 100644 --- a/components/landing-page/navbar.tsx +++ b/components/landing-page/navbar.tsx @@ -99,7 +99,7 @@ const Navbar = () => { ? `/polda/${poldaName}` : satkerName ? `/satker/${satkerName}` - : "/"; + : ""; let menu = ""; @@ -205,6 +205,21 @@ const Navbar = () => { const [open, setOpen] = useState(false); + useEffect(() => { + if (!pathname) return; + + if ( + pathname.startsWith("/_next") || + pathname.startsWith("/api") || + pathname.includes("favicon") + ) + return; + + if (!pathname.startsWith("/in")) { + router.replace("/in"); + } + }, [pathname]); + return (
diff --git a/lib/menus.ts b/lib/menus.ts index fc2d661b..12f7a391 100644 --- a/lib/menus.ts +++ b/lib/menus.ts @@ -4321,13 +4321,13 @@ export function getMenuList(pathname: string, t: any): Group[] { icon: "heroicons:arrow-trending-up", children: [], }, - { - href: "/admin/settings/banner", - label: "Banner", - active: pathname === "/admin/settings/banner", - icon: "heroicons:arrow-trending-up", - children: [], - }, + // { + // href: "/admin/settings/banner", + // label: "Banner", + // active: pathname === "/admin/settings/banner", + // icon: "heroicons:arrow-trending-up", + // children: [], + // }, // { // href: "/admin/settings/feedback", // label: "Feedback", diff --git a/middleware.ts b/middleware.ts index f8a2d2b1..4b9b79aa 100644 --- a/middleware.ts +++ b/middleware.ts @@ -1,29 +1,65 @@ import createMiddleware from "next-intl/middleware"; import { NextRequest, NextResponse } from "next/server"; -import { locales } from "@/config"; import { routing } from "./i18n/routing"; -// export default async function middleware(request: NextRequest) { -// // Step 1: Use the incoming request (example) -// const defaultLocale = "in"; -// // const defaultLocale = request.headers.get("dashcode-locale") || "in"; +const intlMiddleware = createMiddleware(routing); -// // Step 2: Create and call the next-intl middleware (example) -// const handleI18nRouting = createMiddleware({ -// locales: ["in", "en"], -// defaultLocale: "in", -// }); -// const response = handleI18nRouting(request); +export default function middleware(request: NextRequest) { + const { pathname } = request.nextUrl; -// // Step 3: Alter the response (example) -// response.headers.set("dashcode-locale", defaultLocale); + // Abaikan asset & API + if ( + pathname.startsWith("/api") || + pathname.startsWith("/_next") || + pathname.startsWith("/favicon") || + pathname.startsWith("/assets") + ) { + return NextResponse.next(); + } -// return response; -// } + // Jika sudah mengandung /in โ†’ pakai next-intl + if (pathname.startsWith("/in")) { + return intlMiddleware(request); + } -export default createMiddleware(routing); + // Jika TIDAK mengandung /in โ†’ selalu tambahkan /in + // Contoh: + // /image/detail/... โ†’ /in/image/detail/... + const url = request.nextUrl.clone(); + url.pathname = `/in${pathname}`; + return NextResponse.redirect(url); +} export const config = { - // Match only internationalized pathnames - matcher: ["/", "/(in|en)/:path*"], + matcher: ["/((?!_next|api|favicon.ico|assets).*)"], }; + +// import createMiddleware from "next-intl/middleware"; +// import { NextRequest, NextResponse } from "next/server"; +// import { locales } from "@/config"; +// import { routing } from "./i18n/routing"; + +// // export default async function middleware(request: NextRequest) { +// // // Step 1: Use the incoming request (example) +// // const defaultLocale = "in"; +// // // const defaultLocale = request.headers.get("dashcode-locale") || "in"; + +// // // Step 2: Create and call the next-intl middleware (example) +// // const handleI18nRouting = createMiddleware({ +// // locales: ["in", "en"], +// // defaultLocale: "in", +// // }); +// // const response = handleI18nRouting(request); + +// // // Step 3: Alter the response (example) +// // response.headers.set("dashcode-locale", defaultLocale); + +// // return response; +// // } + +// export default createMiddleware(routing); + +// export const config = { +// // Match only internationalized pathnames +// matcher: ["/", "/(in|en)/:path*"], +// }; diff --git a/service/content/content.ts b/service/content/content.ts index 144748b5..6db33e68 100644 --- a/service/content/content.ts +++ b/service/content/content.ts @@ -46,6 +46,39 @@ export async function listDataAll( ); } +export async function listDataSatker( + isForSelf: any, + isApproval: any, + page: any, + limit: any, + search: any, + typeId: any, + statusFilter: any, + needApprovalFromLevel: any, + creator: any, + source: any, + startDate: any, + endDate: any, + title: string = "" +) { + return await httpGetInterceptor( + `media/list?enablePage=1&sortBy=createdAt&sort=desc&isAllSatker=1` + + `&size=${limit}` + + `&page=${page}` + + `&isForSelf=${isForSelf}` + + `&isApproval=${isApproval}` + + `&typeId=${typeId || ""}` + + `&statusId=${statusFilter || ""}` + + `&needApprovalFromLevel=${needApprovalFromLevel || ""}` + + `&creator=${encodeURIComponent(creator || "")}` + + `&source=${encodeURIComponent(source || "")}` + + `&startDate=${startDate || ""}` + + `&endDate=${endDate || ""}` + + `&title=${encodeURIComponent(title || search || "")}` + ); +} + + export async function listDataImage( size: any = "", page: any = "",