mediahub-fe/components/form/content/teks-update-form.tsx

2093 lines
82 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import React, {
ChangeEvent,
Fragment,
useEffect,
useRef,
useState,
} from "react";
import { useForm, Controller } from "react-hook-form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Card } from "@/components/ui/card";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { useParams, useRouter } from "next/navigation";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} 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";
import {
createMedia,
getTagsBySubCategoryId,
listEnableCategory,
uploadThumbnail,
updateFilePlacements,
} from "@/service/content/content";
import { getUserLevelForAssignments } from "@/service/task";
import { detailMedia } from "@/service/curated-content/curated-content";
import { Badge } from "@/components/ui/badge";
import { CloudUpload, MailIcon } from "lucide-react";
import { useDropzone } from "react-dropzone";
import { Icon } from "@iconify/react/dist/iconify.js";
import Image from "next/image";
import { error, loading } from "@/lib/swal";
import { Upload } from "tus-js-client";
import { getCsrfToken } from "@/service/auth";
import { useTranslations } from "next-intl";
import dynamic from "next/dynamic";
import { htmlToString } from "@/utils/globals";
import {
Dialog,
DialogClose,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { v4 as uuidv4 } from "uuid";
const teksSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
description: z
.string()
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
// tags: z.string().min(1, { message: "Judul diperlukan" }),
});
type Category = {
id: string;
name: string;
};
type PlacementType = "all" | "mabes" | "polda" | "international" | string;
interface FilePlacement {
mediaFileId: number;
placements?: PlacementType[];
}
interface TempFileItem {
id: number | string;
}
type Detail = {
id: string;
title: string;
description: string;
slug: string;
categoryId: number;
category: {
id: number;
name: string;
};
publishedForObject: {
id: number;
name: string;
};
creatorName: string;
categoryName: string;
thumbnailLink: string;
tags: string;
};
interface FileWithPreview extends File {
id: string;
preview: string;
}
interface Destination {
id: string;
name: string;
levelNumber: number;
subDestination?: SubDestination[];
}
interface SubDestination {
id: string;
name: string;
levelNumber: number;
}
type Option = {
id: string;
label: string;
};
const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
);
export default function FormTeksUpdate() {
const MySwal = withReactContent(Swal);
const router = useRouter();
const { id } = useParams() as { id: string };
console.log(id);
const editor = useRef(null);
type TeksSchema = z.infer<typeof teksSchema>;
let progressInfo: any = [];
let counterUpdateProgress = 0;
const [progressList, setProgressList] = useState<any>([]);
let uploadPersen = 0;
const [isStartUpload, setIsStartUpload] = useState(false);
const [counterProgress, setCounterProgress] = useState(0);
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId");
const scheduleId = Cookies.get("scheduleId");
const scheduleType = Cookies.get("scheduleType");
const t = useTranslations("Form");
const [categories, setCategories] = useState<Category[]>([]);
const [selectedCategory, setSelectedCategory] = useState<any>();
const [tags, setTags] = useState<any[]>([]);
const [detail, setDetail] = useState<Detail>();
const [refresh, setRefresh] = useState(false);
const [selectedPublishers, setSelectedPublishers] = useState<number[]>([]);
const [files, setFiles] = useState<FileWithPreview[]>([]);
const [fileUnitSelections, setFileUnitSelections] = useState<
Array<{
semua: boolean;
nasional: boolean;
wilayah: boolean;
international: boolean;
polda: boolean;
satker: boolean;
}>
>([]);
// State global untuk kompatibilitas (akan dihapus nanti)
const [unitSelection, setUnitSelection] = useState({
semua: false,
nasional: false,
wilayah: false,
international: false,
polda: false,
satker: false,
});
const [checkedLevels, setCheckedLevels] = useState<Set<number>>(new Set());
const [listDest, setListDest] = useState<Destination[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [expandedPolda, setExpandedPolda] = useState<Record<number, boolean>>(
{}
);
const [fileCheckedLevels, setFileCheckedLevels] = useState<
Array<Set<number>>
>([]);
const [isUpdatingFromMainCheckbox, setIsUpdatingFromMainCheckbox] =
useState(false);
const [mainCheckboxChangeType, setMainCheckboxChangeType] =
useState<string>("");
const [selectedTarget, setSelectedTarget] = useState("");
const [filePlacements, setFilePlacements] = useState<string[][]>([]);
const [tempFile, setTempFile] = useState<TempFileItem[]>([]);
const [publishedFor, setPublishedFor] = useState<string[]>([]);
const inputRef = useRef<HTMLInputElement>(null);
let fileTypeId = "3";
const isDetailOfRegionShowed = false;
const { getRootProps, getInputProps } = useDropzone({
onDrop: (acceptedFiles) => {
setFiles(acceptedFiles.map((file) => Object.assign(file, {
id: uuidv4(),
preview: URL.createObjectURL(file)
})));
},
accept: {
"application/pdf": [],
"application/msword": [], // .doc
"application/vnd.openxmlformats-officedocument.wordprocessingml.document":
[], // .docx
},
});
const options: Option[] = [
{ id: "all", label: "SEMUA" },
{ id: "5", label: "UMUM" },
{ id: "6", label: "JOURNALIS" },
{ id: "7", label: "POLRI" },
{ id: "8", label: "KSP" },
];
const {
control,
handleSubmit,
setValue,
formState: { errors },
} = useForm<TeksSchema>({
resolver: zodResolver(teksSchema),
});
// const handleKeyDown = (e: any) => {
// const newTag = e.target.value.trim(); // Ambil nilai input
// if (e.key === "Enter" && newTag) {
// e.preventDefault(); // Hentikan submit form
// if (!tags.includes(newTag)) {
// setTags((prevTags) => [...prevTags, newTag]); // Tambah tag baru
// setValue("tags", ""); // Kosongkan input
// }
// }
// };
const handleImageChange = (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.files) {
const files = Array.from(event.target.files);
setSelectedFiles((prevImages: any) => [...prevImages, ...files]);
console.log("DATAFILE::", selectedFiles);
}
};
const handleRemoveImage = (index: number) => {
setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index));
};
// const handleCheckboxChange = (id: number) => {
// setSelectedPublishers((prev) =>
// prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
// );
// };
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 untuk sinkronisasi checkbox modal dengan checkbox utama
useEffect(() => {
if (
listDest.length > 0 &&
isUpdatingFromMainCheckbox &&
mainCheckboxChangeType
) {
syncModalWithMainCheckbox();
}
}, [isUpdatingFromMainCheckbox, mainCheckboxChangeType]);
// useEffect untuk update checkbox utama ketika pilihan modal berubah
useEffect(() => {
if (!isUpdatingFromMainCheckbox && listDest.length > 0) {
updateMainCheckboxFromModalLegacy();
}
}, [checkedLevels, isUpdatingFromMainCheckbox]);
useEffect(() => {
async function initState() {
getCategories();
}
initState();
}, []);
const getCategories = async () => {
try {
const category = await listEnableCategory(fileTypeId);
const resCategory: Category[] = category?.data?.data?.content;
setCategories(resCategory);
console.log("data category", resCategory);
if (scheduleId && scheduleType === "3") {
const findCategory = resCategory.find((o) =>
o.name.toLowerCase().includes("pers rilis")
);
if (findCategory) {
// setValue("categoryId", findCategory.id);
setSelectedCategory(findCategory.id); // Set the selected category
const response = await getTagsBySubCategoryId(findCategory.id);
setTags(response?.data?.data);
}
}
} catch (error) {
console.error("Failed to fetch categories:", error);
}
};
// 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) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
checkedLevels.has(Number(item.id))
).length;
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 hasSelectedSatker = checkedSatkerCount > 0;
// Update unitSelection berdasarkan yang dipilih di modal
setUnitSelection((prev) => {
const newState = { ...prev };
// Update individual checkboxes
newState.polda = hasSelectedPolda;
newState.satker = hasSelectedSatker;
// Update checkbox "semua" berdasarkan semua checkbox yang aktif
newState.semua =
newState.nasional &&
newState.wilayah &&
newState.international &&
hasSelectedPolda &&
hasSelectedSatker;
return newState;
});
// Update filePlacements berdasarkan perubahan di modal
if (hasSelectedPolda) {
setupPlacement(0, "polda", true);
} else {
setupPlacement(0, "polda", false);
}
if (hasSelectedSatker) {
setupPlacement(0, "satker", true);
} else {
setupPlacement(0, "satker", false);
}
}
};
// 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 === "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));
});
}
});
} else if (mainCheckboxChangeType === "polda_unchecked") {
// Clear polda dari checkedLevels, tapi jangan hapus SATKER POLRI
listDest.forEach((item: any) => {
if (item.levelNumber === 2 && item.name !== "SATKER POLRI") {
newCheckedLevels.delete(Number(item.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);
// Update filePlacements berdasarkan perubahan di modal
if (mainCheckboxChangeType === "polda_checked") {
setupPlacement(0, "polda", true);
} else if (mainCheckboxChangeType === "polda_unchecked") {
setupPlacement(0, "polda", false);
} else if (mainCheckboxChangeType === "satker_checked") {
setupPlacement(0, "satker", true);
} else if (mainCheckboxChangeType === "satker_unchecked") {
setupPlacement(0, "satker", false);
} else if (mainCheckboxChangeType === "semua_checked") {
setupPlacement(0, "all", true);
} else if (mainCheckboxChangeType === "semua_unchecked") {
setupPlacement(0, "all", false);
}
// Reset flag setelah sinkronisasi selesai
setIsUpdatingFromMainCheckbox(false);
setMainCheckboxChangeType("");
}
};
// 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.satker = value;
// Update fileCheckedLevels untuk sinkronisasi dengan modal
setFileCheckedLevels((prevLevels) => {
const newArray = [...prevLevels];
const currentFileLevels = new Set<number>(
newArray[fileIndex] || new Set()
);
if (value) {
// Checklist semua item di modal
listDest.forEach((item: any) => {
currentFileLevels.add(Number(item.id));
if (item.subDestination) {
item.subDestination.forEach((sub: any) => {
currentFileLevels.add(Number(sub.id));
});
}
});
} else {
// Unchecklist semua item di modal
currentFileLevels.clear();
}
newArray[fileIndex] = currentFileLevels;
return newArray;
});
} else {
// Jika wilayah dicentang, auto centang POLDA, SATKER
if (key === "wilayah") {
currentSelection.wilayah = value;
if (value) {
// Ketika wilayah dicentang, auto centang POLDA, SATKER
currentSelection.polda = true;
currentSelection.satker = true;
// Update fileCheckedLevels untuk mengisi semua POLDA dan SATKER POLRI ketika wilayah dicentang
setFileCheckedLevels((prevLevels) => {
const newArray = [...prevLevels];
const currentFileLevels = new Set<number>(
newArray[fileIndex] || new Set()
);
// Checklist semua POLDA di modal
listDest.forEach((item: any) => {
if (item.levelNumber === 2 && item.name !== "SATKER POLRI") {
currentFileLevels.add(Number(item.id));
}
});
// Checklist SATKER POLRI dan semua sub-itemsnya
const satkerItem = listDest.find((item: any) => item.name === "SATKER POLRI");
if (satkerItem) {
currentFileLevels.add(Number(satkerItem.id));
// Checklist semua sub-items di bawah SATKER POLRI
if (satkerItem.subDestination) {
satkerItem.subDestination.forEach((sub: any) => {
currentFileLevels.add(Number(sub.id));
});
}
}
newArray[fileIndex] = currentFileLevels;
return newArray;
});
} else {
// Ketika wilayah di-uncheck, uncheck POLDA, SATKER dan hapus data POLDA dari fileCheckedLevels
currentSelection.polda = false;
currentSelection.satker = false;
// Update fileCheckedLevels untuk menghapus semua POLDA dan SATKER POLRI ketika wilayah di-uncheck
setFileCheckedLevels((prevLevels) => {
const newArray = [...prevLevels];
const currentFileLevels = new Set<number>(
newArray[fileIndex] || new Set()
);
// Hapus semua POLDA dari modal
listDest.forEach((item: any) => {
if (item.levelNumber === 2 && item.name !== "SATKER POLRI") {
currentFileLevels.delete(Number(item.id));
}
});
// Hapus SATKER POLRI dan semua sub-itemsnya
const satkerItem = listDest.find((item: any) => item.name === "SATKER POLRI");
if (satkerItem) {
currentFileLevels.delete(Number(satkerItem.id));
// Hapus semua sub-items di bawah SATKER POLRI
if (satkerItem.subDestination) {
satkerItem.subDestination.forEach((sub: any) => {
currentFileLevels.delete(Number(sub.id));
});
}
}
newArray[fileIndex] = currentFileLevels;
return newArray;
});
}
} else {
// Update salah satu saja
currentSelection[key] = value;
}
// Cek apakah semua selain "semua" sudah dicentang
const allChecked = [
"nasional",
"wilayah",
"international",
"polda",
"satker",
].every((k) => currentSelection[k as keyof typeof unitSelection]);
currentSelection.semua = allChecked;
}
newSelections[fileIndex] = currentSelection;
return newSelections;
});
// Update filePlacements berdasarkan perubahan checkbox
setupPlacement(fileIndex, key, value);
};
// Fungsi untuk mengupdate checklist levels untuk file tertentu
const handleFileCheckboxChangePlacement = (
fileIndex: number,
levelId: number
) => {
setFileCheckedLevels((prev) => {
const newArray = [...prev];
const currentFileLevels = new Set<number>(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;
});
// Update filePlacements berdasarkan perubahan di modal
// Cek apakah ini adalah POLDA atau SATKER yang diubah
const changedItem = listDest.find((item: any) => Number(item.id) === levelId);
if (changedItem) {
if (changedItem.levelNumber === 2 && changedItem.name !== "SATKER POLRI") {
// Ini adalah POLDA
const isChecked = fileCheckedLevels[fileIndex]?.has(levelId) || false;
setupPlacement(fileIndex, "polda", isChecked);
} else if (changedItem.name === "SATKER POLRI") {
// Ini adalah SATKER POLRI
const isChecked = fileCheckedLevels[fileIndex]?.has(levelId) || false;
setupPlacement(fileIndex, "satker", isChecked);
}
}
};
// 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);
// 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;
currentSelection.satker = Boolean(isSatkerChecked);
// Update checkbox "semua" berdasarkan semua checkbox yang aktif
currentSelection.semua =
currentSelection.nasional &&
currentSelection.wilayah &&
currentSelection.international &&
currentSelection.polda &&
currentSelection.satker;
newSelections[fileIndex] = currentSelection;
return newSelections;
});
};
const toggleExpand = (id: number) => {
setExpandedPolda((prev) => ({
...prev,
[id]: !prev[id],
}));
};
// Fungsi untuk menangani "Pilih Semua" sub-items di bawah SATKER POLRI
const handleSelectAllSubItems = (fileIndex: number, polda: any) => {
setFileCheckedLevels((prev) => {
const newArray = [...prev];
const currentFileLevels = new Set<number>(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 SATKER POLRI 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;
});
// Update filePlacements berdasarkan perubahan di modal
// Cek apakah ini adalah SATKER POLRI yang diubah
if (polda.name === "SATKER POLRI") {
const isChecked = fileCheckedLevels[fileIndex]?.has(Number(polda.id)) || false;
setupPlacement(fileIndex, "satker", isChecked);
}
};
const getPlacement = () => {
const temp: Array<{ mediaFileId: number | string; placements: string; customLocationPlacements: string }> = [];
for (let i = 0; i < files.length; i++) {
const file = files[i] as any;
const now = filePlacements[i];
if (file?.id && Array.isArray(now) && now.length > 0) {
const normalizedNow = now.map((item): PlacementType => {
const value = String(item);
if (value === "nasional") return "mabes";
if (value === "wilayah") return "polda";
if (value === "semua") return "all";
return item as PlacementType;
});
const uniqueNow = Array.from(new Set(normalizedNow));
const nowArr = uniqueNow.join(",");
// Dapatkan checked levels untuk file ini (berbasis index)
const currentFileCheckedLevels = fileCheckedLevels[i]
? Array.from(fileCheckedLevels[i])
: [];
temp.push({
mediaFileId: file.id,
placements: nowArr,
customLocationPlacements: currentFileCheckedLevels.join(","),
});
}
}
return temp;
};
const setupPlacement = (
index: number,
placement: string,
checked: boolean
) => {
let temp = [...filePlacements];
if (checked) {
if (placement === "all") {
temp[index] = ["all", "mabes", "polda", "satker", "international"];
// Update fileCheckedLevels untuk sinkronisasi dengan modal ketika "all" diklik
setFileCheckedLevels((prevLevels) => {
const newArray = [...prevLevels];
const currentFileLevels = new Set<number>(
newArray[index] || new Set()
);
// Checklist semua item di modal
listDest.forEach((item: any) => {
currentFileLevels.add(Number(item.id));
if (item.subDestination) {
item.subDestination.forEach((sub: any) => {
currentFileLevels.add(Number(sub.id));
});
}
});
newArray[index] = currentFileLevels;
return newArray;
});
// Update fileUnitSelections untuk checkbox tingkat utama
setFileUnitSelections((prevSelections) => {
const newSelections = [...prevSelections];
const currentSelection = { ...newSelections[index] };
// Set semua checkbox tingkat utama ke true
currentSelection.nasional = true;
currentSelection.wilayah = true;
currentSelection.international = true;
currentSelection.polda = true;
currentSelection.satker = true;
currentSelection.semua = true;
newSelections[index] = currentSelection;
return newSelections;
});
} else if (placement === "satker") {
// Ketika satker di-checklist, HANYA tambahkan satker saja
const now = temp[index] || [];
if (!now.includes("satker")) {
now.push("satker");
}
temp[index] = now;
} else if (placement === "polda") {
// Ketika polda di-checklist, tambahkan polda ke filePlacements
const now = temp[index] || [];
if (!now.includes("polda")) {
now.push("polda");
}
temp[index] = now;
} else if (placement === "wilayah") {
// Ketika wilayah dicentang, tambahkan wilayah, polda, dan satker
const now = temp[index] || [];
if (!now.includes("wilayah")) {
now.push("wilayah");
}
if (!now.includes("polda")) {
now.push("polda");
}
if (!now.includes("satker")) {
now.push("satker");
}
temp[index] = now;
} else {
// Handle mapping dari UI key ke backend value
let placementToAdd = placement;
if (placement === "nasional") {
placementToAdd = "mabes";
} else if (placement === "semua") {
placementToAdd = "all";
}
const now = temp[index] || [];
if (!now.includes(placementToAdd)) {
now.push(placementToAdd);
}
// Auto-checklist "all" jika nasional, wilayah, dan international ter-checklist
const requiredItems = ["mabes", "wilayah", "international"];
const hasAllRequired = requiredItems.every(item => now.includes(item));
if (hasAllRequired && !now.includes("all")) {
now.push("all");
}
temp[index] = now;
}
} else {
if (placement === "all") {
temp[index] = [];
// Update fileCheckedLevels untuk sinkronisasi dengan modal ketika "all" di-unchecklist
setFileCheckedLevels((prevLevels) => {
const newArray = [...prevLevels];
const currentFileLevels = new Set<number>(
newArray[index] || new Set()
);
// Unchecklist semua item di modal
currentFileLevels.clear();
newArray[index] = currentFileLevels;
return newArray;
});
// Update fileUnitSelections untuk checkbox tingkat utama
setFileUnitSelections((prevSelections) => {
const newSelections = [...prevSelections];
const currentSelection = { ...newSelections[index] };
// Set semua checkbox tingkat utama ke false
currentSelection.nasional = false;
currentSelection.wilayah = false;
currentSelection.international = false;
currentSelection.polda = false;
currentSelection.satker = false;
currentSelection.semua = false;
newSelections[index] = currentSelection;
return newSelections;
});
} else {
if (placement === "wilayah") {
// Ketika wilayah di-uncheck, hapus wilayah, polda, dan satker
const now = temp[index]?.filter((a) =>
a !== "wilayah" && a !== "polda" && a !== "satker"
);
temp[index] = now;
} else if (placement === "polda") {
// Ketika polda di-uncheck, hapus polda dari filePlacements
const now = temp[index]?.filter((a) => a !== "polda");
temp[index] = now;
} else if (placement === "satker") {
// Ketika satker di-uncheck, hapus satker dari filePlacements
const now = temp[index]?.filter((a) => a !== "satker");
temp[index] = now;
} else {
// Handle mapping dari UI key ke backend value
let placementToRemove = placement;
if (placement === "nasional") {
placementToRemove = "mabes";
} else if (placement === "semua") {
placementToRemove = "all";
}
const now = temp[index]?.filter((a) => a !== placementToRemove);
temp[index] = now;
}
// Hapus "all" jika tidak semua item ter-checklist
const currentNow = temp[index] || [];
if (currentNow.includes("all")) {
const requiredItems = ["mabes", "wilayah", "international"];
const hasAllRequired = requiredItems.every(item => currentNow.includes(item));
if (!hasAllRequired) {
const newData = currentNow.filter((b) => b !== "all");
temp[index] = newData;
}
}
}
}
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<number>(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 === "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
listDest.forEach((item: any) => {
if (item.levelNumber === 2 && item.name !== "SATKER POLRI") {
currentFileLevels.delete(Number(item.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;
});
// Update filePlacements berdasarkan perubahan di modal
if (placement === "polda" || placement === "satker") {
setupPlacement(fileIndex, placement, checked);
}
};
useEffect(() => {
async function initState() {
if (id) {
const response = await detailMedia(id);
const details = response?.data?.data;
setDetail(details);
setSelectedTarget(String(details.category.id));
// Set form values immediately and then again after a delay to ensure editor is ready
setValue("title", details.title);
setValue("description", details.htmlDescription);
setValue("creatorName", details.creatorName);
// Set again after delay to ensure editor has loaded
setTimeout(() => {
setValue("title", details.title);
setValue("description", details.htmlDescription);
setValue("creatorName", details.creatorName);
}, 500);
if (details?.files) {
// Format files dengan id unik
const formattedFiles = details.files.map((file: any) => ({
...file,
id: file.id || uuidv4(),
}));
setFiles(formattedFiles);
// Inisialisasi filePlacements dari detail (biarkan format backend, normalisasi dilakukan saat submit)
const initialFilePlacements: string[][] = details.files.map((file: any) => {
if (file.placements) {
return file.placements.split(",").map((p: string) => p.trim());
}
return [];
});
setFilePlacements(initialFilePlacements);
// Inisialisasi fileCheckedLevels dari detail
const initialFileCheckedLevels: Set<number>[] = details.files.map((file: any) => {
const checkedLevels = new Set<number>();
if (file.customLocationPlacements) {
const levelIds = file.customLocationPlacements.split(",").map((id: string) => Number(id.trim()));
levelIds.forEach((id: number) => {
if (!isNaN(id)) {
checkedLevels.add(id);
}
});
}
return checkedLevels;
});
setFileCheckedLevels(initialFileCheckedLevels);
// Inisialisasi fileUnitSelections dari detail
const initialFileUnitSelections = details.files.map((file: any) => {
const selection = {
semua: false,
nasional: false,
wilayah: false,
international: false,
polda: false,
satker: false,
};
if (file.placements) {
const placements = file.placements.split(",").map((p: string) => p.trim());
if (placements.includes("all")) {
selection.semua = true;
selection.nasional = true;
selection.wilayah = true;
selection.international = true;
selection.polda = true;
selection.satker = true;
} else {
if (placements.includes("mabes")) selection.nasional = true;
if (placements.includes("international")) selection.international = true;
if (placements.includes("polda")) selection.polda = true;
if (placements.includes("satker")) selection.satker = true;
// Wilayah aktif jika ada "wilayah" ATAU ada polda/satker
if (
placements.includes("wilayah") ||
placements.includes("polda") ||
placements.includes("satker")
) {
selection.wilayah = true;
}
}
}
return selection;
});
setFileUnitSelections(initialFileUnitSelections);
}
if (details?.publishedFor) {
// Split string "7" to an array ["7"] if needed
setPublishedFor(details.publishedFor.split(","));
}
if (details?.tags) {
setTags(details.tags.split(",").map((tag: string) => tag.trim()));
}
// const matchingCategory = categories.find(
// (category) => category.id === details.categoryId
// );
// if (matchingCategory) {
// setSelectedTarget(matchingCategory.name);
// }
// setSelectedTarget(details.categoryId); // Untuk dropdown
}
}
initState();
}, [refresh, setValue]);
const handleCheckboxChange = (id: string) => {
if (id === "all") {
// Select all options except "all"
const allOptions = options
.filter((opt) => opt.id !== "all")
.map((opt) => opt.id);
setPublishedFor(
publishedFor.length === allOptions.length ? [] : allOptions
);
} else {
// Toggle individual option
setPublishedFor((prev) =>
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
);
}
};
const save = async (data: TeksSchema) => {
loading();
const finalTags = tags.join(", ");
const requestData = {
...data,
id: detail?.id,
title: data.title,
description: htmlToString(data.description),
htmlDescription: data.description,
fileTypeId,
categoryId: selectedTarget,
subCategoryId: selectedTarget,
uploadedBy: "2b7c8d83-d298-4b19-9f74-b07924506b58",
statusId: "1",
publishedFor: publishedFor.join(","),
creatorName: data.creatorName,
tags: finalTags,
isYoutube: false,
isInternationalMedia: false,
};
const response = await createMedia(requestData);
console.log("Form Data Submitted:", requestData);
if (response?.error) {
error(response?.message);
return false;
}
const formMedia = new FormData();
const thumbnail = files[0];
formMedia.append("file", thumbnail);
const responseThumbnail = await uploadThumbnail(id, formMedia);
if (responseThumbnail?.error == true) {
error(responseThumbnail?.message);
return false;
}
const progressInfoArr = [];
for (const item of files) {
progressInfoArr.push({ percentage: 0, fileName: item.name });
}
progressInfo = progressInfoArr;
setIsStartUpload(true);
setProgressList(progressInfoArr);
// Update file placements terlebih dahulu (sebelum upload)
const placementData = getPlacement();
if (placementData.length > 0) {
const responseFilePlacements = await updateFilePlacements(placementData);
if (responseFilePlacements?.error) {
error(responseFilePlacements?.message);
return false;
}
}
close();
// showProgress();
files.map(async (item: any, index: number) => {
await uploadResumableFile(
index,
String(id),
item,
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0"
);
});
MySwal.fire({
title: "Sukses",
text: "Data berhasil disimpan.",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push("/in/contributor/content/teks");
});
};
async function uploadResumableFile(
idx: number,
id: string,
file: any,
duration: string
) {
console.log(idx, id, file, duration);
// const placements = getPlacement(file.placements);
// console.log("Placementttt: : ", placements);
const resCsrf = await getCsrfToken();
const csrfToken = resCsrf?.data?.token;
console.log("CSRF TOKEN : ", csrfToken);
const headers = {
"X-XSRF-TOKEN": csrfToken,
};
if (!file.secondaryUrl || file.secondaryUrl == "") {
const upload = new Upload(file, {
endpoint: `${process.env.NEXT_PUBLIC_API}/media/file/upload`,
headers: headers,
retryDelays: [0, 3000, 6000, 12_000, 24_000],
chunkSize: 20_000,
metadata: {
mediaid: id,
filename: file.name,
filetype: file.type,
duration,
isWatermark: "true", // hardcode
},
onBeforeRequest: function (req) {
var xhr = req.getUnderlyingObject();
xhr.withCredentials = true;
},
onError: async (e: any) => {
console.log("Error upload :", e);
error(e);
},
onChunkComplete: (
chunkSize: any,
bytesAccepted: any,
bytesTotal: any
) => {
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
progressInfo[idx].percentage = uploadPersen;
counterUpdateProgress++;
console.log(counterUpdateProgress);
setProgressList(progressInfo);
setCounterProgress(counterUpdateProgress);
},
onSuccess: async () => {
uploadPersen = 100;
progressInfo[idx].percentage = 100;
counterUpdateProgress++;
setCounterProgress(counterUpdateProgress);
successTodo();
},
});
upload.start();
}
}
const onSubmit = (data: TeksSchema) => {
MySwal.fire({
title: "Simpan Data",
text: "Apakah Anda yakin ingin menyimpan data ini?",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Simpan",
}).then((result) => {
if (result.isConfirmed) {
save(data);
}
});
};
const successSubmit = (redirect: string) => {
MySwal.fire({
title: "Sukses",
text: "Data berhasil disimpan.",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push(redirect);
});
};
function successTodo() {
let counter = 0;
for (const element of progressInfo) {
if (element.percentage == 100) {
counter++;
}
}
if (counter == progressInfo.length) {
setIsStartUpload(false);
// hideProgress();
Cookies.remove("idCreate");
successSubmit("/in/contributor/content/teks");
}
}
const handleRemoveAllFiles = () => {
setFiles([]);
};
const renderFilePreview = (file: FileWithPreview) => {
if (file?.type?.startsWith("image")) {
return (
<Image
width={48}
height={48}
alt={file.name}
src={URL.createObjectURL(file)}
className=" rounded border p-0.5"
/>
);
} else {
return <Icon icon="tabler:file-description" />;
}
};
const handleRemoveFile = (file: FileWithPreview) => {
const uploadedFiles = files;
const filtered = uploadedFiles.filter((i) => i.name !== file.name);
setFiles([...filtered]);
};
const fileList = files.map((file) => (
<div
key={file.name}
className=" flex justify-between border px-3.5 py-3 my-6 rounded-md"
>
<div className="flex gap-3 items-center">
<div className="file-preview">{renderFilePreview(file)}</div>
<div>
<div className=" text-sm text-card-foreground">{file.name}</div>
<div className=" text-xs font-light text-muted-foreground">
{Math.round(file.size / 100) / 10 > 1000 ? (
<>{(Math.round(file.size / 100) / 10000).toFixed(1)}</>
) : (
<>{(Math.round(file.size / 100) / 10).toFixed(1)}</>
)}
{" kb"}
</div>
</div>
</div>
<Button
type="button"
size="icon"
color="destructive"
variant="outline"
className=" border-none rounded-full"
onClick={() => handleRemoveFile(file)}
>
<Icon icon="tabler:x" className=" h-5 w-5" />
</Button>
</div>
));
const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter" && e.currentTarget.value.trim()) {
e.preventDefault();
const newTag = e.currentTarget.value.trim();
if (!tags.includes(newTag)) {
setTags((prevTags) => [...prevTags, newTag]); // Tambahkan tag baru
if (inputRef.current) {
inputRef.current.value = ""; // Kosongkan input
}
}
}
};
const handleRemoveTag = (index: number) => {
setTags((prevTags) => prevTags.filter((_, i) => i !== index));
};
const handleEditTag = (index: number, newValue: string) => {
setTags((prevTags) =>
prevTags.map((tag, i) => (i === index ? newValue : tag))
);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
{detail !== undefined ? (
<div className="flex flex-col lg:flex-row gap-10">
<Card className="w-full lg:w-8/12">
<div className="px-6 py-6">
<p className="text-lg font-semibold mb-3">
{t("form-text", { defaultValue: "Form Text" })}
</p>
<div className="gap-5 mb-5">
{/* Input Title */}
<div className="space-y-2 py-3">
<Label>{t("title", { defaultValue: "Title" })}</Label>
<Controller
control={control}
name="title"
render={({ field }) => (
<Input
size="md"
type="text"
value={field?.value}
onChange={field.onChange}
placeholder="Enter Title"
/>
)}
/>
{errors.title?.message && (
<p className="text-red-400 text-sm">
{errors.title.message}
</p>
)}
</div>
<div className="flex items-center">
<div className="py-3 w-full space-y-2">
<Label>{t("category", { defaultValue: "Category" })}</Label>
<Select
value={selectedTarget}
onValueChange={(id) => {
console.log("Selected Category:", id);
setSelectedTarget(id);
}}
>
<SelectTrigger size="md">
<SelectValue placeholder="Pilih" />
</SelectTrigger>
<SelectContent>
{/* Show the category from details if it doesn't exist in categories list */}
{detail &&
!categories.find(
(cat) =>
String(cat.id) === String(detail.category.id)
) && (
<SelectItem
key={String(detail.category.id)}
value={String(detail.category.id)}
>
{detail.category.name}
</SelectItem>
)}
{categories.map((category) => (
<SelectItem
key={String(category.id)}
value={String(category.id)}
>
{" "}
{category.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
<div className="py-3 space-y-2">
<Label>
{t("description", { defaultValue: "Description" })}
</Label>
<Controller
control={control}
name="description"
render={({ field: { onChange, value } }) => (
<CustomEditor onChange={onChange} initialData={value} />
)}
/>
{errors.description?.message && (
<p className="text-red-400 text-sm">
{errors.description.message}
</p>
)}
</div>
<div className="py-3 space-y-2">
<Label>
{t("select-file", { defaultValue: "Select File" })}
</Label>
{/* <Input
id="fileInput"
type="file"
onChange={handleImageChange}
/> */}
<Fragment>
<div {...getRootProps({ className: "dropzone" })}>
<input {...getInputProps()} />
<div className=" w-full text-center border-dashed border border-default-200 dark:border-default-300 rounded-md py-[52px] flex items-center flex-col">
<CloudUpload className="text-default-300 w-10 h-10" />
<h4 className=" text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
{/* Drop files here or click to upload. */}
{t("drag-file", { defaultValue: "Drag File" })}
</h4>
<div className=" text-xs text-muted-foreground">
{t("upload-file-text-max", {
defaultValue: "Upload File Text Max",
})}
</div>
</div>
</div>
{files.length ? (
<Fragment>
<div>{fileList}</div>
<div className=" flex justify-between gap-2">
<div className="flex flex-row items-center gap-3 py-3">
<Label>
{t("watermark", { defaultValue: "Watermark" })}
</Label>
<div className="flex items-center gap-3">
<Switch defaultChecked color="primary" id="c2" />
</div>
</div>
<Button
color="destructive"
onClick={handleRemoveAllFiles}
>
{t("remove-all", { defaultValue: "Remove All" })}
</Button>
</div>
</Fragment>
) : null}
{files.length > 0 && (
<div className="mt-4">
<Label className="text-md font-semibold">
{" "}
{t("file-media", { defaultValue: "File Media" })}
</Label>
<div className="grid gap-4">
{files.map((file: any, index: any) => (
<div
key={file.id}
className="flex items-center border p-2 rounded-md"
>
<img
src={file.thumbnailFileUrl}
alt={file.fileName}
className="w-16 h-16 object-cover rounded-md mr-4"
/>
<div className="flex flex-wrap gap-3 items-center ">
<div className="flex-grow">
<p className="font-medium">{file.fileName}</p>
<a
href={file.url}
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 text-sm"
>
{t("view-file", {
defaultValue: "View File",
})}
</a>
</div>
<div className="bg-white rounded-md p-4 border">
{/* Checkbox Tingkat Utama */}
<div className="space-y-4">
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{[
{ key: "semua", label: "Semua" },
{
key: "nasional",
label: "Nasional",
},
{ key: "wilayah", label: "Wilayah" },
{
key: "international",
label: "Internasional",
},
].map((item, idx) => (
<div
key={item.key}
className="flex items-center gap-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50"
>
<Checkbox
// id={`${item.key}-${index}`}
checked={
fileUnitSelections[files.indexOf(file)]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => {
handleFileUnitChange(
files.indexOf(file),
item.key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
files.indexOf(file),
item.key as keyof typeof unitSelection,
Boolean(value)
);
}}
/>
<Label
htmlFor={`${item.key}-${index}`}
className="text-sm font-medium cursor-pointer"
>
{item.label}
</Label>
</div>
))}
</div>
{/* Detail Wilayah */}
{fileUnitSelections[files.indexOf(file)]?.wilayah && isDetailOfRegionShowed && (
<div className="border-t border-gray-200 pt-2">
<p className="text-sm font-medium text-gray-700 mb-2">
Detail Wilayah:
</p>
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
{[
{ key: "polda", label: "POLDA" },
{ key: "satker", label: "SATKER" },
].map((item, idx) => (
<div
key={item.key}
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
>
<Checkbox
id={`${item.key}-${index}`}
checked={
fileUnitSelections[files.indexOf(file)]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => {
handleFileUnitChange(
files.indexOf(file),
item.key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
files.indexOf(file),
item.key as keyof typeof unitSelection,
Boolean(value)
);
}}
/>
<Label
htmlFor={`${item.key}-${index}`}
className="text-sm font-medium cursor-pointer"
>
{item.label}
</Label>
</div>
))}
{/* Tombol Kustom sejajar dengan checkbox */}
<div className="flex items-center justify-center p-3">
<Dialog>
<DialogTrigger asChild>
<Button
variant="outline"
size="sm"
className="gap-2"
>
<Icon
icon="material-symbols:tune"
width={16}
height={16}
/>
{t("custom", {
defaultValue: "Kustom",
})}
</Button>
</DialogTrigger>
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
<DialogHeader className="border-b border-gray-200 pb-4">
<DialogTitle className="text-lg font-semibold">
Daftar Wilayah POLDA dan SATKER
</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
{listDest.map(
(polda: any) => (
<div
key={polda.id}
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
>
{/* Header POLDA */}
<div className="flex items-center justify-between">
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
<Checkbox
checked={
fileCheckedLevels[
files.indexOf(file)
]?.has(
Number(
polda.id
)
) || false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
files.indexOf(file),
Number(polda.id)
)
}
/>
<span className="font-semibold text-gray-900 text-sm">
{polda.name}
</span>
</Label>
{/* Tombol expand hanya untuk SATKER POLRI */}
{polda.name === "SATKER POLRI" && polda.subDestination && (
<button
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
toggleExpand(
Number(polda.id)
);
}}
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
>
<Icon
icon={
expandedPolda[Number(polda.id)]
? "mdi:chevron-up"
: "mdi:chevron-down"
}
width={16}
height={16}
/>
</button>
)}
</div>
{/* Sub-items hanya untuk SATKER POLRI */}
{polda.name === "SATKER POLRI" && polda.subDestination &&
expandedPolda[Number(polda.id)] && (
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
{/* Tombol Pilih Semua untuk sub-items */}
<div className="mb-2 flex justify-start">
{(() => {
const allSubItemsChecked =
polda.subDestination?.every(
(
sub: any
) =>
fileCheckedLevels[
files.indexOf(file)
]?.has(
Number(
sub.id
)
)
);
return (
<Button
size="sm"
variant="outline"
className="text-xs h-6 px-2"
onClick={() =>
handleSelectAllSubItems(
files.indexOf(file),
polda
)
}
>
{allSubItemsChecked ? (
<>
<Icon
icon="material-symbols:check-indeterminate-small"
width={
12
}
height={
12
}
className="mr-1"
/>
Batal
Semua
</>
) : (
<>
<Icon
icon="material-symbols:check-all"
width={
12
}
height={
12
}
className="mr-1"
/>
Pilih
Semua
</>
)}
</Button>
);
})()}
</div>
<div className="space-y-1">
{polda.subDestination.map(
(
sub: any
) => (
<Label
key={
sub.id
}
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
>
<Checkbox
checked={
fileCheckedLevels[
files.indexOf(file)
]?.has(
Number(
sub.id
)
) ||
false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
files.indexOf(file),
Number(sub.id)
)
}
/>
<span className="text-gray-700">
{
sub.name
}
</span>
</Label>
)
)}
</div>
</div>
)}
</div>
)
)}
</div>
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
<DialogClose asChild>
<Button variant="outline">
{t("cancel", {
defaultValue: "Batal",
})}
</Button>
</DialogClose>
<DialogClose asChild>
<Button>Simpan</Button>
</DialogClose>
</div>
</DialogContent>
</Dialog>
</div>
</div>
</div>
)}
</div>
</div>
</div>
</div>
))}
</div>
</div>
)}
</Fragment>
</div>
</div>
</div>
</Card>
<div className="w-full lg:w-4/12">
<Card className=" h-[800px]">
<div className="px-3 py-3">
<div className="space-y-2">
<Label>{t("creator", { defaultValue: "Creator" })}</Label>
<Controller
control={control}
name="creatorName"
render={({ field }) => (
<Input
size="md"
type="text"
value={field?.value}
onChange={field.onChange}
placeholder="Enter Title"
/>
)}
/>
{errors.creatorName?.message && (
<p className="text-red-400 text-sm">
{errors.creatorName.message}
</p>
)}
</div>
</div>
{/* <div className="mt-3 px-3">
<Label>Pratinjau Gambar Utama</Label>
<Card className="mt-2">
<img
src={detail.thumbnailLink}
alt="Thumbnail Gambar Utama"
className="w-full h-auto rounded"
/>
</Card>
</div> */}
<div className="px-3 py-3">
<div className="space-y-2">
<Label>{t("tags", { defaultValue: "Tags" })}</Label>
<Input
type="text"
id="tags"
placeholder="Add a tag and press Enter"
onKeyDown={handleAddTag}
ref={inputRef}
/>
<div className="mt-3 flex flex-wrap gap-2">
{tags.map((tag, index) => (
<span
key={index}
className="flex items-center gap-2 px-2 py-1 rounded-lg bg-black text-white text-sm"
>
<input
type="text"
value={tag}
onChange={(e) => handleEditTag(index, e.target.value)}
className="bg-black text-white border-none focus:outline-none w-auto"
/>
<button
value={tag}
type="button"
onClick={() => handleRemoveTag(index)}
className="remove-tag-button text-white"
>
×
</button>
</span>
))}
</div>
</div>
</div>
<div className="px-3 py-3">
<div className="flex flex-col gap-6">
<Label>
{t("publish-target", { defaultValue: "Publish Target" })}
</Label>
{options.map((option) => (
<div key={option.id} className="flex gap-2 items-center">
<Checkbox
id={option.id}
checked={
option.id === "all"
? publishedFor.length ===
options.filter((opt: any) => opt.id !== "all")
.length
: publishedFor.includes(option.id)
}
onCheckedChange={() => handleCheckboxChange(option.id)}
/>
<Label htmlFor={option.id}>{option.label}</Label>
</div>
))}
</div>
</div>
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
<MailIcon />
<p className="">
{t("suggestion-box", { defaultValue: "Suggestion Box" })} (0)
</p>
</div>
<div className="px-3 py-3">
<p>{t("information", { defaultValue: "Information" })}:</p>
{/* <p>{detail?.status}</p> */}
</div>
</Card>
<div className="flex flex-row justify-end gap-3">
<div className="mt-4">
<Button type="submit" color="primary">
{t("submit", { defaultValue: "Submit" })}
</Button>
</div>
<div className="mt-4">
<Button type="submit" color="primary" variant="outline">
{t("cancel", { defaultValue: "Cancel" })}
</Button>
</div>
</div>
</div>
</div>
) : (
""
)}
</form>
);
}