From 24a5e2a5c1c579c09c4680be308796e4c64381e9 Mon Sep 17 00:00:00 2001 From: Sabda Yagra Date: Tue, 19 Aug 2025 00:07:26 +0700 Subject: [PATCH 1/9] fix: add checkbox wilayah in aprove content, add select date in dashboard generate report --- .../agenda-setting/calender-view.tsx | 27 +- .../content/spit/table-spit/table-spit.tsx | 1 - .../content/teks/components/table-teks.tsx | 4 +- .../report/components/report-table.tsx | 111 ++++- components/form/content/audio-detail-form.tsx | 365 +++++++++++++++- components/form/content/image-detail-form.tsx | 407 +++++++++++++++++- components/form/content/teks-detail-form.tsx | 393 +++++++++++++++-- components/form/content/video-detail-form.tsx | 356 ++++++++++++++- components/form/task/task-edit-form.tsx | 22 +- service/content/content.ts | 2 +- 10 files changed, 1578 insertions(+), 110 deletions(-) diff --git a/app/[locale]/(protected)/contributor/agenda-setting/calender-view.tsx b/app/[locale]/(protected)/contributor/agenda-setting/calender-view.tsx index 5c6963d2..d1e19165 100644 --- a/app/[locale]/(protected)/contributor/agenda-setting/calender-view.tsx +++ b/app/[locale]/(protected)/contributor/agenda-setting/calender-view.tsx @@ -115,7 +115,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => { const [selectedCategory, setSelectedCategory] = useState([]); const [selectedEventDate, setSelectedEventDate] = useState(null); const roleId = Number(getCookiesDecrypt("urie")) || 0; - console.log("DUARR", roleId) + const levelNumber = Number(getCookiesDecrypt("ulne")) || 0; const userLevelId = Number(getCookiesDecrypt("ulie")) || 0; const [calendarEvents, setCalendarEvents] = useState([]); const [isLoading, setIsLoading] = useState(false); @@ -415,7 +415,9 @@ const CalendarView = ({ categories }: CalendarViewProps) => {
{events.length === 0 ? (
-

{t("no-data-yet", { defaultValue: "No Data Yet" })}

+

+ {t("no-data-yet", { defaultValue: "No Data Yet" })} +

) : ( <> @@ -527,7 +529,16 @@ const CalendarView = ({ categories }: CalendarViewProps) => { - {[3, 11, 2, 12].includes(roleId) && ( + {/* {[3, 11, 2, 12].includes(roleId) && ( + + )} */} + {[3, 11, 2, 12].includes(roleId) && levelNumber !== 3 && ( - {t("monitoring-results", { defaultValue: "Monitoring Results" })} + + {t("monitoring-results", { + defaultValue: "Monitoring Results", + })} + {getModalContent()} diff --git a/app/[locale]/(protected)/contributor/content/spit/table-spit/table-spit.tsx b/app/[locale]/(protected)/contributor/content/spit/table-spit/table-spit.tsx index 016c9e30..00d73202 100644 --- a/app/[locale]/(protected)/contributor/content/spit/table-spit/table-spit.tsx +++ b/app/[locale]/(protected)/contributor/content/spit/table-spit/table-spit.tsx @@ -179,7 +179,6 @@ const TableSPIT = () => { React.useEffect(() => { fetchData(); getCategories(); - console.log("TRIGGERRRR"); }, [statusFilter, page, showData, search, dateFilter]); async function getCategories() { diff --git a/app/[locale]/(protected)/contributor/content/teks/components/table-teks.tsx b/app/[locale]/(protected)/contributor/content/teks/components/table-teks.tsx index 1c0a57d2..eea434ee 100644 --- a/app/[locale]/(protected)/contributor/content/teks/components/table-teks.tsx +++ b/app/[locale]/(protected)/contributor/content/teks/components/table-teks.tsx @@ -67,7 +67,6 @@ import useTableColumns from "./columns"; const TableTeks = () => { const router = useRouter(); const searchParams = useSearchParams(); - const [dataTable, setDataTable] = React.useState([]); const [totalData, setTotalData] = React.useState(1); const [sorting, setSorting] = React.useState([]); @@ -77,7 +76,7 @@ const TableTeks = () => { const [columnVisibility, setColumnVisibility] = React.useState({}); const [rowSelection, setRowSelection] = React.useState({}); - const [showData, setShowData] = React.useState("50"); + const [showData, setShowData] = React.useState("10"); const [pagination, setPagination] = React.useState({ pageIndex: 0, pageSize: Number(showData), @@ -88,7 +87,6 @@ const TableTeks = () => { const [search, setSearch] = React.useState(""); const userId = getCookiesDecrypt("uie"); const userLevelId = getCookiesDecrypt("ulie"); - const [categories, setCategories] = React.useState([]); const [selectedCategories, setSelectedCategories] = React.useState( [] diff --git a/app/[locale]/(protected)/contributor/report/components/report-table.tsx b/app/[locale]/(protected)/contributor/report/components/report-table.tsx index 976a1e27..1a2ad488 100644 --- a/app/[locale]/(protected)/contributor/report/components/report-table.tsx +++ b/app/[locale]/(protected)/contributor/report/components/report-table.tsx @@ -88,7 +88,7 @@ const ReportTable = () => { const [columnVisibility, setColumnVisibility] = React.useState({}); const [rowSelection, setRowSelection] = React.useState({}); - const [showData, setShowData] = React.useState("50"); + const [showData, setShowData] = React.useState("10"); const [pagination, setPagination] = React.useState({ pageIndex: 0, pageSize: Number(showData), @@ -106,6 +106,8 @@ const ReportTable = () => { const [statusFilter, setStatusFilter] = React.useState([]); const [openPreview, setOpenPreview] = React.useState(false); const [previewData, setPreviewData] = React.useState(null); + const [openDateDialog, setOpenDateDialog] = React.useState(false); + const [reportDate, setReportDate] = React.useState(""); const handlePreview = (id: string) => { const url = `https://new.netidhub.com/api/media/report/view?id=${id}`; @@ -184,8 +186,6 @@ const ReportTable = () => { ? prev.filter((id: any) => id !== categoryId) : [...prev, categoryId] ); - - // Perbarui filter kategori setCategoryFilter((prev) => { const updatedCategories = prev.split(",").filter(Boolean).map(Number); @@ -206,22 +206,65 @@ const ReportTable = () => { } const handleSearch = (e: React.ChangeEvent) => { - setSearch(e.target.value); // Perbarui state search - table.getColumn("judul")?.setFilterValue(e.target.value); // Set filter tabel + setSearch(e.target.value); + table.getColumn("judul")?.setFilterValue(e.target.value); }; - const handleGenerateReport = async () => { - const today = new Date(); - const formattedDate = format(today, "dd-MM-yyyy"); // Hasil: 22-04-2025 - const title = `Report ${formattedDate}`; + // const handleGenerateReport = async () => { + // const today = new Date(); + // const formattedDate = format(today, "dd-MM-yyyy"); + // const title = `Report ${formattedDate}`; + // const requestData = { + // title, + // date: reportDate, + // }; + + // try { + // const response = await saveReport(requestData); + + // if (response?.error) { + // MySwal.fire( + // "Error", + // response?.message || "Gagal menyimpan laporan", + // "error" + // ); + // return; + // } + + // MySwal.fire({ + // title: "Sukses", + // text: "Laporan berhasil dibuat.", + // icon: "success", + // confirmButtonColor: "#3085d6", + // confirmButtonText: "OK", + // }).then(() => { + // fetchData(); + // }); + // } catch (error) { + // console.error("Generate report error:", error); + // MySwal.fire("Error", "Terjadi kesalahan saat membuat laporan", "error"); + // } + // }; + + const handleGenerateReport = async () => { + if (!reportDate) { + MySwal.fire( + "Warning", + "Silakan pilih tanggal laporan terlebih dahulu", + "warning" + ); + return; + } + + const title = `Report ${format(new Date(reportDate), "dd-MM-yyyy")}`; const requestData = { title, + date: reportDate, }; try { const response = await saveReport(requestData); - if (response?.error) { MySwal.fire( "Error", @@ -238,7 +281,8 @@ const ReportTable = () => { confirmButtonColor: "#3085d6", confirmButtonText: "OK", }).then(() => { - fetchData(); // Refresh tabel setelah generate + fetchData(); + setReportDate(""); }); } catch (error) { console.error("Generate report error:", error); @@ -269,10 +313,19 @@ const ReportTable = () => {
- {t("table", { defaultValue: "Table" })} {t("report", { defaultValue: "Report" })} + {t("table", { defaultValue: "Table" })}{" "} + {t("report", { defaultValue: "Report" })}
- */} + @@ -472,6 +525,38 @@ const ReportTable = () => { totalPage={totalPage} />
+ + + + Pilih Tanggal Laporan + +
+ + setReportDate(e.target.value)} + /> +
+ + +
+
+
+
); }; diff --git a/components/form/content/audio-detail-form.tsx b/components/form/content/audio-detail-form.tsx index 8a792d37..e9d5120b 100644 --- a/components/form/content/audio-detail-form.tsx +++ b/components/form/content/audio-detail-form.tsx @@ -19,7 +19,6 @@ import { } from "@/components/ui/select"; import { Checkbox } from "@/components/ui/checkbox"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; - import { register } from "module"; import { Switch } from "@/components/ui/switch"; import Cookies from "js-cookie"; @@ -35,7 +34,7 @@ import { getDataApprovalByMediaUpload, } from "@/service/curated-content/curated-content"; import { Badge } from "@/components/ui/badge"; -import { MailIcon, Music } from "lucide-react"; +import { ChevronDown, ChevronUp, MailIcon, Music } from "lucide-react"; import { Swiper, SwiperSlide } from "swiper/react"; import "swiper/css"; import "swiper/css/free-mode"; @@ -51,6 +50,7 @@ import { Dialog, DialogContent, DialogTitle, + DialogTrigger, } from "@/components/ui/dialog"; import { Textarea } from "@/components/ui/textarea"; import { loading } from "@/config/swal"; @@ -66,6 +66,7 @@ import { formatDateToIndonesian } from "@/utils/globals"; import ApprovalHistoryModal from "@/components/modal/approval-history-modal"; import { useDropzone } from "react-dropzone"; import AudioPlayer from "@/components/audio-player"; +import { getUserLevelForAssignments } from "@/service/task"; const imageSchema = z.object({ title: z.string().min(1, { message: "Judul diperlukan" }), @@ -115,6 +116,17 @@ const ViewEditor = dynamic( { ssr: false } ); +interface Destination { + id: string; + name: string; + subDestination?: SubDestination[]; +} + +interface SubDestination { + id: string; + name: string; +} + export default function FormAudioDetail() { const MySwal = withReactContent(Swal); const router = useRouter(); @@ -155,6 +167,135 @@ export default function FormAudioDetail() { const [wavesurfer, setWavesurfer] = useState(); const [isPlaying, setIsPlaying] = useState(false); const [approval, setApproval] = useState(); + const [expandedPolda, setExpandedPolda] = useState([{}]); + const [unitSelection, setUnitSelection] = useState({ + semua: false, + nasional: false, + wilayah: false, + international: false, + polda: false, + polres: false, + satker: false, + }); + const [isLoading, setIsLoading] = useState(false); + const [listDest, setListDest] = useState([]); + const [checkedLevels, setCheckedLevels] = useState(new Set()); + + const toggleExpand = (id: number) => { + setExpandedPolda((prev) => ({ + ...prev, + [id]: !prev[id], + })); + }; + + useEffect(() => { + if (detail?.assignedToTopLevel) { + const outputSet = new Set( + detail.assignedToTopLevel.split(",").map(Number) + ); + setUnitSelection({ + semua: outputSet.has(0), + nasional: outputSet.has(1), + wilayah: outputSet.has(2), + international: outputSet.has(3), + polda: outputSet.has(4), + polres: outputSet.has(5), + satker: outputSet.has(6), + }); + } + }, [detail?.fileTypeOutput]); + + useEffect(() => { + async function fetchPoldaPolres() { + setIsLoading(true); + try { + const response = await getUserLevelForAssignments(); + setListDest(response?.data?.data.list); + const initialExpandedState = response?.data?.data.list.reduce( + (acc: any, polda: any) => { + acc[polda.id] = false; + return acc; + }, + {} + ); + setExpandedPolda(initialExpandedState); + console.log("polres", initialExpandedState); + } catch (error) { + console.error("Error fetching Polda/Polres data:", error); + } finally { + setIsLoading(false); + } + } + fetchPoldaPolres(); + }, []); + + useEffect(() => { + const updated = new Set(checkedLevels); + + if (unitSelection.polda) { + listDest.forEach((polda) => { + updated.add(polda.id); + }); + } + + if (unitSelection.polres) { + listDest.forEach((polda) => { + polda?.subDestination?.forEach((polres: any) => { + updated.add(polres.id); + }); + }); + } + + setCheckedLevels(updated); + }, [unitSelection.polda, unitSelection.polres, listDest]); + + const handleUnitChange = ( + key: keyof typeof unitSelection, + value: boolean + ) => { + if (key === "semua") { + const newState = { + semua: value, + nasional: value, + wilayah: value, + international: value, + polda: value, + polres: value, + satker: value, + }; + setUnitSelection(newState); + } else { + const updatedSelection = { + ...unitSelection, + [key]: value, + }; + + const allChecked = [ + "nasional", + "wilayah", + "international", + "polda", + "polres", + "satker", + ].every((k) => updatedSelection[k as keyof typeof unitSelection]); + + updatedSelection.semua = allChecked; + + setUnitSelection(updatedSelection); + } + }; + + const handleCheckboxChangePlacement = (levelId: number) => { + setCheckedLevels((prev: any) => { + const updatedLevels = new Set(prev); + if (updatedLevels.has(levelId)) { + updatedLevels.delete(levelId); + } else { + updatedLevels.add(levelId); + } + return updatedLevels; + }); + }; const onDrop = (acceptedFiles: File[]) => { setUploadedFiles(acceptedFiles); @@ -247,7 +388,7 @@ export default function FormAudioDetail() { if (findCategory) { // setValue("categoryId", findCategory.id); - setSelectedCategory(findCategory.id); + setSelectedCategory(findCategory.id); const response = await getTagsBySubCategoryId(findCategory.id); setTags(response?.data?.data); } @@ -270,10 +411,7 @@ export default function FormAudioDetail() { if (id) { const response = await detailMedia(id); const details = response?.data?.data; - console.log("detail", details); setFiles(details?.files); - console.log("ISI FILES:", details?.files); - setDetail(details); setMain({ type: details?.fileType.name, @@ -283,6 +421,13 @@ export default function FormAudioDetail() { }); setupPlacementCheck(details?.files?.length); + if (details?.assignedToLevel) { + const levels = new Set( + details.assignedToLevel.split(",").map(Number) + ); + setCheckedLevels(levels); + } + if (details?.publishedForObject) { const publisherIds = details?.publishedForObject.map( (obj: any) => obj.id @@ -358,9 +503,9 @@ export default function FormAudioDetail() { console.log("getPlaa", filePlacements); const temp = []; for (let i = 0; i < filePlacements?.length; i++) { - if (filePlacements[i].length !== 0) { - const now = filePlacements[i].filter((a) => a !== "all"); - const data = { mediaFileId: files[i].id, placements: now.join(",") }; + if (filePlacements[i]?.length !== 0) { + const now = filePlacements[i]?.filter((a) => a !== "all"); + const data = { mediaFileId: files[i]?.id, placements: now.join(",") }; temp.push(data); } } @@ -374,6 +519,7 @@ export default function FormAudioDetail() { message: description, // files: [], files: isUserMabesApprover ? getPlacement() : [], + customLocationPlacement: Array.from(checkedLevels).join(","), }; setModalOpen(false); loading(); @@ -420,9 +566,9 @@ export default function FormAudioDetail() { temp[index] = ["all", "mabes", "polda", "international"]; } else { const now = temp[index]; - now.push(placement); - if (now.length === 3 && !now.includes("all")) { - now.push("all"); + now?.push(placement); + if (now?.length === 3 && !now?.includes("all")) { + now?.push("all"); } temp[index] = now; } @@ -431,9 +577,8 @@ export default function FormAudioDetail() { temp[index] = []; } else { const now = temp[index].filter((a) => a !== placement); - console.log("now", now); temp[index] = now; - if (now.length === 3 && now.includes("all")) { + if (now?.length === 3 && now?.includes("all")) { const newData = now.filter((b) => b !== "all"); temp[index] = newData; } @@ -734,7 +879,195 @@ export default function FormAudioDetail() {
- {isUserMabesApprover && ( +
+ {/* --- Checkbox utama --- */} +
+ {[ + "semua", + "nasional", + "wilayah", + "international", + ].map((key, index) => ( +
+ { + handleUnitChange( + key as keyof typeof unitSelection, + value as boolean + ); + setupPlacement( + index, + key, + Boolean(value) + ); + }} + /> + +
+ ))} +
+ + {/* --- Kalau wilayah dicentang baru tampilkan detail --- */} + {unitSelection.wilayah && ( +
+
+ {["polda", "polres", "satker"].map( + (key, index) => ( +
+ { + handleUnitChange( + key as keyof typeof unitSelection, + value as boolean + ); + setupPlacement( + index, + key, + Boolean(value) + ); + }} + /> + +
+ ) + )} +
+ + {/* --- Custom Button + Dialog --- */} + + + + + + + + Daftar Wilayah Polda dan Polres + + +
+ {listDest.map((polda: any) => { + const poldaChecked = + unitSelection.polda; + const polresChecked = + unitSelection.polres; + const isPoldaDisabled = poldaChecked; + const isPolresDisabled = + polresChecked; + + return ( +
+ + + {expandedPolda[polda.id] && ( +
+ {polda?.subDestination?.map( + (polres: any) => ( + + ) + )} +
+ )} +
+ ); + })} +
+
+
+
+ )} +
+ {/* {isUserMabesApprover && (
- )} + )} */} )) diff --git a/components/form/content/image-detail-form.tsx b/components/form/content/image-detail-form.tsx index 86a56f6c..d16491e2 100644 --- a/components/form/content/image-detail-form.tsx +++ b/components/form/content/image-detail-form.tsx @@ -36,7 +36,7 @@ import { getDataApprovalByMediaUpload, } from "@/service/curated-content/curated-content"; import { Badge } from "@/components/ui/badge"; -import { MailIcon } from "lucide-react"; +import { ChevronDown, ChevronUp, MailIcon } from "lucide-react"; import { Swiper, SwiperSlide } from "swiper/react"; import "swiper/css"; import "swiper/css/free-mode"; @@ -67,6 +67,7 @@ import SuggestionModal from "@/components/modal/suggestions-modal"; import { formatDateToIndonesian } from "@/utils/globals"; import ApprovalHistoryModal from "@/components/modal/approval-history-modal"; import Image from "next/image"; +import { getUserLevelForAssignments } from "@/service/task"; const imageSchema = z.object({ title: z.string().min(1, { message: "Judul diperlukan" }), @@ -116,6 +117,17 @@ const ViewEditor = dynamic( { ssr: false } ); +interface Destination { + id: string; + name: string; + subDestination?: SubDestination[]; +} + +interface SubDestination { + id: string; + name: string; +} + export default function FormImageDetail() { const MySwal = withReactContent(Swal); const router = useRouter(); @@ -123,13 +135,13 @@ export default function FormImageDetail() { const userLevelId = getCookiesDecrypt("ulie"); const userLevelName = Cookies.get("state"); const roleId = getCookiesDecrypt("urie"); + const [listDest, setListDest] = useState([]); console.log("LALALALA", userLevelName); const [modalOpen, setModalOpen] = useState(false); const { id } = useParams() as { id: string }; console.log(id); const editor = useRef(null); type ImageSchema = z.infer; - const t = useTranslations("Form"); const [selectedFiles, setSelectedFiles] = useState([]); const taskId = Cookies.get("taskId"); @@ -149,10 +161,21 @@ export default function FormImageDetail() { const [thumbsSwiper, setThumbsSwiper] = useState(null); const [isUserMabesApprover, setIsUserMabesApprover] = useState(false); const [approval, setApproval] = useState(); - + const [unitSelection, setUnitSelection] = useState({ + semua: false, + nasional: false, + wilayah: false, + international: false, + polda: false, + polres: false, + satker: false, + }); + const [isLoading, setIsLoading] = useState(false); + const [checkedLevels, setCheckedLevels] = useState(new Set()); const [selectedTarget, setSelectedTarget] = useState(""); const [files, setFiles] = useState([]); const [rejectedFiles, setRejectedFiles] = useState([]); + const [expandedPolda, setExpandedPolda] = useState([{}]); const [wilayahPublish, setWilayahPublish] = React.useState({ semua: false, nasional: false, @@ -165,6 +188,117 @@ export default function FormImageDetail() { let fileTypeId = "1"; + const toggleExpand = (id: number) => { + setExpandedPolda((prev) => ({ + ...prev, + [id]: !prev[id], + })); + }; + + useEffect(() => { + if (detail?.assignedToTopLevel) { + const outputSet = new Set( + detail.assignedToTopLevel.split(",").map(Number) + ); + setUnitSelection({ + semua: outputSet.has(0), + nasional: outputSet.has(1), + wilayah: outputSet.has(2), + international: outputSet.has(3), + polda: outputSet.has(4), + polres: outputSet.has(5), + satker: outputSet.has(6), + }); + } + }, [detail?.fileTypeOutput]); + + // const handlePoldaPolresChange = () => { + // return Array.from(checkedLevels).join(","); // Mengonversi Set ke string + // }; + + useEffect(() => { + async function fetchPoldaPolres() { + setIsLoading(true); + try { + const response = await getUserLevelForAssignments(); + setListDest(response?.data?.data.list); + const initialExpandedState = response?.data?.data.list.reduce( + (acc: any, polda: any) => { + acc[polda.id] = false; + return acc; + }, + {} + ); + setExpandedPolda(initialExpandedState); + console.log("polres", initialExpandedState); + } catch (error) { + console.error("Error fetching Polda/Polres data:", error); + } finally { + setIsLoading(false); + } + } + fetchPoldaPolres(); + }, []); + + useEffect(() => { + const updated = new Set(checkedLevels); + + if (unitSelection.polda) { + listDest.forEach((polda) => { + updated.add(polda.id); // hanya id polda + }); + } + + if (unitSelection.polres) { + listDest.forEach((polda) => { + polda?.subDestination?.forEach((polres: any) => { + updated.add(polres.id); // hanya id polres + }); + }); + } + + setCheckedLevels(updated); + }, [unitSelection.polda, unitSelection.polres, listDest]); + + const handleUnitChange = ( + key: keyof typeof unitSelection, + value: boolean + ) => { + if (key === "semua") { + // Jika klik Semua, set semua value ke true/false + const newState = { + semua: value, + nasional: value, + wilayah: value, + international: value, + polda: value, + polres: value, + satker: value, + }; + setUnitSelection(newState); + } else { + // Update salah satu saja + const updatedSelection = { + ...unitSelection, + [key]: value, + }; + + // Cek apakah semua selain "semua" sudah dicentang + const allChecked = [ + "nasional", + "wilayah", + "international", + "polda", + "polres", + "satker", + ].every((k) => updatedSelection[k as keyof typeof unitSelection]); + + updatedSelection.semua = allChecked; + + setUnitSelection(updatedSelection); + } + }; + const { control, handleSubmit, @@ -191,6 +325,18 @@ export default function FormImageDetail() { ); }; + const handleCheckboxChangePlacement = (levelId: number) => { + setCheckedLevels((prev: any) => { + const updatedLevels = new Set(prev); + if (updatedLevels.has(levelId)) { + updatedLevels.delete(levelId); + } else { + updatedLevels.add(levelId); + } + return updatedLevels; + }); + }; + useEffect(() => { async function initState() { getCategories(); @@ -214,7 +360,7 @@ export default function FormImageDetail() { if (findCategory) { // setValue("categoryId", findCategory.id); - setSelectedCategory(findCategory.id); // Set the selected category + setSelectedCategory(findCategory.id); const response = await getTagsBySubCategoryId(findCategory.id); setTags(response?.data?.data); } @@ -248,6 +394,13 @@ export default function FormImageDetail() { }); setupPlacementCheck(details?.files?.length); + if (details?.assignedToLevel) { + const levels = new Set( + details.assignedToLevel.split(",").map(Number) + ); + setCheckedLevels(levels); + } + if (details.publishedForObject) { const publisherIds = details.publishedForObject.map( (obj: any) => obj.id @@ -327,8 +480,8 @@ export default function FormImageDetail() { statusId: status, message: description, files: isUserMabesApprover ? getPlacement() : [], + customLocationPlacement: Array.from(checkedLevels).join(","), }; - setModalOpen(false); loading(); const response = await submitApproval(data); @@ -368,7 +521,7 @@ export default function FormImageDetail() { temp[index] = ["all", "mabes", "polda", "international"]; } else { const now = temp[index]; - now.push(placement); + now?.push(placement); if (now.length === 3 && !now.includes("all")) { now.push("all"); } @@ -397,6 +550,7 @@ export default function FormImageDetail() { rejects.push(id); setRejectedFiles(rejects); } + const handleMain = ( type: string, url: string, @@ -454,7 +608,9 @@ export default function FormImageDetail() {
-

{t("form-image", { defaultValue: "Form Image" })}

+

+ {t("form-image", { defaultValue: "Form Image" })} +

{/* Input Title */}
@@ -495,16 +651,23 @@ export default function FormImageDetail() { {/* Show the category from details if it doesn't exist in categories list */} - {detail && !categories.find(cat => String(cat.id) === String(detail.category.id)) && ( - - {detail.category.name} - - )} + {detail && + !categories.find( + (cat) => + String(cat.id) === String(detail.category.id) + ) && ( + + {detail.category.name} + + )} {categories.map((category) => ( - + {category.name} ))} @@ -514,7 +677,9 @@ export default function FormImageDetail() {
- +
- +
- +
- {t("leave-comment", { defaultValue: "Leave Comment" })} + + {t("leave-comment", { defaultValue: "Leave Comment" })} +
{status == "2" @@ -760,7 +931,199 @@ export default function FormImageDetail() {
- {isUserMabesApprover && ( +
+ {/* --- Checkbox utama --- */} +
+ {[ + "semua", + "nasional", + "wilayah", + "international", + ].map((key, index) => ( +
+ { + handleUnitChange( + key as keyof typeof unitSelection, + value as boolean + ); + setupPlacement( + index, + key, + Boolean(value) + ); + }} + /> + +
+ ))} +
+ + {/* --- Kalau wilayah dicentang baru tampilkan detail --- */} + {unitSelection.wilayah && ( +
+
+ {["polda", "polres", "satker"].map( + (key, index) => ( +
+ { + handleUnitChange( + key as keyof typeof unitSelection, + value as boolean + ); + setupPlacement( + index, + key, + Boolean(value) + ); + }} + /> + +
+ ) + )} +
+ + {/* --- Custom Button + Dialog --- */} + + + + + + + + Daftar Wilayah Polda dan Polres + + +
+ {listDest.map((polda: any) => { + const poldaChecked = + unitSelection.polda; + const polresChecked = + unitSelection.polres; + const isPoldaDisabled = + poldaChecked; + const isPolresDisabled = + polresChecked; + + return ( +
+ + + {expandedPolda[polda.id] && ( +
+ {polda?.subDestination?.map( + (polres: any) => ( + + ) + )} +
+ )} +
+ ); + })} +
+
+
+
+ )} +
+ {/* {isUserMabesApprover && (
- )} + )} */}
)) diff --git a/components/form/content/teks-detail-form.tsx b/components/form/content/teks-detail-form.tsx index 19804d13..17868d03 100644 --- a/components/form/content/teks-detail-form.tsx +++ b/components/form/content/teks-detail-form.tsx @@ -35,7 +35,7 @@ import { getDataApprovalByMediaUpload, } from "@/service/curated-content/curated-content"; import { Badge } from "@/components/ui/badge"; -import { MailIcon } from "lucide-react"; +import { ChevronDown, ChevronUp, MailIcon } from "lucide-react"; import { Swiper, SwiperSlide } from "swiper/react"; import "swiper/css"; import "swiper/css/free-mode"; @@ -51,6 +51,7 @@ import { Dialog, DialogContent, DialogTitle, + DialogTrigger, } from "@/components/ui/dialog"; import { Textarea } from "@/components/ui/textarea"; import { loading } from "@/config/swal"; @@ -64,6 +65,7 @@ import { formatDateToIndonesian } from "@/utils/globals"; import ApprovalHistoryModal from "@/components/modal/approval-history-modal"; import FileTextPreview from "./file-preview-text"; import FileTextThumbnail from "./file-text-thumbnail"; +import { getUserLevelForAssignments } from "@/service/task"; const imageSchema = z.object({ title: z.string().min(1, { message: "Judul diperlukan" }), @@ -113,19 +115,28 @@ const ViewEditor = dynamic( { ssr: false } ); +interface Destination { + id: string; + name: string; + subDestination?: SubDestination[]; +} + +interface SubDestination { + id: string; + name: string; +} + export default function FormTeksDetail() { const MySwal = withReactContent(Swal); const router = useRouter(); const userId = getCookiesDecrypt("uie"); const userLevelId = getCookiesDecrypt("ulie"); const roleId = getCookiesDecrypt("urie"); - const [modalOpen, setModalOpen] = useState(false); const { id } = useParams() as { id: string }; console.log(id); const editor = useRef(null); type ImageSchema = z.infer; - const [selectedFiles, setSelectedFiles] = useState([]); const taskId = Cookies.get("taskId"); const scheduleId = Cookies.get("scheduleId"); @@ -141,20 +152,146 @@ export default function FormTeksDetail() { const [main, setMain] = useState([]); const [detailThumb, setDetailThumb] = useState([]); const [thumbsSwiper, setThumbsSwiper] = useState(null); - const t = useTranslations("Form"); const [selectedTarget, setSelectedTarget] = useState(""); const [files, setFiles] = useState([]); const [rejectedFiles, setRejectedFiles] = useState([]); const [isMabesApprover, setIsMabesApprover] = useState(false); - const [filePlacements, setFilePlacements] = useState([]); const [isUserMabesApprover, setIsUserMabesApprover] = useState(false); - const [approval, setApproval] = useState(); + const [expandedPolda, setExpandedPolda] = useState([{}]); + const [unitSelection, setUnitSelection] = useState({ + semua: false, + nasional: false, + wilayah: false, + international: false, + polda: false, + polres: false, + satker: false, + }); + const [isLoading, setIsLoading] = useState(false); + const [listDest, setListDest] = useState([]); + const [checkedLevels, setCheckedLevels] = useState(new Set()); let fileTypeId = "3"; + const toggleExpand = (id: number) => { + setExpandedPolda((prev) => ({ + ...prev, + [id]: !prev[id], + })); + }; + + useEffect(() => { + if (detail?.assignedToTopLevel) { + const outputSet = new Set( + detail.assignedToTopLevel.split(",").map(Number) + ); + setUnitSelection({ + semua: outputSet.has(0), + nasional: outputSet.has(1), + wilayah: outputSet.has(2), + international: outputSet.has(3), + polda: outputSet.has(4), + polres: outputSet.has(5), + satker: outputSet.has(6), + }); + } + }, [detail?.fileTypeOutput]); + + useEffect(() => { + async function fetchPoldaPolres() { + setIsLoading(true); + try { + const response = await getUserLevelForAssignments(); + setListDest(response?.data?.data.list); + const initialExpandedState = response?.data?.data.list.reduce( + (acc: any, polda: any) => { + acc[polda.id] = false; + return acc; + }, + {} + ); + setExpandedPolda(initialExpandedState); + console.log("polres", initialExpandedState); + } catch (error) { + console.error("Error fetching Polda/Polres data:", error); + } finally { + setIsLoading(false); + } + } + fetchPoldaPolres(); + }, []); + + useEffect(() => { + const updated = new Set(checkedLevels); + + if (unitSelection.polda) { + listDest.forEach((polda) => { + updated.add(polda.id); + }); + } + + if (unitSelection.polres) { + listDest.forEach((polda) => { + polda?.subDestination?.forEach((polres: any) => { + updated.add(polres.id); + }); + }); + } + + setCheckedLevels(updated); + }, [unitSelection.polda, unitSelection.polres, listDest]); + + const handleUnitChange = ( + key: keyof typeof unitSelection, + value: boolean + ) => { + if (key === "semua") { + const newState = { + semua: value, + nasional: value, + wilayah: value, + international: value, + polda: value, + polres: value, + satker: value, + }; + setUnitSelection(newState); + } else { + const updatedSelection = { + ...unitSelection, + [key]: value, + }; + + const allChecked = [ + "nasional", + "wilayah", + "international", + "polda", + "polres", + "satker", + ].every((k) => updatedSelection[k as keyof typeof unitSelection]); + + updatedSelection.semua = allChecked; + + setUnitSelection(updatedSelection); + } + }; + + const handleCheckboxChangePlacement = (levelId: number) => { + setCheckedLevels((prev: any) => { + const updatedLevels = new Set(prev); + if (updatedLevels.has(levelId)) { + updatedLevels.delete(levelId); + } else { + updatedLevels.add(levelId); + } + return updatedLevels; + }); + }; + const { control, handleSubmit, @@ -248,6 +385,13 @@ export default function FormTeksDetail() { format: details?.files[0]?.format, }); + if (details?.assignedToLevel) { + const levels = new Set( + details.assignedToLevel.split(",").map(Number) + ); + setCheckedLevels(levels); + } + if (details.publishedForObject) { const publisherIds = details.publishedForObject.map( (obj: any) => obj.id @@ -313,9 +457,9 @@ export default function FormTeksDetail() { console.log("getPlaa", filePlacements); const temp = []; for (let i = 0; i < filePlacements?.length; i++) { - if (filePlacements[i].length !== 0) { - const now = filePlacements[i].filter((a) => a !== "all"); - const data = { mediaFileId: files[i].id, placements: now.join(",") }; + if (filePlacements[i]?.length !== 0) { + const now = filePlacements[i]?.filter((a) => a !== "all"); + const data = { mediaFileId: files[i]?.id, placements: now?.join(",") }; temp.push(data); } } @@ -328,6 +472,7 @@ export default function FormTeksDetail() { statusId: status, message: description, files: isUserMabesApprover ? getPlacement() : [], + customLocationPlacement: Array.from(checkedLevels).join(","), }; setModalOpen(false); @@ -367,9 +512,9 @@ export default function FormTeksDetail() { temp[index] = ["all", "mabes", "polda", "international"]; } else { const now = temp[index]; - now.push(placement); - if (now.length === 3 && !now.includes("all")) { - now.push("all"); + now?.push(placement); + if (now?.length === 3 && !now?.includes("all")) { + now?.push("all"); } temp[index] = now; } @@ -377,11 +522,10 @@ export default function FormTeksDetail() { if (placement === "all") { temp[index] = []; } else { - const now = temp[index].filter((a) => a !== placement); - console.log("now", now); + const now = temp[index]?.filter((a) => a !== placement); temp[index] = now; - if (now.length === 3 && now.includes("all")) { - const newData = now.filter((b) => b !== "all"); + if (now?.length === 3 && now?.includes("all")) { + const newData = now?.filter((b) => b !== "all"); temp[index] = newData; } } @@ -472,16 +616,23 @@ export default function FormTeksDetail() { {/* Show the category from details if it doesn't exist in categories list */} - {detail && !categories.find(cat => String(cat.id) === String(detail.category.id)) && ( - - {detail.category.name} - - )} + {detail && + !categories.find( + (cat) => + String(cat.id) === String(detail.category.id) + ) && ( + + {detail.category.name} + + )} {categories.map((category) => ( - + {category.name} ))} @@ -696,7 +847,195 @@ export default function FormTeksDetail() {
- {isUserMabesApprover && ( +
+ {/* --- Checkbox utama --- */} +
+ {[ + "semua", + "nasional", + "wilayah", + "international", + ].map((key, index) => ( +
+ { + handleUnitChange( + key as keyof typeof unitSelection, + value as boolean + ); + setupPlacement( + index, + key, + Boolean(value) + ); + }} + /> + +
+ ))} +
+ + {/* --- Kalau wilayah dicentang baru tampilkan detail --- */} + {unitSelection.wilayah && ( +
+
+ {["polda", "polres", "satker"].map( + (key, index) => ( +
+ { + handleUnitChange( + key as keyof typeof unitSelection, + value as boolean + ); + setupPlacement( + index, + key, + Boolean(value) + ); + }} + /> + +
+ ) + )} +
+ + {/* --- Custom Button + Dialog --- */} + + + + + + + + Daftar Wilayah Polda dan Polres + + +
+ {listDest.map((polda: any) => { + const poldaChecked = + unitSelection.polda; + const polresChecked = + unitSelection.polres; + const isPoldaDisabled = poldaChecked; + const isPolresDisabled = + polresChecked; + + return ( +
+ + + {expandedPolda[polda.id] && ( +
+ {polda?.subDestination?.map( + (polres: any) => ( + + ) + )} +
+ )} +
+ ); + })} +
+
+
+
+ )} +
+ {/* {isUserMabesApprover && (
- )} + )} */}
)) diff --git a/components/form/content/video-detail-form.tsx b/components/form/content/video-detail-form.tsx index 698856c4..0e49164d 100644 --- a/components/form/content/video-detail-form.tsx +++ b/components/form/content/video-detail-form.tsx @@ -35,7 +35,7 @@ import { getDataApprovalByMediaUpload, } from "@/service/curated-content/curated-content"; import { Badge } from "@/components/ui/badge"; -import { MailIcon } from "lucide-react"; +import { ChevronDown, ChevronUp, MailIcon } from "lucide-react"; import { Swiper, SwiperSlide } from "swiper/react"; import "swiper/css"; import "swiper/css/free-mode"; @@ -51,6 +51,7 @@ import { Dialog, DialogContent, DialogTitle, + DialogTrigger, } from "@/components/ui/dialog"; import { Textarea } from "@/components/ui/textarea"; import { loading } from "@/config/swal"; @@ -64,6 +65,7 @@ import SuggestionModal from "@/components/modal/suggestions-modal"; import { formatDateToIndonesian } from "@/utils/globals"; import ApprovalHistoryModal from "@/components/modal/approval-history-modal"; import VideoPlayer from "@/utils/video-player"; +import { getUserLevelForAssignments } from "@/service/task"; const imageSchema = z.object({ title: z.string().min(1, { message: "Judul diperlukan" }), @@ -114,19 +116,27 @@ const ViewEditor = dynamic( { ssr: false } ); +interface Destination { + id: string; + name: string; + subDestination?: SubDestination[]; +} + +interface SubDestination { + id: string; + name: string; +} + export default function FormVideoDetail() { const MySwal = withReactContent(Swal); const router = useRouter(); const userId = getCookiesDecrypt("uie"); const userLevelId = getCookiesDecrypt("ulie"); const roleId = getCookiesDecrypt("urie"); - const [modalOpen, setModalOpen] = useState(false); const { id } = useParams() as { id: string }; - console.log(id); const editor = useRef(null); type ImageSchema = z.infer; - const [selectedFiles, setSelectedFiles] = useState([]); const taskId = Cookies.get("taskId"); const scheduleId = Cookies.get("scheduleId"); @@ -142,7 +152,6 @@ export default function FormVideoDetail() { const [main, setMain] = useState([]); const [detailVideo, setDetailVideo] = useState([]); const [thumbsSwiper, setThumbsSwiper] = useState(null); - const t = useTranslations("Form"); const [filePlacements, setFilePlacements] = useState([]); const [selectedTarget, setSelectedTarget] = useState(""); @@ -150,9 +159,138 @@ export default function FormVideoDetail() { const [rejectedFiles, setRejectedFiles] = useState([]); const [isUserMabesApprover, setIsUserMabesApprover] = useState(false); const [approval, setApproval] = useState(); + const [expandedPolda, setExpandedPolda] = useState([{}]); + const [unitSelection, setUnitSelection] = useState({ + semua: false, + nasional: false, + wilayah: false, + international: false, + polda: false, + polres: false, + satker: false, + }); + const [isLoading, setIsLoading] = useState(false); + const [listDest, setListDest] = useState([]); + const [checkedLevels, setCheckedLevels] = useState(new Set()); let fileTypeId = "2"; + const toggleExpand = (id: number) => { + setExpandedPolda((prev) => ({ + ...prev, + [id]: !prev[id], + })); + }; + + useEffect(() => { + if (detail?.assignedToTopLevel) { + const outputSet = new Set( + detail.assignedToTopLevel.split(",").map(Number) + ); + setUnitSelection({ + semua: outputSet.has(0), + nasional: outputSet.has(1), + wilayah: outputSet.has(2), + international: outputSet.has(3), + polda: outputSet.has(4), + polres: outputSet.has(5), + satker: outputSet.has(6), + }); + } + }, [detail?.fileTypeOutput]); + + useEffect(() => { + async function fetchPoldaPolres() { + setIsLoading(true); + try { + const response = await getUserLevelForAssignments(); + setListDest(response?.data?.data.list); + const initialExpandedState = response?.data?.data.list.reduce( + (acc: any, polda: any) => { + acc[polda.id] = false; + return acc; + }, + {} + ); + setExpandedPolda(initialExpandedState); + console.log("polres", initialExpandedState); + } catch (error) { + console.error("Error fetching Polda/Polres data:", error); + } finally { + setIsLoading(false); + } + } + fetchPoldaPolres(); + }, []); + + useEffect(() => { + const updated = new Set(checkedLevels); + + if (unitSelection.polda) { + listDest.forEach((polda) => { + updated.add(polda.id); + }); + } + + if (unitSelection.polres) { + listDest.forEach((polda) => { + polda?.subDestination?.forEach((polres: any) => { + updated.add(polres.id); + }); + }); + } + + setCheckedLevels(updated); + }, [unitSelection.polda, unitSelection.polres, listDest]); + + const handleUnitChange = ( + key: keyof typeof unitSelection, + value: boolean + ) => { + if (key === "semua") { + const newState = { + semua: value, + nasional: value, + wilayah: value, + international: value, + polda: value, + polres: value, + satker: value, + }; + setUnitSelection(newState); + } else { + const updatedSelection = { + ...unitSelection, + [key]: value, + }; + + const allChecked = [ + "nasional", + "wilayah", + "international", + "polda", + "polres", + "satker", + ].every((k) => updatedSelection[k as keyof typeof unitSelection]); + + updatedSelection.semua = allChecked; + + setUnitSelection(updatedSelection); + } + }; + + const handleCheckboxChangePlacement = (levelId: number) => { + setCheckedLevels((prev: any) => { + const updatedLevels = new Set(prev); + if (updatedLevels.has(levelId)) { + updatedLevels.delete(levelId); + } else { + updatedLevels.add(levelId); + } + return updatedLevels; + }); + }; + const { control, handleSubmit, @@ -238,6 +376,13 @@ export default function FormVideoDetail() { format: details?.files[0]?.format, }); + if (details?.assignedToLevel) { + const levels = new Set( + details.assignedToLevel.split(",").map(Number) + ); + setCheckedLevels(levels); + } + if (details?.publishedForObject) { const publisherIds = details.publishedForObject.map( (obj: any) => obj.id @@ -303,6 +448,7 @@ export default function FormVideoDetail() { statusId: status, message: description, files: isUserMabesApprover ? getPlacement() : [], + customLocationPlacement: Array.from(checkedLevels).join(","), }; setModalOpen(false); loading(); @@ -354,9 +500,9 @@ export default function FormVideoDetail() { temp[index] = ["all", "mabes", "polda", "international"]; } else { const now = temp[index]; - now.push(placement); - if (now.length === 3 && !now.includes("all")) { - now.push("all"); + now?.push(placement); + if (now?.length === 3 && !now.includes("all")) { + now?.push("all"); } temp[index] = now; } @@ -367,7 +513,7 @@ export default function FormVideoDetail() { const now = temp[index].filter((a) => a !== placement); console.log("now", now); temp[index] = now; - if (now.length === 3 && now.includes("all")) { + if (now?.length === 3 && now.includes("all")) { const newData = now.filter((b) => b !== "all"); temp[index] = newData; } @@ -715,7 +861,195 @@ export default function FormVideoDetail() {
- {isUserMabesApprover && ( +
+ {/* --- Checkbox utama --- */} +
+ {[ + "semua", + "nasional", + "wilayah", + "international", + ].map((key, index) => ( +
+ { + handleUnitChange( + key as keyof typeof unitSelection, + value as boolean + ); + setupPlacement( + index, + key, + Boolean(value) + ); + }} + /> + +
+ ))} +
+ + {/* --- Kalau wilayah dicentang baru tampilkan detail --- */} + {unitSelection.wilayah && ( +
+
+ {["polda", "polres", "satker"].map( + (key, index) => ( +
+ { + handleUnitChange( + key as keyof typeof unitSelection, + value as boolean + ); + setupPlacement( + index, + key, + Boolean(value) + ); + }} + /> + +
+ ) + )} +
+ + {/* --- Custom Button + Dialog --- */} + + + + + + + + Daftar Wilayah Polda dan Polres + + +
+ {listDest.map((polda: any) => { + const poldaChecked = + unitSelection.polda; + const polresChecked = + unitSelection.polres; + const isPoldaDisabled = poldaChecked; + const isPolresDisabled = + polresChecked; + + return ( +
+ + + {expandedPolda[polda.id] && ( +
+ {polda?.subDestination?.map( + (polres: any) => ( + + ) + )} +
+ )} +
+ ); + })} +
+
+
+
+ )} +
+ {/* {isUserMabesApprover && (
- )} + )} */}
)) diff --git a/components/form/task/task-edit-form.tsx b/components/form/task/task-edit-form.tsx index d0304471..833ee76f 100644 --- a/components/form/task/task-edit-form.tsx +++ b/components/form/task/task-edit-form.tsx @@ -318,7 +318,7 @@ export default function FormTaskEdit() { }; const handlePoldaPolresChange = () => { - return Array.from(checkedLevels).join(","); // Mengonversi Set ke string + return Array.from(checkedLevels).join(","); }; const handleUnitChange = ( @@ -471,9 +471,9 @@ export default function FormTaskEdit() { await uploadResumableFile( index, String(id), - item, // Use .file to access the actual File object + item, "4", - "0" // Optional: Replace with actual duration if available + "0" ); }); @@ -808,11 +808,11 @@ export default function FormTaskEdit() {
{listDest.map((polda: any) => { - const poldaChecked = unitSelection.polda; // kontrol polda luar - const polresChecked = unitSelection.polres; // kontrol polres luar + const poldaChecked = unitSelection.polda; + const polresChecked = unitSelection.polres; - const isPoldaDisabled = poldaChecked; // lock checkbox polda dialog jika polda luar dicentang - const isPolresDisabled = polresChecked; // lock checkbox polres dialog jika polres luar dicentang + const isPoldaDisabled = poldaChecked; + const isPolresDisabled = polresChecked; return (
@@ -820,7 +820,7 @@ export default function FormTaskEdit() { { if (isPoldaDisabled) return; @@ -852,7 +852,7 @@ export default function FormTaskEdit() { checked={ polresChecked || checkedLevels.has(polres.id) - } // auto-centang jika polres luar dicentang + } disabled={isPolresDisabled} onCheckedChange={() => { if (isPolresDisabled) return; @@ -876,7 +876,7 @@ export default function FormTaskEdit() {
setMainType(value)} // value={String(mainType)} // onValueChange={(value) => setMainType(Number(value))} @@ -893,7 +893,7 @@ export default function FormTaskEdit() { {t("assigment-type", { defaultValue: "Assigment Type" })}{" "} setTaskType(value)} className="flex flex-wrap gap-3" > diff --git a/service/content/content.ts b/service/content/content.ts index 5486f765..14fddef0 100644 --- a/service/content/content.ts +++ b/service/content/content.ts @@ -63,7 +63,7 @@ export async function listDataImage( isInt: boolean = false ) { return await httpGetInterceptor( - `media/list?enablePage=1&size=${size}&sortBy=createdAt&sort=desc&page=${page}&typeId=1&isForSelf=${isForSelf}&isApproval=${isApproval}&categoryId=${categoryFilter}&statusId=${statusFilter}&creatorUserLevelName=${source}&creatorName=${creator}&startDate=${startDate}&endDate=${endDate}&title=${title}&creatorGroupLevelName=${creatorGroup}&needApprovalFromLevel=${needApprovalFromLevel}&isInt=${isInt}` + `media/list?enablePage=1&size=${size}&sortBy=createdAt&sort=desc&page=${page}&typeId=1&isForSelf=${isForSelf}&isApproval=${isApproval}&categoryId=${categoryFilter}&statusId=${statusFilter}&creatorUserLevelName=${source}&creatorName=${creator}&startDate=${startDate}&endDate=${endDate}&title=${title}&creatorGroupLevelName=${creatorGroup}&needApprovalFromLevel=${needApprovalFromLevel}&isInt=${isInt}&isPublish=` ); } From c12dbf159699909b3523e13c554e0110060b0bb3 Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Tue, 19 Aug 2025 07:54:40 +0700 Subject: [PATCH 2/9] update : selection box user levels --- .../form/contest/contest-detail-form.tsx | 234 +- docs/USER_LEVEL_RESPONSE.json | 3554 +++++++++++++++++ 2 files changed, 3783 insertions(+), 5 deletions(-) create mode 100644 docs/USER_LEVEL_RESPONSE.json diff --git a/components/form/contest/contest-detail-form.tsx b/components/form/contest/contest-detail-form.tsx index 7db533cb..496e8357 100644 --- a/components/form/contest/contest-detail-form.tsx +++ b/components/form/contest/contest-detail-form.tsx @@ -118,9 +118,9 @@ export default function FormContestDetail() { const [detail, setDetail] = useState(); const [refresh] = useState(false); const [date, setDate] = useState(); - const [listDest, setListDest] = useState([]); - const [checkedLevels, setCheckedLevels] = useState(new Set()); - const [expandedPolda, setExpandedPolda] = useState([{}]); + const [listDest, setListDest] = useState([]); + const [checkedLevels, setCheckedLevels] = useState>(new Set()); + const [expandedPolda, setExpandedPolda] = useState>({}); const [isLoading, setIsLoading] = useState(false); const [audioFile, setAudioFile] = useState(null); const [imageFiles, setImageFiles] = useState([]); @@ -153,6 +153,11 @@ export default function FormContestDetail() { satker: false, }); + // State untuk melacak apakah perubahan berasal dari checkbox Pelaksana Tugas + const [isUpdatingFromPelaksana, setIsUpdatingFromPelaksana] = useState(false); + // State untuk melacak jenis perubahan spesifik + const [pelaksanaChangeType, setPelaksanaChangeType] = useState(""); + const { control, handleSubmit, @@ -192,6 +197,16 @@ export default function FormContestDetail() { fetchPoldaPolres(); }, []); + // useEffect untuk sinkronisasi checkbox modal dengan Pelaksana Tugas + // Ketika unitSelection berubah dari checkbox Pelaksana Tugas: + // - Jika di-checklist: checklist semua item sesuai kategori di modal + // - Jika di-unchecklist: unchecklist semua item di modal + useEffect(() => { + if (listDest.length > 0) { + syncModalWithUnitSelection(); + } + }, [unitSelection, listDest]); + useEffect(() => { async function initState() { if (id) { @@ -237,6 +252,61 @@ export default function FormContestDetail() { } }, [detail?.targetOutput]); + // Fungsi untuk update unitSelection berdasarkan checkbox modal + // Checkbox di Pelaksana Tugas hanya akan aktif jika SEMUA item dalam kategori tersebut dichecklist + const updateUnitSelectionFromModal = (levelId: number) => { + setTimeout(() => { + // Hitung total item yang tersedia untuk setiap kategori + const totalPolda = listDest.filter((item: any) => + item.levelNumber === 2 && item.name !== "SATKER POLRI" + ).length; + + const totalPolres = listDest.reduce((total: number, item: any) => { + if (item.subDestination) { + return total + item.subDestination.length; + } + return total; + }, 0); + + const satkerItem = listDest.find((item: any) => item.name === "SATKER POLRI"); + const totalSatker = satkerItem ? (1 + (satkerItem.subDestination?.length || 0)) : 0; + + // Hitung item yang dichecklist untuk setiap kategori + const checkedPoldaCount = listDest.filter((item: any) => + item.levelNumber === 2 && + item.name !== "SATKER POLRI" && + checkedLevels.has(item.id) + ).length; + + const checkedPolresCount = listDest.reduce((total: number, item: any) => { + if (item.subDestination) { + return total + item.subDestination.filter((sub: any) => checkedLevels.has(sub.id)).length; + } + return total; + }, 0); + + const checkedSatkerCount = satkerItem ? ( + (checkedLevels.has(satkerItem.id) ? 1 : 0) + + (satkerItem.subDestination?.filter((sub: any) => checkedLevels.has(sub.id)).length || 0) + ) : 0; + + // Checkbox hanya aktif jika SEMUA item dalam kategori tersebut dichecklist + const hasCheckedPolda = totalPolda > 0 && checkedPoldaCount === totalPolda; + const hasCheckedPolres = totalPolres > 0 && checkedPolresCount === totalPolres; + const hasCheckedSatker = totalSatker > 0 && checkedSatkerCount === totalSatker; + + // Update unitSelection berdasarkan checkbox yang aktif di modal + setUnitSelection(prev => ({ + ...prev, + polda: hasCheckedPolda, + polres: hasCheckedPolres, + satker: hasCheckedSatker, + // allUnit hanya true jika semua kategori terpenuhi + allUnit: hasCheckedPolda && hasCheckedPolres && hasCheckedSatker + })); + }, 0); + }; + const handleCheckboxChange = (levelId: number) => { setCheckedLevels((prev) => { const updatedLevels = new Set(prev); @@ -247,6 +317,85 @@ export default function FormContestDetail() { } return updatedLevels; }); + + // Update unitSelection berdasarkan perubahan di modal + updateUnitSelectionFromModal(levelId); + }; + + // Fungsi untuk sinkronisasi checkbox modal dengan Pelaksana Tugas + const syncModalWithUnitSelection = () => { + // Hanya jalankan sinkronisasi jika perubahan berasal dari checkbox Pelaksana Tugas + if (isUpdatingFromPelaksana) { + // Khusus untuk unchecklist POLRES: hanya unchecklist polres, pertahankan polda + if (pelaksanaChangeType === "polres_unchecked") { + const newCheckedLevels = new Set(checkedLevels); + + // Hapus semua polres dari modal, tapi pertahankan polda + listDest.forEach((item: any) => { + if (item.subDestination && item.levelNumber === 2 && item.name !== "SATKER POLRI") { + item.subDestination.forEach((polres: any) => { + newCheckedLevels.delete(polres.id); + }); + } + }); + + setCheckedLevels(newCheckedLevels); + } + // Untuk perubahan lainnya, jalankan logika normal + else if (unitSelection.polda || unitSelection.polres || unitSelection.satker) { + // Mulai dengan checkbox yang sudah ada untuk mempertahankan pilihan manual user + const newCheckedLevels = new Set(checkedLevels); + + listDest.forEach((item: any) => { + // Jika polda dichecklist, checklist semua polda (levelNumber 2, bukan SATKER POLRI) + if (unitSelection.polda && item.levelNumber === 2 && item.name !== "SATKER POLRI") { + newCheckedLevels.add(item.id); + } + + // Jika satker dichecklist, checklist SATKER POLRI dan sub-itemnya + if (unitSelection.satker && item.name === "SATKER POLRI") { + newCheckedLevels.add(item.id); + if (item.subDestination) { + item.subDestination.forEach((sub: any) => { + newCheckedLevels.add(sub.id); + }); + } + } + + // Jika polres dichecklist + if (unitSelection.polres && item.subDestination) { + // Jika checkbox POLDA di Pelaksana Tugas juga aktif, checklist semua polres + if (unitSelection.polda && item.levelNumber === 2 && item.name !== "SATKER POLRI") { + item.subDestination.forEach((polres: any) => { + newCheckedLevels.add(polres.id); + }); + } + // Jika checkbox POLDA di Pelaksana Tugas tidak aktif, tapi ada POLDA yang dichecklist di modal + else if (!unitSelection.polda && item.levelNumber === 2 && item.name !== "SATKER POLRI") { + // Cek apakah POLDA ini sudah dichecklist di modal + if (checkedLevels.has(item.id)) { + // Jika ya, checklist semua polres dari POLDA ini + item.subDestination.forEach((polres: any) => { + newCheckedLevels.add(polres.id); + }); + } + } + } + }); + + setCheckedLevels(newCheckedLevels); + } else { + // Jika tidak ada unitSelection yang aktif, unchecklist semua item di modal + // Setelah itu user bisa checklist secara manual + setCheckedLevels(new Set()); + } + + // Reset flag setelah sinkronisasi selesai + setTimeout(() => { + setIsUpdatingFromPelaksana(false); + setPelaksanaChangeType(""); + }, 100); + } }; const handlePoldaPolresChange = () => { @@ -706,6 +855,10 @@ export default function FormContestDetail() { id={key} checked={unitSelection[key as keyof typeof unitSelection]} onCheckedChange={(value) => { + // Set flag bahwa perubahan berasal dari checkbox Pelaksana Tugas + setIsUpdatingFromPelaksana(true); + setPelaksanaChangeType(key + (value ? "_checked" : "_unchecked")); + if (key === "allUnit") { const newValue = Boolean(value); setUnitSelection({ @@ -716,6 +869,22 @@ export default function FormContestDetail() { satker: newValue, }); } else { + // Validasi khusus untuk POLRES + if (key === "polres" && value) { + // Cek apakah ada POLDA yang sudah dichecklist di modal + const hasCheckedPolda = listDest.some((item: any) => + item.levelNumber === 2 && + item.name !== "SATKER POLRI" && + checkedLevels.has(item.id) + ); + + if (!hasCheckedPolda) { + // Jika tidak ada POLDA yang dichecklist di modal, tampilkan peringatan dan batalkan + alert("Harap pilih POLDA di Modal List terlebih dahulu sebelum mengaktifkan checkbox POLRES."); + return; // Batalkan perubahan + } + } + setUnitSelection((prev) => { const updated = { ...prev, [key]: Boolean(value) }; // Update 'allUnit' jika semua sub-checkbox true @@ -758,7 +927,12 @@ export default function FormContestDetail() { /> {polda.name}
))} @@ -897,7 +907,7 @@ export default function FormContestDetail() { }} />
))} From 9fc973d15130fd95d94f824b075af3cdac9898e9 Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Tue, 19 Aug 2025 10:01:29 +0700 Subject: [PATCH 4/9] update : selection box user levels on agenda-settings and task --- .../agenda-setting/event-modal.tsx | 471 ++++++++++++++++-- components/form/task/task-form.tsx | 262 ++++++++-- 2 files changed, 638 insertions(+), 95 deletions(-) diff --git a/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx b/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx index 9840e9b4..57445c81 100644 --- a/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx +++ b/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx @@ -108,8 +108,8 @@ const EventModal = ({ const router = useRouter(); const pathname = usePathname(); const [isLoading, setIsLoading] = useState(false); - const [checkedLevels, setCheckedLevels] = useState(new Set()); - const [expandedPolda, setExpandedPolda] = useState([{}]); + const [checkedLevels, setCheckedLevels] = useState>(new Set()); + const [expandedPolda, setExpandedPolda] = useState>({}); const [audioFile, setAudioFile] = useState(null); const [isRecording, setIsRecording] = useState(false); const [timer, setTimer] = useState(120); @@ -151,7 +151,13 @@ const EventModal = ({ satker: false, international: false, }); - const levelNumber = getCookiesDecrypt("ulne"); + + // State untuk melacak apakah perubahan berasal dari checkbox Jenis Agenda + const [isUpdatingFromJenisAgenda, setIsUpdatingFromJenisAgenda] = useState(false); + // State untuk melacak jenis perubahan spesifik + const [jenisAgendaChangeType, setJenisAgendaChangeType] = useState(""); + + const levelNumber = Number(getCookiesDecrypt("ulne")) || 0; const userLevelId = getCookiesDecrypt("ulie"); const poldaState = Cookies.get("state"); const [agendaType, setAgendaType] = React.useState(""); @@ -253,15 +259,242 @@ const EventModal = ({ fetchDetailData(); }, [event, setValue]); + // useEffect untuk sinkronisasi checkbox modal dengan Jenis Agenda + useEffect(() => { + if (listDest.length > 0 && isUpdatingFromJenisAgenda && jenisAgendaChangeType) { + syncModalWithJenisAgenda(); + } + }, [isUpdatingFromJenisAgenda, jenisAgendaChangeType]); + + // useEffect untuk update wilayahPublish ketika pilihan modal berubah + useEffect(() => { + if (!isUpdatingFromJenisAgenda && listDest.length > 0) { + updateWilayahPublishFromModal(); + } + }, [checkedLevels, isUpdatingFromJenisAgenda]); + + // Fungsi untuk update wilayahPublish berdasarkan checkbox modal + const updateWilayahPublishFromModal = () => { + // Hanya update jika tidak sedang dalam proses update dari Jenis Agenda + if (!isUpdatingFromJenisAgenda && listDest.length > 0) { + // Hitung item yang dipilih berdasarkan checkedLevels + const checkedPoldaCount = listDest.filter((item: any) => + item.levelNumber === 2 && + item.name !== "SATKER POLRI" && + checkedLevels.has(Number(item.id)) + ).length; + + const checkedPolresCount = listDest.reduce((total: number, item: any) => { + if (item.subDestination) { + return total + item.subDestination.filter((sub: any) => checkedLevels.has(Number(sub.id))).length; + } + return total; + }, 0); + + const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI"); + const checkedSatkerCount = satkerItem ? ( + (checkedLevels.has(Number(satkerItem.id)) ? 1 : 0) + + (satkerItem.subDestination?.filter((sub: any) => checkedLevels.has(Number(sub.id))).length || 0) + ) : 0; + + // Checkbox aktif jika ADA item yang dipilih dalam kategori tersebut + const hasSelectedPolda = checkedPoldaCount > 0; + const hasSelectedPolres = checkedPolresCount > 0; + const hasSelectedSatker = checkedSatkerCount > 0; + + // Update arrays untuk backend + const newSelectedPolda = listDest + .filter((item: any) => + item.levelNumber === 2 && + item.name !== "SATKER POLRI" && + checkedLevels.has(Number(item.id)) + ) + .map((item: any) => String(item.id)); + + const newSelectedPolres: string[] = []; + listDest.forEach((item: any) => { + if (item.subDestination) { + item.subDestination.forEach((sub: any) => { + if (checkedLevels.has(Number(sub.id))) { + newSelectedPolres.push(String(sub.id)); + } + }); + } + }); + + const newSelectedSatker: string[] = []; + if (satkerItem) { + if (checkedLevels.has(Number(satkerItem.id))) { + newSelectedSatker.push(String(satkerItem.id)); + } + if (satkerItem.subDestination) { + satkerItem.subDestination.forEach((sub: any) => { + if (checkedLevels.has(Number(sub.id))) { + newSelectedSatker.push(String(sub.id)); + } + }); + } + } + + // Update state arrays + setSelectedPolda(newSelectedPolda); + setSelectedPolres(newSelectedPolres); + setSelectedSatker(newSelectedSatker); + + // Update wilayahPublish berdasarkan yang dipilih di modal + setWilayahPublish(prev => { + const newState = { ...prev }; + + // Update individual checkboxes + newState.polda = hasSelectedPolda; + newState.polres = hasSelectedPolres; + newState.satker = hasSelectedSatker; + + // Update checkbox "semua" berdasarkan level user + if (levelNumber === 1) { + // Level 1: semua checkbox harus aktif (nasional, polda, polres, satker, international) + newState.semua = newState.nasional && hasSelectedPolda && hasSelectedPolres && hasSelectedSatker && newState.international; + } else if (levelNumber === 2) { + // Level 2: hanya polres yang perlu aktif + newState.semua = hasSelectedPolres; + } else { + newState.semua = false; + } + + return newState; + }); + + // Update agendaType berdasarkan checkbox yang aktif + const selectedKeys = []; + if (hasSelectedPolda) selectedKeys.push(wilayahValueMap.polda); + if (hasSelectedPolres) selectedKeys.push(wilayahValueMap.polres); + if (hasSelectedSatker) selectedKeys.push(wilayahValueMap.satker); + + setAgendaType(selectedKeys.join(",")); + } + }; + + // Fungsi untuk sinkronisasi checkbox modal dengan Jenis Agenda + const syncModalWithJenisAgenda = () => { + // Hanya jalankan sinkronisasi jika perubahan berasal dari checkbox Jenis Agenda + if (isUpdatingFromJenisAgenda) { + const newCheckedLevels = new Set(checkedLevels); + + // Handle checklist actions - menambahkan semua item ke modal + if (jenisAgendaChangeType === "polda_checked") { + // Checklist semua polda + listDest.forEach((item: any) => { + if (item.levelNumber === 2 && item.name !== "SATKER POLRI") { + newCheckedLevels.add(Number(item.id)); + } + }); + } else if (jenisAgendaChangeType === "polres_checked") { + // Checklist semua polres, tapi hanya yang poldanya sudah di-checklist + listDest.forEach((item: any) => { + if (item.levelNumber === 2 && item.name !== "SATKER POLRI" && newCheckedLevels.has(Number(item.id))) { + if (item.subDestination) { + item.subDestination.forEach((polres: any) => { + newCheckedLevels.add(Number(polres.id)); + }); + } + } + }); + } else if (jenisAgendaChangeType === "satker_checked") { + // Checklist satker + const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI"); + if (satkerItem) { + newCheckedLevels.add(Number(satkerItem.id)); + if (satkerItem.subDestination) { + satkerItem.subDestination.forEach((sub: any) => { + newCheckedLevels.add(Number(sub.id)); + }); + } + } + } + // Handle unchecklist actions - menghapus item dari modal + else if (jenisAgendaChangeType === "polres_unchecked") { + // Clear polres dari checkedLevels + listDest.forEach((item: any) => { + if (item.subDestination) { + item.subDestination.forEach((polres: any) => { + newCheckedLevels.delete(Number(polres.id)); + }); + } + }); + } else if (jenisAgendaChangeType === "polda_unchecked") { + // Clear polda dan polres dari checkedLevels + listDest.forEach((item: any) => { + if (item.levelNumber === 2 && item.name !== "SATKER POLRI") { + newCheckedLevels.delete(Number(item.id)); + // Juga clear polres dari polda ini + if (item.subDestination) { + item.subDestination.forEach((polres: any) => { + newCheckedLevels.delete(Number(polres.id)); + }); + } + } + }); + setWilayahPublish(prev => ({ ...prev, polres: false })); + } else if (jenisAgendaChangeType === "satker_unchecked") { + // Clear satker dari checkedLevels + const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI"); + if (satkerItem) { + newCheckedLevels.delete(Number(satkerItem.id)); + if (satkerItem.subDestination) { + satkerItem.subDestination.forEach((sub: any) => { + newCheckedLevels.delete(Number(sub.id)); + }); + } + } + } + + setCheckedLevels(newCheckedLevels); + + // Reset flag setelah sinkronisasi selesai + setIsUpdatingFromJenisAgenda(false); + setJenisAgendaChangeType(""); + } + }; + useEffect(() => { setIsDatePickerOpen(false); }, [onClose]); + useEffect(() => { + async function fetchPoldaPolres() { + try { + const response = await getUserLevelForAssignments(); + setListDest(response?.data?.data.list); + const initialExpandedState = response?.data?.data.list.reduce( + (acc: any, polda: any) => { + acc[polda.id] = false; + return acc; + }, + {} + ); + setExpandedPolda(initialExpandedState); + } catch (error) { + console.error("Error fetching Polda/Polres data:", error); + } + } + fetchPoldaPolres(); + }, []); + const handleCheckboxChange = (levelId: number) => { setCheckedLevels((prev) => { const updatedLevels = new Set(prev); - if (updatedLevels.has(levelId)) { + const isCurrentlyChecked = updatedLevels.has(levelId); + + if (isCurrentlyChecked) { updatedLevels.delete(levelId); + + // Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya + const poldaItem = listDest.find((item: any) => Number(item.id) === levelId) as any; + if (poldaItem && poldaItem.subDestination) { + poldaItem.subDestination.forEach((polres: any) => { + updatedLevels.delete(Number(polres.id)); + }); + } } else { updatedLevels.add(levelId); } @@ -279,6 +512,10 @@ const EventModal = ({ }; const toggleWilayah = (key: string) => { + // Set flag bahwa perubahan berasal dari checkbox Jenis Agenda + setIsUpdatingFromJenisAgenda(true); + setJenisAgendaChangeType(key + (wilayahPublish[key as keyof typeof wilayahPublish] ? "_unchecked" : "_checked")); + setWilayahPublish((prev: any) => { let newState = { ...prev }; if (key === "semua") { @@ -294,15 +531,96 @@ const EventModal = ({ if (newChecked) { setAgendaType("0,1,2,3,4,5"); + // Checklist semua item di modal ketika "semua" di-checklist + const allCheckedLevels = new Set(); + listDest.forEach((item: any) => { + allCheckedLevels.add(Number(item.id)); + if (item.subDestination) { + item.subDestination.forEach((sub: any) => { + allCheckedLevels.add(Number(sub.id)); + }); + } + }); + setCheckedLevels(allCheckedLevels); } else { setAgendaType(""); + // Clear semua pilihan modal ketika "semua" di-unchecklist + setCheckedLevels(new Set()); } return newState; } + // Validasi khusus untuk POLRES + if (key === "polres" && !prev[key]) { + // Cek apakah ada POLDA yang sudah dipilih di modal + const hasSelectedPolda = listDest.some((item: any) => + item.levelNumber === 2 && + item.name !== "SATKER POLRI" && + checkedLevels.has(Number(item.id)) + ); + + if (!hasSelectedPolda) { + // Jika tidak ada POLDA yang dipilih, tampilkan peringatan dan batalkan + alert("Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."); + // Reset flag karena perubahan dibatalkan + setIsUpdatingFromJenisAgenda(false); + setJenisAgendaChangeType(""); + return prev; // Batalkan perubahan + } + } + newState[key] = !prev[key]; - newState.semua = false; + + // Jika checkbox di-unchecklist, clear pilihan modal yang sesuai + if (prev[key]) { + const newCheckedLevels = new Set(checkedLevels); + if (key === "polda") { + // Clear polda dan polres + listDest.forEach((item: any) => { + if (item.levelNumber === 2 && item.name !== "SATKER POLRI") { + newCheckedLevels.delete(Number(item.id)); + if (item.subDestination) { + item.subDestination.forEach((polres: any) => { + newCheckedLevels.delete(Number(polres.id)); + }); + } + } + }); + } else if (key === "polres") { + // Clear polres + listDest.forEach((item: any) => { + if (item.subDestination) { + item.subDestination.forEach((polres: any) => { + newCheckedLevels.delete(Number(polres.id)); + }); + } + }); + } else if (key === "satker") { + // Clear satker + const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI"); + if (satkerItem) { + newCheckedLevels.delete(Number(satkerItem.id)); + if (satkerItem.subDestination) { + satkerItem.subDestination.forEach((sub: any) => { + newCheckedLevels.delete(Number(sub.id)); + }); + } + } + } + setCheckedLevels(newCheckedLevels); + } + + // Update checkbox "semua" berdasarkan status semua checkbox lainnya + // Untuk level 1: semua, nasional, polda, polres, satker, international harus aktif + // Untuk level 2: semua, polres harus aktif + if (levelNumber === 1) { + newState.semua = newState.nasional && newState.polda && newState.polres && newState.satker && newState.international; + } else if (levelNumber === 2) { + newState.semua = newState.polres; + } else { + newState.semua = false; + } const selectedKeys = Object.entries(newState) .filter(([k, v]) => v && k !== "semua") @@ -452,7 +770,7 @@ const EventModal = ({ const onDeleteEventAction = async () => { try { - } catch (error) {} + } catch (error) { } }; const handleOpenDeleteModal = (eventId: string) => { @@ -632,7 +950,7 @@ const EventModal = ({ ); }; - const handleRemoveFile = (id: number) => {}; + const handleRemoveFile = (id: number) => { }; async function doDelete(id: any) { loading(); @@ -800,7 +1118,8 @@ const EventModal = ({
- {roleId === 1 && ( + {levelNumber === 1 && ( + <>
- )} -
Polda - {wilayahPublish.polda && ( - - setSelectedPolda(data) - } - /> - )}
- {(roleId === 1 || roleId === 4 || roleId === 3) && ( + + )} + + {(levelNumber === 1 || levelNumber === 2) && (
Polres - {wilayahPublish.polres && ( - - setSelectedPolres(data) - } - /> - )}
)} - {(roleId === 1 || roleId === 2) && ( + + {levelNumber === 1 && ( + <>
Satker - {wilayahPublish.satker && ( - - setSelectedSatker(data) - } - /> - )}
- )} - {roleId === 1 && (
+ )} + +
+ + + + + + + + Daftar Wilayah Polda dan Polres + + +
+ {listDest?.map((polda: any) => ( +
+ + {expandedPolda[polda.id] && ( +
+ + {polda?.subDestination?.map((polres: any) => ( + + ))} +
+ )} +
+ ))} +
+
+
+
@@ -1102,8 +1482,7 @@ const EventModal = ({ type="button" onClick={onPlayPause} disabled={isPlaying} - className={`flex items-center gap-2 ${ - isPlaying + className={`flex items-center gap-2 ${isPlaying ? "bg-gray-300 cursor-not-allowed" : "bg-primary text-white" } p-2 rounded`} diff --git a/components/form/task/task-form.tsx b/components/form/task/task-form.tsx index fa8ef43c..7a360d0d 100644 --- a/components/form/task/task-form.tsx +++ b/components/form/task/task-form.tsx @@ -122,7 +122,7 @@ export default function FormTask() { const [detail, setDetail] = useState(); const [refresh] = useState(false); // const [listDest, setListDest] = useState([]); - const [checkedLevels, setCheckedLevels] = useState(new Set()); + const [checkedLevels, setCheckedLevels] = useState>(new Set()); const [expandedPolda, setExpandedPolda] = useState([{}]); const [isLoading, setIsLoading] = useState(false); const [audioFile, setAudioFile] = useState(null); @@ -146,6 +146,12 @@ export default function FormTask() { polres: false, satker: false, }); + + // State untuk melacak apakah perubahan berasal dari checkbox Penerima Tugas + const [isUpdatingFromPenerimaTugas, setIsUpdatingFromPenerimaTugas] = useState(false); + // State untuk melacak jenis perubahan spesifik + const [penerimaTugasChangeType, setPenerimaTugasChangeType] = useState(""); + const [links, setLinks] = useState([""]); const { register, @@ -194,13 +200,26 @@ export default function FormTask() { const handleCheckboxChange = (levelId: number) => { setCheckedLevels((prev) => { const updatedLevels = new Set(prev); - if (updatedLevels.has(levelId)) { + const isCurrentlyChecked = updatedLevels.has(levelId); + + if (isCurrentlyChecked) { updatedLevels.delete(levelId); + + // Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya + const poldaItem = listDest.find((item: any) => Number(item.id) === levelId); + if (poldaItem && poldaItem.subDestination) { + poldaItem.subDestination.forEach((polres: any) => { + updatedLevels.delete(Number(polres.id)); + }); + } } else { updatedLevels.add(levelId); } return updatedLevels; }); + + // Update unitSelection berdasarkan perubahan di modal + updateUnitSelectionFromModal(); }; const handlePoldaPolresChange = () => { @@ -211,6 +230,10 @@ export default function FormTask() { key: keyof typeof unitSelection, value: boolean ) => { + // Set flag bahwa perubahan berasal dari checkbox Penerima Tugas + setIsUpdatingFromPenerimaTugas(true); + setPenerimaTugasChangeType(key + (value ? "_checked" : "_unchecked")); + if (key === "allUnit") { const newState = { allUnit: value, @@ -220,8 +243,23 @@ export default function FormTask() { satker: value, }; setUnitSelection(newState); - console.log("QQQ", newState); } else { + // Validasi khusus untuk POLRES + if (key === "polres" && value) { + // Cek apakah ada POLDA yang sudah dichecklist di modal + const hasCheckedPolda = listDest.some((item: any) => + item.levelNumber === 2 && + item.name !== "SATKER POLRI" && + checkedLevels.has(Number(item.id)) + ); + + if (!hasCheckedPolda) { + // Jika tidak ada POLDA yang dichecklist di modal, tampilkan peringatan dan batalkan + alert("Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."); + return; // Batalkan perubahan + } + } + const updatedSelection = { ...unitSelection, [key]: value, @@ -234,7 +272,6 @@ export default function FormTask() { updatedSelection.allUnit = allChecked; setUnitSelection(updatedSelection); - console.log("AAA", updatedSelection); } }; @@ -348,25 +385,141 @@ export default function FormTask() { } }; + // useEffect untuk sinkronisasi checkbox modal dengan Penerima Tugas useEffect(() => { - const updated = new Set(checkedLevels); - - if (unitSelection.polda) { - listDest.forEach((polda) => { - updated.add(polda.id); // hanya id polda - }); + if (listDest.length > 0) { + syncModalWithPenerimaTugas(); } + }, [unitSelection, listDest]); - if (unitSelection.polres) { - listDest.forEach((polda) => { - polda?.subDestination?.forEach((polres: any) => { - updated.add(polres.id); // hanya id polres + // Fungsi untuk update unitSelection berdasarkan checkbox modal + const updateUnitSelectionFromModal = () => { + setTimeout(() => { + // Hitung total item yang tersedia untuk setiap kategori + const totalPolda = listDest.filter((item: any) => + item.levelNumber === 2 && item.name !== "SATKER POLRI" + ).length; + + const totalPolres = listDest.reduce((total: number, item: any) => { + if (item.subDestination) { + return total + item.subDestination.length; + } + return total; + }, 0); + + const satkerItem = listDest.find((item: any) => item.name === "SATKER POLRI"); + const totalSatker = satkerItem ? (1 + (satkerItem.subDestination?.length || 0)) : 0; + + // Hitung item yang dichecklist untuk setiap kategori + const checkedPoldaCount = listDest.filter((item: any) => + item.levelNumber === 2 && + item.name !== "SATKER POLRI" && + checkedLevels.has(Number(item.id)) + ).length; + + const checkedPolresCount = listDest.reduce((total: number, item: any) => { + if (item.subDestination) { + return total + item.subDestination.filter((sub: any) => checkedLevels.has(Number(sub.id))).length; + } + return total; + }, 0); + + const checkedSatkerCount = satkerItem ? ( + (checkedLevels.has(Number(satkerItem.id)) ? 1 : 0) + + (satkerItem.subDestination?.filter((sub: any) => checkedLevels.has(Number(sub.id))).length || 0) + ) : 0; + + // Checkbox hanya aktif jika SEMUA item dalam kategori tersebut dichecklist + const hasCheckedPolda = totalPolda > 0 && checkedPoldaCount === totalPolda; + const hasCheckedPolres = totalPolres > 0 && checkedPolresCount === totalPolres; + const hasCheckedSatker = totalSatker > 0 && checkedSatkerCount === totalSatker; + + // Update unitSelection berdasarkan checkbox yang aktif di modal + setUnitSelection(prev => ({ + ...prev, + polda: hasCheckedPolda, + polres: hasCheckedPolres, + satker: hasCheckedSatker, + // allUnit hanya true jika semua kategori terpenuhi + allUnit: hasCheckedPolda && hasCheckedPolres && hasCheckedSatker + })); + }, 0); + }; + + // Fungsi untuk sinkronisasi checkbox modal dengan Penerima Tugas + const syncModalWithPenerimaTugas = () => { + // Hanya jalankan sinkronisasi jika perubahan berasal dari checkbox Penerima Tugas + if (isUpdatingFromPenerimaTugas) { + // Khusus untuk unchecklist POLRES: hanya unchecklist polres, pertahankan polda + if (penerimaTugasChangeType === "polres_unchecked") { + const newCheckedLevels = new Set(checkedLevels); + + // Hapus semua polres dari modal, tapi pertahankan polda + listDest.forEach((item: any) => { + if (item.subDestination && item.levelNumber === 2 && item.name !== "SATKER POLRI") { + item.subDestination.forEach((polres: any) => { + newCheckedLevels.delete(Number(polres.id)); + }); + } }); - }); + + setCheckedLevels(newCheckedLevels); + } + // Untuk perubahan lainnya, jalankan logika normal + else if (unitSelection.polda || unitSelection.polres || unitSelection.satker) { + // Mulai dengan checkbox yang sudah ada untuk mempertahankan pilihan manual user + const newCheckedLevels = new Set(checkedLevels); + + listDest.forEach((item: any) => { + // Jika polda dichecklist, checklist semua polda (levelNumber 2, bukan SATKER POLRI) + if (unitSelection.polda && item.levelNumber === 2 && item.name !== "SATKER POLRI") { + newCheckedLevels.add(Number(item.id)); + } + + // Jika satker dichecklist, checklist SATKER POLRI dan sub-itemnya + if (unitSelection.satker && item.name === "SATKER POLRI") { + newCheckedLevels.add(Number(item.id)); + if (item.subDestination) { + item.subDestination.forEach((sub: any) => { + newCheckedLevels.add(Number(sub.id)); + }); + } + } + + // Jika polres dichecklist + if (unitSelection.polres && item.subDestination) { + // Jika checkbox POLDA di Penerima Tugas juga aktif, checklist semua polres + if (unitSelection.polda && item.levelNumber === 2 && item.name !== "SATKER POLRI") { + item.subDestination.forEach((polres: any) => { + newCheckedLevels.add(Number(polres.id)); + }); + } + // Jika checkbox POLDA di Penerima Tugas tidak aktif, tapi ada POLDA yang dichecklist di modal + else if (!unitSelection.polda && item.levelNumber === 2 && item.name !== "SATKER POLRI") { + // Cek apakah POLDA ini sudah dichecklist di modal + if (checkedLevels.has(Number(item.id))) { + // Jika ya, checklist semua polres dari POLDA ini + item.subDestination.forEach((polres: any) => { + newCheckedLevels.add(Number(polres.id)); + }); + } + } + } + }); + + setCheckedLevels(newCheckedLevels); + } else { + // Jika tidak ada unitSelection yang aktif, unchecklist semua item di modal + setCheckedLevels(new Set()); + } + + // Reset flag setelah sinkronisasi selesai + setTimeout(() => { + setIsUpdatingFromPenerimaTugas(false); + setPenerimaTugasChangeType(""); + }, 100); } - - setCheckedLevels(updated); - }, [unitSelection.polda, unitSelection.polres, listDest]); + }; const onSubmit = (data: TaskSchema) => { MySwal.fire({ @@ -604,7 +757,6 @@ export default function FormTask() {
{Object.keys(unitSelection).map((key) => { - const isDisabled = key === "polres" && !unitSelection.polda; return (
handleUnitChange( key as keyof typeof unitSelection, @@ -621,7 +772,7 @@ export default function FormTask() { } />
); @@ -639,30 +790,22 @@ export default function FormTask() { Daftar Wilayah Polda dan Polres
- {listDest.map((polda: any) => { - const poldaChecked = unitSelection.polda; // kontrol polda luar - const polresChecked = unitSelection.polres; // kontrol polres luar - - const isPoldaDisabled = poldaChecked; // lock checkbox polda dialog jika polda luar dicentang - const isPolresDisabled = polresChecked; // lock checkbox polres dialog jika polres luar dicentang - - return ( + {listDest.map((polda: any) => (
- {expandedPolda[polda.id] && (
+ {polda?.subDestination?.map((polres: any) => (
)}
- ); - })} + ))}
From 7a4e7cbefe714c35ca068534105e72f6b8df69d2 Mon Sep 17 00:00:00 2001 From: Sabda Yagra Date: Wed, 20 Aug 2025 22:43:10 +0700 Subject: [PATCH 5/9] fixing --- .../calendar-polri/component/columns.tsx | 182 +++++++++++++++--- .../form/schedule/form-calendar-polri.tsx | 133 ++++++++++++- components/landing-page/hero-new.tsx | 2 +- 3 files changed, 280 insertions(+), 37 deletions(-) diff --git a/app/[locale]/(protected)/contributor/schedule/calendar-polri/component/columns.tsx b/app/[locale]/(protected)/contributor/schedule/calendar-polri/component/columns.tsx index 85ac17b6..255f01cc 100644 --- a/app/[locale]/(protected)/contributor/schedule/calendar-polri/component/columns.tsx +++ b/app/[locale]/(protected)/contributor/schedule/calendar-polri/component/columns.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import { ColumnDef } from "@tanstack/react-table"; import { Eye, MoreVertical, SquarePen, Trash2 } from "lucide-react"; -import { cn } from "@/lib/utils"; +import { cn, getCookiesDecrypt } from "@/lib/utils"; import { DropdownMenu, DropdownMenuContent, @@ -17,9 +17,10 @@ import withReactContent from "sweetalert2-react-content"; import Swal from "sweetalert2"; import { error } from "@/lib/swal"; import { deleteCalendar } from "@/service/schedule/schedule"; +import { loading, success } from "@/config/swal"; const useTableColumns = () => { - const t = useTranslations("Table"); // Panggil di dalam hook + const t = useTranslations("Table"); const columns: ColumnDef[] = [ { @@ -100,15 +101,13 @@ const useTableColumns = () => { enableHiding: false, cell: ({ row }) => { const MySwal = withReactContent(Swal); + const levelNumber = Number(getCookiesDecrypt("ulne")); // 1 = Mabes, 2 = Polda + const calendarOwnerLevel = Number(row.original.assignedToLevel); // dari API + const calendarOwner = row.original.assignedTo; // ID unit Polda/Mabes dari API + const myUnit = getCookiesDecrypt("unitId"); // misal ID unit Polda login async function doDelete(id: any) { - // loading(); - const data = { - id, - }; - const response = await deleteCalendar(id); - if (response?.error) { error(response.message); return false; @@ -144,6 +143,29 @@ const useTableColumns = () => { } }); }; + + // === RULE AKSI === + let canEdit = false; + let canDelete = false; + const canView = true; + + if (levelNumber === 1) { + // Mabes -> bebas + canEdit = true; + canDelete = true; + } else if (levelNumber === 2) { + // Polda + if (calendarOwnerLevel === 1) { + // kalender Mabes -> hanya view + canEdit = false; + canDelete = false; + } else if (calendarOwnerLevel === 2 && calendarOwner === myUnit) { + // kalender polda sendiri -> bisa edit/delete + canEdit = true; + canDelete = true; + } + } + return ( @@ -156,34 +178,134 @@ const useTableColumns = () => { - - - - Detail + {canView && ( + + + + Detail + + + )} + + {canEdit && ( + + + + Edit + + + )} + + {canDelete && ( + handleDeleteCalendars(row.original.id)} + className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none" + > + + Delete - - - - - Edit - - - handleDeleteCalendars(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 deleteCalendar(id); + + // 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 handleDeleteCalendars = (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); + // } + // }); + // }; + // return ( + // + // + // + // + // + // + // + // + // Detail + // + // + // + // + // + // Edit + // + // + // handleDeleteCalendars(row.original.id)} + // className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none" + // > + // + // Delete + // + // + // + // ); + // }, + // }, ]; return columns; diff --git a/components/form/schedule/form-calendar-polri.tsx b/components/form/schedule/form-calendar-polri.tsx index b75b79e0..c0d2dd0d 100644 --- a/components/form/schedule/form-calendar-polri.tsx +++ b/components/form/schedule/form-calendar-polri.tsx @@ -41,7 +41,7 @@ import { } from "@/components/ui/popover"; import { Calendar } from "@/components/ui/calendar"; import { format } from "date-fns"; -import { cn } from "@/lib/utils"; +import { cn, getCookiesDecrypt } from "@/lib/utils"; import { getUserLevelForAssignments } from "@/service/task"; import { Card } from "@/components/ui/card"; import { useDropzone } from "react-dropzone"; @@ -84,6 +84,8 @@ export function CalendarPolriAdd() { >([]); const [imageFiles, setImageFiles] = React.useState([]); const [date, setDate] = React.useState(); + const levelNumber = Number(getCookiesDecrypt("ulne")) || 0; + const roleId = Number(getCookiesDecrypt("urie")) || 0; const [unitSelection, setUnitSelection] = React.useState({ semua: false, @@ -143,7 +145,7 @@ export function CalendarPolriAdd() { }; const handlePoldaPolresChange = () => { - return Array.from(checkedLevels).join(","); // Mengonversi Set ke string + return Array.from(checkedLevels).join(","); }; const handleUnitChange = ( @@ -233,7 +235,6 @@ export function CalendarPolriAdd() { const resCsrf = await getCsrfToken(); const csrfToken = resCsrf?.data?.token; - console.log("CSRF TOKEN : ", csrfToken); const headers = { "X-XSRF-TOKEN": csrfToken, }; @@ -387,7 +388,7 @@ export function CalendarPolriAdd() {

Publish Area

-
+ {/*
{Object.keys(unitSelection).map((key) => (
))} +
*/} +
+ {Object.keys(unitSelection) + .filter((key) => { + // Jika login sebagai polda (2), hanya tampilkan polda + if (levelNumber === 2) return key === "polda"; + // Jika login sebagai satker (4), hanya tampilkan satker + if (levelNumber === 4) return key === "satker"; + // Selain itu tampilkan semua + return true; + }) + .map((key) => ( +
+ + handleUnitChange( + key as keyof typeof unitSelection, + value as boolean + ) + } + /> + +
+ ))}
-
+ + {levelNumber !== 2 && levelNumber !== 4 && ( +
+ + + + + + + + Daftar Wilayah Polda dan Polres + + +
+ {listDest.map((polda: any) => ( +
+ + {expandedPolda[polda.id] && ( +
+ + {polda?.subDestination?.map((polres: any) => ( + + ))} +
+ )} +
+ ))} +
+
+
+
+ )} + + {/*
-
+
*/}
diff --git a/components/landing-page/hero-new.tsx b/components/landing-page/hero-new.tsx index 0ab87c99..9488908d 100644 --- a/components/landing-page/hero-new.tsx +++ b/components/landing-page/hero-new.tsx @@ -536,7 +536,7 @@ const HeroNew = (props: { group?: string }) => { Date: Thu, 21 Aug 2025 10:38:38 +0700 Subject: [PATCH 6/9] update : image detail approval --- components/form/content/image-detail-form.tsx | 396 ++++++++++++++---- 1 file changed, 313 insertions(+), 83 deletions(-) diff --git a/components/form/content/image-detail-form.tsx b/components/form/content/image-detail-form.tsx index d16491e2..21c4c98b 100644 --- a/components/form/content/image-detail-form.tsx +++ b/components/form/content/image-detail-form.tsx @@ -171,11 +171,16 @@ export default function FormImageDetail() { satker: false, }); const [isLoading, setIsLoading] = useState(false); - const [checkedLevels, setCheckedLevels] = useState(new Set()); + const [checkedLevels, setCheckedLevels] = useState>(new Set()); const [selectedTarget, setSelectedTarget] = useState(""); const [files, setFiles] = useState([]); const [rejectedFiles, setRejectedFiles] = useState([]); - const [expandedPolda, setExpandedPolda] = useState([{}]); + const [expandedPolda, setExpandedPolda] = useState>({}); + + // State untuk melacak apakah perubahan berasal dari checkbox utama + const [isUpdatingFromMainCheckbox, setIsUpdatingFromMainCheckbox] = useState(false); + // State untuk melacak jenis perubahan spesifik + const [mainCheckboxChangeType, setMainCheckboxChangeType] = useState(""); const [wilayahPublish, setWilayahPublish] = React.useState({ semua: false, nasional: false, @@ -240,30 +245,175 @@ export default function FormImageDetail() { fetchPoldaPolres(); }, []); + // useEffect untuk sinkronisasi checkbox modal dengan checkbox utama useEffect(() => { - const updated = new Set(checkedLevels); + if (listDest.length > 0 && isUpdatingFromMainCheckbox && mainCheckboxChangeType) { + syncModalWithMainCheckbox(); + } + }, [isUpdatingFromMainCheckbox, mainCheckboxChangeType]); - if (unitSelection.polda) { - listDest.forEach((polda) => { - updated.add(polda.id); // hanya id polda + // useEffect untuk update checkbox utama ketika pilihan modal berubah + useEffect(() => { + if (!isUpdatingFromMainCheckbox && listDest.length > 0) { + updateMainCheckboxFromModal(); + } + }, [checkedLevels, isUpdatingFromMainCheckbox]); + + // Fungsi untuk update checkbox utama berdasarkan checkbox modal + const updateMainCheckboxFromModal = () => { + if (!isUpdatingFromMainCheckbox && listDest.length > 0) { + // Hitung item yang dipilih berdasarkan checkedLevels + const checkedPoldaCount = listDest.filter((item: any) => + item.levelNumber === 2 && + item.name !== "SATKER POLRI" && + checkedLevels.has(Number(item.id)) + ).length; + + const checkedPolresCount = listDest.reduce((total: number, item: any) => { + if (item.subDestination && item.name !== "SATKER POLRI") { + // Hanya hitung sub-item dari POLDA (bukan dari SATKER POLRI) + return total + item.subDestination.filter((sub: any) => checkedLevels.has(Number(sub.id))).length; + } + return total; + }, 0); + + const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI"); + const checkedSatkerCount = satkerItem ? ( + (checkedLevels.has(Number(satkerItem.id)) ? 1 : 0) + + (satkerItem.subDestination?.filter((sub: any) => checkedLevels.has(Number(sub.id))).length || 0) + ) : 0; + + // Checkbox aktif jika ADA item yang dipilih dalam kategori tersebut + const hasSelectedPolda = checkedPoldaCount > 0; + const hasSelectedPolres = checkedPolresCount > 0; + const hasSelectedSatker = checkedSatkerCount > 0; + + // Update unitSelection berdasarkan yang dipilih di modal + setUnitSelection(prev => { + const newState = { ...prev }; + + // Update individual checkboxes + newState.polda = hasSelectedPolda; + newState.polres = hasSelectedPolres; + newState.satker = hasSelectedSatker; + + // Update checkbox "semua" berdasarkan semua checkbox yang aktif + newState.semua = newState.nasional && newState.wilayah && newState.international && hasSelectedPolda && hasSelectedPolres && hasSelectedSatker; + + return newState; }); } + }; - if (unitSelection.polres) { - listDest.forEach((polda) => { - polda?.subDestination?.forEach((polres: any) => { - updated.add(polres.id); // hanya id polres + // Fungsi untuk sinkronisasi checkbox modal dengan checkbox utama + const syncModalWithMainCheckbox = () => { + if (isUpdatingFromMainCheckbox) { + const newCheckedLevels = new Set(checkedLevels); + + // Handle checklist actions - menambahkan semua item ke modal + if (mainCheckboxChangeType === "polda_checked") { + // Checklist semua polda + listDest.forEach((item: any) => { + if (item.levelNumber === 2 && item.name !== "SATKER POLRI") { + newCheckedLevels.add(Number(item.id)); + } }); - }); + } else if (mainCheckboxChangeType === "polres_checked") { + // Checklist semua polres, tapi hanya yang poldanya sudah di-checklist + // Jangan checklist sub-item SATKER POLRI + listDest.forEach((item: any) => { + if (item.levelNumber === 2 && item.name !== "SATKER POLRI" && newCheckedLevels.has(Number(item.id))) { + if (item.subDestination) { + item.subDestination.forEach((polres: any) => { + newCheckedLevels.add(Number(polres.id)); + }); + } + } + }); + + // Tidak perlu menghapus SATKER ketika POLRES di-checklist + // Biarkan keduanya bisa aktif bersamaan + // SATKER dan POLRES adalah konsep yang berbeda: + // - SATKER: unit-unit seperti ITWASUM, BAINTELKAM, dll. + // - POLRES: unit-unit seperti POLRES METRO JAKARTA PUSAT, dll. + } else if (mainCheckboxChangeType === "satker_checked") { + // Checklist satker + const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI"); + if (satkerItem) { + newCheckedLevels.add(Number(satkerItem.id)); + // Checklist semua sub-item yang ada di bawah SATKER (bukan POLRES) + if (satkerItem.subDestination) { + satkerItem.subDestination.forEach((sub: any) => { + newCheckedLevels.add(Number(sub.id)); + }); + } + } + } else if (mainCheckboxChangeType === "semua_checked") { + // Checklist semua item + listDest.forEach((item: any) => { + newCheckedLevels.add(Number(item.id)); + // Checklist semua sub-item di bawah setiap item + if (item.subDestination) { + item.subDestination.forEach((sub: any) => { + newCheckedLevels.add(Number(sub.id)); + }); + } + }); + } + // Handle unchecklist actions - menghapus item dari modal + else if (mainCheckboxChangeType === "polres_unchecked") { + // Clear polres dari checkedLevels, tapi jangan hapus sub-item SATKER POLRI + listDest.forEach((item: any) => { + if (item.subDestination && item.name !== "SATKER POLRI") { + item.subDestination.forEach((polres: any) => { + newCheckedLevels.delete(Number(polres.id)); + }); + } + }); + } else if (mainCheckboxChangeType === "polda_unchecked") { + // Clear polda dan polres dari checkedLevels, tapi jangan hapus SATKER POLRI + listDest.forEach((item: any) => { + if (item.levelNumber === 2 && item.name !== "SATKER POLRI") { + newCheckedLevels.delete(Number(item.id)); + if (item.subDestination) { + item.subDestination.forEach((polres: any) => { + newCheckedLevels.delete(Number(polres.id)); + }); + } + } + }); + } else if (mainCheckboxChangeType === "satker_unchecked") { + // Clear satker dan semua sub-item di bawahnya dari checkedLevels + const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI"); + if (satkerItem) { + newCheckedLevels.delete(Number(satkerItem.id)); + if (satkerItem.subDestination) { + satkerItem.subDestination.forEach((sub: any) => { + newCheckedLevels.delete(Number(sub.id)); + }); + } + } + } else if (mainCheckboxChangeType === "semua_unchecked") { + // Clear semua + newCheckedLevels.clear(); + } + + setCheckedLevels(newCheckedLevels); + + // Reset flag setelah sinkronisasi selesai + setIsUpdatingFromMainCheckbox(false); + setMainCheckboxChangeType(""); } - - setCheckedLevels(updated); - }, [unitSelection.polda, unitSelection.polres, listDest]); + }; const handleUnitChange = ( key: keyof typeof unitSelection, value: boolean ) => { + // Set flag bahwa perubahan berasal dari checkbox utama + setIsUpdatingFromMainCheckbox(true); + setMainCheckboxChangeType(key + (value ? "_checked" : "_unchecked")); + if (key === "semua") { // Jika klik Semua, set semua value ke true/false const newState = { @@ -277,6 +427,28 @@ export default function FormImageDetail() { }; setUnitSelection(newState); } else { + // Validasi khusus untuk POLRES + if (key === "polres" && value) { + // Cek apakah ada POLDA yang sudah dipilih di modal + const hasSelectedPolda = listDest.some((item: any) => + item.levelNumber === 2 && + item.name !== "SATKER POLRI" && + checkedLevels.has(Number(item.id)) + ); + + if (!hasSelectedPolda) { + // Jika tidak ada POLDA yang dipilih, tampilkan peringatan dan batalkan + alert("Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."); + // Reset flag karena perubahan dibatalkan + setIsUpdatingFromMainCheckbox(false); + setMainCheckboxChangeType(""); + return; // Batalkan perubahan + } + } + + // Tidak ada validasi khusus untuk SATKER dan POLRES + // Keduanya bisa aktif bersamaan + // Update salah satu saja const updatedSelection = { ...unitSelection, @@ -326,12 +498,38 @@ export default function FormImageDetail() { }; const handleCheckboxChangePlacement = (levelId: number) => { - setCheckedLevels((prev: any) => { - const updatedLevels = new Set(prev); - if (updatedLevels.has(levelId)) { + setCheckedLevels((prev: Set) => { + const updatedLevels = new Set(prev); + const isCurrentlyChecked = updatedLevels.has(levelId); + + if (isCurrentlyChecked) { updatedLevels.delete(levelId); + + // Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya + const poldaItem = listDest.find((item: any) => Number(item.id) === levelId) as any; + if (poldaItem && poldaItem.subDestination && poldaItem.name !== "SATKER POLRI") { + poldaItem.subDestination.forEach((polres: any) => { + updatedLevels.delete(Number(polres.id)); + }); + } + + // Jika ini adalah SATKER POLRI yang di-unchecklist, unchecklist juga semua sub-item di bawahnya + if (poldaItem && poldaItem.name === "SATKER POLRI") { + poldaItem.subDestination?.forEach((subItem: any) => { + updatedLevels.delete(Number(subItem.id)); + }); + } } else { updatedLevels.add(levelId); + + // Jika ini adalah SATKER POLRI yang di-checklist, checklist juga semua sub-item di bawahnya + const satkerItem = listDest.find((item: any) => Number(item.id) === levelId) as any; + if (satkerItem && satkerItem.name === "SATKER POLRI") { + // Checklist semua sub-item di bawah SATKER POLRI (bukan POLRES) + satkerItem.subDestination?.forEach((subItem: any) => { + updatedLevels.add(Number(subItem.id)); + }); + } } return updatedLevels; }); @@ -395,7 +593,7 @@ export default function FormImageDetail() { setupPlacementCheck(details?.files?.length); if (details?.assignedToLevel) { - const levels = new Set( + const levels = new Set( details.assignedToLevel.split(",").map(Number) ); setCheckedLevels(levels); @@ -519,10 +717,23 @@ export default function FormImageDetail() { if (checked) { if (placement === "all") { temp[index] = ["all", "mabes", "polda", "international"]; + } else if (placement === "satker") { + // Ketika satker di-checklist, HANYA tambahkan satker saja + // JANGAN otomatis checklist polres + const now = temp[index] || []; + if (!now.includes("satker")) { + now.push("satker"); + } + temp[index] = now; } else { - const now = temp[index]; - now?.push(placement); - if (now.length === 3 && !now.includes("all")) { + const now = temp[index] || []; + if (!now.includes(placement)) { + now.push(placement); + } + // Hanya auto-checklist "all" jika polda, polres, dan mabes ter-checklist + // JANGAN include satker dalam perhitungan auto "all" + const nonSatkerItems = now.filter(item => item !== "satker" && item !== "all"); + if (nonSatkerItems.length === 3 && !now.includes("all")) { now.push("all"); } temp[index] = now; @@ -534,9 +745,13 @@ export default function FormImageDetail() { const now = temp[index].filter((a) => a !== placement); console.log("now", now); temp[index] = now; - if (now.length === 3 && now.includes("all")) { - const newData = now.filter((b) => b !== "all"); - temp[index] = newData; + // Hapus "all" jika tidak semua item ter-checklist + if (now.includes("all")) { + const nonSatkerItems = now.filter(item => item !== "satker" && item !== "all"); + if (nonSatkerItems.length < 3) { + const newData = now.filter((b) => b !== "all"); + temp[index] = newData; + } } } } @@ -1031,45 +1246,30 @@ export default function FormImageDetail() {
- {listDest.map((polda: any) => { - const poldaChecked = - unitSelection.polda; - const polresChecked = - unitSelection.polres; - const isPoldaDisabled = - poldaChecked; - const isPolresDisabled = - polresChecked; - - return ( + {listDest.map((polda: any) => (
-
- ); - })} + ))}
From 3feb406715e5f62707ed03d79701b8e30928056e Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Thu, 21 Aug 2025 17:56:58 +0700 Subject: [PATCH 7/9] feat: update approval modals --- components/form/content/image-detail-form.tsx | 973 +++++++++++------- 1 file changed, 581 insertions(+), 392 deletions(-) diff --git a/components/form/content/image-detail-form.tsx b/components/form/content/image-detail-form.tsx index 21c4c98b..73b97aee 100644 --- a/components/form/content/image-detail-form.tsx +++ b/components/form/content/image-detail-form.tsx @@ -20,7 +20,7 @@ import { import { Checkbox } from "@/components/ui/checkbox"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; -import { register } from "module"; + import { Switch } from "@/components/ui/switch"; import Cookies from "js-cookie"; import { @@ -53,6 +53,7 @@ import { DialogContent, DialogTitle, DialogTrigger, + DialogClose, } from "@/components/ui/dialog"; import { Textarea } from "@/components/ui/textarea"; import { close, loading, successCallback } from "@/config/swal"; @@ -161,6 +162,21 @@ export default function FormImageDetail() { const [thumbsSwiper, setThumbsSwiper] = useState(null); const [isUserMabesApprover, setIsUserMabesApprover] = useState(false); const [approval, setApproval] = useState(); + // State untuk setiap file secara individual + const [fileUnitSelections, setFileUnitSelections] = useState>([]); + + // State untuk setiap file secara individual - checklist levels + const [fileCheckedLevels, setFileCheckedLevels] = useState>>([]); + + // State global untuk kompatibilitas (akan dihapus nanti) const [unitSelection, setUnitSelection] = useState({ semua: false, nasional: false, @@ -255,12 +271,12 @@ export default function FormImageDetail() { // useEffect untuk update checkbox utama ketika pilihan modal berubah useEffect(() => { if (!isUpdatingFromMainCheckbox && listDest.length > 0) { - updateMainCheckboxFromModal(); + updateMainCheckboxFromModalLegacy(); } }, [checkedLevels, isUpdatingFromMainCheckbox]); - // Fungsi untuk update checkbox utama berdasarkan checkbox modal - const updateMainCheckboxFromModal = () => { + // Fungsi untuk update checkbox utama berdasarkan checkbox modal (global/legacy) + const updateMainCheckboxFromModalLegacy = () => { if (!isUpdatingFromMainCheckbox && listDest.length > 0) { // Hitung item yang dipilih berdasarkan checkedLevels const checkedPoldaCount = listDest.filter((item: any) => @@ -406,6 +422,63 @@ export default function FormImageDetail() { } }; + // Fungsi untuk mengupdate state individual file + const handleFileUnitChange = ( + fileIndex: number, + key: keyof typeof unitSelection, + value: boolean + ) => { + setFileUnitSelections(prev => { + const newSelections = [...prev]; + const currentSelection = { ...newSelections[fileIndex] }; + + if (key === "semua") { + // Jika klik Semua, set semua value ke true/false + currentSelection.semua = value; + currentSelection.nasional = value; + currentSelection.wilayah = value; + currentSelection.international = value; + currentSelection.polda = value; + currentSelection.polres = value; + currentSelection.satker = value; + } else { + // Validasi khusus untuk POLRES - harus ada POLDA yang ter-checklist + if (key === "polres" && value) { + const currentFileCheckedLevels = fileCheckedLevels[fileIndex]; + const hasSelectedPolda = currentFileCheckedLevels && listDest.some((item: any) => + item.levelNumber === 2 && + item.name !== "SATKER POLRI" && + currentFileCheckedLevels.has(Number(item.id)) + ); + + if (!hasSelectedPolda) { + alert("Harap pilih POLDA di Modal terlebih dahulu sebelum mengaktifkan checkbox POLRES."); + return prev; // Batalkan perubahan + } + } + + // Update salah satu saja + currentSelection[key] = value; + + // Cek apakah semua selain "semua" sudah dicentang + const allChecked = [ + "nasional", + "wilayah", + "international", + "polda", + "polres", + "satker", + ].every((k) => currentSelection[k as keyof typeof unitSelection]); + + currentSelection.semua = allChecked; + } + + newSelections[fileIndex] = currentSelection; + return newSelections; + }); + }; + + // Fungsi lama untuk kompatibilitas (akan dihapus nanti) const handleUnitChange = ( key: keyof typeof unitSelection, value: boolean @@ -497,6 +570,146 @@ export default function FormImageDetail() { ); }; + // Fungsi untuk mengupdate checklist levels untuk file tertentu + const handleFileCheckboxChangePlacement = (fileIndex: number, levelId: number) => { + setFileCheckedLevels((prev) => { + const newArray = [...prev]; + const currentFileLevels = new Set(newArray[fileIndex]); + const isCurrentlyChecked = currentFileLevels.has(levelId); + + if (isCurrentlyChecked) { + currentFileLevels.delete(levelId); + + // Jika ini adalah POLDA yang di-unchecklist, unchecklist juga semua polres di bawahnya + const poldaItem = listDest.find((item: any) => Number(item.id) === levelId) as any; + if (poldaItem && poldaItem.subDestination && poldaItem.name !== "SATKER POLRI") { + poldaItem.subDestination.forEach((polres: any) => { + currentFileLevels.delete(Number(polres.id)); + }); + } + + // Jika ini adalah SATKER POLRI yang di-unchecklist, unchecklist juga semua sub-item di bawahnya + if (poldaItem && poldaItem.name === "SATKER POLRI") { + poldaItem.subDestination?.forEach((subItem: any) => { + currentFileLevels.delete(Number(subItem.id)); + }); + } + } else { + currentFileLevels.add(levelId); + + // Jika ini adalah SATKER POLRI yang di-checklist, checklist juga semua sub-item di bawahnya + const satkerItem = listDest.find((item: any) => Number(item.id) === levelId) as any; + if (satkerItem && satkerItem.name === "SATKER POLRI") { + // Checklist semua sub-item di bawah SATKER POLRI (bukan POLRES) + satkerItem.subDestination?.forEach((subItem: any) => { + currentFileLevels.add(Number(subItem.id)); + }); + } + } + + newArray[fileIndex] = currentFileLevels; + + // Update checkbox utama berdasarkan perubahan di modal + // Pindahkan ke sini agar state sudah ter-update + setTimeout(() => updateMainCheckboxFromModal(fileIndex), 0); + + return newArray; + }); + }; + + // Fungsi untuk menangani "Pilih Semua" sub-items di bawah POLDA + const handleSelectAllSubItems = (fileIndex: number, polda: any) => { + setFileCheckedLevels((prev) => { + const newArray = [...prev]; + const currentFileLevels = new Set(newArray[fileIndex]); + + // Cek apakah semua sub-items sudah ter-checklist + const allSubItemsChecked = polda.subDestination?.every((sub: any) => + currentFileLevels.has(Number(sub.id)) + ); + + if (allSubItemsChecked) { + // Jika semua sudah ter-checklist, unchecklist semuanya + polda.subDestination?.forEach((sub: any) => { + currentFileLevels.delete(Number(sub.id)); + }); + } else { + // Jika belum semua ter-checklist, checklist semuanya + // Checklist POLDA juga jika belum ter-checklist + if (!currentFileLevels.has(Number(polda.id))) { + currentFileLevels.add(Number(polda.id)); + } + // Checklist semua sub-items + polda.subDestination?.forEach((sub: any) => { + currentFileLevels.add(Number(sub.id)); + }); + } + + newArray[fileIndex] = currentFileLevels; + + // Update checkbox utama berdasarkan perubahan di modal + setTimeout(() => updateMainCheckboxFromModal(fileIndex), 0); + + return newArray; + }); + }; + + // Fungsi untuk mengupdate checkbox utama berdasarkan checklist di modal + const updateMainCheckboxFromModal = (fileIndex: number) => { + setFileUnitSelections((prev) => { + const newSelections = [...prev]; + const currentSelection = { ...newSelections[fileIndex] }; + const currentFileLevels = fileCheckedLevels[fileIndex]; + + if (!currentFileLevels) return prev; + + // Hitung total POLDA yang ada (bukan SATKER POLRI) + const totalPoldaCount = listDest.filter((item: any) => + item.levelNumber === 2 && item.name !== "SATKER POLRI" + ).length; + + // Hitung berapa banyak POLDA yang ter-checklist (bukan SATKER POLRI) + const checkedPoldaCount = listDest.reduce((total: number, item: any) => { + if (item.levelNumber === 2 && item.name !== "SATKER POLRI" && currentFileLevels.has(Number(item.id))) { + return total + 1; + } + return total; + }, 0); + + // Hitung total POLRES yang ada dari POLDA yang ter-checklist + const totalPolresFromCheckedPolda = listDest.reduce((total: number, item: any) => { + if (item.subDestination && item.name !== "SATKER POLRI" && currentFileLevels.has(Number(item.id))) { + return total + item.subDestination.length; + } + return total; + }, 0); + + // Hitung berapa banyak POLRES yang ter-checklist + const checkedPolresCount = listDest.reduce((total: number, item: any) => { + if (item.subDestination && item.name !== "SATKER POLRI") { + // Hanya hitung sub-item dari POLDA (bukan dari SATKER POLRI) + return total + item.subDestination.filter((sub: any) => currentFileLevels.has(Number(sub.id))).length; + } + return total; + }, 0); + + // Cek apakah SATKER POLRI ter-checklist + const satkerItem = listDest.find((item: any) => item.name === "SATKER POLRI"); + const isSatkerChecked = satkerItem && currentFileLevels.has(Number(satkerItem.id)); + + // Update checkbox berdasarkan kondisi + // POLDA aktif jika ada minimal 1 POLDA ter-checklist + currentSelection.polda = checkedPoldaCount > 0; + // POLRES aktif jika ada minimal 1 POLRES ter-checklist + currentSelection.polres = checkedPolresCount > 0; + currentSelection.satker = Boolean(isSatkerChecked); + + newSelections[fileIndex] = currentSelection; + return newSelections; + }); + }; + + // Fungsi lama untuk kompatibilitas (akan dihapus nanti) const handleCheckboxChangePlacement = (levelId: number) => { setCheckedLevels((prev: Set) => { const updatedLevels = new Set(prev); @@ -570,10 +783,28 @@ export default function FormImageDetail() { const setupPlacementCheck = (length: number) => { const temp = []; + const unitSelections = []; + const checkedLevelsArray = []; + for (let i = 0; i < length; i++) { temp.push([]); + // Inisialisasi state untuk setiap file + unitSelections.push({ + semua: false, + nasional: false, + wilayah: false, + international: false, + polda: false, + polres: false, + satker: false, + }); + // Inisialisasi checkedLevels untuk setiap file + checkedLevelsArray.push(new Set()); } + setFilePlacements(temp); + setFileUnitSelections(unitSelections); + setFileCheckedLevels(checkedLevelsArray); }; useEffect(() => { @@ -673,12 +904,20 @@ export default function FormImageDetail() { }; async function save() { + // Gabungkan semua checkedLevels dari semua file + const allCheckedLevels = new Set(); + fileCheckedLevels.forEach((fileLevels) => { + fileLevels.forEach((levelId) => { + allCheckedLevels.add(levelId); + }); + }); + const data = { mediaUploadId: id, statusId: status, message: description, files: isUserMabesApprover ? getPlacement() : [], - customLocationPlacement: Array.from(checkedLevels).join(","), + customLocationPlacement: Array.from(allCheckedLevels).join(","), }; setModalOpen(false); loading(); @@ -756,6 +995,92 @@ export default function FormImageDetail() { } } setFilePlacements(temp); + + // Update checklist levels di modal berdasarkan placement yang diubah + updateModalChecklistLevels(index, placement, checked); + }; + + // Fungsi untuk mengupdate checklist levels di modal berdasarkan placement + const updateModalChecklistLevels = (fileIndex: number, placement: string, checked: boolean) => { + if (!listDest || listDest.length === 0) return; + + setFileCheckedLevels((prev) => { + const newArray = [...prev]; + const currentFileLevels = new Set(newArray[fileIndex]); + + if (checked) { + if (placement === "polda") { + // Checklist semua POLDA (bukan SATKER POLRI) + listDest.forEach((item: any) => { + if (item.levelNumber === 2 && item.name !== "SATKER POLRI") { + currentFileLevels.add(Number(item.id)); + } + }); + } else if (placement === "polres") { + // Checklist POLRES hanya dari POLDA yang sudah ter-checklist + listDest.forEach((item: any) => { + if (item.levelNumber === 2 && item.name !== "SATKER POLRI") { + // Hanya checklist POLRES jika POLDA-nya sudah ter-checklist + if (currentFileLevels.has(Number(item.id))) { + if (item.subDestination) { + item.subDestination.forEach((polres: any) => { + currentFileLevels.add(Number(polres.id)); + }); + } + } + } + }); + } else if (placement === "satker") { + // Checklist SATKER POLRI dan semua sub-item di bawahnya + const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI"); + if (satkerItem) { + currentFileLevels.add(Number(satkerItem.id)); + if (satkerItem.subDestination) { + satkerItem.subDestination.forEach((sub: any) => { + currentFileLevels.add(Number(sub.id)); + }); + } + } + } + } else { + if (placement === "polda") { + // Unchecklist semua POLDA dan POLRES di bawahnya + listDest.forEach((item: any) => { + if (item.levelNumber === 2 && item.name !== "SATKER POLRI") { + currentFileLevels.delete(Number(item.id)); + if (item.subDestination) { + item.subDestination.forEach((polres: any) => { + currentFileLevels.delete(Number(polres.id)); + }); + } + } + }); + } else if (placement === "polres") { + // Unchecklist semua POLRES + listDest.forEach((item: any) => { + if (item.subDestination && item.name !== "SATKER POLRI") { + item.subDestination.forEach((polres: any) => { + currentFileLevels.delete(Number(polres.id)); + }); + } + }); + } else if (placement === "satker") { + // Unchecklist SATKER POLRI dan semua sub-item di bawahnya + const satkerItem: any = listDest.find((item: any) => item.name === "SATKER POLRI"); + if (satkerItem) { + currentFileLevels.delete(Number(satkerItem.id)); + if (satkerItem.subDestination) { + satkerItem.subDestination.forEach((sub: any) => { + currentFileLevels.delete(Number(sub.id)); + }); + } + } + } + } + + newArray[fileIndex] = currentFileLevels; + return newArray; + }); }; function handleDeleteFileApproval(id: number) { @@ -1111,445 +1436,309 @@ export default function FormImageDetail() { )} */} - - - - {t("leave-comment", { defaultValue: "Leave Comment" })} + + + + {t("leave-comment", { defaultValue: "Kelola Persetujuan Konten" })} -
- {status == "2" - ? files?.map((file, index) => ( -
-
+ +
+ {status == "2" && files?.map((file, index) => ( +
+ {/* Header File */} +
+
+
{`files-${index handleImageLoad(e, index)} - className={`h-[100px] object-cover ${ - portraitMap[index] ? "w-auto" : "!w-[200px]" + className={`h-[80px] object-cover ${ + portraitMap[index] ? "w-auto" : "!w-[120px]" }`} />
-
-
- {file.fileName} - - handleDeleteFileApproval(file.id) - } - > - - +
+

File {index + 1}

+

{file.fileName}

+
+
+ +
+ + {/* Section Pengaturan Distribusi */} +
+
+ + Pengaturan Distribusi +
+ + {/* Checkbox Tingkat Utama */} +
+
+

Tingkat Distribusi:

+
+ {[ + { key: "semua", label: "Semua" }, + { key: "nasional", label: "Nasional" }, + { key: "wilayah", label: "Wilayah" }, + { key: "international", label: "Internasional" }, + ].map((item, idx) => ( +
+ { + handleFileUnitChange( + index, + item.key as keyof typeof unitSelection, + value as boolean + ); + setupPlacement(index, item.key, Boolean(value)); + }} + /> + +
+ ))}
-
- {/* --- Checkbox utama --- */} -
+
+ + {/* Detail Wilayah */} + {fileUnitSelections[index]?.wilayah && ( +
+

Detail Wilayah:

+ + {/* Checkbox Sub-kategori dengan tombol Kustom sejajar */} +
{[ - "semua", - "nasional", - "wilayah", - "international", - ].map((key, index) => ( -
+ { key: "polda", label: "POLDA" }, + { key: "polres", label: "POLRES" }, + { key: "satker", label: "SATKER" }, + ].map((item, idx) => ( +
{ - handleUnitChange( - key as keyof typeof unitSelection, + handleFileUnitChange( + index, + item.key as keyof typeof unitSelection, value as boolean ); - setupPlacement( - index, - key, - Boolean(value) - ); + setupPlacement(index, item.key, Boolean(value)); }} /> -
))} -
- - {/* --- Kalau wilayah dicentang baru tampilkan detail --- */} - {unitSelection.wilayah && ( -
-
- {["polda", "polres", "satker"].map( - (key, index) => ( -
- { - handleUnitChange( - key as keyof typeof unitSelection, - value as boolean - ); - setupPlacement( - index, - key, - Boolean(value) - ); - }} - /> - -
- ) - )} -
- - {/* --- Custom Button + Dialog --- */} + + {/* Tombol Kustom sejajar dengan checkbox */} +
- - - - - Daftar Wilayah Polda dan Polres + + + + Daftar Wilayah POLDA dan POLRES -
+
{listDest.map((polda: any) => ( -
-
- +
+ {/* Header POLDA */} +
+ + {polda.subDestination && ( -
- - {expandedPolda[polda.id] && ( -
- - {polda?.subDestination?.map( - (polres: any) => ( - - ) - )} -
)}
+ + {/* Sub-items */} + {polda.subDestination && expandedPolda[polda.id] && ( +
+ {/* Tombol Pilih Semua untuk sub-items */} +
+ {(() => { + const allSubItemsChecked = polda.subDestination?.every((sub: any) => + fileCheckedLevels[index]?.has(Number(sub.id)) + ); + return ( + + ); + })()} +
+
+ {polda.subDestination.map((sub: any) => ( + + ))} +
+
+ )} +
))}
+
+ + + + + + +
- )} -
- {/* {isUserMabesApprover && ( -
-
- - setupPlacement(index, "all", Boolean(e)) - } - /> - -
-
- - setupPlacement( - index, - "mabes", - Boolean(e) - ) - } - /> - -
-
- - setupPlacement( - index, - "polda", - Boolean(e) - ) - } - /> - - {wilayahPublish.polda && ( - - setSelectedPolda(data) - } - /> - )} -
- -
- - setupPlacement( - index, - "international", - Boolean(e) - ) - } - /> - -
- )} */} +
+ )} +
+
+
+ ))} +
+ + {/* Section Komentar */} +
+
+
+ +