"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, deleteFile, getArticleDetail, getTagsBySubCategoryId, listEnableCategory, updateArticle, 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 { 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 dynamic from "next/dynamic"; import { htmlToString } from "@/utils/globals"; import { listArticleCategories, uploadArticleFiles } from "@/service/content"; 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" }), files: z .array(z.any()) .optional() .refine( (files) => !files || files.every( (file: File) => [ "audio/mpeg", "audio/wav", "audio/x-wav", "audio/mp4", "audio/aac", ].includes(file.type) && file.size <= 20 * 1024 * 1024, ), { message: "Hanya file audio (.mp3, .wav, .m4a, .aac) dengan ukuran maksimal 20MB yang diperbolehkan.", }, ), publishedFor: z .array(z.string()) .min(1, { message: "Minimal 1 target publish harus dipilih." }), }); type Category = { id: string; title: 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 { preview: 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; let progressInfo: any = []; let counterUpdateProgress = 0; const [progressList, setProgressList] = useState([]); let uploadPersen = 0; const [isStartUpload, setIsStartUpload] = useState(false); const [counterProgress, setCounterProgress] = useState(0); const [selectedFiles, setSelectedFiles] = useState([]); const taskId = Cookies.get("taskId"); const scheduleId = Cookies.get("scheduleId"); const scheduleType = Cookies.get("scheduleType"); const [categories, setCategories] = useState([]); const [selectedCategory, setSelectedCategory] = useState(); const [tags, setTags] = useState([]); const [detail, setDetail] = useState(null); const [refresh, setRefresh] = useState(false); const [selectedPublishers, setSelectedPublishers] = useState([]); const [detailThumb, setDetailThumb] = useState([]); const [thumbsSwiper, setThumbsSwiper] = useState(null); const [selectedTarget, setSelectedTarget] = useState(""); const [publishedFor, setPublishedFor] = useState([]); const inputRef = useRef(null); const [selectedOptions, setSelectedOptions] = useState<{ [fileId: number]: string[]; }>({}); const options: Option[] = [ { id: "all", name: "SEMUA" }, { id: "4", name: "UMUM" }, { id: "5", name: "JOURNALIS" }, ]; const [unitSelection, setUnitSelection] = useState({ allUnit: false, mabes: false, polda: false, polres: false, }); const [files, setFiles] = useState([]); const [prevFiles, setPrefFiles] = useState([]); let fileTypeId = "4"; const { getRootProps, getInputProps } = useDropzone({ onDrop: (acceptedFiles) => { setFiles(acceptedFiles.map((file) => Object.assign(file))); }, accept: { "audio/*": [], }, }); const { control, handleSubmit, setValue, formState: { errors }, } = useForm({ resolver: zodResolver(audioSchema), defaultValues: { publishedFor: [] }, }); const handleImageChange = (event: ChangeEvent) => { 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 initState() { getCategories(); } initState(); }, []); const getCategories = async () => { const categoryRes = await listArticleCategories(1, 100); setCategories(categoryRes?.data.data || []); }; const handleAddTag = (e: React.KeyboardEvent) => { if (e.key === "Enter" && e.currentTarget.value.trim()) { e.preventDefault(); const newTag = e.currentTarget.value.trim(); if (!tags.includes(newTag)) { setTags((prevTags) => [...prevTags, newTag]); 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)), ); }; useEffect(() => { async function initState() { if (id) { const response = await getArticleDetail(Number(id)); const details = response?.data?.data; setDetail(details); setSelectedCategory(String(details.categories[0].id)); setValue("title", details.title); setValue("description", details.htmlDescription); setValue("creatorName", details.createdByName ?? ""); setTags(details.tags?.split(",").map((t: string) => t.trim()) || []); setPublishedFor(details.publishedFor?.split(",") || []); if (details?.files) { setPrefFiles(details.files); // setFiles(details.files); const initialOptions: { [key: number]: string[] } = {}; details.files.forEach((file: any) => { if (file.placements) { initialOptions[file.id] = mapPlacementsToOptions(file.placements); } }); setSelectedOptions(initialOptions); } 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 mapPlacementsToOptions = (placements: string): string[] => { const mapping: Record = { all: "all", mabes: "nasional", polda: "wilayah", polres: "internasional", }; // Jika placements hanya "all", langsung aktifkan semua checkbox if (placements.trim() === "all") { return ["all", "nasional", "wilayah", "internasional"]; } const options = placements .split(",") .map((p) => mapping[p.trim()]) .filter(Boolean); const allSelected = ["nasional", "wilayah", "internasional"].every((opt) => options.includes(opt), ); return allSelected ? ["all", ...options] : options; }; 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(", "); // 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 payload = { aiArticleId: detail.aiArticleId, categoryIds: selectedCategory, description: htmlToString(data.description), htmlDescription: data.description, tags: tags.join(","), title: data.title, typeId: detail.typeId, slug: detail?.slug ?? data.title.toLowerCase().replace(/\s+/g, "-"), }; // const payload = { // aiArticleId: detail?.aiArticleId ?? "", // categoryIds: selectedCategory ?? "", // createdById: detail?.createdById ?? "", // description: htmlToString(data.description), // htmlDescription: data.description, // isDraft: false, // isPublish: true, // slug: detail?.slug ?? data.title.toLowerCase().replace(/\s+/g, "-"), // statusId: detail?.statusId ?? 1, // tags: tags.join(","), // title: data.title, // typeId: detail?.typeId ?? 4, // }; const res = await updateArticle(Number(id), payload); if (res?.error) { error(res.message || "Gagal memperbarui data"); return; } if (files.length > 0) { const formData = new FormData(); // Add all files to FormData files.forEach((file, index) => { formData.append("files", file); }); const uploadResponse = await uploadArticleFiles(id, formData); } // 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); 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("/admin/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; 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("/admin/content/audio"); } } const handleRemoveAllFiles = () => { setFiles([]); }; const renderFilePreview = (file: FileWithPreview) => { if (file?.type?.startsWith("image")) { return ( {file.name} ); } else { return ; } }; const handleRemoveFile = (file: FileWithPreview) => { const uploadedFiles = files; const filtered = uploadedFiles.filter((i) => i.name !== file.name); setFiles([...filtered]); }; const fileList = files.map((file: any) => (
{" "}
{file.fileName || file.name}
{Math.round(file.size / 100) / 10 > 1000 ? ( <>{(Math.round(file.size / 100) / 10000).toFixed(1)} ) : ( <>{(Math.round(file.size / 100) / 10).toFixed(1)} )} {" kb"}
)); const handleCheckboxChangeImage = (fileId: number, value: string) => { setSelectedOptions((prev: any) => { const currentSelections = prev[fileId] || []; if (value === "all") { // If "all" is clicked, toggle all options if (currentSelections.includes("all")) { return { ...prev, [fileId]: [] }; // Deselect all } return { ...prev, [fileId]: ["all", "nasional", "wilayah", "internasional"], }; // Select all } else { // If any other checkbox is clicked, toggle that checkbox const updatedSelections = currentSelections.includes(value) ? currentSelections.filter((option: any) => option !== value) : [...currentSelections, value]; // If all individual options are selected, include "all" automatically const isAllSelected = ["nasional", "wilayah", "internasional"].every( (opt) => updatedSelections.includes(opt), ); return { ...prev, [fileId]: isAllSelected ? ["all", ...updatedSelections] : updatedSelections.filter((opt: any) => opt !== "all"), }; } }); }; 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 (
{detail !== undefined ? (

Form Audio

{/* Input Title */}
( )} /> {errors.title?.message && (

{errors.title.message}

)}
( )} /> {errors.description?.message && (

{errors.description.message}

)}
{/* */}

{/* Drop files here or click to upload. */} Drag File

Upload File Audio Max
{files.length ? (
{fileList}
{/*
*/} {/* */}
) : null} {prevFiles?.length > 0 && prevFiles.map((file: any) => (
{" "}
{file.fileName || file.name}
{Math.round(file.size / 100) / 10 > 1000 ? ( <> {( Math.round(file.size / 100) / 10000 ).toFixed(1)} ) : ( <> {(Math.round(file.size / 100) / 10).toFixed( 1, )} )} {" kb"}
))} {/* {files.length > 0 && (
{files.map((file: any) => (
{file.fileName}
))}
)} */}
} /> {errors.creatorName?.message && (

{errors.creatorName.message}

)}
{tags.map((tag, index) => ( handleEditTag(index, e.target.value)} className="bg-black text-white border-none focus:outline-none w-auto" /> ))}
(
{options.map((option) => { const isAllChecked = field.value.length === options.filter((opt: any) => opt.id !== "all") .length; const isChecked = option.id === "all" ? isAllChecked : field.value.includes(option.id); const handleChange = () => { let updated: string[] = []; if (option.id === "all") { updated = isAllChecked ? [] : options .filter((opt: any) => opt.id !== "all") .map((opt: any) => opt.id); } else { updated = isChecked ? field.value.filter( (val) => val !== option.id, ) : [...field.value, option.id]; if (isAllChecked && option.id !== "all") { updated = updated.filter( (val) => val !== "all", ); } } field.onChange(updated); setPublishedFor(updated); }; return (
); })} {errors.publishedFor && (

{errors.publishedFor.message}

)}
)} />
{/*

{t("suggestion-box", { defaultValue: "Suggestion Box" })} (0)

{t("information", { defaultValue: "Information" })}:

{detail?.status}

*/}
) : ( "" )}
); }