feat:update create content rewrite

This commit is contained in:
Anang Yusman 2025-06-18 14:19:30 +08:00
parent d2658b294b
commit 81c3c1e781
5 changed files with 1316 additions and 674 deletions

View File

@ -40,6 +40,7 @@ import { uploadThumbnailBlog } from "@/service/blog/blog";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { import {
generateDataArticle, generateDataArticle,
generateDataRewrite,
getDetailArticle, getDetailArticle,
getGenerateKeywords, getGenerateKeywords,
getGenerateTitle, getGenerateTitle,
@ -55,6 +56,7 @@ import dynamic from "next/dynamic";
import { getCsrfToken } from "@/service/auth"; import { getCsrfToken } from "@/service/auth";
import { Link } from "@/i18n/routing"; import { Link } from "@/i18n/routing";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { useParams } from "next/navigation";
interface FileWithPreview extends File { interface FileWithPreview extends File {
preview: string; preview: string;
@ -82,6 +84,10 @@ export default function FormAudio() {
const router = useRouter(); const router = useRouter();
const editor = useRef(null); const editor = useRef(null);
type AudioSchema = z.infer<typeof audioSchema>; type AudioSchema = z.infer<typeof audioSchema>;
const params = useParams();
const locale = params?.locale;
const [selectedFileType, setSelectedFileType] = useState("original");
const [selectedFiles, setSelectedFiles] = useState<File[]>([]); const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId"); const taskId = Cookies.get("taskId");
@ -97,6 +103,11 @@ export default function FormAudio() {
const [preview, setPreview] = useState<string | null>(null); const [preview, setPreview] = useState<string | null>(null);
const [selectedLanguage, setSelectedLanguage] = useState(""); const [selectedLanguage, setSelectedLanguage] = useState("");
const [selectedWritingStyle, setSelectedWritingStyle] =
useState("professional");
const [editorContent, setEditorContent] = useState(""); // Untuk original editor
const [rewriteEditorContent, setRewriteEditorContent] = useState("");
const [selectedSEO, setSelectedSEO] = useState<string>(""); const [selectedSEO, setSelectedSEO] = useState<string>("");
const [title, setTitle] = useState<string>(""); const [title, setTitle] = useState<string>("");
const [selectedAdvConfig, setSelectedAdvConfig] = useState<string>(""); const [selectedAdvConfig, setSelectedAdvConfig] = useState<string>("");
@ -111,7 +122,6 @@ export default function FormAudio() {
null null
); );
const [selectedMainKeyword, setSelectedMainKeyword] = useState(""); const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
const [selectedWritingStyle, setSelectedWritingStyle] = useState("");
const [selectedSize, setSelectedSize] = useState(""); const [selectedSize, setSelectedSize] = useState("");
const [detailData, setDetailData] = useState<any>(null); const [detailData, setDetailData] = useState<any>(null);
const [articleImages, setArticleImages] = useState<string[]>([]); const [articleImages, setArticleImages] = useState<string[]>([]);
@ -133,6 +143,8 @@ export default function FormAudio() {
const [isStartUpload, setIsStartUpload] = useState(false); const [isStartUpload, setIsStartUpload] = useState(false);
const [counterProgress, setCounterProgress] = useState(0); const [counterProgress, setCounterProgress] = useState(0);
const [isContentRewriteClicked, setIsContentRewriteClicked] = useState(false);
const [showRewriteEditor, setShowRewriteEditor] = useState(false);
const [files, setFiles] = useState<FileWithPreview[]>([]); const [files, setFiles] = useState<FileWithPreview[]>([]);
const [publishedFor, setPublishedFor] = useState<string[]>([]); const [publishedFor, setPublishedFor] = useState<string[]>([]);
@ -155,15 +167,11 @@ export default function FormAudio() {
const audioSchema = z.object({ const audioSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
description: z description: z.string().optional(),
.string() descriptionOri: z.string().optional(), // Original editor
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }) rewriteDescription: z.string().optional(), // Rewrite editor
.or(
z.literal(articleBody || "").refine((val) => val.length > 0, {
message: "Deskripsi diperlukan.",
})
),
creatorName: z.string().min(1, { message: "Creator diperlukan" }), creatorName: z.string().min(1, { message: "Creator diperlukan" }),
// tags: z.string().min(1, { message: "Judul diperlukan" }), // tags: z.string().min(1, { message: "Judul diperlukan" }),
}); });
@ -175,6 +183,11 @@ export default function FormAudio() {
formState: { errors }, formState: { errors },
} = useForm<AudioSchema>({ } = useForm<AudioSchema>({
resolver: zodResolver(audioSchema), resolver: zodResolver(audioSchema),
defaultValues: {
description: "",
descriptionOri: "",
rewriteDescription: "",
},
}); });
const doGenerateMainKeyword = async () => { const doGenerateMainKeyword = async () => {
@ -437,12 +450,24 @@ export default function FormAudio() {
} }
}; };
useEffect(() => {
if (articleBody) {
// Set ke dua field jika rewrite juga aktif
setValue("description", articleBody);
setValue("rewriteDescription", articleBody);
}
}, [articleBody, setValue]);
const save = async (data: AudioSchema) => { const save = async (data: AudioSchema) => {
loading(); loading();
const finalTags = tags.join(", "); const finalTags = tags.join(", ");
const finalTitle = isSwitchOn ? title : data.title; const finalTitle = isSwitchOn ? title : data.title;
const finalDescription = articleBody || data.description; const finalDescription = isSwitchOn
if (!finalDescription.trim()) { ? data.description
: selectedFileType === "rewrite"
? data.rewriteDescription
: data.descriptionOri;
if (!finalDescription?.trim()) {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error"); MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
return; return;
} }
@ -719,6 +744,45 @@ export default function FormAudio() {
} }
}, [title, getValues, setValue]); }, [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 ( return (
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<div className="flex flex-col lg:flex-row gap-10"> <div className="flex flex-col lg:flex-row gap-10">
@ -960,27 +1024,23 @@ export default function FormAudio() {
<div className="pt-3"> <div className="pt-3">
<div className="flex flex-row justify-between items-center"> <div className="flex flex-row justify-between items-center">
{selectedArticleId && ( {selectedArticleId && (
<Link
href={`/contributor/content/audio/update-seo/${selectedArticleId}`}
target="_blank"
rel="noopener noreferrer"
>
<Button <Button
className="mb-2" className="mb-2"
size="sm" size="sm"
variant={"outline"} variant={"outline"}
color="primary" color="primary"
onClick={() => {
const url = `/${locale}/contributor/content/image/update-seo/${selectedArticleId}`;
window.open(url, "_blank", "noopener,noreferrer");
}}
> >
{t("update")} {t("update")}
</Button> </Button>
</Link>
)} )}
</div> </div>
</div> </div>
</div> </div>
</div> <div className="py-3 space-y-2">
)}
<div className="space-y-2">
<Label>{t("description")}</Label> <Label>{t("description")}</Label>
<Controller <Controller
control={control} control={control}
@ -988,11 +1048,16 @@ export default function FormAudio() {
render={({ field: { onChange, value } }) => render={({ field: { onChange, value } }) =>
isLoadingData ? ( isLoadingData ? (
<div className="flex justify-center items-center h-40"> <div className="flex justify-center items-center h-40">
<p className="text-gray-500">Loading Proses Data...</p> <p className="text-gray-500">
Loading Proses Data...
</p>
</div> </div>
) : ( ) : (
<CustomEditor <CustomEditor
onChange={onChange} onChange={(value: any) => {
onChange(value);
setEditorContent(value);
}}
initialData={articleBody || value} initialData={articleBody || value}
/> />
) )
@ -1004,6 +1069,110 @@ export default function FormAudio() {
</p> </p>
)} )}
</div> </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>{t("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>{t("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>
</>
)}
<div className="py-3 space-y-2"> <div className="py-3 space-y-2">
<Label>{t("select-file")}</Label> <Label>{t("select-file")}</Label>
{/* <Input {/* <Input

View File

@ -40,6 +40,7 @@ import { uploadThumbnailBlog } from "@/service/blog/blog";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { import {
generateDataArticle, generateDataArticle,
generateDataRewrite,
getDetailArticle, getDetailArticle,
getGenerateKeywords, getGenerateKeywords,
getGenerateTitle, getGenerateTitle,
@ -94,6 +95,7 @@ export default function FormImage() {
const scheduleId = Cookies.get("scheduleId"); const scheduleId = Cookies.get("scheduleId");
const scheduleType = Cookies.get("scheduleType"); const scheduleType = Cookies.get("scheduleType");
const roleId = getCookiesDecrypt("urie"); const roleId = getCookiesDecrypt("urie");
const [selectedFileType, setSelectedFileType] = useState("original");
const [categories, setCategories] = useState<Category[]>([]); const [categories, setCategories] = useState<Category[]>([]);
const [selectedCategory, setSelectedCategory] = useState<any>(); const [selectedCategory, setSelectedCategory] = useState<any>();
@ -116,7 +118,10 @@ export default function FormImage() {
null null
); );
const [selectedMainKeyword, setSelectedMainKeyword] = useState(""); const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
const [selectedWritingStyle, setSelectedWritingStyle] = useState(""); const [selectedWritingStyle, setSelectedWritingStyle] =
useState("professional");
const [editorContent, setEditorContent] = useState(""); // Untuk original editor
const [rewriteEditorContent, setRewriteEditorContent] = useState("");
const [selectedSize, setSelectedSize] = useState(""); const [selectedSize, setSelectedSize] = useState("");
const [detailData, setDetailData] = useState<any>(null); const [detailData, setDetailData] = useState<any>(null);
const [articleImages, setArticleImages] = useState<string[]>([]); const [articleImages, setArticleImages] = useState<string[]>([]);
@ -125,6 +130,8 @@ export default function FormImage() {
const [content, setContent] = useState(""); const [content, setContent] = useState("");
const [isContentRewriteClicked, setIsContentRewriteClicked] = useState(false);
const [selectedTarget, setSelectedTarget] = useState(""); const [selectedTarget, setSelectedTarget] = useState("");
const [unitSelection, setUnitSelection] = useState({ const [unitSelection, setUnitSelection] = useState({
allUnit: false, allUnit: false,
@ -140,6 +147,7 @@ export default function FormImage() {
let uploadPersen = 0; let uploadPersen = 0;
const [isStartUpload, setIsStartUpload] = useState(false); const [isStartUpload, setIsStartUpload] = useState(false);
const [counterProgress, setCounterProgress] = useState(0); const [counterProgress, setCounterProgress] = useState(0);
const [showRewriteEditor, setShowRewriteEditor] = useState(false);
const [files, setFiles] = useState<FileWithPreview[]>([]); const [files, setFiles] = useState<FileWithPreview[]>([]);
const [filesTemp, setFilesTemp] = useState<File[]>([]); const [filesTemp, setFilesTemp] = useState<File[]>([]);
@ -164,16 +172,10 @@ export default function FormImage() {
const imageSchema = z.object({ const imageSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
description: z description: z.string().optional(),
.string() descriptionOri: z.string().optional(),
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }) rewriteDescription: z.string().optional(),
.or(
z.literal(articleBody || "").refine((val) => val.length > 0, {
message: "Deskripsi diperlukan.",
})
),
creatorName: z.string().min(1, { message: "Creator diperlukan" }), creatorName: z.string().min(1, { message: "Creator diperlukan" }),
// tags: z.string().min(1, { message: "Judul diperlukan" }),
}); });
const { const {
@ -184,6 +186,11 @@ export default function FormImage() {
formState: { errors }, formState: { errors },
} = useForm<ImageSchema>({ } = useForm<ImageSchema>({
resolver: zodResolver(imageSchema), resolver: zodResolver(imageSchema),
defaultValues: {
description: "",
descriptionOri: "",
rewriteDescription: "",
},
}); });
const doGenerateMainKeyword = async () => { const doGenerateMainKeyword = async () => {
@ -446,13 +453,25 @@ export default function FormImage() {
} }
}; };
useEffect(() => {
if (articleBody) {
setValue("description", articleBody);
setValue("rewriteDescription", articleBody);
}
}, [articleBody, setValue]);
const save = async (data: ImageSchema) => { const save = async (data: ImageSchema) => {
loading(); loading();
const finalTags = tags.join(", "); const finalTags = tags.join(", ");
const finalTitle = isSwitchOn ? title : data.title; const finalTitle = isSwitchOn ? title : data.title;
const finalDescription = articleBody || data.description; // const finalDescription = articleBody || data.description;
const finalDescription = isSwitchOn
? data.description
: selectedFileType === "rewrite"
? data.rewriteDescription
: data.descriptionOri;
if (!finalDescription.trim()) { if (!finalDescription?.trim()) {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error"); MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
return; return;
} }
@ -716,6 +735,45 @@ export default function FormImage() {
} }
}, [title, getValues, setValue]); }, [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 ( return (
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<div className="flex flex-col lg:flex-row gap-10"> <div className="flex flex-col lg:flex-row gap-10">
@ -930,6 +988,7 @@ export default function FormImage() {
color="primary" color="primary"
onClick={handleGenerateArtikel} onClick={handleGenerateArtikel}
size="sm" size="sm"
type="button"
> >
Generate Article Generate Article
</Button> </Button>
@ -972,8 +1031,6 @@ export default function FormImage() {
</div> </div>
</div> </div>
</div> </div>
</div>
)}
<div className="py-3 space-y-2"> <div className="py-3 space-y-2">
<Label>{t("description")}</Label> <Label>{t("description")}</Label>
<Controller <Controller
@ -982,11 +1039,16 @@ export default function FormImage() {
render={({ field: { onChange, value } }) => render={({ field: { onChange, value } }) =>
isLoadingData ? ( isLoadingData ? (
<div className="flex justify-center items-center h-40"> <div className="flex justify-center items-center h-40">
<p className="text-gray-500">Loading Proses Data...</p> <p className="text-gray-500">
Loading Proses Data...
</p>
</div> </div>
) : ( ) : (
<CustomEditor <CustomEditor
onChange={onChange} onChange={(value: any) => {
onChange(value);
setEditorContent(value);
}}
initialData={articleBody || value} initialData={articleBody || value}
/> />
) )
@ -998,6 +1060,110 @@ export default function FormImage() {
</p> </p>
)} )}
</div> </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>{t("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>{t("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>
</>
)}
<div className="py-3 space-y-2"> <div className="py-3 space-y-2">
<Label>{t("select-file")}</Label> <Label>{t("select-file")}</Label>
{/* <Input {/* <Input

View File

@ -114,7 +114,7 @@ export default function FormConvertSPIT() {
const { id } = useParams() as { id: string }; const { id } = useParams() as { id: string };
console.log(id); console.log(id);
const editor = useRef(null); const editor = useRef(null);
// type ImageSchema = z.infer<typeof imageSchema>; type ImageSchema = z.infer<typeof imageSchema>;
const [selectedFiles, setSelectedFiles] = useState<File[]>([]); const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId"); const taskId = Cookies.get("taskId");
@ -165,24 +165,10 @@ export default function FormConvertSPIT() {
const imageSchema = z.object({ const imageSchema = z.object({
contentTitle: z.string().min(1, { message: "Judul diperlukan" }), contentTitle: z.string().min(1, { message: "Judul diperlukan" }),
contentDescription: z contentDescription: z.string().optional(),
.string()
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
contentCreator: z.string().min(1, { message: "Creator diperlukan" }),
contentRewriteDescription: z.string().optional(), contentRewriteDescription: z.string().optional(),
contentCreator: z.string().min(1, { message: "Creator diperlukan" }),
}); });
// .refine(
// (data) => {
// if (isContentRewriteClicked) {
// return detail?.contentRewriteDescription?.trim().length > 0;
// }
// return true;
// },
// {
// path: ["contentRewriteDescription"],
// message: "File hasil rewrite wajib diisi",
// }
// );
const options: Option[] = [ const options: Option[] = [
{ id: "all", label: "SEMUA" }, { id: "all", label: "SEMUA" },
@ -194,33 +180,39 @@ export default function FormConvertSPIT() {
let fileTypeId = "1"; let fileTypeId = "1";
// const { const {
// control, control,
// handleSubmit, handleSubmit,
// setValue, setValue,
// formState: { errors }, formState: { errors },
// } = useForm<ImageSchema>({ } = useForm<ImageSchema>({
// resolver: zodResolver(imageSchema),
// defaultValues: {
// contentTitle: detail?.contentTitle || "",
// contentDescription: detail?.contentDescription || "",
// contentCreator: detail?.contentCreator || "",
// contentRewriteDescription: detail?.contentRewriteDescription || "",
// // dll
// },
// });
const form = useForm<z.infer<typeof imageSchema>>({
resolver: zodResolver(imageSchema), resolver: zodResolver(imageSchema),
defaultValues: { defaultValues: {
contentTitle: detail?.contentTitle || "", contentTitle: detail?.contentTitle || "",
contentDescription: detail?.contentDescription || "", contentDescription: detail?.contentDescription || "",
contentCreator: detail?.contentCreator || "", contentCreator: detail?.contentCreator || "",
contentRewriteDescription: detail?.contentRewriteDescription || "",
// dll // dll
}, },
}); });
// const form = useForm<z.infer<typeof imageSchema>>({
// resolver: zodResolver(imageSchema),
// defaultValues: {
// contentTitle: detail?.contentTitle || "",
// contentDescription: detail?.contentDescription || "",
// contentCreator: detail?.contentCreator || "",
// // dll
// },
// });
useEffect(() => {
if (articleBody) {
setValue("contentRewriteDescription", articleBody);
}
}, [articleBody, setValue]);
const handleRemoveTag = (index: any) => { const handleRemoveTag = (index: any) => {
setTags((prevTags) => prevTags.filter((_, i) => i !== index)); setTags((prevTags) => prevTags.filter((_, i) => i !== index));
}; };
@ -237,10 +229,10 @@ export default function FormConvertSPIT() {
setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index)); setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index));
}; };
const handleDirectSave = () => { // const handleDirectSave = () => {
const values = form.getValues(); // const values = form.getValues();
onSubmit(values); // onSubmit(values);
}; // };
useEffect(() => { useEffect(() => {
async function initState() { async function initState() {
@ -346,10 +338,10 @@ export default function FormConvertSPIT() {
setDetail(details); setDetail(details);
setFiles(details?.contentList); setFiles(details?.contentList);
setupPlacementCheck(details?.contentList?.length); setupPlacementCheck(details?.contentList?.length);
form.setValue("contentTitle", details?.contentTitle || ""); setValue("contentTitle", details?.contentTitle || "");
form.setValue("contentDescription", details?.contentDescription || ""); setValue("contentDescription", details?.contentDescription || "");
form.setValue("contentCreator", details?.contentCreator || ""); setValue("contentCreator", details?.contentCreator || "");
form.setValue( setValue(
"contentRewriteDescription", "contentRewriteDescription",
details?.contentRewriteDescription || "" details?.contentRewriteDescription || ""
); );
@ -471,12 +463,7 @@ export default function FormConvertSPIT() {
); );
}; };
const save = async (data: { const save = async (data: ImageSchema) => {
contentTitle: string;
contentDescription: string;
contentRewriteDescription?: string;
contentCreator: string;
}): Promise<void> => {
const temp = []; const temp = [];
for (const element of detail.contentList) { for (const element of detail.contentList) {
temp.push([]); temp.push([]);
@ -514,7 +501,7 @@ export default function FormConvertSPIT() {
}); });
}; };
const onSubmit = async (data: z.infer<typeof imageSchema>) => { const onSubmit = (data: ImageSchema) => {
MySwal.fire({ MySwal.fire({
title: "Simpan Data", title: "Simpan Data",
text: "Apakah Anda yakin ingin menyimpan data ini?", text: "Apakah Anda yakin ingin menyimpan data ini?",
@ -671,8 +658,7 @@ export default function FormConvertSPIT() {
return ( return (
<> <>
<Form {...form}> <form onSubmit={handleSubmit(onSubmit)}>
<form onSubmit={form.handleSubmit(onSubmit)}>
{detail !== undefined ? ( {detail !== undefined ? (
<div className="flex flex-col lg:flex-row gap-10"> <div className="flex flex-col lg:flex-row gap-10">
<Card className="w-full lg:w-8/12"> <Card className="w-full lg:w-8/12">
@ -683,7 +669,7 @@ export default function FormConvertSPIT() {
<div className="space-y-2 py-3"> <div className="space-y-2 py-3">
<Label>{t("title")}</Label> <Label>{t("title")}</Label>
<Controller <Controller
control={form.control} control={control}
name="contentTitle" name="contentTitle"
render={({ field }) => ( render={({ field }) => (
<Input <Input
@ -737,15 +723,9 @@ export default function FormConvertSPIT() {
<div className="py-3 space-y-2"> <div className="py-3 space-y-2">
<Label>{t("description")}</Label> <Label>{t("description")}</Label>
<Controller <Controller
control={form.control} control={control}
name="contentDescription" name="contentDescription"
render={({ field: { onChange, value } }) => ( render={({ field: { onChange, value } }) => (
// <JoditEditor
// ref={editor}
// value={detail?.contentDescription}
// onChange={onChange}
// className="dark:text-black"
// />
<CustomEditor <CustomEditor
onChange={onChange} onChange={onChange}
initialData={detail?.contentDescription} initialData={detail?.contentDescription}
@ -807,10 +787,7 @@ export default function FormConvertSPIT() {
</div> </div>
)} )}
<div className="flex items-center space-x-2 mt-3"> <div className="flex items-center space-x-2 mt-3">
<RadioGroupItem <RadioGroupItem value="rewrite" id="rewrite-file" />
value="rewrite"
id="rewrite-file"
/>
<Label htmlFor="rewrite-file"> <Label htmlFor="rewrite-file">
Select File Rewrite Select File Rewrite
</Label> </Label>
@ -818,7 +795,7 @@ export default function FormConvertSPIT() {
<div className="py-3 space-y-2"> <div className="py-3 space-y-2">
<Label>{t("file-rewrite")}</Label> <Label>{t("file-rewrite")}</Label>
<Controller <Controller
control={form.control} control={control}
name="contentRewriteDescription" name="contentRewriteDescription"
render={({ field: { onChange, value } }) => render={({ field: { onChange, value } }) =>
isLoadingData ? ( isLoadingData ? (
@ -830,11 +807,7 @@ export default function FormConvertSPIT() {
) : ( ) : (
<CustomEditor <CustomEditor
onChange={onChange} onChange={onChange}
initialData={ initialData={articleBody || value}
articleBody ||
detail?.contentRewriteDescription ||
value
}
/> />
) )
} }
@ -983,9 +956,7 @@ export default function FormConvertSPIT() {
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Checkbox <Checkbox
id="terms" id="terms"
checked={filePlacements[index]?.includes( checked={filePlacements[index]?.includes("mabes")}
"mabes"
)}
onCheckedChange={(e) => onCheckedChange={(e) =>
setupPlacement(index, "mabes", Boolean(e)) setupPlacement(index, "mabes", Boolean(e))
} }
@ -1000,9 +971,7 @@ export default function FormConvertSPIT() {
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Checkbox <Checkbox
id="terms" id="terms"
checked={filePlacements[index]?.includes( checked={filePlacements[index]?.includes("polda")}
"polda"
)}
onCheckedChange={(e) => onCheckedChange={(e) =>
setupPlacement(index, "polda", Boolean(e)) setupPlacement(index, "polda", Boolean(e))
} }
@ -1049,7 +1018,7 @@ export default function FormConvertSPIT() {
<div className="space-y-2"> <div className="space-y-2">
<Label>{t("creator")}</Label> <Label>{t("creator")}</Label>
<Controller <Controller
control={form.control} control={control}
name="contentCreator" name="contentCreator"
render={({ field }) => ( render={({ field }) => (
<Input <Input
@ -1099,10 +1068,7 @@ export default function FormConvertSPIT() {
<div className="flex flex-col gap-3 space-y-2"> <div className="flex flex-col gap-3 space-y-2">
<Label>{t("publish-target")}</Label> <Label>{t("publish-target")}</Label>
{options.map((option) => ( {options.map((option) => (
<div <div key={option.id} className="flex gap-2 items-center">
key={option.id}
className="flex gap-2 items-center"
>
<Checkbox <Checkbox
id={option.id} id={option.id}
checked={ checked={
@ -1144,7 +1110,6 @@ export default function FormConvertSPIT() {
"" ""
)} )}
</form> </form>
</Form>
</> </>
); );
} }

View File

@ -16,7 +16,7 @@ import * as z from "zod";
import { Upload } from "tus-js-client"; import { Upload } from "tus-js-client";
import Swal from "sweetalert2"; import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content"; import withReactContent from "sweetalert2-react-content";
import { redirect, useRouter } from "next/navigation"; import { redirect, useParams, useRouter } from "next/navigation";
import { import {
Select, Select,
SelectContent, SelectContent,
@ -40,6 +40,7 @@ import { uploadThumbnailBlog } from "@/service/blog/blog";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { import {
generateDataArticle, generateDataArticle,
generateDataRewrite,
getDetailArticle, getDetailArticle,
getGenerateKeywords, getGenerateKeywords,
getGenerateTitle, getGenerateTitle,
@ -83,11 +84,15 @@ export default function FormTeks() {
const editor = useRef(null); const editor = useRef(null);
type TeksSchema = z.infer<typeof teksSchema>; type TeksSchema = z.infer<typeof teksSchema>;
const params = useParams();
const locale = params?.locale;
const [selectedFiles, setSelectedFiles] = useState<File[]>([]); const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId"); const taskId = Cookies.get("taskId");
const scheduleId = Cookies.get("scheduleId"); const scheduleId = Cookies.get("scheduleId");
const scheduleType = Cookies.get("scheduleType"); const scheduleType = Cookies.get("scheduleType");
const roleId = getCookiesDecrypt("urie"); const roleId = getCookiesDecrypt("urie");
const [selectedFileType, setSelectedFileType] = useState("original");
const [categories, setCategories] = useState<Category[]>([]); const [categories, setCategories] = useState<Category[]>([]);
const [selectedCategory, setSelectedCategory] = useState<any>(); const [selectedCategory, setSelectedCategory] = useState<any>();
@ -102,6 +107,10 @@ export default function FormTeks() {
const [editingArticleId, setEditingArticleId] = useState<string | null>(null); const [editingArticleId, setEditingArticleId] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(false); const [isLoading, setIsLoading] = useState<boolean>(false);
const [isLoadingData, setIsLoadingData] = useState<boolean>(false); const [isLoadingData, setIsLoadingData] = useState<boolean>(false);
const [selectedWritingStyle, setSelectedWritingStyle] =
useState("professional");
const [editorContent, setEditorContent] = useState(""); // Untuk original editor
const [rewriteEditorContent, setRewriteEditorContent] = useState("");
const [articleIds, setArticleIds] = useState<string[]>([]); const [articleIds, setArticleIds] = useState<string[]>([]);
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false); const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
@ -109,8 +118,9 @@ export default function FormTeks() {
const [selectedArticleId, setSelectedArticleId] = useState<string | null>( const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
null null
); );
const [isContentRewriteClicked, setIsContentRewriteClicked] = useState(false);
const [showRewriteEditor, setShowRewriteEditor] = useState(false);
const [selectedMainKeyword, setSelectedMainKeyword] = useState(""); const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
const [selectedWritingStyle, setSelectedWritingStyle] = useState("");
const [selectedSize, setSelectedSize] = useState(""); const [selectedSize, setSelectedSize] = useState("");
const [detailData, setDetailData] = useState<any>(null); const [detailData, setDetailData] = useState<any>(null);
const [articleImages, setArticleImages] = useState<string[]>([]); const [articleImages, setArticleImages] = useState<string[]>([]);
@ -157,14 +167,9 @@ export default function FormTeks() {
const teksSchema = z.object({ const teksSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
description: z description: z.string().optional(),
.string() descriptionOri: z.string().optional(), // Original editor
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }) rewriteDescription: z.string().optional(),
.or(
z.literal(articleBody || "").refine((val) => val.length > 0, {
message: "Deskripsi diperlukan.",
})
),
creatorName: z.string().min(1, { message: "Creator diperlukan" }), creatorName: z.string().min(1, { message: "Creator diperlukan" }),
// tags: z.string().min(1, { message: "Judul diperlukan" }), // tags: z.string().min(1, { message: "Judul diperlukan" }),
}); });
@ -177,6 +182,11 @@ export default function FormTeks() {
formState: { errors }, formState: { errors },
} = useForm<TeksSchema>({ } = useForm<TeksSchema>({
resolver: zodResolver(teksSchema), resolver: zodResolver(teksSchema),
defaultValues: {
description: "",
descriptionOri: "",
rewriteDescription: "",
},
}); });
const doGenerateMainKeyword = async () => { const doGenerateMainKeyword = async () => {
@ -439,12 +449,26 @@ export default function FormTeks() {
} }
}; };
useEffect(() => {
if (articleBody) {
// Set ke dua field jika rewrite juga aktif
setValue("description", articleBody);
setValue("rewriteDescription", articleBody);
}
}, [articleBody, setValue]);
const save = async (data: TeksSchema) => { const save = async (data: TeksSchema) => {
loading(); loading();
const finalTags = tags.join(", "); const finalTags = tags.join(", ");
const finalTitle = isSwitchOn ? title : data.title; const finalTitle = isSwitchOn ? title : data.title;
const finalDescription = articleBody || data.description;
if (!finalDescription.trim()) { const finalDescription = isSwitchOn
? data.description
: selectedFileType === "rewrite"
? data.rewriteDescription
: data.descriptionOri;
if (!finalDescription?.trim()) {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error"); MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
return; return;
} }
@ -709,6 +733,45 @@ export default function FormTeks() {
} }
}, [title, getValues, setValue]); }, [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 ( return (
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<div className="flex flex-col lg:flex-row gap-10"> <div className="flex flex-col lg:flex-row gap-10">
@ -923,6 +986,7 @@ export default function FormTeks() {
color="primary" color="primary"
onClick={handleGenerateArtikel} onClick={handleGenerateArtikel}
size="sm" size="sm"
type="button"
> >
Generate Article Generate Article
</Button> </Button>
@ -949,27 +1013,23 @@ export default function FormTeks() {
<div className="pt-3"> <div className="pt-3">
<div className="flex flex-row justify-between items-center"> <div className="flex flex-row justify-between items-center">
{selectedArticleId && ( {selectedArticleId && (
<Link
href={`/contributor/content/teks/update-seo/${selectedArticleId}`}
target="_blank"
rel="noopener noreferrer"
>
<Button <Button
className="mb-2" className="mb-2"
size="sm" size="sm"
variant={"outline"} variant={"outline"}
color="primary" color="primary"
onClick={() => {
const url = `/${locale}/contributor/content/image/update-seo/${selectedArticleId}`;
window.open(url, "_blank", "noopener,noreferrer");
}}
> >
{t("update")} {t("update")}
</Button> </Button>
</Link>
)} )}
</div> </div>
</div> </div>
</div> </div>
</div> <div className="py-3 space-y-2">
)}
<div className="space-y-2">
<Label>{t("description")}</Label> <Label>{t("description")}</Label>
<Controller <Controller
control={control} control={control}
@ -977,11 +1037,16 @@ export default function FormTeks() {
render={({ field: { onChange, value } }) => render={({ field: { onChange, value } }) =>
isLoadingData ? ( isLoadingData ? (
<div className="flex justify-center items-center h-40"> <div className="flex justify-center items-center h-40">
<p className="text-gray-500">Loading Proses Data...</p> <p className="text-gray-500">
Loading Proses Data...
</p>
</div> </div>
) : ( ) : (
<CustomEditor <CustomEditor
onChange={onChange} onChange={(value: any) => {
onChange(value);
setEditorContent(value);
}}
initialData={articleBody || value} initialData={articleBody || value}
/> />
) )
@ -993,6 +1058,110 @@ export default function FormTeks() {
</p> </p>
)} )}
</div> </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>{t("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>{t("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>
</>
)}
<div className="py-3 space-y-2"> <div className="py-3 space-y-2">
<Label>{t("select-file")}</Label> <Label>{t("select-file")}</Label>
{/* <Input {/* <Input

View File

@ -16,7 +16,7 @@ import * as z from "zod";
import { Upload } from "tus-js-client"; import { Upload } from "tus-js-client";
import Swal from "sweetalert2"; import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content"; import withReactContent from "sweetalert2-react-content";
import { redirect, useRouter } from "next/navigation"; import { redirect, useParams, useRouter } from "next/navigation";
import { import {
Select, Select,
SelectContent, SelectContent,
@ -40,6 +40,7 @@ import { uploadThumbnailBlog } from "@/service/blog/blog";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { import {
generateDataArticle, generateDataArticle,
generateDataRewrite,
getDetailArticle, getDetailArticle,
getGenerateKeywords, getGenerateKeywords,
getGenerateTitle, getGenerateTitle,
@ -82,12 +83,15 @@ export default function FormVideo() {
const router = useRouter(); const router = useRouter();
const editor = useRef(null); const editor = useRef(null);
type VideoSchema = z.infer<typeof videoSchema>; type VideoSchema = z.infer<typeof videoSchema>;
const params = useParams();
const locale = params?.locale;
const [selectedFiles, setSelectedFiles] = useState<File[]>([]); const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId"); const taskId = Cookies.get("taskId");
const scheduleId = Cookies.get("scheduleId"); const scheduleId = Cookies.get("scheduleId");
const scheduleType = Cookies.get("scheduleType"); const scheduleType = Cookies.get("scheduleType");
const roleId = getCookiesDecrypt("urie"); const roleId = getCookiesDecrypt("urie");
const [selectedFileType, setSelectedFileType] = useState("original");
const t = useTranslations("Form"); const t = useTranslations("Form");
const [categories, setCategories] = useState<Category[]>([]); const [categories, setCategories] = useState<Category[]>([]);
@ -97,6 +101,11 @@ export default function FormVideo() {
const [preview, setPreview] = useState<string | null>(null); const [preview, setPreview] = useState<string | null>(null);
const [selectedLanguage, setSelectedLanguage] = useState(""); const [selectedLanguage, setSelectedLanguage] = useState("");
const [selectedWritingStyle, setSelectedWritingStyle] =
useState("professional");
const [editorContent, setEditorContent] = useState(""); // Untuk original editor
const [rewriteEditorContent, setRewriteEditorContent] = useState("");
const [selectedSEO, setSelectedSEO] = useState<string>(""); const [selectedSEO, setSelectedSEO] = useState<string>("");
const [title, setTitle] = useState<string>(""); const [title, setTitle] = useState<string>("");
const [selectedAdvConfig, setSelectedAdvConfig] = useState<string>(""); const [selectedAdvConfig, setSelectedAdvConfig] = useState<string>("");
@ -111,13 +120,17 @@ export default function FormVideo() {
null null
); );
const [selectedMainKeyword, setSelectedMainKeyword] = useState(""); const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
const [selectedWritingStyle, setSelectedWritingStyle] = useState(""); // const [selectedWritingStyle, setSelectedWritingStyle] = useState("");
const [selectedSize, setSelectedSize] = useState(""); const [selectedSize, setSelectedSize] = useState("");
const [detailData, setDetailData] = useState<any>(null); const [detailData, setDetailData] = useState<any>(null);
const [articleImages, setArticleImages] = useState<string[]>([]); const [articleImages, setArticleImages] = useState<string[]>([]);
const [isSwitchOn, setIsSwitchOn] = useState<boolean>(false); const [isSwitchOn, setIsSwitchOn] = useState<boolean>(false);
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const [showRewriteEditor, setShowRewriteEditor] = useState(false);
const [isContentRewriteClicked, setIsContentRewriteClicked] = useState(false);
const [selectedTarget, setSelectedTarget] = useState(""); const [selectedTarget, setSelectedTarget] = useState("");
const [unitSelection, setUnitSelection] = useState({ const [unitSelection, setUnitSelection] = useState({
allUnit: false, allUnit: false,
@ -155,14 +168,9 @@ export default function FormVideo() {
const videoSchema = z.object({ const videoSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }), title: z.string().min(1, { message: "Judul diperlukan" }),
description: z description: z.string().optional(),
.string() descriptionOri: z.string().optional(), // Original editor
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }) rewriteDescription: z.string().optional(),
.or(
z.literal(articleBody || "").refine((val) => val.length > 0, {
message: "Deskripsi diperlukan.",
})
),
creatorName: z.string().min(1, { message: "Creator diperlukan" }), creatorName: z.string().min(1, { message: "Creator diperlukan" }),
// tags: z.string().min(1, { message: "Judul diperlukan" }), // tags: z.string().min(1, { message: "Judul diperlukan" }),
}); });
@ -175,6 +183,11 @@ export default function FormVideo() {
formState: { errors }, formState: { errors },
} = useForm<VideoSchema>({ } = useForm<VideoSchema>({
resolver: zodResolver(videoSchema), resolver: zodResolver(videoSchema),
defaultValues: {
description: "",
descriptionOri: "",
rewriteDescription: "",
},
}); });
const doGenerateMainKeyword = async () => { const doGenerateMainKeyword = async () => {
@ -437,15 +450,30 @@ export default function FormVideo() {
} }
}; };
useEffect(() => {
if (articleBody) {
// Set ke dua field jika rewrite juga aktif
setValue("description", articleBody);
setValue("rewriteDescription", articleBody);
}
}, [articleBody, setValue]);
const save = async (data: VideoSchema) => { const save = async (data: VideoSchema) => {
loading(); loading();
const finalTags = tags.join(", "); const finalTags = tags.join(", ");
const finalTitle = isSwitchOn ? title : data.title; const finalTitle = isSwitchOn ? title : data.title;
const finalDescription = articleBody || data.description; // const finalDescription = articleBody || data.description;
if (!finalDescription.trim()) { const finalDescription = isSwitchOn
? data.description
: selectedFileType === "rewrite"
? data.rewriteDescription
: data.descriptionOri;
if (!finalDescription?.trim()) {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error"); MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
return; return;
} }
let requestData: { let requestData: {
title: string; title: string;
description: string; description: string;
@ -726,6 +754,45 @@ export default function FormVideo() {
} }
}, [title, getValues, setValue]); }, [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 ( return (
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<div className="flex flex-col lg:flex-row gap-10"> <div className="flex flex-col lg:flex-row gap-10">
@ -939,6 +1006,7 @@ export default function FormVideo() {
color="primary" color="primary"
onClick={handleGenerateArtikel} onClick={handleGenerateArtikel}
size="sm" size="sm"
type="button"
> >
Generate Article Generate Article
</Button> </Button>
@ -965,27 +1033,23 @@ export default function FormVideo() {
<div className="pt-3"> <div className="pt-3">
<div className="flex flex-row justify-between items-center"> <div className="flex flex-row justify-between items-center">
{selectedArticleId && ( {selectedArticleId && (
<Link
href={`/contributor/content/video/update-seo/${selectedArticleId}`}
target="_blank"
rel="noopener noreferrer"
>
<Button <Button
className="mb-2" className="mb-2"
size="sm" size="sm"
variant={"outline"} variant={"outline"}
color="primary" color="primary"
onClick={() => {
const url = `/${locale}/contributor/content/image/update-seo/${selectedArticleId}`;
window.open(url, "_blank", "noopener,noreferrer");
}}
> >
{t("update")} {t("update")}
</Button> </Button>
</Link>
)} )}
</div> </div>
</div> </div>
</div> </div>
</div> <div className="py-3 space-y-2">
)}
<div className="space-y-2">
<Label>{t("description")}</Label> <Label>{t("description")}</Label>
<Controller <Controller
control={control} control={control}
@ -993,11 +1057,16 @@ export default function FormVideo() {
render={({ field: { onChange, value } }) => render={({ field: { onChange, value } }) =>
isLoadingData ? ( isLoadingData ? (
<div className="flex justify-center items-center h-40"> <div className="flex justify-center items-center h-40">
<p className="text-gray-500">Loading Proses Data...</p> <p className="text-gray-500">
Loading Proses Data...
</p>
</div> </div>
) : ( ) : (
<CustomEditor <CustomEditor
onChange={onChange} onChange={(value: any) => {
onChange(value);
setEditorContent(value);
}}
initialData={articleBody || value} initialData={articleBody || value}
/> />
) )
@ -1009,6 +1078,110 @@ export default function FormVideo() {
</p> </p>
)} )}
</div> </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>{t("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>{t("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>
</>
)}
<div className="py-3 space-y-2"> <div className="py-3 space-y-2">
<Label>{t("select-file")}</Label> <Label>{t("select-file")}</Label>
{/* <Input {/* <Input