mediahub-fe/app/[locale]/(public)/content-management/rewrite/create/[id]/page.tsx

463 lines
19 KiB
TypeScript
Raw Normal View History

2025-01-07 11:52:16 +00:00
"use client";
import HeaderManagement from "@/components/landing-page/header-management";
import SidebarManagement from "@/components/landing-page/sidebar-management";
import { useRouter } from "@/i18n/routing";
2025-01-07 19:36:40 +00:00
import { getCookiesDecrypt, setCookiesEncrypt } from "@/lib/utils";
2025-01-08 06:15:04 +00:00
import { useParams, useSearchParams } from "next/navigation";
2025-01-07 19:36:40 +00:00
import React, { useEffect, useState } from "react";
2025-01-07 11:52:16 +00:00
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select";
2025-01-07 19:36:40 +00:00
import { Controller, useForm } from "react-hook-form";
import * as z from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import CustomEditor from "@/components/editor/custom-editor";
import { generateDataArticle, getDetailArticle } from "@/service/content/ai";
import { Button } from "@/components/ui/button";
2025-01-08 06:15:04 +00:00
import { close, error, loading } from "@/config/swal";
2025-01-07 19:36:40 +00:00
import { saveContentRewrite } from "@/service/content/content";
import { getPublicSuggestionList } from "@/service/landing/landing";
import { getDetail } from "@/service/detail/detail";
import { yupResolver } from "@hookform/resolvers/yup";
import * as Yup from "yup";
import { htmlToString } from "@/utils/globals";
import Cookies from "js-cookie";
2025-01-08 06:15:04 +00:00
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
2025-01-07 11:52:16 +00:00
2025-01-07 19:36:40 +00:00
const imageSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
2025-01-08 06:15:04 +00:00
mainKeyword: z.string().min(1, { message: "Keyword diperlukan" }),
seo: z.string().min(1, { message: "Tuliskan kata kunci atau frasa yang relevan dengan blog Anda, lalu tekan enter" }),
2025-01-07 19:36:40 +00:00
description: z.string().min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
// tags: z.string().min(1, { message: "Judul diperlukan" }),
});
2025-01-12 08:21:11 +00:00
const page = (props: any) => {
2025-01-07 19:36:40 +00:00
const { states } = props;
2025-01-07 11:52:16 +00:00
const MySwal = withReactContent(Swal);
const router = useRouter();
const [, setLoadingState] = useState(false);
const searchParams = useSearchParams();
2025-01-08 06:15:04 +00:00
const getParams = useParams();
const id: any = getParams?.id;
2025-01-07 19:36:40 +00:00
const [content, setContent] = useState<any>([]);
2025-01-07 11:52:16 +00:00
const [isFromSPIT, setIsFromSPIT] = useState(false);
const [listSuggestion, setListSuggestion] = useState();
2025-01-07 19:36:40 +00:00
const [main, setMain] = useState<any>();
2025-01-07 11:52:16 +00:00
const userId = getCookiesDecrypt("uie");
const userRoleId = getCookiesDecrypt("urie");
2025-01-07 19:36:40 +00:00
const [articleIds, setArticleIds] = useState<any>([]);
2025-01-07 11:52:16 +00:00
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
2025-01-07 19:36:40 +00:00
const [selectedArticleId, setSelectedArticleId] = useState<any>(null);
2025-01-07 11:52:16 +00:00
const [articleBody, setArticleBody] = useState("");
const [selectedAdvConfig, setSelectedAdvConfig] = useState("");
const [selectedWritingStyle, setSelectedWritingStyle] = useState("");
const [selectedContextType, setSelectedContextType] = useState("");
const [selectedLanguage, setSelectedLanguage] = useState("");
const [selectedTitle, setSelectedTitle] = useState("");
const [selectedSize, setSelectedSize] = useState("");
2025-01-07 19:36:40 +00:00
const [detailArticle, setDetailArticle] = useState<any>(null);
const [isLoadingData, setIsLoadingData] = useState<boolean>(false);
const [detailData, setDetailData] = useState<any>(null);
const [articleImages, setArticleImages] = useState<string[]>([]);
2025-01-07 11:52:16 +00:00
const userLevelId = getCookiesDecrypt("ulie");
const roleId = getCookiesDecrypt("urie");
2025-01-08 06:15:04 +00:00
2025-01-07 19:36:40 +00:00
type ImageSchema = z.infer<typeof imageSchema>;
const validationSchema = Yup.object().shape({
title: Yup.string().required("Judul tidak boleh kosong"),
2025-01-08 06:15:04 +00:00
mainKeyword: Yup.string().required("Keyword tidak boleh kosong"),
seo: Yup.string().required("Tuliskan kata kunci atau frasa yang relevan dengan blog Anda, lalu tekan enter"),
description: Yup.string().required("Narasi Penugasan harus lebih dari 2 karakter."),
2025-01-07 19:36:40 +00:00
});
let componentMounted = true;
const { control } = useForm<ImageSchema>({
resolver: zodResolver(imageSchema),
});
const formOptions = {
resolver: yupResolver(validationSchema),
};
const {
register,
handleSubmit,
reset,
formState: { errors },
setValue,
2025-01-08 06:15:04 +00:00
getValues,
2025-01-07 19:36:40 +00:00
} = useForm(formOptions);
const save = async (data: any) => {
const request = {
title: data.title,
articleId: detailArticle?.id,
mediaUploadId: id.split("-")?.[0],
articleBody: detailArticle?.articleBody,
metaTitle: detailArticle?.metaTitle,
metaDescription: detailArticle?.metaDescription,
mainKeyword: detailArticle?.mainKeyword,
additionalKeyword: detailArticle?.additionalKeyword,
articleSize: detailArticle?.articleSize,
style: detailArticle?.style,
website: detailArticle?.website,
imageUrl: detailArticle?.imageUrl,
};
loading();
const res = await saveContentRewrite(request);
if (res?.error) {
error(res?.message);
return false;
}
successSubmit();
};
function successSubmit() {
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
router.push("/content-management/rewrite");
}
});
}
function onSubmit(data: any) {
MySwal.fire({
title: "Simpan Data",
text: "",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Simpan",
}).then((result) => {
if (result.isConfirmed) {
save(data);
}
});
}
2025-01-08 06:15:04 +00:00
2025-01-07 19:36:40 +00:00
useEffect(() => {
async function initState() {
setLoadingState(true);
const response = await getDetail(id, states == "polda" ? "polda" : "mabes");
console.log("Detail dataaaa ::", response);
if (response?.data?.data?.isActive == false) {
window.location.replace("/");
}
const responseGet = await getPublicSuggestionList(id?.split("-")?.[0]);
// close();
if (componentMounted) {
2025-01-08 06:15:04 +00:00
setValue("title", response?.data?.data?.title);
setValue("mainKeyword", response?.data?.data?.title);
setValue("seo", response?.data?.data?.description);
2025-01-10 03:30:32 +00:00
setContent(response?.data?.data);
setIsFromSPIT(response?.data?.data?.isFromSPIT);
2025-01-07 19:36:40 +00:00
setListSuggestion(responseGet?.data?.data);
2025-01-08 06:15:04 +00:00
console.log("data list file", response?.data?.data);
2025-01-10 03:30:32 +00:00
// const mainUrl = response?.data?.data?.files[0]?.url;
// const ticket = response?.data?.data?.files[0]?.ticket;
2025-01-07 19:36:40 +00:00
// const urlBlob = await getBlobContent(mainUrl, ticket);
setMain({
2025-01-10 03:30:32 +00:00
id: response?.data?.data?.files[0]?.id,
type: response?.data?.data?.fileType.name,
url: Number(response?.data?.data?.fileType?.id) == 4 ? response?.data?.data?.files[0]?.secondaryUrl : response?.data?.data?.files[0]?.url,
thumbnailFileUrl: response?.data?.data?.files[0]?.thumbnailFileUrl,
names: response?.data?.data?.files[0]?.fileName,
format: response?.data?.data?.files[0]?.format,
widthPixel: response?.data?.data?.files[0]?.widthPixel,
heightPixel: response?.data?.data?.files[0]?.heightPixel,
size: response?.data?.data?.files[0]?.size,
caption: response?.data?.data?.files[0]?.caption,
2025-01-07 19:36:40 +00:00
});
// Send Meta Data
const metaData = {
2025-01-10 03:30:32 +00:00
title: response?.data?.data?.title,
image: response?.data?.data?.thumbnailLink,
2025-01-07 19:36:40 +00:00
};
setCookiesEncrypt("meta_data", metaData);
setLoadingState(false); // (2) write some value to state
// await new Promise(resolve => setTimeout(resolve, 1000)); // 2 sec
// setIdm();
}
return () => {
// This code runs when component is unmounted
componentMounted = false; // (4) set it to false if we leave the page
};
}
initState();
}, [id]);
const handleGenerateArtikel = async () => {
loading();
const request = {
2025-01-08 06:15:04 +00:00
advConfig: "",
2025-01-07 19:36:40 +00:00
style: selectedWritingStyle,
website: "None",
connectToWeb: true,
lang: selectedLanguage,
pointOfView: "None",
2025-01-08 06:15:04 +00:00
title: getValues("title"),
2025-01-07 19:36:40 +00:00
imageSource: "Web",
2025-01-08 06:15:04 +00:00
mainKeyword: getValues("mainKeyword"),
additionalKeywords: getValues("seo"),
2025-01-07 19:36:40 +00:00
targetCountry: null,
articleSize: selectedSize,
projectId: 2,
createdBy: roleId,
2025-01-08 06:15:04 +00:00
// clientId: "ngDLPPiorplznw2jTqVe3YFCz5xqKfUJ",
clientId: "mediahubClientId",
2025-01-07 19:36:40 +00:00
};
2025-01-08 06:15:04 +00:00
console.log("Request", request);
2025-01-07 19:36:40 +00:00
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: any) => {
if (prevIds.length < 5) {
return [...prevIds, newArticleId];
} else {
const updatedIds = [...prevIds];
updatedIds[4] = newArticleId;
return updatedIds;
}
});
Cookies.set("nulisAIArticleIdTemp", articleIds);
};
2025-01-08 06:15:04 +00:00
const handleArticleIdClick = async (id: string) => {
setIsLoadingData(true);
let retryCount = 0;
const maxRetries = 20;
try {
2025-01-09 01:59:46 +00:00
const waitForStatusUpdate: any = async () => {
2025-01-08 06:15:04 +00:00
while (retryCount < maxRetries) {
const res = await getDetailArticle(id);
const articleData = res?.data?.data;
2025-01-07 19:36:40 +00:00
2025-01-08 06:15:04 +00:00
if (articleData?.status === 2) {
return articleData;
}
2025-01-07 19:36:40 +00:00
2025-01-08 06:15:04 +00:00
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, "");
console.log("lalalala", cleanArticleBody);
const articleImagesData = articleData?.imagesUrl?.split(",");
setValue("description", cleanArticleBody || "");
setArticleBody(cleanArticleBody || "");
2025-01-09 01:59:46 +00:00
setDetailArticle(articleData);
2025-01-08 06:15:04 +00:00
setSelectedArticleId(id);
setArticleImages(articleImagesData || []);
} catch (error) {
console.error("Error fetching article details:", error);
} finally {
setIsLoadingData(false);
}
2025-01-07 19:36:40 +00:00
};
2025-01-07 11:52:16 +00:00
return (
<>
<HeaderManagement />
2025-01-31 12:51:04 +00:00
<div className="flex flex-col lg:flex-row">
2025-01-07 11:52:16 +00:00
<SidebarManagement />
2025-01-31 12:51:04 +00:00
<div className="w-full lg:w-2/3 p-8 lg:p-12">
2025-01-07 11:52:16 +00:00
<div className="flex flex-col">
<div className="text-xl font-bold mb-5">Content Rewrite</div>
<div className="p-8 border border-black rounded-lg">
2025-01-07 19:36:40 +00:00
<form method="POST" onSubmit={handleSubmit(onSubmit)}>
2025-01-08 06:15:04 +00:00
{content && (
<div className="flex flex-col gap-2 ">
2025-01-31 12:51:04 +00:00
<div className="flex flex-col lg:flex-row gap-2">
2025-01-08 06:15:04 +00:00
<div className="gap-1 flex flex-col mb-3">
<p className="font-semibold">Bahasa</p>
<Select value={selectedLanguage} onValueChange={setSelectedLanguage}>
2025-01-31 12:51:04 +00:00
<SelectTrigger className="w-full lg:w-[180px]">
2025-01-08 06:15:04 +00:00
<SelectValue placeholder="Pilih Bahasa" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Pilih Bahasa</SelectLabel>
<SelectItem value="id">Indonesia</SelectItem>
<SelectItem value="en">English</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</div>
<div className="gap-1 flex flex-col mb-3">
<p className="font-semibold">Context Type</p>
<Select value={selectedContextType} onValueChange={setSelectedContextType}>
2025-01-31 12:51:04 +00:00
<SelectTrigger className="w-full lg:w-[180px]">
2025-01-08 06:15:04 +00:00
<SelectValue placeholder="Select Context" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Select Context Type</SelectLabel>
<SelectItem value="text">Text</SelectItem>
<SelectItem value="article">Article</SelectItem>
<SelectItem value="transcript">Transcript</SelectItem>
<SelectItem value="url">URL</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</div>
<div className="gap-1 flex flex-col mb-3">
<p className="font-semibold">Writing Style</p>
<Select value={selectedWritingStyle} onValueChange={setSelectedWritingStyle}>
2025-01-31 12:51:04 +00:00
<SelectTrigger className="w-full lg:w-[180px]">
2025-01-08 06:15:04 +00:00
<SelectValue placeholder="Select Writing" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Select Writing Style</SelectLabel>
<SelectItem value="firendly">Friendly</SelectItem>
<SelectItem value="profesional">Profesional</SelectItem>
<SelectItem value="informational">Informational</SelectItem>
<SelectItem value="neutral">Neutral</SelectItem>
<SelectItem value="witty">Witty</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</div>
<div className="gap-1 flex flex-col mb-3">
<p className="font-semibold">Article Size</p>
<Select value={selectedSize} onValueChange={setSelectedSize}>
2025-01-31 12:51:04 +00:00
<SelectTrigger className="w-full lg:w-[180px]">
2025-01-08 06:15:04 +00:00
<SelectValue placeholder="Select Size" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Select Article Size</SelectLabel>
<SelectItem value="news">News (300 - 900 words)</SelectItem>
<SelectItem value="info">Info (900 - 2000 words)</SelectItem>
<SelectItem value="detail">Detail (2000 - 5000 words)</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</div>
2025-01-07 19:36:40 +00:00
</div>
2025-01-08 06:15:04 +00:00
<div className="mb-3">
<p className="font-semibold">Judul</p>
<Controller
control={control}
name="title"
render={({ field: { onChange, value } }) => <Input type="text" className={`w-full border py-3 rounded-lg ${errors.title ? "is-invalid" : ""}`} {...register("title")} id="title" value={value} onChange={onChange} />}
/>
2025-01-07 19:36:40 +00:00
</div>
2025-01-08 06:15:04 +00:00
<div className="mb-3">
<p className="font-semibold">Main Keyword</p>
<Controller
control={control}
name="mainKeyword"
render={({ field: { onChange, value } }) => (
<Input type="text" className={`w-full border py-3 rounded-lg ${errors.mainKeyword ? "is-invalid" : ""}`} {...register("mainKeyword")} id="mainKeyword" value={value} onChange={onChange} />
)}
/>
2025-01-07 19:36:40 +00:00
</div>
2025-01-08 06:15:04 +00:00
<div className="mb-3">
<p className="font-semibold">SEO</p>
<Controller
control={control}
name="seo"
render={({ field: { onChange, value } }) => (
<Textarea className="py-20" id="seo" placeholder="Tuliskan kata kunci atau frasa yang relevan dengan blog Anda, lalu tekan enter" {...register("seo")} onChange={onChange} value={value} />
)}
/>
</div>
<div className="mb-4">
2025-01-31 12:51:04 +00:00
<a onClick={handleGenerateArtikel} className="text-blue-500 cursor-pointer hover:bg-blue-700 hover:text-white border border-blue-500 rounded-md p-2 text-sm lg:text-base">
2025-01-08 06:15:04 +00:00
Generate Artikel
</a>
{isGeneratedArticle && (
<div className="mt-3 ml-2 pb-0">
{articleIds.map((id: any, index: any) => (
<p
key={index}
2025-01-09 01:59:46 +00:00
className={`text-black border border-black w-fit rounded-md p-3 m-1 ${
selectedArticleId === id
? "text-[#31ce36] w-fit p-3 hover:bg-[#31ce36] hover:text-white cursor-pointer border border-[#31ce36]"
: "text-[#48abf7] p-3 hover:text-white hover:bg-[#48abf7] w-fit cursor-pointer border border-[#48abf7]"
}`}
2025-01-08 06:15:04 +00:00
onClick={() => handleArticleIdClick(id)}
>
{id}
</p>
))}
</div>
)}
2025-01-07 19:36:40 +00:00
</div>
2025-01-08 06:15:04 +00:00
<div className=" mb-3">
<p className="font-semibold">Deskripsi Baru</p>
<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={onChange} initialData={articleBody || value} />
)
}
/>
2025-01-09 01:59:46 +00:00
{articleBody === null || articleBody === "" ? <p className="text-red-400 px-0 text-sm">Deskripsi tidak boleh kosong*</p> : ""}
2025-01-08 06:15:04 +00:00
</div>
<div className="flex flex-row gap-3">
<Button
type="button"
onClick={() => {
router.back();
}}
2025-01-31 12:51:04 +00:00
className="border border-blue-400 hover:bg-blue-400 hover:text-white text-blue-400 text-sm lg:text-base"
2025-01-08 06:15:04 +00:00
>
Kembali
</Button>
2025-01-31 12:51:04 +00:00
<Button type="submit" className="border border-blue-500 bg-blue-500 text-sm lg:text-base text-white">
2025-01-08 06:15:04 +00:00
Simpan
</Button>
</div>
2025-01-07 11:52:16 +00:00
</div>
2025-01-08 06:15:04 +00:00
)}
2025-01-07 11:52:16 +00:00
</form>
</div>
</div>
</div>
</div>
</>
);
};
export default page;