feat: add rewrite content
This commit is contained in:
parent
2c951e85ab
commit
4a5c4b592f
|
|
@ -293,7 +293,7 @@ const Galery = (props: any) => {
|
|||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{video?.mediaUpload?.title}</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className="flex items-center gap-1">
|
||||
<a >
|
||||
<a>
|
||||
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
||||
</a>
|
||||
</DropdownMenuTrigger>
|
||||
|
|
@ -334,11 +334,7 @@ const Galery = (props: any) => {
|
|||
contentAudio?.length > 0 ? (
|
||||
<div className=" grid grid-cols-1 gap-6 ">
|
||||
{contentAudio?.map((audio: any) => (
|
||||
<Link
|
||||
href={`/audio/detail/${audio?.mediaUpload?.slug}`}
|
||||
key={audio?.id}
|
||||
className="flex flex-col sm:flex-row items-center hover:scale-110 transition-transform duration-300 bg-white dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full"
|
||||
>
|
||||
<div key={audio?.id} className="flex flex-col sm:flex-row items-center bg-white dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full">
|
||||
<div className="flex items-center justify-center bg-red-500 text-white rounded-lg w-16 h-8 lg:h-16">
|
||||
<svg width="32" height="34" viewBox="0 0 32 34" fill="null" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
|
|
@ -347,9 +343,9 @@ const Galery = (props: any) => {
|
|||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex flex-col flex-1">
|
||||
<Link href={`/audio/detail/${audio?.mediaUpload?.slug}`} className="flex flex-col flex-1">
|
||||
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">{audio?.mediaUpload?.title}</div>
|
||||
</div>
|
||||
</Link>
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<div className="mt-2">
|
||||
<img src="/assets/wave.svg" className="w-80" />
|
||||
|
|
@ -365,7 +361,36 @@ const Galery = (props: any) => {
|
|||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className="flex items-center gap-1">
|
||||
<a>
|
||||
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
||||
</a>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem>
|
||||
<button onClick={() => handleSaveWishlist(audio?.mediaUpload?.id)} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
||||
<Icon icon="material-symbols:bookmark-outline" fontSize={20} />
|
||||
<p className="text-base font-semibold">Simpan</p>
|
||||
</button>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<Link href={`/content-management/rewrite/create/${audio?.mediaUpload?.slug}`} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
||||
<Icon icon="jam:write" fontSize={20} />
|
||||
<p className="text-base font-semibold">Content Rewrite</p>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<div className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
||||
<button onClick={() => copyToClip(audio?.mediaUpload?.slug)} className="w-full flex items-center gap-2">
|
||||
<Icon icon="oi:share" fontSize={20} />
|
||||
<p className="text-base font-semibold">Bagikan</p>
|
||||
</button>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
|
|
@ -377,12 +402,41 @@ const Galery = (props: any) => {
|
|||
contentImage?.length > 0 ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{contentImage?.map((image: any) => (
|
||||
<Card key={image?.id} className="hover:scale-110 transition-transform duration-300">
|
||||
<Card key={image?.id} className="">
|
||||
<CardContent className="flex flex-col bg-black dark:bg-white w-full rounded-lg p-0">
|
||||
<Link href={`/image/detail/${image?.mediaUpload?.slug}`}>
|
||||
<img src={image?.mediaUpload?.thumbnailLink} className="h-40 object-cover items-center justify-center cursor-pointer rounded-lg place-self-center" />
|
||||
<div className="font-semibold p-4 text-white text-xs lg:text-sm dark:text-black truncate w-full">{image?.mediaUpload?.title}</div>
|
||||
</Link>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className="flex items-center gap-1">
|
||||
<a>
|
||||
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
||||
</a>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem>
|
||||
<button onClick={() => handleSaveWishlist(image?.mediaUpload?.id)} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
||||
<Icon icon="material-symbols:bookmark-outline" fontSize={20} />
|
||||
<p className="text-base font-semibold">Simpan</p>
|
||||
</button>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<Link href={`/content-management/rewrite/create/${image?.mediaUpload?.slug}`} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
||||
<Icon icon="jam:write" fontSize={20} />
|
||||
<p className="text-base font-semibold">Content Rewrite</p>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<div className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
||||
<button onClick={() => copyToClip(image?.mediaUpload?.slug)} className="w-full flex items-center gap-2">
|
||||
<Icon icon="oi:share" fontSize={20} />
|
||||
<p className="text-base font-semibold">Bagikan</p>
|
||||
</button>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
|
|
@ -395,7 +449,7 @@ const Galery = (props: any) => {
|
|||
) : contentDocument.length > 0 ? (
|
||||
<div className=" grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{contentDocument?.map((document: any) => (
|
||||
<Link href={`/document/detail/${document?.mediaUpload?.slug}`} key={document?.id} className="flex flex-col bg-yellow-500 sm:flex-row items-center dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full">
|
||||
<div key={document?.id} className="flex flex-col bg-yellow-500 sm:flex-row items-center dark:bg-gray-800 cursor-pointer shadow-md rounded-lg p-4 gap-4 w-full">
|
||||
<div className="flex items-center justify-center rounded-lg w-16 h-16">
|
||||
<svg width="28" height="34" viewBox="0 0 28 34" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
|
|
@ -406,7 +460,9 @@ const Galery = (props: any) => {
|
|||
</div>
|
||||
|
||||
<div className="flex flex-col flex-1 gap-2">
|
||||
<div className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">{document?.mediaUpload?.title}</div>
|
||||
<Link href={`/document/detail/${document?.mediaUpload?.slug}`} className="font-semibold text-gray-900 dark:text-white mt-1 text-sm">
|
||||
{document?.mediaUpload?.title}
|
||||
</Link>
|
||||
<div className="flex gap-2 items-center text-xs text-red-500 dark:text-red-500">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512">
|
||||
<path fill="#f00" d="M224 30v256h-64l96 128l96-128h-64V30zM32 434v48h448v-48z" />
|
||||
|
|
@ -414,7 +470,36 @@ const Galery = (props: any) => {
|
|||
Download Dokumen
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className="flex items-center gap-1">
|
||||
<a>
|
||||
<Icon className="text-white ml-1" fontSize={25} icon="tabler:dots" />
|
||||
</a>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem>
|
||||
<button onClick={() => handleSaveWishlist(document?.mediaUpload?.id)} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
||||
<Icon icon="material-symbols:bookmark-outline" fontSize={20} />
|
||||
<p className="text-base font-semibold">Simpan</p>
|
||||
</button>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<Link href={`/content-management/rewrite/create/${document?.mediaUpload?.slug}`} className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
||||
<Icon icon="jam:write" fontSize={20} />
|
||||
<p className="text-base font-semibold">Content Rewrite</p>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<div className="flex items-center gap-1 hover:bg-slate-600 w-full rounded-lg">
|
||||
<button onClick={() => copyToClip(document?.mediaUpload?.slug)} className="w-full flex items-center gap-2">
|
||||
<Icon icon="oi:share" fontSize={20} />
|
||||
<p className="text-base font-semibold">Bagikan</p>
|
||||
</button>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -3,30 +3,51 @@
|
|||
import HeaderManagement from "@/components/landing-page/header-management";
|
||||
import SidebarManagement from "@/components/landing-page/sidebar-management";
|
||||
import { useRouter } from "@/i18n/routing";
|
||||
import { getCookiesDecrypt } from "@/lib/utils";
|
||||
import { getCookiesDecrypt, setCookiesEncrypt } from "@/lib/utils";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import React, { useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Swal from "sweetalert2";
|
||||
import withReactContent from "sweetalert2-react-content";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
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";
|
||||
import { error, loading } from "@/config/swal";
|
||||
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";
|
||||
|
||||
const page = () => {
|
||||
// const { states } = props;
|
||||
const imageSchema = z.object({
|
||||
title: 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" }),
|
||||
});
|
||||
|
||||
const page = (props: { states?: any }) => {
|
||||
const { states } = props;
|
||||
const [profile, setProfile] = useState();
|
||||
const MySwal = withReactContent(Swal);
|
||||
const router = useRouter();
|
||||
const [, setLoadingState] = useState(false);
|
||||
const searchParams = useSearchParams();
|
||||
const id: any = searchParams?.get("title");
|
||||
const [content, setContent] = useState([]);
|
||||
const [content, setContent] = useState<any>([]);
|
||||
const [isFromSPIT, setIsFromSPIT] = useState(false);
|
||||
const [listSuggestion, setListSuggestion] = useState();
|
||||
const [main, setMain] = useState();
|
||||
const [main, setMain] = useState<any>();
|
||||
const userId = getCookiesDecrypt("uie");
|
||||
const userRoleId = getCookiesDecrypt("urie");
|
||||
const [articleIds, setArticleIds] = useState([]);
|
||||
const [articleIds, setArticleIds] = useState<any>([]);
|
||||
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
|
||||
const [selectedArticleId, setSelectedArticleId] = useState(null);
|
||||
const [selectedArticleId, setSelectedArticleId] = useState<any>(null);
|
||||
const [articleBody, setArticleBody] = useState("");
|
||||
const [selectedAdvConfig, setSelectedAdvConfig] = useState("");
|
||||
const [selectedWritingStyle, setSelectedWritingStyle] = useState("");
|
||||
|
|
@ -36,11 +57,235 @@ const page = () => {
|
|||
const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
|
||||
const [selectedSEO, setSelectedSEO] = useState("");
|
||||
const [selectedSize, setSelectedSize] = useState("");
|
||||
const [detailArticle, setDetailArticle] = useState(null);
|
||||
const [detailArticle, setDetailArticle] = useState<any>(null);
|
||||
const [contentType, setContentType] = useState("all");
|
||||
const [isLoadingData, setIsLoadingData] = useState<boolean>(false);
|
||||
const [detailData, setDetailData] = useState<any>(null);
|
||||
const [articleImages, setArticleImages] = useState<string[]>([]);
|
||||
|
||||
const userLevelId = getCookiesDecrypt("ulie");
|
||||
const roleId = getCookiesDecrypt("urie");
|
||||
type ImageSchema = z.infer<typeof imageSchema>;
|
||||
|
||||
const validationSchema = Yup.object().shape({
|
||||
title: Yup.string().required("Judul tidak boleh kosong"),
|
||||
});
|
||||
|
||||
let componentMounted = true;
|
||||
|
||||
const { control } = useForm<ImageSchema>({
|
||||
resolver: zodResolver(imageSchema),
|
||||
});
|
||||
|
||||
const formOptions = {
|
||||
resolver: yupResolver(validationSchema),
|
||||
};
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
reset,
|
||||
formState: { errors },
|
||||
setValue,
|
||||
} = useForm(formOptions);
|
||||
|
||||
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 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
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) {
|
||||
setContent(response?.data.data);
|
||||
setIsFromSPIT(response?.data.data?.isFromSPIT);
|
||||
setListSuggestion(responseGet?.data?.data);
|
||||
console.log("data list file", response?.data.data?.files);
|
||||
// const mainUrl = response?.data.data?.files[0]?.url;
|
||||
// const ticket = response?.data.data?.files[0]?.ticket;
|
||||
// const urlBlob = await getBlobContent(mainUrl, ticket);
|
||||
setMain({
|
||||
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,
|
||||
});
|
||||
|
||||
// Send Meta Data
|
||||
const metaData = {
|
||||
title: response?.data.data?.title,
|
||||
image: response?.data.data?.thumbnailLink,
|
||||
};
|
||||
|
||||
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 = {
|
||||
advConfig: selectedAdvConfig,
|
||||
style: selectedWritingStyle,
|
||||
website: "None",
|
||||
connectToWeb: true,
|
||||
lang: selectedLanguage,
|
||||
pointOfView: "None",
|
||||
title: content?.title,
|
||||
imageSource: "Web",
|
||||
mainKeyword: content?.title,
|
||||
additionalKeywords: content?.htmlDescription,
|
||||
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 handleArticleClick = async (id: any) => {
|
||||
const res = await getDetailArticle(id);
|
||||
const articleData = res?.data?.data;
|
||||
const cleanArticleBody = articleData?.articleBody?.replace(/<img[^>]*>/g, "");
|
||||
|
||||
const articleImagesData = articleData?.imagesUrl?.split(","); // Split URLs into an array
|
||||
|
||||
setArticleBody(cleanArticleBody || ""); // Set articleBody for CKEditor
|
||||
setDetailArticle(articleData);
|
||||
setSelectedArticleId(id);
|
||||
// setArticleImages(articleImagesData || []); // Set images array in state
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderManagement />
|
||||
|
|
@ -51,88 +296,166 @@ const page = () => {
|
|||
<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">
|
||||
<form>
|
||||
<div className="flex flex-row gap-2 justify-between mb-3">
|
||||
<div className="gap-1">
|
||||
<p className="font-semibold">Bahasa</p>
|
||||
<Select value={selectedLanguage} onValueChange={(e: any) => setSelectedLanguage(e.target.value)}>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue placeholder="Pilih Bahasa" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>Pilih Bahasa</SelectLabel>
|
||||
<SelectItem value="indonesia">Indonesia</SelectItem>
|
||||
<SelectItem value="english">English</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<form method="POST" onSubmit={handleSubmit(onSubmit)}>
|
||||
{/* {content && ( */}
|
||||
<div className="flex flex-col gap-2 ">
|
||||
<div className="flex flex-row gap-2">
|
||||
<div className="gap-1 flex flex-col mb-3">
|
||||
<p className="font-semibold">Bahasa</p>
|
||||
<Select value={selectedLanguage} onValueChange={(e: any) => setSelectedLanguage(e.target.value)}>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue placeholder="Pilih Bahasa" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>Pilih Bahasa</SelectLabel>
|
||||
<SelectItem value="indonesia">Indonesia</SelectItem>
|
||||
<SelectItem value="english">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={(e: any) => setSelectedContextType(e.target.value)}>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<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={(e: any) => setSelectedWritingStyle(e.target.value)}>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<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={(e: any) => setSelectedSize(e.target.value)}>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<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>
|
||||
</div>
|
||||
<div className="gap-1">
|
||||
<p className="font-semibold">Context Type</p>
|
||||
<Select value={selectedContextType} onValueChange={(e: any) => setSelectedContextType(e.target.value)}>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<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 className="mb-3">
|
||||
<p className="font-semibold">Judul</p>
|
||||
<input
|
||||
type="text"
|
||||
// className="w-full border py-3 rounded-lg"
|
||||
className={`w-full border py-3 rounded-lg ${errors.title ? "is-invalid" : ""}`}
|
||||
{...register("title", {
|
||||
value: content?.title,
|
||||
})}
|
||||
id="title"
|
||||
defaultValue={content?.title}
|
||||
// onChange={(e) => setSelectedTitle(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="gap-1">
|
||||
<p className="font-semibold">Writing Style</p>
|
||||
<Select value={selectedWritingStyle} onValueChange={(e: any) => setSelectedWritingStyle(e.target.value)}>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<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 className="mb-3">
|
||||
<p className="font-semibold">Main Keyword</p>
|
||||
<input
|
||||
type="text"
|
||||
className="w-full border py-3 rounded-lg mb-4"
|
||||
id="mainKeyword"
|
||||
name="mainKeyword"
|
||||
// onChange={(e) =>
|
||||
// setSelectedMainKeyword(e.target.value)
|
||||
// }
|
||||
placeholder="Masukan Main Keyword disini!"
|
||||
defaultValue={content?.title}
|
||||
/>
|
||||
</div>
|
||||
<div className="gap-1">
|
||||
<p className="font-semibold">Article Size</p>
|
||||
<Select value={selectedSize} onValueChange={(e: any) => setSelectedSize(e.target.value)}>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<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 className="mb-3">
|
||||
<p className="font-semibold">SEO</p>
|
||||
<textarea
|
||||
className="border py-15 w-full rounded-lg"
|
||||
id="seo"
|
||||
name="seo"
|
||||
placeholder="Tuliskan kata kunci atau frasa yang relevan dengan blog Anda, lalu tekan enter"
|
||||
// value={selectedSEO}
|
||||
// onChange={(e) => setSelectedSEO(e.target.value)}
|
||||
defaultValue={htmlToString(content?.htmlDescription)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<a onClick={handleGenerateArtikel} className="text-blue-500 hover:bg-blue-700 hover:text-white border border-blue-500 rounded-md p-2">
|
||||
Generate Artikel
|
||||
</a>
|
||||
{isGeneratedArticle && (
|
||||
<div className="mt-3 ml-2 pb-0">
|
||||
{articleIds.map((id: any, index: any) => (
|
||||
<p key={index} className={`text-black m-1 ${selectedArticleId === id ? "bg-[#31ce36] border border-[#31ce36]" : "bg-[#48abf7] border border-[#48abf7]"}`} onClick={() => handleArticleClick(id)}>
|
||||
{id}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<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={(event: any) => setArticleBody(event.editor.getData())} initialData={articleBody || value} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
{articleBody === null || articleBody === "" ? <div className="text-red-400 px-0 text-sm">Deskripsi tidak boleh kosong*</div> : ""}
|
||||
</div>
|
||||
<div className="flex flex-row gap-3">
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
window.close();
|
||||
}}
|
||||
className="border border-blue-400 hover:bg-blue-400 hover:text-white text-blue-400"
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
<Button type="submit" className="border border-blue-500 bg-blue-500 text-white">
|
||||
Simpan
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xl font-semibold mb-5">Judul</p>
|
||||
<input
|
||||
type="text"
|
||||
className="w-full border rounded-lg"
|
||||
// {...register("title", {
|
||||
// value: content?.title,
|
||||
// })}
|
||||
id="title"
|
||||
// defaultValue={content?.title}
|
||||
// onChange={(e) => setSelectedTitle(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* )} */}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import { generateDataArticle } from "@/service/content/ai";
|
|||
import HeaderManagement from "@/components/landing-page/header-management";
|
||||
import SidebarManagement from "@/components/landing-page/sidebar-management";
|
||||
import { saveContentRewrite } from "@/service/content/content";
|
||||
import JoditEditor from "jodit-react";
|
||||
|
||||
const page = (props: any) => {
|
||||
const { states } = props;
|
||||
|
|
@ -286,7 +285,6 @@ const page = (props: any) => {
|
|||
<div className="font-semibold mb-3">
|
||||
<label htmlFor="description">Deskripsi Artikel</label>
|
||||
</div>
|
||||
{/* <JoditEditor ref={editor} value={field.value} className="dark:text-black" onChange={field.onChange} /> */}
|
||||
{articleBody === null || articleBody === "" ? <div className="w-full px-0 text-sm">Deskripsi tidak boleh kosong</div> : ""}
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -24,23 +24,11 @@ const Login = ({ params: { locale } }: { params: { locale: string } }) => {
|
|||
>
|
||||
<div className="max-w-[520px] pt-16 ps-20 ">
|
||||
<Link href="/" className="mb-6 inline-block">
|
||||
<Image
|
||||
src="/assets/mediahub-logo.png"
|
||||
alt=""
|
||||
width={250}
|
||||
height={250}
|
||||
className="mb-10 w-full h-full"
|
||||
/>
|
||||
<Image src="/assets/mediahub-logo.png" alt="" width={250} height={250} className="mb-10 w-full h-full" />
|
||||
</Link>
|
||||
</div>
|
||||
<div className="absolute left-0 2xl:bottom-[-160px] bottom-[-130px] h-full w-full z-[-1]">
|
||||
<Image
|
||||
src="/assets/vector-login.svg"
|
||||
alt=""
|
||||
width={300}
|
||||
height={300}
|
||||
className="mb-10 w-full h-full"
|
||||
/>
|
||||
<Image src="/assets/vector-login.svg" alt="" width={300} height={300} className="mb-10 w-full h-full" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 relative">
|
||||
|
|
@ -52,24 +40,21 @@ const Login = ({ params: { locale } }: { params: { locale: string } }) => {
|
|||
</Link>
|
||||
</div>
|
||||
<div className="text-left 2xl:mb-10 mb-4 mt-10">
|
||||
<h4 className="font-semibold text-3xl text-left">
|
||||
Silahkan masuk ke akun Anda terlebih dahulu
|
||||
</h4>
|
||||
<h4 className="font-semibold text-3xl text-left">Silahkan masuk ke akun Anda terlebih dahulu</h4>
|
||||
<div className="text-default-500 text-base">
|
||||
Belum punya akun?{" "}
|
||||
<span className="text-red-500">registrasi</span>
|
||||
Belum punya akun? <span className="text-red-500">registrasi</span>
|
||||
</div>
|
||||
</div>
|
||||
<LoginForm />
|
||||
<div className="relative border-b-[#9AA2AF] border-opacity-[16%] border-b pt-6">
|
||||
{/* <div className="relative border-b-[#9AA2AF] border-opacity-[16%] border-b pt-6">
|
||||
<div className="absolute inline-block bg-default-50 dark:bg-default-100 left-1/2 top-1/2 transform -translate-x-1/2 px-4 min-w-max text-sm text-default-500 font-normal">
|
||||
Or continue with
|
||||
</div>
|
||||
</div>
|
||||
<div className="max-w-[242px] mx-auto mt-8 w-full">
|
||||
</div> */}
|
||||
{/* <div className="max-w-[242px] mx-auto mt-8 w-full">
|
||||
<Social locale={locale} />
|
||||
</div>
|
||||
<div className="md:max-w-[345px] mx-auto font-normal text-default-500 mt-12 uppercase text-sm">
|
||||
</div> */}
|
||||
{/* <div className="md:max-w-[345px] mx-auto font-normal text-default-500 mt-12 uppercase text-sm">
|
||||
Don’t have an account?{" "}
|
||||
<Link
|
||||
href="/auth/register"
|
||||
|
|
@ -77,11 +62,11 @@ const Login = ({ params: { locale } }: { params: { locale: string } }) => {
|
|||
>
|
||||
Sign up
|
||||
</Link>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
<div className="text-xs font-normal text-default-500 z-[999] pb-10 text-center">
|
||||
{/* <div className="text-xs font-normal text-default-500 z-[999] pb-10 text-center">
|
||||
<Copyright />
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,6 @@
|
|||
import { httpDeleteInterceptor, httpGetInterceptor, httpPostInterceptor } from "../http-config/http-interceptor-service";
|
||||
|
||||
export async function getDetail(id: any, state: any) {
|
||||
const url = `media/public?slug=${id}&state=${state}`;
|
||||
return httpGetInterceptor(url);
|
||||
}
|
||||
|
|
@ -110,3 +110,8 @@ export async function saveWishlist(data: { mediaUploadId: string }) {
|
|||
const url = "/media/wishlist";
|
||||
return httpPostInterceptor(url, data);
|
||||
}
|
||||
|
||||
export async function getPublicSuggestionList(id: any) {
|
||||
const url = `media/public/suggestion?mediaId=${id}`;
|
||||
return httpGetInterceptor(url);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../typescript/bin/tsc" "$@"
|
||||
else
|
||||
exec node "$basedir/../typescript/bin/tsc" "$@"
|
||||
fi
|
||||
|
|
@ -0,0 +1 @@
|
|||
../typescript/bin/tsc
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\typescript\bin\tsc" %*
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../typescript/bin/tsc" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../typescript/bin/tsc" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../typescript/bin/tsc" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../typescript/bin/tsc" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../typescript/bin/tsserver" "$@"
|
||||
else
|
||||
exec node "$basedir/../typescript/bin/tsserver" "$@"
|
||||
fi
|
||||
|
|
@ -0,0 +1 @@
|
|||
../typescript/bin/tsserver
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\typescript\bin\tsserver" %*
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../typescript/bin/tsserver" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../typescript/bin/tsserver" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../typescript/bin/tsserver" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../typescript/bin/tsserver" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
0
vendor/ckeditor5/node_modules/typescript/bin/tsserver
generated
vendored
Normal file → Executable file
0
vendor/ckeditor5/node_modules/typescript/bin/tsserver
generated
vendored
Normal file → Executable file
Loading…
Reference in New Issue