kontenhumas-fe/components/form/content/audio-visual/video-form.tsx

1925 lines
69 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,
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 { useDropzone } from "react-dropzone";
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 { Switch } from "@/components/ui/switch";
import Cookies from "js-cookie";
import Image from "next/image";
import dynamic from "next/dynamic";
import { CloudUpload } from "lucide-react";
import { toast } from "sonner";
import { htmlToString } from "@/utils/globals";
import { getCookiesDecrypt } from "@/lib/utils";
import { getCsrfToken } from "@/service/auth";
import {
createArticle,
uploadArticleFiles,
uploadArticleThumbnail,
listArticleCategories,
listEnableCategory,
getTagsBySubCategoryId,
CreateArticleData,
} from "@/service/content/content";
import {
generateDataArticle,
generateDataRewrite,
getGenerateKeywords,
getGenerateTitle,
getDetailArticle,
} from "@/service/content/ai";
const CustomEditor = dynamic(
() => import("@/components/editor/custom-editor"),
{ ssr: false }
);
interface FileWithPreview extends File {
preview: string;
}
export default function FormVideo() {
const MySwal = withReactContent(Swal);
const router = useRouter();
const params = useParams();
const roleId = getCookiesDecrypt("urie");
const userId = Cookies.get("userId");
const [files, setFiles] = useState<FileWithPreview[]>([]);
const [fileError, setFileError] = useState<string | null>(null);
const [publishedFor, setPublishedFor] = useState<string[]>([]);
const [title, setTitle] = useState<string>("");
const [categories, setCategories] = useState<any[]>([]);
const [selectedCategory, setSelectedCategory] = useState<any>();
const [preview, setPreview] = useState<string | null>(null);
const [thumbnail, setThumbnail] = useState<File | null>(null);
const [isSwitchOn, setIsSwitchOn] = useState<boolean>(false);
const [selectedFileType, setSelectedFileType] = useState("original");
const [editorContent, setEditorContent] = useState("");
const [rewriteEditorContent, setRewriteEditorContent] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [articleBody, setArticleBody] = useState("");
const [showRewriteEditor, setShowRewriteEditor] = useState(false);
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
const [articleIds, setArticleIds] = useState<string[]>([]);
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
null
);
const [isLoadingData, setIsLoadingData] = useState(false);
const [tags, setTags] = useState<string[]>([]);
const inputRef = useRef<HTMLInputElement>(null);
// --- SCHEMA & FORM SETUP ---
const videoSchema = z.object({
title: z.string().min(1, { message: "Judul wajib diisi." }),
description: z.string().optional(),
descriptionOri: z.string().optional(),
rewriteDescription: z.string().optional(),
creatorName: z.string().min(1, { message: "Nama pembuat wajib diisi." }),
files: z
.array(z.any())
.min(1, { message: "Minimal 1 file harus diunggah." }),
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,
formState: { errors },
} = useForm<z.infer<typeof videoSchema>>({
resolver: zodResolver(videoSchema),
defaultValues: {
title: "",
description: "",
descriptionOri: "",
rewriteDescription: "",
creatorName: "",
files: [],
categoryId: "",
tags: [],
publishedFor: [],
},
});
// --- FIX ① setValue hanya jalan saat publishedFor berubah setelah form siap ---
useEffect(() => {
if (publishedFor && publishedFor.length > 0) {
setValue("publishedFor", publishedFor);
}
}, [publishedFor, setValue]);
// --- FIX ② pindahkan setValue("files") ke useEffect ---
useEffect(() => {
if (files.length > 0) {
setValue("files", files, { shouldValidate: true });
}
}, [files, setValue]);
// --- FIX ③ setValue("title") hanya jalan setelah form mount ---
useEffect(() => {
const formReady = getValues("title") !== undefined;
if (formReady && !getValues("title") && title) {
setValue("title", title);
}
}, [title, getValues, setValue]);
// --- DROPZONE HANDLER aman ---
const { getRootProps, getInputProps } = useDropzone({
accept: { "video/mp4": [".mp4"], "video/quicktime": [".mov"] },
maxSize: 500 * 1024 * 1024,
multiple: true,
onDrop: (acceptedFiles, fileRejections) => {
setFileError(null);
if (fileRejections.length > 0) {
const messages = fileRejections
.map((rej) => rej.errors.map((e) => e.message).join(", "))
.join(", ");
setFileError(messages || "File tidak valid");
return;
}
if (acceptedFiles.length === 0) {
setFileError("Wajib upload minimal 1 file video");
return;
}
const filesWithPreview = acceptedFiles.map((file) =>
Object.assign(file, { preview: URL.createObjectURL(file) })
);
setFiles((prev) => [...prev, ...filesWithPreview]); // ⛔ Tidak lagi ada setValue di sini
},
});
// --- FETCH CATEGORY AMAN ---
useEffect(() => {
const getCategories = async () => {
try {
const category = await listArticleCategories(1, 100);
if (!category?.error) {
const mapped =
category?.data?.data?.map((item: any) => ({
id: item.id,
name: item.title,
})) || [];
setCategories(mapped);
} else {
const fallback = await listEnableCategory("2");
setCategories(fallback?.data?.data?.content || []);
}
} catch (err) {
console.error("Error fetching category:", err);
}
};
getCategories();
}, []);
// --- THUMBNAIL PREVIEW CLEANUP ---
useEffect(() => {
return () => {
if (preview) URL.revokeObjectURL(preview);
};
}, [preview]);
// --- FORM SUBMIT ---
const onSubmit = (data: z.infer<typeof videoSchema>) => {
MySwal.fire({
title: "Simpan Data",
text: "Apakah Anda yakin ingin menyimpan data ini?",
icon: "warning",
showCancelButton: true,
confirmButtonText: "Simpan",
cancelButtonText: "Batal",
}).then((result) => {
if (result.isConfirmed) save(data);
});
};
// --- SIMULASI SAVE ---
async function save(data: z.infer<typeof videoSchema>) {
console.log("✅ Data tersimpan:", data);
toast.success("Data berhasil disimpan!");
}
// --- RENDER ---
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Card className="p-6">
<Label>Title</Label>
<Controller
control={control}
name="title"
render={({ field }) => (
<Input
type="text"
value={field.value}
onChange={field.onChange}
placeholder="Masukkan judul"
/>
)}
/>
{errors.title && (
<p className="text-red-500 text-sm">{errors.title.message}</p>
)}
<div className="mt-4">
<Label>Upload File</Label>
<div
{...getRootProps()}
className="border-dashed border p-6 text-center cursor-pointer"
>
<input {...getInputProps()} />
<CloudUpload className="mx-auto mb-2 text-gray-400" />
<p>Drag & Drop file video di sini</p>
</div>
{fileError && (
<p className="text-red-500 text-sm mt-2">{fileError}</p>
)}
</div>
{files.length > 0 && (
<div className="mt-4 space-y-2">
{files.map((f, i) => (
<div key={i} className="flex justify-between border p-2 rounded">
<span>{f.name}</span>
<Button
variant="outline"
size="sm"
onClick={() =>
setFiles((prev) => prev.filter((x) => x.name !== f.name))
}
>
Hapus
</Button>
</div>
))}
</div>
)}
<div className="mt-6">
<Button type="submit" color="primary">
Submit
</Button>
</div>
</Card>
</form>
);
}
// "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 { Switch } from "@/components/ui/switch";
// import Cookies from "js-cookie";
// import { Textarea } from "@/components/ui/textarea";
// import { getCookiesDecrypt } from "@/lib/utils";
// import { useDropzone } from "react-dropzone";
// import { Icon } from "@iconify/react";
// import { CloudUpload } from "lucide-react";
// import Image from "next/image";
// import { error, loading } from "@/config/swal";
// import { Item } from "@radix-ui/react-dropdown-menu";
// import dynamic from "next/dynamic";
// import { getCsrfToken } from "@/service/auth";
// import {
// createMedia,
// createArticle,
// getTagsBySubCategoryId,
// listEnableCategory,
// listArticleCategories,
// uploadThumbnail,
// uploadArticleFiles,
// uploadArticleThumbnail,
// CreateArticleData,
// } from "@/service/content/content";
// import { request } from "http";
// import { toast } from "sonner";
// import { htmlToString } from "@/utils/globals";
// import {
// generateDataArticle,
// generateDataRewrite,
// getDetailArticle,
// getGenerateKeywords,
// getGenerateTitle,
// } from "@/service/content/ai";
// import Link from "next/link";
// const CustomEditor = dynamic(
// () => {
// return import("@/components/editor/custom-editor");
// },
// { ssr: false }
// );
// interface FileWithPreview extends File {
// preview: string;
// }
// type Category = {
// id: string;
// name: string;
// };
// type Option = {
// id: string;
// label: string;
// };
// export default function FormVideo() {
// const MySwal = withReactContent(Swal);
// const router = useRouter();
// const editor = useRef(null);
// type VideoSchema = z.infer<typeof videoSchema>;
// const params = useParams();
// const locale = params?.locale;
// const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
// const taskId = Cookies.get("taskId");
// const scheduleId = Cookies.get("scheduleId");
// const scheduleType = Cookies.get("scheduleType");
// const roleId = getCookiesDecrypt("urie");
// const [selectedFileType, setSelectedFileType] = useState("original");
// const [categories, setCategories] = useState<Category[]>([]);
// const [selectedCategory, setSelectedCategory] = useState<any>();
// const [tags, setTags] = useState<any[]>([]);
// const [thumbnail, setThumbnail] = useState<File | null>(null);
// const [preview, setPreview] = useState<string | null>(null);
// const [selectedLanguage, setSelectedLanguage] = useState("");
// const [selectedWritingStyle, setSelectedWritingStyle] =
// useState("professional");
// const [editorContent, setEditorContent] = useState("");
// const [rewriteEditorContent, setRewriteEditorContent] = useState("");
// const [selectedSEO, setSelectedSEO] = useState<string>("");
// const [title, setTitle] = useState<string>("");
// const [selectedAdvConfig, setSelectedAdvConfig] = useState<string>("");
// const [editingArticleId, setEditingArticleId] = useState<string | null>(null);
// const [isLoading, setIsLoading] = useState<boolean>(false);
// const [isLoadingData, setIsLoadingData] = useState<boolean>(false);
// const [articleIds, setArticleIds] = useState<string[]>([]);
// const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
// const [articleBody, setArticleBody] = useState<string>("");
// const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
// null
// );
// const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
// const [publishedForError, setPublishedForError] = useState<string | null>(
// null
// );
// const userId = Cookies.get("userId");
// const [selectedSize, setSelectedSize] = useState("");
// const [detailData, setDetailData] = useState<any>(null);
// const [articleImages, setArticleImages] = useState<string[]>([]);
// const [isSwitchOn, setIsSwitchOn] = useState<boolean>(false);
// const inputRef = useRef<HTMLInputElement>(null);
// const [fileError, setFileError] = useState<string | null>(null);
// const [showRewriteEditor, setShowRewriteEditor] = useState(false);
// const [isContentRewriteClicked, setIsContentRewriteClicked] = useState(false);
// const [selectedTarget, setSelectedTarget] = useState("");
// const [unitSelection, setUnitSelection] = useState({
// allUnit: false,
// mabes: false,
// polda: false,
// polres: false,
// });
// let fileTypeId = "2";
// 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 [publishedFor, setPublishedFor] = useState<string[]>([]);
// const [files, setFiles] = useState<FileWithPreview[]>([]);
// const options: Option[] = [
// { id: "all", label: "SEMUA" },
// { id: "4", label: "UMUM" },
// { id: "5", label: "JOURNALIS" },
// ];
// const MAX_FILE_SIZE = 100 * 1024 * 1024;
// const ACCEPTED_FILE_TYPES = ["video/mp4", "video/quicktime"];
// const { getRootProps, getInputProps } = useDropzone({
// accept: {
// "video/mp4": [".mp4"],
// "video/quicktime": [".mov"],
// },
// maxSize: 500 * 1024 * 1024,
// multiple: true,
// onDrop: (acceptedFiles, fileRejections) => {
// setFileError(null);
// if (fileRejections.length > 0) {
// const messages = fileRejections
// .map((rej) => rej.errors.map((e) => e.message).join(", "))
// .join(", ");
// setFileError(messages || "File tidak valid");
// return;
// }
// if (acceptedFiles.length === 0) {
// setFileError("Wajib upload minimal 1 file video");
// return;
// }
// const filesWithPreview = acceptedFiles.map((file) =>
// Object.assign(file, { preview: URL.createObjectURL(file) })
// );
// setFiles((prev) => {
// const updatedFiles = [...prev, ...filesWithPreview];
// setValue("files", updatedFiles, { shouldValidate: true });
// return updatedFiles;
// });
// },
// });
// const videoSchema = z.object({
// title: z.string().min(1, { message: "titleRequired" }),
// description: z.string().optional(),
// descriptionOri: z.string().optional(),
// rewriteDescription: z.string().optional(),
// creatorName: z.string().min(1, { message: "creatorRequired" }),
// files: z
// .array(z.any())
// .min(1, { message: "Minimal 1 file harus diunggah." })
// .refine(
// (files) =>
// files.every(
// (file: File) =>
// ["video/mp4", "video/mov", "video/avi"].includes(file.type) &&
// file.size <= 100 * 1024 * 1024
// ),
// {
// message:
// "Hanya file .mp4, .mov, .avi, 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,
// formState: { errors },
// } = useForm<z.infer<typeof videoSchema>>({
// resolver: zodResolver(videoSchema),
// defaultValues: {
// title: "",
// description: "",
// descriptionOri: "",
// rewriteDescription: "",
// creatorName: "",
// files: [],
// categoryId: "",
// tags: [],
// publishedFor: [],
// },
// });
// useEffect(() => {
// setValue("publishedFor", publishedFor);
// }, [publishedFor, setValue]);
// 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));
// };
// 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(
// /<img[^>]*>/g,
// ""
// );
// const articleImagesData = articleData?.imagesUrl?.split(",");
// setArticleBody(cleanArticleBody || "");
// setDetailData(articleData);
// setSelectedArticleId(id);
// setArticleImages(articleImagesData || []);
// } catch (error) {
// console.error("Error fetching article details:", error);
// } finally {
// setIsLoadingData(false);
// }
// };
// 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]);
// if (inputRef.current) {
// inputRef.current.value = "";
// }
// }
// }
// };
// const handleRemoveTag = (index: number) => {
// setTags((prevTags) => prevTags.filter((_, i) => i !== index));
// };
// const handleRemoveImage = (index: number) => {
// setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index));
// };
// useEffect(() => {
// async function initState() {
// getCategories();
// // setVideoActive(fileTypeId == '2');
// // getRoles();
// }
// initState();
// }, []);
// const getCategories = async () => {
// try {
// // Use new Article Categories API
// const category = await listArticleCategories(1, 100);
// console.log("Article categories response:", category);
// if (category?.error) {
// console.error("Failed to fetch article categories:", category.message);
// // Fallback to old API if new one fails
// const fallbackCategory = await listEnableCategory(fileTypeId);
// const resCategory: Category[] =
// fallbackCategory?.data.data.content || [];
// setCategories(resCategory);
// return;
// }
// // Handle new API response structure
// const resCategory: Category[] =
// category?.data?.data?.map((item: any) => ({
// id: item.id,
// name: item.title, // map title to name for backward compatibility
// title: item.title,
// description: item.description,
// ...item,
// })) || [];
// setCategories(resCategory);
// console.log("Article categories loaded:", resCategory);
// if (scheduleId && scheduleType === "3") {
// const findCategory = resCategory.find((o) =>
// o.name.toLowerCase().includes("pers rilis")
// );
// if (findCategory) {
// setSelectedCategory(findCategory.id);
// const response = await getTagsBySubCategoryId(findCategory.id);
// setTags(response?.data?.data);
// }
// }
// } catch (error) {
// console.error("Failed to fetch categories:", error);
// // Fallback to old API if error occurs
// try {
// const fallbackCategory = await listEnableCategory(fileTypeId);
// const resCategory: Category[] =
// fallbackCategory?.data.data.content || [];
// setCategories(resCategory);
// } catch (fallbackError) {
// console.error("Fallback category fetch also failed:", fallbackError);
// }
// }
// };
// 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: VideoSchema) => {
// if (publishedFor.length === 0) {
// setPublishedForError("Minimal 1 target publish harus dipilih.");
// return;
// } else {
// setPublishedForError(null);
// }
// loading();
// const finalTags = data.tags.join(", ");
// const finalTitle = isSwitchOn ? title : data.title;
// // const finalDescription = articleBody || data.description;
// const finalDescription = isSwitchOn
// ? data.description
// : selectedFileType === "rewrite"
// ? data.rewriteDescription
// : data.descriptionOri;
// if (!finalDescription?.trim()) {
// MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
// return;
// }
// function formatDateForBackend(date: Date) {
// const pad = (n: number) => (n < 10 ? "0" + n : n);
// return (
// date.getFullYear() +
// "-" +
// pad(date.getMonth() + 1) +
// "-" +
// pad(date.getDate()) +
// " " +
// pad(date.getHours()) +
// ":" +
// pad(date.getMinutes()) +
// ":" +
// pad(date.getSeconds())
// );
// }
// 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),
// htmlDescription: finalDescription,
// 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) {
// // New Articles API request data structure
// const articleData: CreateArticleData = {
// aiArticleId: 0, // default 0
// categoryIds: selectedCategory.toString(),
// createdAt: formatDateForBackend(new Date()), // ✅ format sesuai backend
// createdById: Number(userId), // isi dengan userId valid
// description: htmlToString(finalDescription),
// htmlDescription: finalDescription,
// isDraft: true,
// isPublish: false,
// oldId: 0,
// slug: finalTitle
// .toLowerCase()
// .replace(/\s+/g, "-")
// .replace(/[^a-z0-9-]/g, ""),
// tags: finalTags,
// title: finalTitle,
// typeId: 2,
// };
// // Use new Articles API
// const response = await createArticle(articleData);
// console.log("Article Data Submitted:", articleData);
// console.log("Article API Response:", response);
// if (response?.error) {
// MySwal.fire(
// "Error",
// response.message || "Failed to create article",
// "error"
// );
// return false;
// }
// // Get the article ID from the new API response
// const articleId = response?.data?.data?.id;
// Cookies.set("idCreate", articleId, { expires: 1 });
// id = articleId;
// // Upload files using new article-files API
// const formData = new FormData();
// // Add all files to FormData
// files.forEach((file, index) => {
// formData.append("files", file);
// });
// console.log("Uploading files to article:", articleId);
// console.log("Files to upload:", files.length);
// try {
// const uploadResponse = await uploadArticleFiles(articleId, formData);
// if (uploadResponse?.error) {
// MySwal.fire(
// "Error",
// uploadResponse.message || "Failed to upload files",
// "error"
// );
// return false;
// }
// console.log("Files uploaded successfully:", uploadResponse);
// // Upload thumbnail using first file as thumbnail
// if (files.length > 0) {
// const thumbnailFormData = new FormData();
// thumbnailFormData.append("files", files[0]); // Use first file as thumbnail
// console.log("Uploading thumbnail for article:", articleId);
// try {
// const thumbnailResponse = await uploadArticleThumbnail(
// articleId,
// thumbnailFormData
// );
// if (thumbnailResponse?.error) {
// console.warn(
// "Thumbnail upload failed:",
// thumbnailResponse.message
// );
// // Don't fail the whole process if thumbnail upload fails
// } else {
// console.log(
// "Thumbnail uploaded successfully:",
// thumbnailResponse
// );
// }
// } catch (thumbnailError) {
// console.warn("Thumbnail upload error:", thumbnailError);
// // Don't fail the whole process if thumbnail upload fails
// }
// }
// } catch (uploadError) {
// console.error("Upload error:", uploadError);
// MySwal.fire(
// "Error",
// "Failed to upload files. Please try again.",
// "error"
// );
// return false;
// }
// // Show success message
// MySwal.fire({
// title: "Sukses",
// text: "Article dan files berhasil disimpan.",
// icon: "success",
// confirmButtonColor: "#3085d6",
// confirmButtonText: "OK",
// }).then(() => {
// router.push("/admin/content/video");
// });
// Cookies.remove("idCreate");
// return;
// }
// Cookies.remove("idCreate");
// };
// useEffect(() => {
// return () => {
// if (preview) {
// URL.revokeObjectURL(preview);
// }
// };
// }, [preview]);
// const onSubmit = (data: VideoSchema) => {
// 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;
// console.log("CSRF TOKEN : ", csrfToken);
// 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", // 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 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/video");
// }
// }
// const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
// const file = e.target.files?.[0];
// if (file) {
// setThumbnail(file); // Simpan file asli tanpa dimodifikasi
// setPreview(URL.createObjectURL(file)); // Simpan preview string terpisah
// console.log("Selected Thumbnail:", file);
// }
// };
// const renderFilePreview = (file: FileWithPreview) => {
// if (file.type.startsWith("image")) {
// return (
// <Image
// width={48}
// height={48}
// alt={file.name}
// src={URL.createObjectURL(file)}
// className=" rounded border p-0.5"
// />
// );
// } else {
// return <Icon icon="tabler:file-description" />;
// }
// };
// const handleRemoveFile = (file: FileWithPreview) => {
// const uploadedFiles = files;
// const filtered = uploadedFiles.filter((i) => i.name !== file.name);
// setFiles([...filtered]);
// };
// const fileList = files.map((file) => (
// <div
// key={file.name}
// className=" flex justify-between border px-3.5 py-3 my-6 rounded-md"
// >
// <div className="flex gap-3 items-center">
// {/* <div className="file-preview">{renderFilePreview(file)}</div> */}
// <svg
// xmlns="http://www.w3.org/2000/svg"
// width="48"
// height="48"
// viewBox="0 0 24 24"
// >
// <g fill="none" fillRule="evenodd">
// <path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z" />
// <path
// fill="currentColor"
// d="M20 3a2 2 0 0 1 1.995 1.85L22 5v14a2 2 0 0 1-1.85 1.995L20 21H4a2 2 0 0 1-1.995-1.85L2 19V5a2 2 0 0 1 1.85-1.995L4 3zm0 2H4v14h16zm-9.66 2.638l.518.23l.338.16l.387.19l.43.218l.47.25l.507.28l.266.152l.518.305l.474.292l.43.273l.38.253l.48.33l.364.263l.095.07a1.234 1.234 0 0 1 0 1.98l-.323.235l-.44.308l-.356.239l-.405.263l-.453.283l-.499.3l-.534.309l-.509.282l-.471.25l-.43.22l-.386.188l-.622.288l-.23.1a1.234 1.234 0 0 1-1.714-.99l-.058-.565l-.032-.374l-.042-.664l-.023-.508l-.015-.555l-.004-.294l-.002-.305q0-.31.006-.6l.015-.555l.023-.507l.027-.457l.03-.401l.075-.744a1.235 1.235 0 0 1 1.715-.992m.611 2.501l-.436-.218l-.029.487l-.022.551l-.013.61l-.002.325l.002.325l.013.609l.01.283l.026.52l.015.235l.434-.218l.487-.256l.535-.294l.284-.162l.551-.326l.494-.306l.436-.28l.196-.13l-.407-.27l-.466-.294a30 30 0 0 0-.803-.48l-.283-.161l-.534-.294z"
// />
// </g>
// </svg>
// <div>
// <div className=" text-sm text-card-foreground">{file.name}</div>
// <div className=" text-xs font-light text-muted-foreground">
// {Math.round(file.size / 100) / 10 > 1000 ? (
// <>{(Math.round(file.size / 100) / 10000).toFixed(1)}</>
// ) : (
// <>{(Math.round(file.size / 100) / 10).toFixed(1)}</>
// )}
// {" kb"}
// </div>
// </div>
// </div>
// <Button
// size="icon"
// color="destructive"
// variant="outline"
// className=" border-none rounded-full"
// onClick={() => handleRemoveFile(file)}
// >
// <Icon icon="tabler:x" className=" h-5 w-5" />
// </Button>
// </div>
// ));
// const handleRemoveAllFiles = () => {
// setFiles([]);
// };
// useEffect(() => {
// // Jika input title kosong, isi dengan hasil generate title
// 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: editorContent, // Ambil isi editor original
// 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 (
// <form onSubmit={handleSubmit(onSubmit)}>
// <div className="flex flex-col lg:flex-row gap-10 border">
// <Card className="w-full lg:w-8/12 m-2">
// <div className="px-6 py-6">
// <p className="text-lg font-semibold mb-3">Form Video</p>
// <div className="gap-5 mb-5">
// <div className="space-y-2 py-3">
// <Label>Title</Label>
// <Controller
// control={control}
// name="title"
// render={({ field }) => (
// <Input
// type="text"
// value={field.value}
// onChange={field.onChange}
// placeholder="Enter Title"
// />
// )}
// />
// {errors.title?.message && (
// <p className="text-red-400 text-sm">{errors.title.message}</p>
// )}
// </div>
// <div className="flex items-center">
// <div className="py-3 w-full space-y-2">
// <Label>Category</Label>
// <Controller
// control={control}
// name="categoryId"
// render={({ field }) => (
// <div className="w-full">
// <Select
// value={field.value}
// onValueChange={(value) => {
// field.onChange(value);
// setSelectedCategory(value);
// }}
// >
// <SelectTrigger>
// <SelectValue placeholder="Pilih" />
// </SelectTrigger>
// <SelectContent>
// {categories.map((category) => (
// <SelectItem
// key={category.id}
// value={category.id.toString()}
// >
// {category.name}
// </SelectItem>
// ))}
// </SelectContent>
// </Select>
// {errors.categoryId && (
// <p className="text-sm text-red-500 mt-1">
// {errors.categoryId.message}
// </p>
// )}
// </div>
// )}
// />
// </div>
// </div>
// <div className="flex flex-row items-center gap-3 py-2">
// <Label>Ai Assistance</Label>
// <div className="flex items-center gap-3">
// <Switch
// defaultChecked={isSwitchOn}
// color="primary"
// id="c2"
// onCheckedChange={(checked: boolean) =>
// setIsSwitchOn(checked)
// }
// />
// </div>
// </div>
// {isSwitchOn && (
// <div>
// <div className="flex flex-row gap-3">
// <div className="space-y-2 py-3 w-4/12">
// <Label>Language</Label>
// <Select onValueChange={setSelectedLanguage}>
// <SelectTrigger>
// <SelectValue placeholder="Pilih" />
// </SelectTrigger>
// <SelectContent>
// <SelectItem value="id">Indonesia</SelectItem>
// <SelectItem value="en">English</SelectItem>
// </SelectContent>
// </Select>
// </div>
// <div className="space-y-2 py-3 w-4/12">
// <Label>Writing Style</Label>
// <Select onValueChange={setSelectedWritingStyle}>
// <SelectTrigger>
// <SelectValue placeholder="Pilih" />
// </SelectTrigger>
// <SelectContent>
// <SelectItem value="friendly">Friendly</SelectItem>
// <SelectItem value="profesional">
// Profesional
// </SelectItem>
// <SelectItem value="informational">
// Informational
// </SelectItem>
// <SelectItem value="neutral">Neutral</SelectItem>
// <SelectItem value="witty">Witty</SelectItem>
// </SelectContent>
// </Select>
// </div>
// <div className="space-y-2 py-3 w-4/12">
// <Label>Article Size</Label>
// <Select onValueChange={setSelectedSize}>
// <SelectTrigger>
// <SelectValue placeholder="Pilih" />
// </SelectTrigger>
// <SelectContent>
// <SelectItem value="news">
// News (300 - 900 words)
// </SelectItem>
// <SelectItem value="info">
// Info (900 - 2000 words)
// </SelectItem>
// <SelectItem value="detail">
// Detail (2000 - 5000 words)
// </SelectItem>
// </SelectContent>
// </Select>
// </div>
// </div>
// <div className="mt-5">
// <div className="flex flex-row items-center gap-3 mb-3">
// <Label>Main Keyword</Label>
// <Button
// variant="outline"
// color="primary"
// onClick={doGenerateMainKeyword}
// disabled={isLoading}
// >
// {isLoading ? "Processing..." : "Proses"}
// </Button>
// </div>
// <div>
// <Input
// type="text"
// value={selectedMainKeyword}
// onChange={(e) => setSelectedMainKeyword(e.target.value)}
// placeholder="Enter Main Keyword"
// />
// {/* )}
// /> */}
// </div>
// </div>
// <div className="mt-5">
// <div className="flex flex-row items-center gap-3 mb-3">
// <Label>Title</Label>
// <Button
// variant="outline"
// color="primary"
// onClick={doGenerateTitle}
// disabled={isLoading}
// >
// {isLoading ? "Generating..." : "Generate"}
// </Button>
// </div>
// <div>
// <Input
// type="text"
// value={title}
// onChange={(e) => setTitle(e.target.value)}
// placeholder="Generated Title"
// />
// </div>
// </div>
// <div className="mt-5">
// <div className="flex flex-row items-center gap-3 mb-3">
// <Label>Seo</Label>
// <Button
// variant={"outline"}
// color="primary"
// onClick={doGenerateKeyword}
// disabled={isLoading}
// >
// {isLoading ? "Generating..." : "Generate"}
// </Button>
// </div>
// <p className="font-semibold">
// Keywords To Include In The Text",
// </p>
// <p className="text-sm">Title Key</p>
// <div className="mt-3">
// <Textarea
// value={selectedSEO}
// onChange={(e) => setSelectedSEO(e.target.value)}
// placeholder="Enter Title"
// />
// </div>
// </div>
// <div className="mt-5">
// <Label>Special Instructions (Optional)</Label>
// <div className="mt-3">
// <Controller
// control={control}
// name="title"
// render={({ field }) => (
// <Textarea
// value={field.value}
// onChange={field.onChange}
// placeholder="Enter Title"
// />
// )}
// />
// </div>
// </div>
// <div>
// <div className="my-5">
// <Button
// // variant={"outline"}
// color="primary"
// onClick={handleGenerateArtikel}
// size="sm"
// type="button"
// >
// Generate Article
// </Button>
// </div>
// {isGeneratedArticle && (
// <div className="mt-3 pb-0 flex flex-row">
// {articleIds.map((id: string, index: number) => (
// <p
// key={index}
// className={`mr-3 px-3 py-2 rounded-md ${
// selectedArticleId === id
// ? "bg-green-500 text-white"
// : "border-2 border-green-500 text-green-500"
// }`}
// onClick={() => handleArticleIdClick(id)}
// >
// {"Narasi " + (index + 1)}
// </p>
// ))}
// </div>
// )}
// <div className="pt-3">
// <div className="flex flex-row justify-between items-center">
// {selectedArticleId && (
// <Button
// className="mb-2"
// size="sm"
// variant={"outline"}
// color="primary"
// onClick={() => {
// const url = `/${locale}/contributor/content/image/update-seo/${selectedArticleId}`;
// window.open(url, "_blank", "noopener,noreferrer");
// }}
// >
// Update
// </Button>
// )}
// </div>
// </div>
// </div>
// <div className="py-3 space-y-2">
// <Label>Description</Label>
// <Controller
// control={control}
// name="description"
// render={({ field: { onChange, value } }) =>
// isLoadingData ? (
// <div className="flex justify-center items-center h-40">
// <p className="text-gray-500">
// Loading Proses Data...
// </p>
// </div>
// ) : (
// <CustomEditor
// onChange={(value: any) => {
// onChange(value);
// setEditorContent(value);
// }}
// initialData={articleBody || value}
// />
// )
// }
// />
// {errors.description?.message && (
// <p className="text-red-400 text-sm">
// {errors.description.message}
// </p>
// )}
// </div>
// </div>
// )}
// {!isSwitchOn && (
// <>
// <RadioGroup
// onValueChange={(value) => setSelectedFileType(value)}
// value={selectedFileType}
// className=" grid-cols-1"
// >
// <div className="">
// <RadioGroupItem value="original" id="original-file" />
// <Label htmlFor="original-file">
// Select Original Description
// </Label>
// </div>
// <div className="py-3 space-y-2">
// <Label>Description</Label>
// <Controller
// control={control}
// name="descriptionOri"
// render={({ field: { onChange, value } }) => (
// <CustomEditor
// onChange={(value: any) => {
// onChange(value);
// setEditorContent(value);
// }}
// initialData={value}
// />
// )}
// />
// {errors.description?.message && (
// <p className="text-red-400 text-sm">
// {errors.description.message}
// </p>
// )}
// </div>
// <p className="text-sm font-semibold">Content Rewrite</p>
// <div className="my-2">
// <Button
// size="sm"
// type="button"
// onClick={handleRewriteClick}
// className="bg-blue-500 text-white py-2 px-4 rounded"
// >
// Content Rewrite
// </Button>
// </div>
// {showRewriteEditor && (
// <div>
// {isGeneratedArticle && (
// <div className="mt-3 pb-0 flex flex-row ">
// {articleIds.map((id: string, index: number) => (
// <Button
// type="button"
// key={index}
// className={`mr-3 px-3 py-2 rounded-md ${
// selectedArticleId === id
// ? "bg-green-500 text-white"
// : "border-2 border-green-500 bg-white text-green-500 hover:bg-green-500 hover:text-white hover:border-green-500"
// }`}
// onClick={() => handleArticleIdClick(id)}
// >
// {"Narasi " + (index + 1)}
// </Button>
// ))}
// </div>
// )}
// <div className="flex items-center space-x-2 mt-3">
// <RadioGroupItem value="rewrite" id="rewrite-file" />
// <Label htmlFor="rewrite-file">
// Select Description Rewrite
// </Label>
// </div>
// <div className="py-3 space-y-2">
// <Label>File Rewrite</Label>
// <Controller
// control={control}
// name="rewriteDescription"
// render={({ field: { onChange, value } }) =>
// isLoadingData ? (
// <div className="flex justify-center items-center h-40">
// <p className="text-gray-500">
// Loading Proses Data...
// </p>
// </div>
// ) : (
// <CustomEditor
// onChange={(value: any) => {
// onChange(value);
// setRewriteEditorContent(value);
// }}
// initialData={articleBody || value}
// />
// )
// }
// />
// </div>
// </div>
// )}
// </RadioGroup>
// </>
// )}
// <Controller
// control={control}
// name="files"
// render={({ field }) => (
// <div className="py-3 space-y-2">
// <Label>Select File</Label>
// <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">
// Drag File
// </h4>
// <div className="text-xs text-muted-foreground">
// Upload File Video Max
// </div>
// </div>
// </div>
// {files.length > 0 && (
// <div className="mt-2 space-y-1">
// {files.map((file, idx) => (
// <div
// key={idx}
// className="flex items-center justify-between rounded border border-default-200 dark:border-default-300 px-2 py-1 text-sm"
// >
// <span className="truncate">{file.name}</span>
// <span className="text-muted-foreground">
// {(file.size / (1024 * 1024)).toFixed(2)} MB
// </span>
// </div>
// ))}
// <div className="flex justify-between gap-2 mt-1">
// <Button
// color="destructive"
// onClick={handleRemoveAllFiles}
// >
// Remove All
// </Button>
// </div>
// </div>
// )}
// {errors.files && (
// <p className="text-red-500 text-sm">
// {errors.files.message}
// </p>
// )}
// </div>
// )}
// />
// </div>
// </div>
// </Card>
// <div className="w-full lg:w-4/12 m-2">
// <Card className="h-fit">
// <div className="px-3 py-3">
// <div className="space-y-2">
// <Label>Creator</Label>
// <Controller
// control={control}
// name="creatorName"
// render={({ field }) => (
// <Input
// 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">
// <Label htmlFor="fileInput">Gambar Utama</Label>
// <Input id="fileInput" type="file" onChange={handleImageChange} />
// </div>
// {preview && (
// <div className="mt-3 px-3">
// <img
// src={preview}
// alt="Thumbnail Preview"
// className="w-full h-auto rounded"
// />
// </div>
// )}
// <div className="px-3 py-3 space-y-2">
// <Label htmlFor="tags">Tags</Label>
// <Controller
// control={control}
// name="tags"
// render={({ field }) => (
// <>
// <Input
// type="text"
// id="tags"
// placeholder="Add a tag and press Enter"
// onKeyDown={(e) => {
// if (e.key === "Enter" && e.currentTarget.value.trim()) {
// e.preventDefault();
// field.onChange([
// ...field.value,
// e.currentTarget.value.trim(),
// ]);
// e.currentTarget.value = "";
// }
// }}
// />
// <div className="mt-3">
// {field.value.map((tag: string, index: number) => (
// <span
// key={index}
// className="px-1 py-1 rounded-lg bg-black text-white mr-2 text-sm font-sans"
// >
// {tag}{" "}
// <button
// type="button"
// onClick={() => {
// const updatedTags = field.value.filter(
// (_, i) => i !== index
// );
// field.onChange(updatedTags);
// }}
// className="remove-tag-button"
// >
// ×
// </button>
// </span>
// ))}
// </div>
// </>
// )}
// />
// {/* Tampilkan error */}
// {errors.tags?.message && (
// <p className="text-red-400 text-sm">{errors.tags.message}</p>
// )}
// </div>
// <Controller
// control={control}
// name="publishedFor"
// render={({ field }) => (
// <div className="px-3 py-3">
// <div className="flex flex-col gap-3 space-y-2">
// <Label>Publish Target</Label>
// {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 (
// <div
// key={option.id}
// className="flex gap-2 items-center"
// >
// <Checkbox
// id={option.id}
// checked={isChecked}
// onCheckedChange={handleChange}
// className="border"
// />
// <Label htmlFor={option.id}>{option.label}</Label>
// </div>
// );
// })}
// {errors.publishedFor && (
// <p className="text-red-500 text-sm">
// {errors.publishedFor.message}
// </p>
// )}
// </div>
// </div>
// )}
// />
// </Card>
// <div className="flex flex-row justify-end gap-3">
// <div className="mt-4">
// <Button type="submit" color="primary">
// Submit
// </Button>
// </div>
// <div className="mt-4">
// <Button type="button" color="primary" variant="outline">
// Cancel
// </Button>
// </div>
// </div>
// </div>
// </div>
// </form>
// );
// }