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

2388 lines
96 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 Cookies from "js-cookie";
import {
createMedia,
deleteFile,
getTagsBySubCategoryId,
listEnableCategory,
updateFilePlacements,
uploadThumbnail,
} from "@/service/content/content";
import { detailMedia } from "@/service/curated-content/curated-content";
import { Badge } from "@/components/ui/badge";
import { CloudUpload, MailIcon } from "lucide-react";
import { Swiper, SwiperSlide } from "swiper/react";
import ReactAudioPlayer from "react-audio-player";
import { FreeMode, Navigation, Thumbs } from "swiper/modules";
import { useDropzone } from "react-dropzone";
import Image from "next/image";
import { Icon } from "@iconify/react/dist/iconify.js";
import { error } from "@/lib/swal";
import { getCsrfToken } from "@/service/auth";
import { Upload } from "tus-js-client";
import { useTranslations } from "next-intl";
import dynamic from "next/dynamic";
import { Link } from "@/i18n/routing";
import { htmlToString } from "@/utils/globals";
import { getUserLevelForAssignments } from "@/service/task";
import {
Dialog,
DialogClose,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { v4 as uuidv4 } from "uuid";
import { getCookiesDecrypt } from "@/lib/utils";
import { close, loading } from "@/config/swal";
import { translateText } from "@/service/content/ai";
const audioSchema = 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;
};
creatorName: string;
categoryName: string;
thumbnailLink: string;
tags: string;
};
interface FileWithPreview extends File {
id: string;
preview: string;
}
interface Destination {
id: string;
name: string;
subDestination?: SubDestination[];
}
interface SubDestination {
id: string;
name: string;
}
type Option = {
id: string;
name: string;
};
const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
);
export default function FormAudioUpdate() {
const MySwal = withReactContent(Swal);
const router = useRouter();
const { id } = useParams() as { id: string };
// console.log(id);
const editor = useRef(null);
type AudioSchema = z.infer<typeof audioSchema>;
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 [detailThumb, setDetailThumb] = useState<any>([]);
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
const [selectedTarget, setSelectedTarget] = useState("");
const [publishedFor, setPublishedFor] = useState<string[]>([]);
const inputRef = useRef<HTMLInputElement>(null);
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 [isLoadingTranslate, setIsLoadingTranslate] = useState(false);
const [translatedContent, setTranslatedContent] = React.useState("");
const [selectedLang, setSelectedLang] = React.useState<"id" | "en">("id");
const roleId = getCookiesDecrypt("urie");
const [translatedTitle, setTranslatedTitle] = useState("");
const options: Option[] = [
{ id: "all", name: "SEMUA" },
{ id: "5", name: "UMUM" },
{ id: "6", name: "JOURNALIS" },
{ id: "7", name: "POLRI" },
{ id: "8", name: "KSP" },
];
const [files, setFiles] = useState<FileWithPreview[]>([]);
const [prevFiles, setPrefFiles] = useState([]);
const [filePlacements, setFilePlacements] = useState<string[][]>([]);
const [tempFile, setTempFile] = useState<TempFileItem[]>([]);
let fileTypeId = "4";
const isDetailOfRegionShowed = false;
const { getRootProps, getInputProps } = useDropzone({
onDrop: (acceptedFiles) => {
setFiles((prevFiles) => [
...prevFiles,
...acceptedFiles.map((file) =>
Object.assign(file, {
id: uuidv4(), // generate unique id
preview: URL.createObjectURL(file),
})
),
]);
},
accept: {
"audio/*": [],
},
});
const {
control,
handleSubmit,
setValue,
getValues,
formState: { errors },
} = useForm<AudioSchema>({
resolver: zodResolver(audioSchema),
});
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));
};
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;
});
}
};
// 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);
// Reset flag setelah sinkronisasi selesai
setIsUpdatingFromMainCheckbox(false);
setMainCheckboxChangeType("");
}
};
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))
);
};
// 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;
});
};
// 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;
});
};
// 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;
});
};
const getPlacement = () => {
const temp = [];
for (let i = 0; i < filePlacements?.length; i++) {
const file = files[i] as any;
if (
file.id &&
filePlacements[file.id] &&
filePlacements[file.id].length > 0
) {
const now = filePlacements[file.id];
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
const currentFileCheckedLevels = fileCheckedLevels[file.id]
? Array.from(fileCheckedLevels[file.id])
: [];
const data = {
mediaFileId: file.id,
placements: nowArr,
customLocationPlacements: currentFileCheckedLevels.join(","),
};
temp.push(data);
}
}
return temp;
};
const setupPlacement = (
index: number,
placement: string,
checked: boolean
) => {
let temp = [...filePlacements];
if (checked) {
if (placement === "all") {
temp[index] = ["all", "mabes", "polda", "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;
});
};
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) {
const formattedFiles = details.files.map((file: any) => ({
...file,
id: file.id,
fileName: file.fileName,
size: file.size,
thumbnailFileUrl: file.thumbnailFileUrl,
url: file.url,
}));
setFiles(formattedFiles);
setPrefFiles(details.files);
setTempFile(details?.files);
// Inisialisasi filePlacements dari detail
const initialFilePlacements = details.files.map((file: any) => {
if (file.placements) {
// Map dari format backend ke format internal
const mappedPlacements = file.placements
.split(",")
.map((p: string) => {
const trimmed = p.trim();
switch (trimmed) {
case "all":
return "all";
case "mabes":
return "nasional";
case "polda":
return "wilayah";
case "satker":
return "satker";
case "international":
return "international";
default:
return trimmed;
}
});
return mappedPlacements;
}
return [];
});
setFilePlacements(initialFilePlacements);
// Inisialisasi fileCheckedLevels dari detail
const initialFileCheckedLevels = details.files.map((file: any) => {
if (file.customLocationPlacements) {
const levelIds = file.customLocationPlacements
.split(",")
.map((id: string) => Number(id.trim()))
.filter((id: number) => !isNaN(id));
return new Set(levelIds);
}
return new Set<number>();
});
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());
// Map dari format backend ke checkbox
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("wilayah")) {
selection.wilayah = true;
}
if (placements.includes("polda")) {
selection.polda = true;
selection.wilayah = true; // Auto-check wilayah when polda is present
}
if (placements.includes("satker")) {
selection.satker = true;
selection.wilayah = true; // Auto-check wilayah when satker is present
}
if (placements.includes("international")) {
selection.international = 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);
const filesData = details.files || [];
const fileUrls = filesData.map((file: { secondaryUrl: string }) =>
file.secondaryUrl ? file.secondaryUrl : "default-image.jpg"
);
setDetailThumb(fileUrls);
}
}
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: AudioSchema) => {
const finalTags = tags.join(", ");
// ✅ tentukan isi description sesuai pilihan bahasa
// const descFinal =
// selectedLang === "en" && translatedContent
// ? translatedContent
// : data.description;
const descFinal = translatedContent || data.description;
const finalTitle = translatedTitle || data.title;
const requestData = {
...data,
id: detail?.id,
title: finalTitle,
description: htmlToString(descFinal),
htmlDescription: descFinal,
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
const responseFilePlacements = await updateFilePlacements(getPlacement());
if (responseFilePlacements?.error) {
error(responseFilePlacements?.message);
return false;
}
close();
// showProgress();
files.map(async (item: any, index: number) => {
await uploadResumableFile(index, String(id), item, "0");
});
MySwal.fire({
title: "Sukses",
text: "Data berhasil disimpan.",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push("/in/contributor/content/audio");
});
};
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: "false", // 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: AudioSchema) => {
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/audio");
}
}
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: any) => (
<div
key={file.id} // Gunakan ID file sebagai key
className="flex justify-between border px-3.5 py-3 my-6 rounded-md"
>
<div className="flex gap-3 items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
width="48"
height="48"
viewBox="0 0 20 20"
>
<path
fill="currentColor"
d="M14.702 2.226A1 1 0 0 1 16 3.18v6.027a5.5 5.5 0 0 0-1-.184V6.18L8 8.368V15.5a2.5 2.5 0 1 1-1-2V5.368a1 1 0 0 1 .702-.955zM8 7.32l7-2.187V3.18L8 5.368zM5.5 14a1.5 1.5 0 1 0 0 3a1.5 1.5 0 0 0 0-3m13.5.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0m-2.265-.436l-2.994-1.65a.5.5 0 0 0-.741.438v3.3a.5.5 0 0 0 .741.438l2.994-1.65a.5.5 0 0 0 0-.876"
/>
</svg>{" "}
<div>
<div className="text-sm text-card-foreground">
{file.fileName || 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)} // Kirim ID spesifik
>
<Icon icon="tabler:x" className="h-5 w-5" />
</Button>
</div>
));
function success() {
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
// window.location.reload();
}
});
}
const handleDeleteFile = (id: number) => {
MySwal.fire({
title: "Hapus file",
text: "Apakah Anda yakin ingin menghapus file ini?",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
async function doDelete(id: number) {
const data = { id };
try {
const response = await deleteFile(data);
if (response?.error) {
error(response.message);
return;
}
// Jika berhasil, hapus file dari state lokal
setFiles((prevFiles: any) =>
prevFiles.filter((file: any) => file.id !== id)
);
success();
} catch (err) {
error("Terjadi kesalahan saat menghapus file");
}
}
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-audio", { defaultValue: "Form Audio" })}
</p>
<div className="gap-5 mb-5">
{/* Input Title */}
<div className="space-y-2 py-3">
<div className="flex justify-between items-center">
<Label>{t("title", { defaultValue: "Title" })}</Label>
{roleId === "14" && (
<button
type="button"
onClick={async () => {
try {
loading();
setIsLoadingTranslate(true);
const res = await translateText({
text: getValues("title"),
sourceLang: "ID",
targetLang: "EN",
});
if (!res.error) {
const resultText =
res?.data?.data?.translations?.[0]?.text || "";
setTranslatedTitle(resultText);
}
} catch (err) {
close();
console.error("Translate title gagal:", err);
} finally {
close();
setIsLoadingTranslate(false);
}
}}
className="px-3 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600"
>
{isLoadingTranslate
? "Translating..."
: "Translate Title"}
</button>
)}
</div>
<Controller
control={control}
name="title"
render={({ field }) => (
<Input
size="md"
type="text"
value={field.value}
onChange={field.onChange}
placeholder="Enter Title"
/>
)}
/>
{/* English translated title appears below when available */}
{translatedTitle && (
<div className="mt-3">
<Label className="text-sm font-semibold">
English Version
</Label>
<Input
size="md"
type="text"
value={translatedTitle}
onChange={(e) => setTranslatedTitle(e.target.value)}
placeholder="Translated Title"
/>
</div>
)}
{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">
<div className="flex justify-between items-center">
<Label>
{t("description", { defaultValue: "Description" })}
</Label>
{roleId === "14" && (
<button
type="button"
onClick={async () => {
try {
loading();
setIsLoadingTranslate(true);
const res = await translateText({
text: getValues("description"),
sourceLang: "ID",
targetLang: "EN",
});
if (!res.error) {
const resultText =
res?.data?.data?.translations?.[0]?.text || "";
// Overwrite data.description but still show both
setTranslatedContent(resultText);
}
} catch (err) {
close();
console.error("Translate gagal:", err);
} finally {
close();
setIsLoadingTranslate(false);
}
}}
className="px-3 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600"
>
{isLoadingTranslate
? "Translating..."
: "Translate Description"}
</button>
)}
</div>
{/* Editor Bahasa Indonesia */}
<div className="mt-3">
<Label className="text-sm font-semibold">
Indonesian Version
</Label>
<Controller
control={control}
name="description"
render={({ field }) => (
<CustomEditor
onChange={field.onChange}
initialData={field.value}
/>
)}
/>
</div>
{/* English translated version muncul setelah translate */}
{translatedContent && (
<div className="mt-5">
<Label className="text-sm font-semibold">
English Version
</Label>
<CustomEditor
onChange={(val: any) => setTranslatedContent(val)}
initialData={translatedContent}
/>
</div>
)}
{errors.description?.message && (
<p className="text-red-400 text-sm">
{errors.description.message}
</p>
)}
</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-audio-max", {
defaultValue: "Upload File Audio 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}
>
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"
>
<div className="flex flex-wrap gap-3 items-center ">
<div className="flex-grow">
<div>
<svg
xmlns="http://www.w3.org/2000/svg"
width="80"
height="80"
viewBox="0 0 20 20"
>
<path
fill="currentColor"
d="M14.702 2.226A1 1 0 0 1 16 3.18v6.027a5.5 5.5 0 0 0-1-.184V6.18L8 8.368V15.5a2.5 2.5 0 1 1-1-2V5.368a1 1 0 0 1 .702-.955zM8 7.32l7-2.187V3.18L8 5.368zM5.5 14a1.5 1.5 0 1 0 0 3a1.5 1.5 0 0 0 0-3m13.5.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0m-2.265-.436l-2.994-1.65a.5.5 0 0 0-.741.438v3.3a.5.5 0 0 0 .741.438l2.994-1.65a.5.5 0 0 0 0-.876"
/>
</svg>{" "}
<p className="font-medium">
{file.fileName}
</p>
</div>
<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
checked={
fileUnitSelections[index]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => {
handleFileUnitChange(
index,
item.key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
index,
item.key,
Boolean(value)
);
}}
/>
<Label
htmlFor={`${item.key}-${index}`}
className="text-sm font-medium cursor-pointer"
>
{item.label}
</Label>
</div>
))}
</div>
{/* Detail Wilayah */}
{fileUnitSelections[index]?.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[index]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => {
handleFileUnitChange(
index,
item.key as keyof typeof unitSelection,
value as boolean
);
setupPlacement(
index,
item.key,
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[
index
]?.has(
Number(
polda.id
)
) || false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
index,
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(
polda.id
);
}}
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
>
<Icon
icon={
expandedPolda[
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[
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[
index
]?.has(
Number(
sub.id
)
)
);
return (
<Button
size="sm"
variant="outline"
className="text-xs h-6 px-2"
onClick={() =>
handleSelectAllSubItems(
index,
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[
index
]?.has(
Number(
sub.id
)
) ||
false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
index,
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>
)}
{/* {files.length > 0 && (
<div className="mt-4">
<Label className="text-lg font-semibold">
{" "}
{t("file-media", { defaultValue: "File Media" })}
</Label>
<div className="grid gap-4">
{files.map((file: 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>
<Label className="flex items-center space-x-2">
<input
type="checkbox"
checked={selectedOptions[
file.id
]?.includes("all")}
onChange={() =>
handleCheckboxChangeImage(
file.id,
"all"
)
}
className="form-checkbox"
/>
<span>{t("all", { defaultValue: "All" })}</span>
</Label>
</div>
<div>
<Label className="flex items-center space-x-2">
<input
type="checkbox"
checked={selectedOptions[
file.id
]?.includes("nasional")}
onChange={() =>
handleCheckboxChangeImage(
file.id,
"nasional"
)
}
className="form-checkbox"
/>
<span>Nasional</span>
</Label>
</div>
<div>
<Label className="flex items-center space-x-2">
<input
type="checkbox"
checked={selectedOptions[
file.id
]?.includes("wilayah")}
onChange={() =>
handleCheckboxChangeImage(
file.id,
"wilayah"
)
}
className="form-checkbox"
/>
<span>Wilayah</span>
</Label>
</div>
<div>
<Label className="flex items-center space-x-2">
<input
type="checkbox"
checked={selectedOptions[
file.id
]?.includes("internasional")}
onChange={() =>
handleCheckboxChangeImage(
file.id,
"internasional"
)
}
className="form-checkbox"
/>
<span>Internasional</span>
</Label>
</div>
</div>
</div>
))}
</div>
</div>
)} */}
</Fragment>
</div>
</div>
</div>
</Card>
<div className="w-full lg:w-4/12">
<Card className="pb-3">
<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="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 space-y-2">
<Label>
{t("publish-target", { defaultValue: "Publish Target" })}
</Label>
{options.map((option: Option) => (
<div key={option.id} className="flex gap-2 items-center">
<Checkbox
id={option.id}
checked={
option.id === "all"
? publishedFor.length ===
options.filter((opt) => opt.id !== "all").length
: publishedFor.includes(option.id)
}
onCheckedChange={() => handleCheckboxChange(option.id)}
/>
<Label htmlFor={option.id}>{option.name}</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">
<Link href={"/contributor/content/audio"}>
<Button type="submit" color="primary" variant="outline">
{t("cancel", { defaultValue: "Cancel" })}
</Button>
</Link>
</div>
</div>
</div>
</div>
) : (
""
)}
</form>
);
}