web-humas-fe/components/form/article/speech-to-text-form.tsx

288 lines
9.5 KiB
TypeScript
Raw Normal View History

2024-11-15 10:53:04 +00:00
"use client";
import { AddIcon, TimesIcon } from "@/components/icons";
2025-02-13 08:25:39 +00:00
import { Input, Textarea } from "@heroui/input";
2024-11-15 10:53:04 +00:00
import {
Button,
Checkbox,
CircularProgress,
Divider,
Select,
SelectItem,
SelectSection,
2025-02-13 08:25:39 +00:00
} from "@heroui/react";
2024-11-15 10:53:04 +00:00
import Link from "next/link";
import { useSearchParams } from "next/navigation";
import { Suspense, useState } from "react";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { useForm } from "react-hook-form";
import Cookies from "js-cookie";
import { close, error, loading } from "@/config/swal";
import {
generateSpeechToText,
getTranscriptById,
2025-05-04 07:14:12 +00:00
} from "@/services/generate-article";
2024-11-15 10:53:04 +00:00
import { delay } from "@/utils/global";
// import OperatorArticleTable from "@/components/table/assistant/create-content/operator/operator-article-table";
// import OperatorTranscriptTable from "@/components/table/assistant/create-content/operator/operator-transcript-table";
// import OperatorArticleDraftTable from "@/components/table/assistant/create-content/operator/operator-draft-article-table";
// import OperatorDetailDraftArticle from "@/components/main/multipool-assistant/operator-detail-draft-article";
const createContentOnlineMediaSchema = z.object({
title: z.string().min(1, { message: "Required" }),
prompt: z.string().min(1, { message: "Required" }),
});
export default function SpeechToText(props: {
generateArticleFromTranscript: (transcriptId: number) => void;
}) {
const searchParam = useSearchParams();
const transcriptQuery = searchParam.get("transcriptId");
let [files, setFiles] = useState<File[]>([]);
const [handleFileStatus, setHandleFileStatus] = useState("");
const [detailTranscript, setDetailTranscript] = useState<string>("");
const [generatedTranscriptId, setGeneratedTranscriptId] = useState<number>();
const [advanceConfig, setAdvanceConfig] = useState(false);
const [language, setLanguage] = useState("id");
const formOptions = { resolver: zodResolver(createContentOnlineMediaSchema) };
type CreateContentSchema = z.infer<typeof createContentOnlineMediaSchema>;
const {
register,
setValue,
handleSubmit,
formState: { errors },
} = useForm<CreateContentSchema>(formOptions);
const handleFileChange = (event: any) => {
if (files.length < 1) {
const newFiles: FileList | null = event.target.files;
if (newFiles) {
const allowedExtensions = [".mp3"];
let temp: File[] = [...files]; // Salin file-file yang sudah ada
for (let i = 0; i < newFiles.length; i++) {
const file = newFiles[i];
const fileExtension = file.name.split(".").pop()?.toLowerCase();
if (
fileExtension &&
allowedExtensions.includes(`.${fileExtension}`)
) {
temp.push(file);
} else {
alert("The supported File formats are .mp3");
}
}
setFiles(temp);
}
}
};
const removeFile = (name: string) => {
const arrayFile: File[] = [];
for (const element of files) {
if (element.name !== name) {
arrayFile.push(element);
}
}
setFiles(arrayFile);
};
const handleGenerateTranscript = (data: any) => {
if (files.length > 0) {
doGenerate(data);
setHandleFileStatus("");
} else {
setHandleFileStatus("Required");
}
};
const doGenerate = async (data: any) => {
loading();
let formData = new FormData();
console.log("data.", data.prompt);
formData.append("filename", files[0]);
formData.append("title", data.title);
formData.append("prompt", data.prompt);
formData.append("language", language);
formData.append("clientId", "humasClientIdTest");
formData.append("createdBy", "123");
const res = await generateSpeechToText(formData);
setGeneratedTranscriptId(res?.data?.data?.id);
close();
initFetch(res?.data?.data?.id);
return false;
};
const checkTranscriptStatus = async (id: number) => {
delay(7000).then(() => {
initFetch(id);
});
};
const initFetch = async (id?: number) => {
if (id) {
const res = await getTranscriptById(id);
console.log("stat", res?.data?.data);
if (res?.data?.data?.status === 0) {
setDetailTranscript("");
checkTranscriptStatus(id);
} else {
setDetailTranscript(res?.data?.data.content);
}
}
};
return (
<Suspense>
<div className="w-full">
<div className="w-full text-sm">
<form method="POST" onSubmit={handleSubmit(handleGenerateTranscript)}>
<div className="flex flex-col">
<p className="font-bold">Upload an Audio File</p>
<p>The supported File formats are .mp3</p>
<p>Max: 25mb</p>
{files.length < 1 && (
<label htmlFor="dropzone-file">
<div className="mt-3 w-[150px] flex justify-center items-center bg-success p-2 text-white rounded-lg gap-2 cursor-pointer">
<input
id="dropzone-file"
type="file"
multiple
accept=".mp3"
className="hidden"
onChange={handleFileChange}
/>
Add file <AddIcon />
</div>
</label>
)}
{handleFileStatus !== "" && files.length < 1 && (
<p className="text-tiny text-danger">{handleFileStatus}</p>
)}
{files.length > 0 &&
files.map((list) => (
<div
key={list.name}
className="w-[350px] mt-3 flex flex-row items-center justify-between border-1 rounded-lg p-2"
>
{list.name}
<a
className="cursor-pointer"
onClick={() => removeFile(list.name)}
>
<TimesIcon />
</a>
</div>
))}
<div className="w-full md:w-1/2 pt-2 pb-4">
<p className="text-black text-sm mb-1">Language</p>
<Select
label=""
labelPlacement="outside"
selectedKeys={[language]}
className="w-full"
variant="bordered"
onChange={(e) =>
e.target.value == "" ? "" : setLanguage(e.target.value)
}
renderValue={(items) => {
return items.map((item) => (
<span
key={item.props?.value}
className="text-black text-xs"
>
{item.textValue}
</span>
));
}}
>
<SelectSection>
<SelectItem key="id">Indonesia</SelectItem>
<SelectItem key="en">English</SelectItem>
</SelectSection>
</Select>
</div>
<div className="w-full md:w-1/2 mt-3">
<p className="text-black text-sm mb-1">
Title<span className="text-red-500">*</span>
</p>
<Input
type="text"
id="keyword"
label=""
className="mb-3"
placeholder="Enter title"
labelPlacement="outside"
variant="bordered"
{...register("title")}
/>
{errors.title?.message && (
<p className="text-tiny text-danger">
{errors.title?.message}
</p>
)}
</div>
<div className="w-full md:w-1/2 mt-3">
<p className="text-black text-sm mb-1">
Keyword<span className="text-red-500">*</span>
</p>
<Textarea
label=""
id="seo"
variant="bordered"
disableAnimation
disableAutosize
minRows={10}
placeholder="Write your text"
{...register("prompt")}
/>
{errors.prompt?.message && (
<p className="text-tiny text-danger">
{errors.prompt?.message}
</p>
)}
</div>
</div>
<div className="flex justify-between mt-3">
<Button className="w-[200px]" color="primary" type="submit">
Generate
</Button>
</div>
{generatedTranscriptId && (
<div className="flex flex-col gap-3 my-3">
<p className="text-xl font-semibold">Result</p>
{detailTranscript === "" ? (
<CircularProgress />
) : (
<p>{detailTranscript}</p>
)}
</div>
)}
{detailTranscript !== "" && (
<Button
className="w-[200px]"
color="primary"
onPress={() => {
generatedTranscriptId &&
props.generateArticleFromTranscript(generatedTranscriptId);
}}
>
Generate Article
</Button>
)}
</form>
</div>
</div>
</Suspense>
);
}