web-humas-fe/components/form/article/generate-bulk-article-form.tsx

505 lines
15 KiB
TypeScript
Raw Normal View History

2024-11-15 10:53:04 +00:00
"use client";
import {
Button,
Input,
Select,
SelectItem,
SelectSection,
2025-02-13 08:25:39 +00:00
} from "@heroui/react";
2024-11-15 10:53:04 +00:00
import { FormEvent, useState } from "react";
import { Controller, useFieldArray, useForm } from "react-hook-form";
import * as z from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { close, error, loading } from "@/config/swal";
import {
generateDataArticle,
getGenerateKeywords,
getGenerateTitle,
getGenerateTopicKeywords,
saveBulkArticle,
2025-05-04 07:14:12 +00:00
} from "@/services/generate-article";
2024-11-15 10:53:04 +00:00
const writingStyle = [
{
id: 1,
name: "Friendly",
},
{
id: 1,
name: "Professional",
},
{
id: 3,
name: "Informational",
},
{
id: 4,
name: "Neutral",
},
{
id: 5,
name: "Witty",
},
];
const articleSize = [
{
id: 1,
name: "News (300 - 900 words)",
value: "News",
},
{
id: 2,
name: "Info (900 - 2000 words)",
value: "Info",
},
{
id: 3,
name: "Detail (2000 - 5000 words)",
value: "Detail",
},
];
const formSchema = z.object({
rows: z.array(
z.object({
mainKeyword: z.string().min(1, {
message: "Main Keyword must be at least 2 characters.",
}),
title: z.string().min(1, {
message: "Title must be at least 2 characters.",
}),
additionalKeyword: z.string().min(1, {
message: "Additional Keyword must be at least 2 characters.",
}),
})
),
});
export default function GenerateBulkArticle(props: {
articleId: (data: number[]) => void;
}) {
const [selectedWritingSyle, setSelectedWritingStyle] =
useState("Informational");
const [selectedArticleSize, setSelectedArticleSize] = useState("News");
const [selectedLanguage, setSelectedLanguage] = useState("id");
const formOptions = {
resolver: zodResolver(formSchema),
defaultValues: {
rows: [{ mainKeyword: "", title: "", additionalKeyword: "" }],
},
};
type UserSettingSchema = z.infer<typeof formSchema>;
const {
register,
control,
handleSubmit,
formState: { errors },
setValue,
getValues,
trigger,
} = useForm<UserSettingSchema>(formOptions);
const { fields, append, remove } = useFieldArray({
control,
name: "rows",
});
const onSubmit = async () => {
loading();
const listData = [];
for (let i = 0; i < fields.length; i++) {
listData.push({
title: getValues(`rows.${i}.title`),
mainKeyword: getValues(`rows.${i}.mainKeyword`),
additionalKeywords: getValues(`rows.${i}.additionalKeyword`),
});
}
const request = {
style: "Friendly",
website: "None",
connectToWeb: true,
lang: selectedLanguage,
pointOfView: "0",
imageSource: "Web",
targetCountry: null,
articleSize: selectedArticleSize,
projectId: 2,
data: listData,
createdBy: "123123",
clientId: "humasClientIdtest",
};
const res = await saveBulkArticle(request);
if (res.error) {
error(res.message);
return false;
}
const temp: number[] = [];
res?.data?.data.map((data: any) => temp.push(data.id));
props.articleId(temp);
close();
};
const generateTitle = async (keyword: string | undefined, index: number) => {
if (keyword) {
const req = {
keyword: keyword,
style: selectedWritingSyle,
website: "None",
connectToWeb: true,
lang: selectedLanguage,
pointOfView: "None",
clientId: "",
};
setValue(`rows.${index}.title`, "process...");
const res = await getGenerateTitle(req);
const data = res?.data?.data;
setValue(`rows.${index}.title`, data);
}
};
const generateKeywords = async (
keyword: string | undefined,
index: number
) => {
if (keyword) {
const req = {
keyword: keyword,
style: selectedWritingSyle,
website: "None",
connectToWeb: true,
lang: selectedLanguage,
pointOfView: "0",
clientId: "",
};
setValue(`rows.${index}.additionalKeyword`, "process...");
const res = await getGenerateKeywords(req);
const data = res?.data?.data;
setValue(`rows.${index}.additionalKeyword`, data);
}
};
const processAll = async () => {
let emptyMainKeyword = 0;
const mainKeyword = getValues(`rows.0.mainKeyword`);
for (let i = 0; i < fields.length; i++) {
const mainKeyNow = getValues(`rows.${i}.mainKeyword`);
if (mainKeyNow === "") {
emptyMainKeyword++;
}
}
if (mainKeyword !== "") {
if (emptyMainKeyword > 0) {
loading();
const res = await getGenerateTopicKeywords({
keyword: mainKeyword,
count: emptyMainKeyword,
});
const data = res?.data?.data;
let j = 0;
for (let i = 0; i < fields.length; i++) {
const mainKeyNow = getValues(`rows.${i}.mainKeyword`);
if (mainKeyNow === "") {
setValue(`rows.${i}.mainKeyword`, data[j]);
j++;
}
}
for (let i = 0; i < fields.length; i++) {
const mainKeyNow = getValues(`rows.${i}.mainKeyword`);
if (getValues(`rows.${i}.title`) == "") {
generateTitle(mainKeyNow, i);
}
if (getValues(`rows.${i}.additionalKeyword`) == "") {
generateKeywords(mainKeyNow, i);
}
}
close();
} else {
loading();
for (let i = 0; i < fields.length; i++) {
const mainKeyNow = getValues(`rows.${i}.mainKeyword`);
if (getValues(`rows.${i}.title`) == "") {
generateTitle(mainKeyNow, i);
}
if (getValues(`rows.${i}.additionalKeyword`) == "") {
generateKeywords(mainKeyNow, i);
}
}
close();
}
}
};
const processRow = (index: number) => {
const addKeyword = getValues(`rows.${index}.mainKeyword`);
generateTitle(addKeyword, index);
generateKeywords(addKeyword, index);
};
const validateFields = async () => {
const validation = await trigger();
if (validation) {
onSubmit();
}
};
return (
<fieldset>
<form
className="flex flex-col gap-2 w-full"
onSubmit={handleSubmit(onSubmit)}
>
<div className="grid grid-cols-1 md:grid-cols-3 gap-5 w-full">
<Select
label="Writing Style"
variant="bordered"
labelPlacement="outside"
placeholder=""
selectedKeys={[selectedWritingSyle]}
onChange={(e) =>
e.target.value !== ""
? setSelectedWritingStyle(e.target.value)
: ""
}
className="w-full"
>
<SelectSection>
{writingStyle.map((style) => (
<SelectItem key={style.name}>{style.name}</SelectItem>
))}
</SelectSection>
</Select>
<Select
label="Article Size"
variant="bordered"
labelPlacement="outside"
placeholder=""
selectedKeys={[selectedArticleSize]}
onChange={(e) =>
e.target.value !== ""
? setSelectedArticleSize(e.target.value)
: ""
}
className="w-full"
>
<SelectSection>
{articleSize.map((size) => (
<SelectItem key={size.value}>{size.name}</SelectItem>
))}
</SelectSection>
</Select>
<Select
label="Language"
variant="bordered"
labelPlacement="outside"
placeholder=""
selectedKeys={[selectedLanguage]}
onChange={(e) =>
e.target.value !== "" ? setSelectedLanguage(e.target.value) : ""
}
className="w-full"
>
<SelectSection>
<SelectItem key="id">Indonesia</SelectItem>
<SelectItem key="en">English</SelectItem>
</SelectSection>
</Select>
</div>
<div className={`flex flex-col md:gap-3 `}>
{fields.map((field, index) => (
<div
key={field.id}
className={` grid grid-cols-1 md:grid-cols-3 gap-5 w-full ${
fields.length === 1
? ""
: "border-b-2 md:border-none py-3 md:py-1"
}`}
>
<div>
<div className="text-[10px] md:text-base flex items-center justify-between">
<div className="flex flex-row items-center gap-1">
<p className="text-sm">Main Keyword</p>
<Button
color="primary"
size="sm"
onClick={() => {
if (index === 0) {
processAll();
} else {
processRow(index);
}
}}
>
Process {index === 0 ? "All" : ""}
</Button>
</div>
<div className="w-[42px] flex flex-row gap-1 md:hidden">
{index === fields.length - 1 && (
<button
className="w-[20px] mb-2 "
type="button"
onClick={() =>
append({
mainKeyword: "",
title: "",
additionalKeyword: "",
})
}
>
+
</button>
)}
{index !== 0 && (
<a
onClick={() => remove(index)}
className="cursor-pointer mb-2 w-[20px] h-[20px]text-center rounded-full flex justify-center items-center"
>
-
</a>
)}
</div>
</div>
<Controller
control={control}
name={`rows.${index}.mainKeyword`}
render={({ field: { onChange, value } }) => (
<Input
type="text"
id="mainKeyword"
placeholder=""
label=""
value={value}
onChange={onChange}
labelPlacement="outside"
className="w-full"
isDisabled={value === "process..."}
variant="bordered"
/>
)}
/>
</div>
<div>
<div className="text-[10px] md:text-base flex items-center gap-1">
<p className="text-sm">Title</p>
<Button
color="primary"
size="sm"
onClick={() =>
generateTitle(
getValues(`rows.${index}.mainKeyword`),
index
)
}
>
Process
</Button>
</div>
<Controller
control={control}
name={`rows.${index}.title`}
render={({ field: { onChange, value } }) => (
<Input
type="text"
id="title"
placeholder=""
label=""
value={value}
onChange={onChange}
labelPlacement="outside"
className="w-full"
isDisabled={value === "process..."}
variant="bordered"
/>
)}
/>
</div>
<div className="w-full flex flex-row items-end gap-1">
<div className="full grow">
<div>
<div className="text-[10px] md:text-base flex items-center gap-1">
<p className="text-sm">SEO</p>
<Button
color="primary"
size="sm"
onClick={() =>
generateKeywords(
getValues(`rows.${index}.mainKeyword`),
index
)
}
>
Process
</Button>
</div>
<Controller
control={control}
name={`rows.${index}.additionalKeyword`}
render={({ field: { onChange, value } }) => (
<Input
type="text"
id="title"
placeholder=""
label=""
value={value}
onChange={onChange}
labelPlacement="outside"
className="w-full"
isDisabled={value === "process..."}
variant="bordered"
/>
)}
/>
</div>
</div>
<div className="w-[42px] flex-row gap-1 hidden md:flex">
{index === fields.length - 1 && (
<button
className="w-[20px] mb-2 "
type="button"
onClick={() =>
append({
mainKeyword: "",
title: "",
additionalKeyword: "",
})
}
>
+
</button>
)}
{index !== 0 && (
<a
onClick={() => remove(index)}
className="cursor-pointer mb-2 w-[20px] h-[20px]text-center rounded-full flex justify-center items-center"
>
-
</a>
)}
</div>
</div>
</div>
))}
</div>
<Button
type="button"
onClick={() => validateFields()}
// disabled={validateFields()}
color="primary"
>
Generate
</Button>
</form>
</fieldset>
);
}