"use client"; import React, { ChangeEvent, useEffect, useRef, Fragment, 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 { Upload } from "tus-js-client"; import Swal from "sweetalert2"; import withReactContent from "sweetalert2-react-content"; import { redirect, 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, listEnableCategoryNew, uploadThumbnail, } from "@/service/content/content"; import { uploadThumbnailBlog } from "@/service/blog/blog"; import { Textarea } from "@/components/ui/textarea"; import { generateDataArticle, generateDataRewrite, getDetailArticle, getGenerateKeywords, getGenerateTitle, translateText, } from "@/service/content/ai"; import { getCookiesDecrypt } from "@/lib/utils"; import { useDropzone } from "react-dropzone"; import { Icon } from "@iconify/react"; import { CloudUpload, X } from "lucide-react"; import Image from "next/image"; import { close, error, loading } from "@/config/swal"; import { Item } from "@radix-ui/react-dropdown-menu"; import { data } from "jquery"; import { options } from "@fullcalendar/core/preact.js"; import dynamic from "next/dynamic"; import { getCsrfToken } from "@/service/auth"; import { Link } from "@/i18n/routing"; import { request } from "http"; import { useLocale, useTranslations } from "next-intl"; import { toast } from "sonner"; import { htmlToString } from "@/utils/globals"; interface FileWithPreview extends File { preview: string; } type Category = { id: string; name: string; }; type Option = { id: string; label: string; }; const CustomEditor = dynamic( () => { return import("@/components/editor/custom-editor"); }, { ssr: false } ); export default function FormImage() { const MySwal = withReactContent(Swal); const router = useRouter(); const editor = useRef(null); const levelNumber = getCookiesDecrypt("ulne"); const roleId = getCookiesDecrypt("urie"); type ImageSchema = z.infer; const params = useParams(); const locale = params?.locale; const t = useTranslations("Form"); const [selectedFiles, setSelectedFiles] = useState([]); const taskId = Cookies.get("taskId"); const scheduleId = Cookies.get("scheduleId"); const scheduleType = Cookies.get("scheduleType"); const [selectedFileType, setSelectedFileType] = useState("original"); const [categories, setCategories] = useState([]); const [selectedCategory, setSelectedCategory] = useState(); const [tags, setTags] = useState([]); const [thumbnail, setThumbnail] = useState(null); const [preview, setPreview] = useState(null); const [selectedLanguage, setSelectedLanguage] = useState(""); const [selectedSEO, setSelectedSEO] = useState(""); const [title, setTitle] = useState(""); const [selectedAdvConfig, setSelectedAdvConfig] = useState(""); const [editingArticleId, setEditingArticleId] = useState(null); const [isLoading, setIsLoading] = useState(false); const [isLoadingData, setIsLoadingData] = useState(false); const [localContent, setLocalContent] = useState(""); const [articleIds, setArticleIds] = useState([]); const [isGeneratedArticle, setIsGeneratedArticle] = useState(false); const [articleBody, setArticleBody] = useState(""); const [selectedArticleId, setSelectedArticleId] = useState( null ); const [selectedMainKeyword, setSelectedMainKeyword] = useState(""); const [selectedWritingStyle, setSelectedWritingStyle] = useState("professional"); const [editorContent, setEditorContent] = useState(""); const [rewriteEditorContent, setRewriteEditorContent] = useState(""); const [selectedSize, setSelectedSize] = useState(""); const [detailData, setDetailData] = useState(null); const [articleImages, setArticleImages] = useState([]); const [isSwitchOn, setIsSwitchOn] = useState(false); const inputRef = useRef(null); const [isContentRewriteClicked, setIsContentRewriteClicked] = useState(false); const [selectedTarget, setSelectedTarget] = useState(""); const [unitSelection, setUnitSelection] = useState({ allUnit: false, mabes: false, polda: false, polres: false, }); let fileTypeId = "1"; let progressInfo: any = []; let counterUpdateProgress = 0; const [progressList, setProgressList] = useState([]); let uploadPersen = 0; const [isStartUpload, setIsStartUpload] = useState(false); const [counterProgress, setCounterProgress] = useState(0); const [showRewriteEditor, setShowRewriteEditor] = useState(false); const [files, setFiles] = useState([]); const [filesTemp, setFilesTemp] = useState([]); const [publishedFor, setPublishedFor] = useState([]); const [isLoadingTranslate, setIsLoadingTranslate] = useState(false); const [translatedContent, setTranslatedContent] = React.useState(""); const [selectedLang, setSelectedLang] = React.useState<"id" | "en">("id"); // 🔹 state untuk translate judul const [translatedTitle, setTranslatedTitle] = useState(""); const [isLoadingTranslateTitle, setIsLoadingTranslateTitle] = useState(false); const options: Option[] = [ { id: "all", label: "SEMUA" }, { id: "5", label: "UMUM" }, { id: "6", label: "JOURNALIS" }, { id: "7", label: "POLRI" }, { id: "8", label: "KSP" }, ]; type FileWithPreview = File & { preview: string; }; const MAX_FILE_SIZE = 100 * 1024 * 1024; const { getRootProps, getInputProps } = useDropzone({ accept: { "image/jpeg": [], "image/png": [], "image/jpg": [], }, multiple: true, onDrop: (acceptedFiles) => { const validFiles = acceptedFiles .filter( (file) => ["image/jpeg", "image/png", "image/jpg"].includes(file.type) && file.size <= MAX_FILE_SIZE ) .map((file) => Object.assign(file, { preview: URL.createObjectURL(file), }) ); if (validFiles.length === 0) { toast.error( "File tidak valid. Hanya .jpg, .jpeg, .png maksimal 100MB yang diperbolehkan." ); return; } setFiles((prev) => { const next = [...prev, ...validFiles]; setValue("files", next, { shouldDirty: true }); return next; }); }, }); useEffect(() => { return () => { files.forEach((file) => URL.revokeObjectURL(file.preview)); }; }, [files]); const imageSchema = z.object({ title: z.string().min(1, { message: t("titleRequired") }), description: z.string().optional(), descriptionOri: z.string().optional(), htmlDescription: z.string().optional(), rewriteDescription: z.string().optional(), creatorName: z.string().min(1, { message: t("creatorRequired") }), files: z .array(z.any()) .min(1, { message: "Minimal 1 file harus diunggah." }) .refine( (files) => files.every( (file: File) => ["image/jpeg", "image/png", "image/jpg"].includes(file.type) && file.size <= 100 * 1024 * 1024 ), { message: "Hanya file .jpg, .jpeg, .png, maksimal 100MB yang diperbolehkan.", } ), categoryId: z.string().min(1, { message: "Kategori wajib dipilih." }), tags: z .array(z.string()) .min(1, { message: "Minimal 1 tag harus ditambahkan." }), publishedFor: z .array(z.string()) .min(1, { message: "Minimal 1 target publish harus dipilih." }), }); const { control, handleSubmit, getValues, setValue, watch, formState: { errors }, } = useForm({ resolver: zodResolver(imageSchema), defaultValues: { title: "", description: "", descriptionOri: "", rewriteDescription: "", creatorName: "", files: [], categoryId: "", tags: [], publishedFor: [], }, }); const doGenerateMainKeyword = async () => { // console.log(selectedMainKeyword); if (selectedMainKeyword?.length > 1) { try { setIsLoading(true); const titleData = { keyword: selectedMainKeyword, style: selectedWritingStyle, website: "0", connectToWeb: true, lang: selectedLanguage, pointOfView: "None", clientId: "", }; // console.log("Sending request for title with data:", titleData); const titleRes = await getGenerateTitle(titleData); setTitle(titleRes?.data?.data || ""); // console.log("Generated title:", titleRes?.data?.data); const keywordsData = { keyword: selectedMainKeyword, style: selectedWritingStyle, website: "0", connectToWeb: true, lang: selectedLanguage, pointOfView: "None", clientId: "", }; // console.log("Sending request for keywords with data:", keywordsData); const keywordsRes = await getGenerateKeywords(keywordsData); setSelectedSEO(keywordsRes?.data?.data || []); // console.log("Generated keywords:", keywordsRes?.data?.data); } catch (error) { console.error("Error during generation process:", error); } finally { setIsLoading(false); } } else { Swal.fire({ icon: "warning", title: "WARNING", text: "Please provide a valid main keyword.", }); console.error("Please provide a valid main keyword."); } }; const doGenerateTitle = async () => { if (selectedMainKeyword?.length > 1) { try { setIsLoading(true); const titleData = { keyword: selectedMainKeyword, style: selectedWritingStyle, website: "0", connectToWeb: true, lang: selectedLanguage, pointOfView: "None", clientId: "", }; // console.log("Sending request for title with data:", titleData); const titleRes = await getGenerateTitle(titleData); setTitle(titleRes?.data?.data || ""); // console.log("Generated title:", titleRes?.data?.data); } catch (error) { console.error("Error generating title:", error); } finally { setIsLoading(false); } } else { Swal.fire({ icon: "warning", title: "WARNING", text: "Please provide a valid title.", }); console.error("Please provide a valid main keyword."); } }; const doGenerateKeyword = async () => { if (selectedMainKeyword?.length > 1) { try { setIsLoading(true); const keywordsData = { keyword: selectedMainKeyword, style: selectedWritingStyle, website: "0", connectToWeb: true, lang: selectedLanguage, pointOfView: "None", clientId: "", }; // console.log("Sending request for keywords with data:", keywordsData); const keywordsRes = await getGenerateKeywords(keywordsData); setSelectedSEO(keywordsRes?.data?.data || []); // console.log("Generated keywords:", keywordsRes?.data?.data); } catch (error) { console.error("Error generating keywords:", error); } finally { setIsLoading(false); } } else { Swal.fire({ icon: "warning", title: "WARNING", text: "Please provide a valid keyword.", }); console.error("Please provide a valid main keyword."); } }; const handleGenerateArtikel = async () => { const request = { advConfig: selectedAdvConfig, style: selectedWritingStyle, website: "None", connectToWeb: true, lang: selectedLanguage, pointOfView: "None", title: title, imageSource: "Web", mainKeyword: selectedMainKeyword, additionalKeywords: selectedSEO, targetCountry: null, articleSize: selectedSize, projectId: 2, createdBy: roleId, clientId: "ngDLPPiorplznw2jTqVe3YFCz5xqKfUJ", }; const res = await generateDataArticle(request); close(); if (res?.error) { console.error(res.message); return false; } const newArticleId = res?.data?.data?.id; setIsGeneratedArticle(true); setArticleIds((prevIds: string[]) => { if (prevIds.length < 3) { return [...prevIds, newArticleId]; } else { const updatedIds = [...prevIds]; updatedIds[2] = newArticleId; return updatedIds; } }); Cookies.set("nulisAIArticleIdTemp", JSON.stringify(articleIds)); }; async function doTranslate() { const data = { text: "Hello World", sourceLang: "EN", targetLang: "ID", }; const result = await translateText(data); console.log("Hasil Translate:", result); } const handleArticleIdClick = async (id: string) => { setIsLoadingData(true); let retryCount = 0; const maxRetries = 20; try { const waitForStatusUpdate = async () => { while (retryCount < maxRetries) { const res = await getDetailArticle(id); const articleData = res?.data?.data; if (articleData?.status === 2) { return articleData; } retryCount++; await new Promise((resolve) => setTimeout(resolve, 5000)); } throw new Error("Timeout: Artikel belum selesai diproses."); }; const articleData = await waitForStatusUpdate(); const cleanArticleBody = articleData?.articleBody?.replace( /]*>/g, "" ); const articleImagesData = articleData?.imagesUrl?.split(","); setArticleBody(cleanArticleBody || ""); setValue("description", cleanArticleBody || ""); setDetailData(articleData); setSelectedArticleId(id); setArticleImages(articleImagesData || []); } catch (error) { console.error("Error fetching article details:", error); } finally { setIsLoadingData(false); } }; 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)) { const updatedTags = [...tags, newTag]; setTags(updatedTags); setValue("tags", updatedTags); if (inputRef.current) { inputRef.current.value = ""; } } } }; const handleRemoveTag = (index: number) => { const updatedTags = tags.filter((_, i) => i !== index); setTags(updatedTags); setValue("tags", updatedTags); }; useEffect(() => { async function initState() { getCategories(); } initState(); }, []); const getCategories = async () => { try { const category = await listEnableCategoryNew(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); const response = await getTagsBySubCategoryId(findCategory.id); setTags(response?.data?.data); } } } catch (error) { console.error("Failed to fetch categories:", error); } }; const handleCheckboxChange = (id: string): void => { if (id === "all") { if (publishedFor.includes("all")) { setPublishedFor([]); } else { setPublishedFor( options .filter((opt: any) => opt.id !== "all") .map((opt: any) => opt.id) ); } } else { const updatedPublishedFor = publishedFor.includes(id) ? publishedFor.filter((item) => item !== id) : [...publishedFor, id]; if (publishedFor.includes("all") && id !== "all") { setPublishedFor(updatedPublishedFor.filter((item) => item !== "all")); } else { setPublishedFor(updatedPublishedFor); } } }; useEffect(() => { if (articleBody) { setValue("description", articleBody); setValue("rewriteDescription", articleBody); } }, [articleBody, setValue]); const save = async (data: ImageSchema) => { loading(); if (files.length === 0) { MySwal.fire("Error", "Minimal 1 file harus diunggah.", "error"); return; } const finalTags = tags.join(", "); // const finalTitle = isSwitchOn ? title : data.title; let finalTitle = isSwitchOn ? title : data.title; // ✅ Jika sudah translate judul → kirim versi English if (translatedTitle && translatedTitle.trim() !== "") { finalTitle = translatedTitle; console.log("📤 Upload Title versi Inggris (translate)"); } // pilih description dasar // let finalDescription = isSwitchOn // ? data.description // : selectedFileType === "rewrite" // ? data.rewriteDescription // : data.descriptionOri; // 👉 tempelkan hasil translate ke field form agar ikut terkirim // if (translatedContent) { // data.descriptionOri = translatedContent; // versi Inggris yang dikirim // } else { // data.descriptionOri = getValues("descriptionOri"); // fallback IDN // } // ✅ Tentukan deskripsi final yang akan dikirim let finalDescription = ""; if (translatedContent && translatedContent.trim() !== "") { // jika user sudah translate → kirim versi Inggris finalDescription = translatedContent; console.log("📤 Upload versi Inggris (translate)"); } else if (data.rewriteDescription && selectedFileType === "rewrite") { finalDescription = data.rewriteDescription; console.log("📤 Upload versi rewrite"); } else { // fallback: gunakan versi Indonesia original finalDescription = data.descriptionOri ?? ""; console.log("📤 Upload versi Indonesia (original)"); } if (!finalDescription?.trim()) { MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error"); return; } let requestData: any = { title: finalTitle, description: htmlToString(finalDescription), // versi plain text htmlDescription: finalDescription, // versi HTML fileTypeId, categoryId: selectedCategory, subCategoryId: selectedCategory, uploadedBy: "2b7c8d83-d298-4b19-9f74-b07924506b58", statusId: "1", publishedFor: publishedFor.join(","), creatorName: data.creatorName, tags: tags.join(", "), isYoutube: false, isInternationalMedia: false, }; // let requestData: { // title: string; // description: string; // htmlDescription: string; // fileTypeId: string; // categoryId: any; // subCategoryId: any; // uploadedBy: string; // statusId: string; // publishedFor: string; // creatorName: string; // tags: string; // isYoutube: boolean; // isInternationalMedia: boolean; // attachFromScheduleId?: number; // } = { // ...data, // title: finalTitle, // description: htmlToString(finalDescription), // plain text // htmlDescription: finalDescription, // versi HTML // fileTypeId, // categoryId: selectedCategory, // subCategoryId: selectedCategory, // uploadedBy: "2b7c8d83-d298-4b19-9f74-b07924506b58", // statusId: "1", // publishedFor: publishedFor.join(","), // creatorName: data.creatorName, // tags: finalTags, // isYoutube: false, // isInternationalMedia: false, // }; let id = Cookies.get("idCreate"); if (scheduleId !== undefined) { requestData.attachFromScheduleId = Number(scheduleId); } if (id == undefined) { const response = await createMedia(requestData); // console.log("Form Data Submitted:", requestData); Cookies.set("idCreate", response?.data?.data, { expires: 1 }); id = response?.data?.data; 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 = files.map((item) => ({ percentage: 0, fileName: item.name, })); progressInfo = progressInfoArr; setIsStartUpload(true); setProgressList(progressInfoArr); // close(); files.map(async (item: any, index: number) => { await uploadResumableFile( index, String(id), item, fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0" ); }); Cookies.remove("idCreate"); }; const onSubmit = (data: ImageSchema) => { 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); } }); }; 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, }; 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", }, 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 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); close(); // hideProgress(); Cookies.remove("idCreate"); successSubmit("/in/contributor/content/image"); } } const handleImageChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { setThumbnail(file); // console.log("Selected Thumbnail:", file); } if (file) { setPreview(URL.createObjectURL(file)); } }; 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) => (
{renderFilePreview(file)}
{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 handleRemoveAllFiles = () => { setFiles([]); }; useEffect(() => { if (!getValues("title") && title) { setValue("title", title); } }, [title, getValues, setValue]); const handleRewriteClick = async () => { setIsContentRewriteClicked(true); const request = { style: selectedWritingStyle, lang: "id", contextType: "text", urlContext: null, context: getValues("descriptionOri"), createdBy: roleId, sentiment: "Humorous", clientId: "7QTW8cMojyayt6qnhqTOeJaBI70W4EaQ", }; const res = await generateDataRewrite(request); close(); if (res?.error) { console.error(res.message); return false; } const newArticleId = res?.data?.data?.id; setIsGeneratedArticle(true); setArticleIds((prevIds: string[]) => { if (prevIds.length < 3) { return [...prevIds, newArticleId]; } else { const updatedIds = [...prevIds]; updatedIds[2] = newArticleId; return updatedIds; } }); Cookies.set("nulisAIArticleIdTemp", JSON.stringify(articleIds)); setShowRewriteEditor(true); }; return (

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

{roleId === "14" && ( )}
{/* Title Indonesia */}
( )} />
{/* Title English (hanya muncul setelah translate) */} {translatedTitle && (
setTranslatedTitle(e.target.value)} placeholder="English version" />
)} {errors.title?.message && (

{errors.title.message}

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

{errors.title.message}

)}
*/} {/*
*/} (
{errors.categoryId && (

{errors.categoryId.message}

)}
)} />
setIsSwitchOn(checked) } />
{isSwitchOn && (
setSelectedMainKeyword(e.target.value)} placeholder="Enter Main Keyword" /> {/* )} /> */}
setTitle(e.target.value)} placeholder="Generated Title" />

{t("Keywords to include in the text", { defaultValue: "Keywords To Include In The Text", })}

{t("title-key", { defaultValue: "Title Key" })}