From da871a8671a147203525388d657c178b38e23c4d Mon Sep 17 00:00:00 2001 From: Anang Yusman Date: Sun, 29 Dec 2024 17:09:10 +0800 Subject: [PATCH] feat:create AI single article --- .../agenda-setting/calender-view.tsx | 2 +- .../content/image/createAi/page.tsx | 15 + .../contributor/content/image/page.tsx | 10 +- components/form/content/image-ai-form.tsx | 816 ++++++++++++++++++ components/landing-page/navbar.tsx | 132 ++- components/partials/header/profile-info.tsx | 77 +- package-lock.json | 244 +++--- service/content/ai.ts | 111 +++ service/http-config/axiosNulisAIInstance.ts | 12 + service/http-config/nulisAIApi.ts | 47 + 10 files changed, 1286 insertions(+), 180 deletions(-) create mode 100644 app/[locale]/(protected)/contributor/content/image/createAi/page.tsx create mode 100644 components/form/content/image-ai-form.tsx create mode 100644 service/content/ai.ts create mode 100644 service/http-config/axiosNulisAIInstance.ts create mode 100644 service/http-config/nulisAIApi.ts diff --git a/app/[locale]/(protected)/contributor/agenda-setting/calender-view.tsx b/app/[locale]/(protected)/contributor/agenda-setting/calender-view.tsx index 9618fb74..73cf2e5a 100644 --- a/app/[locale]/(protected)/contributor/agenda-setting/calender-view.tsx +++ b/app/[locale]/(protected)/contributor/agenda-setting/calender-view.tsx @@ -320,7 +320,7 @@ const CalendarView = ({ events, categories }: CalendarViewProps) => { center: "title", right: "dayGridMonth,timeGridWeek,timeGridDay,listWeek", }} - events={displayedEvents} // Use apiEvents here + events={displayedEvents} editable={true} rerenderDelay={10} eventDurationEditable={false} diff --git a/app/[locale]/(protected)/contributor/content/image/createAi/page.tsx b/app/[locale]/(protected)/contributor/content/image/createAi/page.tsx new file mode 100644 index 00000000..8782ba08 --- /dev/null +++ b/app/[locale]/(protected)/contributor/content/image/createAi/page.tsx @@ -0,0 +1,15 @@ +import SiteBreadcrumb from "@/components/site-breadcrumb"; +import FormImageAI from "@/components/form/content/image-ai-form"; + +const ImageAICreatePage = async () => { + return ( +
+ +
+ +
+
+ ); +}; + +export default ImageAICreatePage; diff --git a/app/[locale]/(protected)/contributor/content/image/page.tsx b/app/[locale]/(protected)/contributor/content/image/page.tsx index d58f7f98..aa43cb4b 100644 --- a/app/[locale]/(protected)/contributor/content/image/page.tsx +++ b/app/[locale]/(protected)/contributor/content/image/page.tsx @@ -63,10 +63,12 @@ const ReactTableImagePage = () => { Unggah Foto - + + + diff --git a/components/form/content/image-ai-form.tsx b/components/form/content/image-ai-form.tsx new file mode 100644 index 00000000..8bc0b421 --- /dev/null +++ b/components/form/content/image-ai-form.tsx @@ -0,0 +1,816 @@ +"use client"; +import React, { ChangeEvent, useEffect, useRef, useState } from "react"; +import { useForm, Controller } from "react-hook-form"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { Label } from "@/components/ui/label"; +import { Card } from "@/components/ui/card"; +import { zodResolver } from "@hookform/resolvers/zod"; +import * as z from "zod"; +import Swal from "sweetalert2"; +import withReactContent from "sweetalert2-react-content"; +import { 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 JoditEditor from "jodit-react"; +import { register } from "module"; +import { Switch } from "@/components/ui/switch"; +import Cookies from "js-cookie"; +import { createTask } from "@/config/api"; +import { + createMedia, + getTagsBySubCategoryId, + listEnableCategory, + uploadThumbnail, +} from "@/service/content/content"; +import { uploadThumbnailBlog } from "@/service/blog/blog"; +import { Textarea } from "@/components/ui/textarea"; +import { loading } from "@/lib/swal"; +import { + generateDataArticle, + getGenerateKeywords, + getGenerateTitle, +} from "@/service/content/ai"; +import { title } from "process"; +import style from "styled-jsx/style"; +import { getCookiesDecrypt } from "@/lib/utils"; + +const imageSchema = z.object({ + title: z.string().min(1, { message: "Judul diperlukan" }), + mainKeyword: z.string().min(1, { message: "Judul diperlukan" }), + seo: z.string().min(1, { message: "Judul diperlukan" }), + description: z + .string() + .min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }), + creatorName: z.string().min(1, { message: "Creator diperlukan" }), + // tags: z.string().min(1, { message: "Judul diperlukan" }), +}); + +type Category = { + id: string; + name: string; +}; + +export default function FormImageAI() { + const MySwal = withReactContent(Swal); + const router = useRouter(); + const editor = useRef(null); + type ImageSchema = z.infer; + + const userLevelId = getCookiesDecrypt("ulie"); + const roleId = getCookiesDecrypt("urie"); + + const [selectedFiles, setSelectedFiles] = useState([]); + const taskId = Cookies.get("taskId"); + const scheduleId = Cookies.get("scheduleId"); + const scheduleType = Cookies.get("scheduleType"); + + const [categories, setCategories] = useState([]); + const [selectedCategory, setSelectedCategory] = useState(); + const [tags, setTags] = useState([]); + const [thumbnail, setThumbnail] = useState(null); + const [preview, setPreview] = useState(null); + const [selectedTitle, setSelectedTitle] = useState(""); + const [selectedMainKeyword, setSelectedMainKeyword] = useState(""); + const [selectedWritingStyle, setSelectedWritingStyle] = useState(""); + const [selectedSize, setSelectedSize] = useState(""); + const [selectedLanguage, setSelectedLanguage] = useState(""); + const [selectedContent, setSelectedContent] = useState("single-article"); + const [selectedSEO, setSelectedSEO] = useState(""); + const [title, setTitle] = useState(""); + const [selectedAdvConfig, setSelectedAdvConfig] = useState(""); + const [articleIds, setArticleIds] = useState([]); + const [selectedArticleId, setSelectedArticleId] = useState( + null + ); + const [isGeneratedArticle, setIsGeneratedArticle] = useState(false); + const [articleBody, setArticleBody] = useState(""); + const [editingArticleId, setEditingArticleId] = useState(null); + const [isLoading, setIsLoading] = useState(false); + + const [selectedTarget, setSelectedTarget] = useState(""); + const [unitSelection, setUnitSelection] = useState({ + allUnit: false, + mabes: false, + polda: false, + polres: false, + }); + + let fileTypeId = "1"; + + const { + control, + handleSubmit, + setValue, + formState: { errors }, + } = useForm({ + resolver: zodResolver(imageSchema), + }); + + 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 { + 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 { + 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 { + console.error("Please provide a valid main keyword."); + } + }; + + const handleGenerateArtikel = async () => { + loading(); + const request = { + advConfig: selectedAdvConfig, + style: selectedWritingStyle, + website: "None", + connectToWeb: true, + lang: selectedLanguage, + pointOfView: "None", + title: selectedTitle, + 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: any) => { + if (prevIds.length < 5) { + return [...prevIds, newArticleId]; + } else { + const updatedIds = [...prevIds]; + updatedIds[4] = newArticleId; + return updatedIds; + } + }); + + Cookies.set("nulisAIArticleIdTemp", articleIds); + }; + + const handleArticleIdClick = (articleId: string) => { + setEditingArticleId(articleId); + setSelectedArticleId(articleId); + }; + + const handleEditClick = () => { + if (editingArticleId) { + console.log("Editing article:", editingArticleId); + // Handle the edit action here + } + }; + + const handleRemoveTag = (index: any) => { + 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 { + const category = await listEnableCategory(fileTypeId); + const resCategory: Category[] = category.data.data.content; + + setCategories(resCategory); + console.log("data category", resCategory); + + if (scheduleId && scheduleType === "3") { + const findCategory = resCategory.find((o) => + o.name.toLowerCase().includes("pers rilis") + ); + + if (findCategory) { + // setValue("categoryId", findCategory.id); + setSelectedCategory(findCategory.id); // Set the selected category + const response = await getTagsBySubCategoryId(findCategory.id); + setTags(response?.data.data); + } + } + } catch (error) { + console.error("Failed to fetch categories:", error); + } + }; + + const save = async (data: ImageSchema) => { + const requestData = { + ...data, + title: data.title, + description: data.description, + htmlDescription: data.description, + fileTypeId, + categoryId: selectedCategory, + subCategoryId: selectedCategory, + uploadedBy: "2b7c8d83-d298-4b19-9f74-b07924506b58", + statusId: "1", + publishedFor: "6", + creatorName: data.creatorName, + tags: "siap", + isYoutube: false, + isInternationalMedia: false, + }; + + const response = await createMedia(requestData); + console.log("Form Data Submitted:", requestData); + + if (response.error) { + MySwal.fire("Error", response.message, "error"); + return; + } + + const imageId = response.data.data.id; + if (imageId && thumbnail) { + const formMedia = new FormData(); + formMedia.append("file", thumbnail); + + const responseThumbnail = await uploadThumbnail(imageId, formMedia); + + if (responseThumbnail.error) { + MySwal.fire("Error", responseThumbnail.message, "error"); + return; + } + } + + MySwal.fire("Sukses", "Data berhasil disimpan.", "success"); + + MySwal.fire({ + title: "Sukses", + text: "Data berhasil disimpan.", + icon: "success", + confirmButtonColor: "#3085d6", + confirmButtonText: "OK", + }).then(() => { + router.push("/en/contributor/content/image"); + }); + }; + + const onSubmit = (data: ImageSchema) => { + MySwal.fire({ + title: "Simpan Data", + text: "Apakah Anda yakin ingin menyimpan data ini?", + icon: "warning", + showCancelButton: true, + cancelButtonColor: "#d33", + confirmButtonColor: "#3085d6", + confirmButtonText: "Simpan", + }).then((result) => { + if (result.isConfirmed) { + save(data); + } + }); + }; + + const handleImageChange = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file) { + setThumbnail(file); + console.log("Selected Thumbnail:", file); + } + if (file) { + setPreview(URL.createObjectURL(file)); + } + }; + + return ( +
+
+ +
+

Form Konten Foto

+
+ {/* Input Title */} +
+ + +
+
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ setSelectedMainKeyword(e.target.value)} + placeholder="Enter Main Keyword" + /> + {/* )} + /> */} +
+
+
+
+ + +
+
+ setTitle(e.target.value)} + placeholder="Generated Title" + /> +
+
+
+
+ + +
+

+ Kata kunci untuk disertakan dalam teks +

+

+ JIka Anda tidak Memberikan kata kunci, kami akan secara + otomatis membuat kata kunci yang relevan dari kata kunci utama + untuk setiap bagian dan menggunakannya untuk membuat artikel. + Untuk menambahkan kata kunci baru, ketik ', + kata kunci'. +

+
+