This commit is contained in:
hanif salafi 2025-10-15 15:27:01 +07:00
commit 4f28de0a12
7 changed files with 724 additions and 261 deletions

View File

@ -145,10 +145,10 @@ export default function FormAudio() {
const [publishedFor, setPublishedFor] = useState<string[]>([]); const [publishedFor, setPublishedFor] = useState<string[]>([]);
const [fileError, setFileError] = useState<string | null>(null); const [fileError, setFileError] = useState<string | null>(null);
type FileWithPreview = File & { preview: string }; type FileWithPreview = File & { preview: string };
const [isLoadingTranslate, setIsLoadingTranslate] = useState(false); const [isLoadingTranslate, setIsLoadingTranslate] = useState(false);
const [translatedContent, setTranslatedContent] = React.useState(""); const [translatedContent, setTranslatedContent] = React.useState("");
const [selectedLang, setSelectedLang] = React.useState<"id" | "en">("id"); const [selectedLang, setSelectedLang] = React.useState<"id" | "en">("id");
const [translatedTitle, setTranslatedTitle] = useState("");
const options: Option[] = [ const options: Option[] = [
{ id: "all", label: "SEMUA" }, { id: "all", label: "SEMUA" },
@ -618,29 +618,49 @@ export default function FormAudio() {
const save = async (data: AudioSchema) => { const save = async (data: AudioSchema) => {
loading(); loading();
const finalTags = tags.join(", "); const finalTags = tags.join(", ");
const finalTitle = isSwitchOn ? title : data.title; // const finalTitle = isSwitchOn ? title : data.title;
// pilih description dasar // // pilih description dasar
let finalDescription = isSwitchOn // let finalDescription = isSwitchOn
? data.description // ? data.description
: selectedFileType === "rewrite" // : selectedFileType === "rewrite"
? data.rewriteDescription // ? data.rewriteDescription
: data.descriptionOri; // : data.descriptionOri;
const finalTitle =
translatedTitle && translatedTitle.trim() !== ""
? translatedTitle
: isSwitchOn
? title
: data.title;
const finalDescription =
translatedContent && translatedContent.trim() !== ""
? translatedContent
: isSwitchOn
? data.description
: selectedFileType === "rewrite"
? data.rewriteDescription
: data.descriptionOri;
if (!finalDescription?.trim()) {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
return;
}
// 👉 tempelkan hasil translate ke field form & pakai sebagai description // 👉 tempelkan hasil translate ke field form & pakai sebagai description
if (translatedContent) { if (translatedContent) {
data.descriptionOri = translatedContent; // update ke form data.descriptionOri = translatedContent;
finalDescription = translatedContent; // pakai untuk request
console.log( console.log(
"🌍 Translate dimasukkan ke descriptionOri:", "🌍 Translate dimasukkan ke descriptionOri:",
translatedContent translatedContent
); );
} }
if (!finalDescription?.trim()) { // if (!finalDescription?.trim()) {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error"); // MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
return; // return;
} // }
let requestData: { let requestData: {
title: string; title: string;
@ -963,7 +983,44 @@ export default function FormAudio() {
<div className="gap-5 mb-5"> <div className="gap-5 mb-5">
{/* Input Title */} {/* Input Title */}
<div className="space-y-2 py-3"> <div className="space-y-2 py-3">
<Label>{t("title", { defaultValue: "Title" })}</Label> <div className="flex justify-between items-center">
<Label>{t("title", { defaultValue: "Title" })}</Label>
{roleId === "14" && (
<button
type="button"
onClick={async () => {
try {
loading();
setIsLoadingTranslate(true);
const res = await translateText({
text: getValues("title"),
sourceLang: "ID",
targetLang: "EN",
});
if (!res.error) {
const resultText =
res?.data?.data?.translations?.[0]?.text || "";
setTranslatedTitle(resultText);
}
} catch (err) {
close();
console.error("Translate title gagal:", err);
} finally {
close();
setIsLoadingTranslate(false);
}
}}
className="px-3 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600"
>
{isLoadingTranslate
? "Translating..."
: "Translate Title"}
</button>
)}
</div>
{/* Title Bahasa Indonesia */}
<Controller <Controller
control={control} control={control}
name="title" name="title"
@ -973,10 +1030,27 @@ export default function FormAudio() {
type="text" type="text"
value={field.value} value={field.value}
onChange={field.onChange} onChange={field.onChange}
placeholder="Enter Title" placeholder="Masukkan Judul Bahasa Indonesia"
/> />
)} )}
/> />
{/* Versi English (muncul setelah translate) */}
{translatedTitle && (
<div className="mt-3">
<Label className="text-sm font-semibold">
English Title
</Label>
<Input
size="md"
type="text"
value={translatedTitle}
onChange={(e) => setTranslatedTitle(e.target.value)}
placeholder="Translated Title"
/>
</div>
)}
{errors.title?.message && ( {errors.title?.message && (
<p className="text-red-400 text-sm">{errors.title.message}</p> <p className="text-red-400 text-sm">{errors.title.message}</p>
)} )}

View File

@ -24,10 +24,6 @@ import {
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { register } from "module";
import { Switch } from "@/components/ui/switch";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { import {
createMedia, createMedia,
@ -197,11 +193,11 @@ export default function FormAudioUpdate() {
useState(false); useState(false);
const [mainCheckboxChangeType, setMainCheckboxChangeType] = const [mainCheckboxChangeType, setMainCheckboxChangeType] =
useState<string>(""); useState<string>("");
const [isLoadingTranslate, setIsLoadingTranslate] = useState(false); const [isLoadingTranslate, setIsLoadingTranslate] = useState(false);
const [translatedContent, setTranslatedContent] = React.useState(""); const [translatedContent, setTranslatedContent] = React.useState("");
const [selectedLang, setSelectedLang] = React.useState<"id" | "en">("id"); const [selectedLang, setSelectedLang] = React.useState<"id" | "en">("id");
const roleId = getCookiesDecrypt("urie"); const roleId = getCookiesDecrypt("urie");
const [translatedTitle, setTranslatedTitle] = useState("");
const options: Option[] = [ const options: Option[] = [
{ id: "all", name: "SEMUA" }, { id: "all", name: "SEMUA" },
@ -1224,16 +1220,19 @@ export default function FormAudioUpdate() {
const finalTags = tags.join(", "); const finalTags = tags.join(", ");
// ✅ tentukan isi description sesuai pilihan bahasa // ✅ tentukan isi description sesuai pilihan bahasa
const descFinal = // const descFinal =
selectedLang === "en" && translatedContent // selectedLang === "en" && translatedContent
? translatedContent // ? translatedContent
: data.description; // : data.description;
const descFinal = translatedContent || data.description;
const finalTitle = translatedTitle || data.title;
const requestData = { const requestData = {
...data, ...data,
id: detail?.id, id: detail?.id,
title: data.title, title: finalTitle,
description: htmlToString(descFinal), description: htmlToString(descFinal),
htmlDescription: descFinal, htmlDescription: descFinal,
fileTypeId, fileTypeId,
categoryId: selectedTarget, categoryId: selectedTarget,
@ -1534,7 +1533,43 @@ export default function FormAudioUpdate() {
<div className="gap-5 mb-5"> <div className="gap-5 mb-5">
{/* Input Title */} {/* Input Title */}
<div className="space-y-2 py-3"> <div className="space-y-2 py-3">
<Label>{t("title", { defaultValue: "Title" })}</Label> <div className="flex justify-between items-center">
<Label>{t("title", { defaultValue: "Title" })}</Label>
{roleId === "14" && (
<button
type="button"
onClick={async () => {
try {
loading();
setIsLoadingTranslate(true);
const res = await translateText({
text: getValues("title"),
sourceLang: "ID",
targetLang: "EN",
});
if (!res.error) {
const resultText =
res?.data?.data?.translations?.[0]?.text || "";
setTranslatedTitle(resultText);
}
} catch (err) {
close();
console.error("Translate title gagal:", err);
} finally {
close();
setIsLoadingTranslate(false);
}
}}
className="px-3 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600"
>
{isLoadingTranslate
? "Translating..."
: "Translate Title"}
</button>
)}
</div>
<Controller <Controller
control={control} control={control}
name="title" name="title"
@ -1542,18 +1577,36 @@ export default function FormAudioUpdate() {
<Input <Input
size="md" size="md"
type="text" type="text"
value={field?.value} value={field.value}
onChange={field.onChange} onChange={field.onChange}
placeholder="Enter Title" placeholder="Enter Title"
/> />
)} )}
/> />
{/* English translated title appears below when available */}
{translatedTitle && (
<div className="mt-3">
<Label className="text-sm font-semibold">
English Version
</Label>
<Input
size="md"
type="text"
value={translatedTitle}
onChange={(e) => setTranslatedTitle(e.target.value)}
placeholder="Translated Title"
/>
</div>
)}
{errors.title?.message && ( {errors.title?.message && (
<p className="text-red-400 text-sm"> <p className="text-red-400 text-sm">
{errors.title.message} {errors.title.message}
</p> </p>
)} )}
</div> </div>
<div className="flex items-center"> <div className="flex items-center">
<div className="py-3 w-full space-y-2"> <div className="py-3 w-full space-y-2">
<Label>{t("category", { defaultValue: "Category" })}</Label> <Label>{t("category", { defaultValue: "Category" })}</Label>
@ -1617,7 +1670,7 @@ export default function FormAudioUpdate() {
if (!res.error) { if (!res.error) {
const resultText = const resultText =
res?.data?.data?.translations?.[0]?.text || ""; res?.data?.data?.translations?.[0]?.text || "";
// Overwrite data.description but still show both
setTranslatedContent(resultText); setTranslatedContent(resultText);
} }
} catch (err) { } catch (err) {
@ -1632,55 +1685,34 @@ export default function FormAudioUpdate() {
> >
{isLoadingTranslate {isLoadingTranslate
? "Translating..." ? "Translating..."
: "Translate to English"} : "Translate Description"}
</button> </button>
)} )}
</div> </div>
{/* Pilihan bahasa untuk posting */}
{roleId === "14" && (
<div className="flex items-center gap-4 mb-2">
<label className="flex items-center gap-2">
<input
type="radio"
value="id"
checked={selectedLang === "id"}
onChange={() => setSelectedLang("id")}
/>
<span>Gunakan Bahasa Indonesia</span>
</label>
</div>
)}
{/* Editor Bahasa Indonesia */} {/* Editor Bahasa Indonesia */}
<Controller <div className="mt-3">
control={control} <Label className="text-sm font-semibold">
name="description" Indonesian Version
render={({ field }) => ( </Label>
<CustomEditor <Controller
onChange={field.onChange} control={control}
initialData={field.value} name="description"
/> render={({ field }) => (
)} <CustomEditor
/> onChange={field.onChange}
initialData={field.value}
/>
)}
/>
</div>
{/* Editor Bahasa Inggris */} {/* English translated version muncul setelah translate */}
{translatedContent && ( {translatedContent && (
<div className="mt-4"> <div className="mt-5">
<div className="flex flex-col"> <Label className="text-sm font-semibold">
<Label className="text-[15px]">English Version</Label> English Version
<label className="flex items-center gap-2"> </Label>
<input
type="radio"
value="en"
checked={selectedLang === "en"}
onChange={() => setSelectedLang("en")}
disabled={!translatedContent}
/>
<span>Gunakan Bahasa Inggris</span>
</label>
</div>
<CustomEditor <CustomEditor
onChange={(val: any) => setTranslatedContent(val)} onChange={(val: any) => setTranslatedContent(val)}
initialData={translatedContent} initialData={translatedContent}

View File

@ -1353,7 +1353,6 @@ export default function FormImage() {
value={selectedFileType} value={selectedFileType}
className="grid-cols-1" className="grid-cols-1"
> >
{/* HAPUS radio, ganti jadi preview side-by-side */}
<div className="py-3 space-y-2"> <div className="py-3 space-y-2">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<Label className="text-[15px]"> <Label className="text-[15px]">

View File

@ -26,8 +26,6 @@ import {
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { register } from "module";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { import {
@ -140,13 +138,12 @@ export default function FormTeks() {
let uploadPersen = 0; let uploadPersen = 0;
const [isStartUpload, setIsStartUpload] = useState(false); const [isStartUpload, setIsStartUpload] = useState(false);
const [counterProgress, setCounterProgress] = useState(0); const [counterProgress, setCounterProgress] = useState(0);
const [files, setFiles] = useState<FileWithPreview[]>([]); const [files, setFiles] = useState<FileWithPreview[]>([]);
const [publishedFor, setPublishedFor] = useState<string[]>([]); const [publishedFor, setPublishedFor] = useState<string[]>([]);
const [isLoadingTranslate, setIsLoadingTranslate] = useState(false); const [isLoadingTranslate, setIsLoadingTranslate] = useState(false);
const [translatedContent, setTranslatedContent] = React.useState(""); const [translatedContent, setTranslatedContent] = React.useState("");
const [selectedLang, setSelectedLang] = React.useState<"id" | "en">("id"); const [selectedLang, setSelectedLang] = React.useState<"id" | "en">("id");
const [translatedTitle, setTranslatedTitle] = useState("");
const options: Option[] = [ const options: Option[] = [
{ id: "all", label: "SEMUA" }, { id: "all", label: "SEMUA" },
@ -647,13 +644,34 @@ export default function FormTeks() {
} }
const finalTags = tags.join(", "); const finalTags = tags.join(", ");
const finalTitle = isSwitchOn ? title : data.title; // const finalTitle = isSwitchOn ? title : data.title;
// const finalDescription = articleBody || data.description; // const finalDescription = articleBody || data.description;
const finalDescription = isSwitchOn // const finalDescription = isSwitchOn
? data.description // ? data.description
: selectedFileType === "rewrite" // : selectedFileType === "rewrite"
? data.rewriteDescription // ? data.rewriteDescription
: data.descriptionOri; // : data.descriptionOri;
// if (!finalDescription?.trim()) {
// MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
// return;
// }
const finalTitle =
translatedTitle && translatedTitle.trim() !== ""
? translatedTitle
: isSwitchOn
? title
: data.title;
const finalDescription =
translatedContent && translatedContent.trim() !== ""
? translatedContent
: isSwitchOn
? data.description
: selectedFileType === "rewrite"
? data.rewriteDescription
: data.descriptionOri;
if (!finalDescription?.trim()) { if (!finalDescription?.trim()) {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error"); MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
@ -974,6 +992,80 @@ export default function FormTeks() {
<div className="gap-5 mb-5"> <div className="gap-5 mb-5">
{/* Input Title */} {/* Input Title */}
<div className="space-y-2 py-3"> <div className="space-y-2 py-3">
<div className="flex justify-between items-center">
<Label>{t("title", { defaultValue: "Title" })}</Label>
{roleId === "14" && (
<button
type="button"
onClick={async () => {
try {
loading();
setIsLoadingTranslate(true);
const res = await translateText({
text: getValues("title"),
sourceLang: "ID",
targetLang: "EN",
});
if (!res.error) {
const resultText =
res?.data?.data?.translations?.[0]?.text || "";
setTranslatedTitle(resultText);
}
} catch (err) {
close();
console.error("Translate title gagal:", err);
} finally {
close();
setIsLoadingTranslate(false);
}
}}
className="px-3 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600"
>
{isLoadingTranslate
? "Translating..."
: "Translate Title"}
</button>
)}
</div>
{/* Title Bahasa Indonesia */}
<Controller
control={control}
name="title"
render={({ field }) => (
<Input
size="md"
type="text"
value={field.value}
onChange={field.onChange}
placeholder="Masukkan Judul Bahasa Indonesia"
/>
)}
/>
{/* Versi English (muncul setelah translate) */}
{translatedTitle && (
<div className="mt-3">
<Label className="text-sm font-semibold">
English Title
</Label>
<Input
size="md"
type="text"
value={translatedTitle}
onChange={(e) => setTranslatedTitle(e.target.value)}
placeholder="Translated Title"
/>
</div>
)}
{errors.title?.message && (
<p className="text-red-400 text-sm">{errors.title.message}</p>
)}
</div>
{/* <div className="space-y-2 py-3">
<Label>{t("title", { defaultValue: "Title" })}</Label> <Label>{t("title", { defaultValue: "Title" })}</Label>
<Controller <Controller
control={control} control={control}
@ -991,7 +1083,7 @@ export default function FormTeks() {
{errors.title?.message && ( {errors.title?.message && (
<p className="text-red-400 text-sm">{errors.title.message}</p> <p className="text-red-400 text-sm">{errors.title.message}</p>
)} )}
</div> </div> */}
<div className="flex items-center"> <div className="flex items-center">
<div className="py-3 w-full space-y-2"> <div className="py-3 w-full space-y-2">
@ -1317,12 +1409,10 @@ export default function FormTeks() {
sourceLang: "ID", sourceLang: "ID",
targetLang: "EN", targetLang: "EN",
}); });
if (!res.error) { if (!res.error) {
const resultText = const resultText =
res?.data?.data?.translations?.[0]?.text || res?.data?.data?.translations?.[0]?.text ||
""; "";
setTranslatedContent(resultText); setTranslatedContent(resultText);
} }
} catch (err) { } catch (err) {
@ -1337,57 +1427,34 @@ export default function FormTeks() {
> >
{isLoadingTranslate {isLoadingTranslate
? "Translating..." ? "Translating..."
: "Translate to English"} : "Translate Description"}
</button> </button>
)} )}
</div> </div>
{/* Pilihan bahasa untuk posting */} {/* Deskripsi Bahasa Indonesia */}
{roleId === "14" && ( <div className="mt-2">
<div className="flex items-center gap-4 mb-2"> <Label className="text-sm font-semibold">
<label className="flex items-center gap-2"> Indonesian Version
<input </Label>
type="radio" <Controller
value="id" control={control}
checked={selectedLang === "id"} name="descriptionOri"
onChange={() => setSelectedLang("id")} render={({ field: { onChange, value } }) => (
<CustomEditor
onChange={onChange}
initialData={value}
/> />
<span>Gunakan Bahasa Indonesia</span> )}
</label> />
</div> </div>
)}
{/* Editor Bahasa Indonesia */} {/* Versi Inggris muncul setelah translate */}
<Controller
control={control}
name="descriptionOri"
render={({ field: { onChange, value } }) => (
<CustomEditor
onChange={onChange}
initialData={value}
/>
)}
/>
{/* Editor Bahasa Inggris */}
{translatedContent && ( {translatedContent && (
<div className="mt-4"> <div className="mt-5">
<div className="flex flex-col"> <Label className="text-sm font-semibold">
<Label className="text-[15px]"> English Version
English Version </Label>
</Label>{" "}
<label className="flex items-center gap-2">
<input
type="radio"
value="en"
checked={selectedLang === "en"}
onChange={() => setSelectedLang("en")}
disabled={!translatedContent} // kalau belum translate, disable
/>
<span>Gunakan Bahasa Inggris</span>
</label>
</div>
<CustomEditor <CustomEditor
onChange={(val: any) => setTranslatedContent(val)} onChange={(val: any) => setTranslatedContent(val)}
initialData={translatedContent} initialData={translatedContent}
@ -1401,6 +1468,7 @@ export default function FormTeks() {
</p> </p>
)} )}
</div> </div>
{/* <div className="py-3 space-y-2"> {/* <div className="py-3 space-y-2">
<Label> <Label>
{t("description", { defaultValue: "Description" })} {t("description", { defaultValue: "Description" })}

View File

@ -204,6 +204,7 @@ export default function FormTeksUpdate() {
const [translatedContent, setTranslatedContent] = React.useState(""); const [translatedContent, setTranslatedContent] = React.useState("");
const [selectedLang, setSelectedLang] = React.useState<"id" | "en">("id"); const [selectedLang, setSelectedLang] = React.useState<"id" | "en">("id");
const roleId = getCookiesDecrypt("urie"); const roleId = getCookiesDecrypt("urie");
const [translatedTitle, setTranslatedTitle] = useState("");
let fileTypeId = "3"; let fileTypeId = "3";
const isDetailOfRegionShowed = false; const isDetailOfRegionShowed = false;
@ -1251,18 +1252,18 @@ export default function FormTeksUpdate() {
loading(); loading();
const finalTags = tags.join(", "); const finalTags = tags.join(", ");
// ✅ tentukan isi description sesuai pilihan bahasa // const descFinal =
const descFinal = // selectedLang === "en" && translatedContent
selectedLang === "en" && translatedContent // ? translatedContent
? translatedContent // : data.description;
: data.description; const descFinal = translatedContent || data.description;
const finalTitle = translatedTitle || data.title;
const requestData = { const requestData = {
...data, ...data,
id: detail?.id, id: detail?.id,
title: data.title, title: finalTitle,
description: htmlToString(descFinal), // plain text description: htmlToString(descFinal),
htmlDescription: descFinal, // HTML (dipakai di editor/preview) htmlDescription: descFinal,
fileTypeId, fileTypeId,
categoryId: selectedTarget, categoryId: selectedTarget,
subCategoryId: selectedTarget, subCategoryId: selectedTarget,
@ -1532,6 +1533,81 @@ export default function FormTeksUpdate() {
<div className="gap-5 mb-5"> <div className="gap-5 mb-5">
{/* Input Title */} {/* Input Title */}
<div className="space-y-2 py-3"> <div className="space-y-2 py-3">
<div className="flex justify-between items-center">
<Label>{t("title", { defaultValue: "Title" })}</Label>
{roleId === "14" && (
<button
type="button"
onClick={async () => {
try {
loading();
setIsLoadingTranslate(true);
const res = await translateText({
text: getValues("title"),
sourceLang: "ID",
targetLang: "EN",
});
if (!res.error) {
const resultText =
res?.data?.data?.translations?.[0]?.text || "";
setTranslatedTitle(resultText);
}
} catch (err) {
close();
console.error("Translate title gagal:", err);
} finally {
close();
setIsLoadingTranslate(false);
}
}}
className="px-3 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600"
>
{isLoadingTranslate
? "Translating..."
: "Translate Title"}
</button>
)}
</div>
<Controller
control={control}
name="title"
render={({ field }) => (
<Input
size="md"
type="text"
value={field.value}
onChange={field.onChange}
placeholder="Enter Title"
/>
)}
/>
{/* English translated title appears below when available */}
{translatedTitle && (
<div className="mt-3">
<Label className="text-sm font-semibold">
English Version
</Label>
<Input
size="md"
type="text"
value={translatedTitle}
onChange={(e) => setTranslatedTitle(e.target.value)}
placeholder="Translated Title"
/>
</div>
)}
{errors.title?.message && (
<p className="text-red-400 text-sm">
{errors.title.message}
</p>
)}
</div>
{/* <div className="space-y-2 py-3">
<Label>{t("title", { defaultValue: "Title" })}</Label> <Label>{t("title", { defaultValue: "Title" })}</Label>
<Controller <Controller
control={control} control={control}
@ -1551,7 +1627,7 @@ export default function FormTeksUpdate() {
{errors.title.message} {errors.title.message}
</p> </p>
)} )}
</div> </div> */}
<div className="flex items-center"> <div className="flex items-center">
<div className="py-3 w-full space-y-2"> <div className="py-3 w-full space-y-2">
<Label>{t("category", { defaultValue: "Category" })}</Label> <Label>{t("category", { defaultValue: "Category" })}</Label>
@ -1592,7 +1668,6 @@ export default function FormTeksUpdate() {
</Select> </Select>
</div> </div>
</div> </div>
<div className="py-3 space-y-2"> <div className="py-3 space-y-2">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<Label> <Label>
@ -1615,7 +1690,7 @@ export default function FormTeksUpdate() {
if (!res.error) { if (!res.error) {
const resultText = const resultText =
res?.data?.data?.translations?.[0]?.text || ""; res?.data?.data?.translations?.[0]?.text || "";
// Overwrite data.description but still show both
setTranslatedContent(resultText); setTranslatedContent(resultText);
} }
} catch (err) { } catch (err) {
@ -1630,55 +1705,34 @@ export default function FormTeksUpdate() {
> >
{isLoadingTranslate {isLoadingTranslate
? "Translating..." ? "Translating..."
: "Translate to English"} : "Translate Description"}
</button> </button>
)} )}
</div> </div>
{/* Pilihan bahasa untuk posting */}
{roleId === "14" && (
<div className="flex items-center gap-4 mb-2">
<label className="flex items-center gap-2">
<input
type="radio"
value="id"
checked={selectedLang === "id"}
onChange={() => setSelectedLang("id")}
/>
<span>Gunakan Bahasa Indonesia</span>
</label>
</div>
)}
{/* Editor Bahasa Indonesia */} {/* Editor Bahasa Indonesia */}
<Controller <div className="mt-3">
control={control} <Label className="text-sm font-semibold">
name="description" Indonesian Version
render={({ field }) => ( </Label>
<CustomEditor <Controller
onChange={field.onChange} control={control}
initialData={field.value} name="description"
/> render={({ field }) => (
)} <CustomEditor
/> onChange={field.onChange}
initialData={field.value}
/>
)}
/>
</div>
{/* Editor Bahasa Inggris */} {/* English translated version muncul setelah translate */}
{translatedContent && ( {translatedContent && (
<div className="mt-4"> <div className="mt-5">
<div className="flex flex-col"> <Label className="text-sm font-semibold">
<Label className="text-[15px]">English Version</Label> English Version
<label className="flex items-center gap-2"> </Label>
<input
type="radio"
value="en"
checked={selectedLang === "en"}
onChange={() => setSelectedLang("en")}
disabled={!translatedContent}
/>
<span>Gunakan Bahasa Inggris</span>
</label>
</div>
<CustomEditor <CustomEditor
onChange={(val: any) => setTranslatedContent(val)} onChange={(val: any) => setTranslatedContent(val)}
initialData={translatedContent} initialData={translatedContent}

View File

@ -26,8 +26,6 @@ import {
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { register } from "module";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { import {
@ -36,7 +34,6 @@ import {
listEnableCategory, listEnableCategory,
uploadThumbnail, uploadThumbnail,
} from "@/service/content/content"; } from "@/service/content/content";
import { uploadThumbnailBlog } from "@/service/blog/blog";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { import {
generateDataArticle, generateDataArticle,
@ -52,10 +49,8 @@ import { Icon } from "@iconify/react";
import { CloudUpload } from "lucide-react"; import { CloudUpload } from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import { close, error, loading } from "@/config/swal"; import { close, error, loading } from "@/config/swal";
import { Item } from "@radix-ui/react-dropdown-menu";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import { getCsrfToken } from "@/service/auth"; import { getCsrfToken } from "@/service/auth";
import { Link } from "@/i18n/routing";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { htmlToString } from "@/utils/globals"; import { htmlToString } from "@/utils/globals";
@ -146,10 +141,12 @@ export default function FormVideo() {
const [counterProgress, setCounterProgress] = useState(0); const [counterProgress, setCounterProgress] = useState(0);
const [publishedFor, setPublishedFor] = useState<string[]>([]); const [publishedFor, setPublishedFor] = useState<string[]>([]);
const [files, setFiles] = useState<FileWithPreview[]>([]); const [files, setFiles] = useState<FileWithPreview[]>([]);
const [isLoadingTranslate, setIsLoadingTranslate] = useState(false); const [isLoadingTranslate, setIsLoadingTranslate] = useState(false);
const [translatedContent, setTranslatedContent] = React.useState(""); const [translatedContent, setTranslatedContent] = React.useState("");
const [selectedLang, setSelectedLang] = React.useState<"id" | "en">("id"); const [selectedLang, setSelectedLang] = React.useState<"id" | "en">("id");
// 🔹 State untuk translate Title
const [translatedTitle, setTranslatedTitle] = useState("");
const [isLoadingTranslateTitle, setIsLoadingTranslateTitle] = useState(false);
const options: Option[] = [ const options: Option[] = [
{ id: "all", label: "SEMUA" }, { id: "all", label: "SEMUA" },
@ -536,28 +533,55 @@ export default function FormVideo() {
} }
loading(); loading();
const finalTags = data.tags.join(", "); const finalTags = data.tags.join(", ");
const finalTitle = isSwitchOn ? title : data.title; // const finalTitle = isSwitchOn ? title : data.title;
// const finalDescription = articleBody || data.description; // const finalDescription = articleBody || data.description;
const finalDescription = isSwitchOn // const finalDescription = isSwitchOn
? data.description // ? data.description
: selectedFileType === "rewrite" // : selectedFileType === "rewrite"
? data.rewriteDescription // ? data.rewriteDescription
: data.descriptionOri; // : data.descriptionOri;
// if (!finalDescription?.trim()) {
// MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
// return;
// }
// // 👉 tempelkan hasil translate ke form agar ikut terkirim
// if (translatedContent) {
// data.descriptionOri = translatedContent;
// console.log(
// "🌍 Translate dimasukkan ke descriptionOri:",
// translatedContent
// );
// }
// Tentukan title final (gunakan versi translate kalau ada)
const finalTitle =
translatedTitle && translatedTitle.trim() !== ""
? translatedTitle
: isSwitchOn
? title
: data.title;
// Tentukan deskripsi final:
// Jika ada hasil translate, kirim itu ke backend
const finalDescription =
translatedContent && translatedContent.trim() !== ""
? translatedContent
: isSwitchOn
? data.description
: selectedFileType === "rewrite"
? data.rewriteDescription
: data.descriptionOri;
if (!finalDescription?.trim()) { if (!finalDescription?.trim()) {
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error"); MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
return; return;
} }
// 👉 tempelkan hasil translate ke form agar ikut terkirim console.log("📝 Deskripsi final yang dikirim:", finalDescription);
if (translatedContent) {
data.descriptionOri = translatedContent;
console.log(
"🌍 Translate dimasukkan ke descriptionOri:",
translatedContent
);
}
let requestData: { let requestData: {
title: string; title: string;
description: string; description: string;
@ -883,7 +907,81 @@ export default function FormVideo() {
{t("form-video", { defaultValue: "Form Video" })} {t("form-video", { defaultValue: "Form Video" })}
</p> </p>
<div className="gap-5 mb-5"> <div className="gap-5 mb-5">
{/* Input Title dengan tombol translate */}
<div className="space-y-2 py-3"> <div className="space-y-2 py-3">
<div className="flex justify-between items-center">
<Label>{t("title", { defaultValue: "Title" })}</Label>
{roleId === "14" && (
<button
type="button"
onClick={async () => {
try {
loading();
setIsLoadingTranslateTitle(true);
const res = await translateText({
text: getValues("title"),
sourceLang: "ID",
targetLang: "EN",
});
if (!res.error) {
const resultText =
res?.data?.data?.translations?.[0]?.text || "";
setTranslatedTitle(resultText);
}
} catch (err) {
console.error("Translate title gagal:", err);
} finally {
close();
setIsLoadingTranslateTitle(false);
}
}}
className="px-3 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600"
>
{isLoadingTranslateTitle
? "Translating..."
: "Translate to English"}
</button>
)}
</div>
{/* Judul Bahasa Indonesia */}
<Controller
control={control}
name="title"
render={({ field }) => (
<Input
size="md"
type="text"
value={field.value}
onChange={field.onChange}
placeholder="Masukkan Judul Bahasa Indonesia"
/>
)}
/>
{/* Versi English (muncul setelah klik translate) */}
{translatedTitle && (
<div className="mt-3">
<Label className="text-sm font-semibold">
English Title
</Label>
<Input
size="md"
type="text"
value={translatedTitle}
onChange={(e) => setTranslatedTitle(e.target.value)}
placeholder="English version"
/>
</div>
)}
{errors.title?.message && (
<p className="text-red-400 text-sm">{errors.title.message}</p>
)}
</div>
{/* <div className="space-y-2 py-3">
<Label>{t("title", { defaultValue: "Title" })}</Label> <Label>{t("title", { defaultValue: "Title" })}</Label>
<Controller <Controller
control={control} control={control}
@ -901,7 +999,7 @@ export default function FormVideo() {
{errors.title?.message && ( {errors.title?.message && (
<p className="text-red-400 text-sm">{errors.title.message}</p> <p className="text-red-400 text-sm">{errors.title.message}</p>
)} )}
</div> </div> */}
<div className="flex items-center"> <div className="flex items-center">
<div className="py-3 w-full space-y-2"> <div className="py-3 w-full space-y-2">
@ -1210,7 +1308,86 @@ export default function FormVideo() {
</Label> </Label>
</div> </div>
{/* Deskripsi dengan translate atas-bawah */}
<div className="py-3 space-y-2"> <div className="py-3 space-y-2">
<div className="flex justify-between items-center">
<Label>
{t("description", { defaultValue: "Description" })}
</Label>
{roleId === "14" && (
<button
type="button"
onClick={async () => {
try {
loading();
setIsLoadingTranslate(true);
const res = await translateText({
text: getValues("descriptionOri"),
sourceLang: "ID",
targetLang: "EN",
});
if (!res.error) {
const resultText =
res?.data?.data?.translations?.[0]?.text ||
"";
setTranslatedContent(resultText);
}
} catch (err) {
console.error("Translate gagal:", err);
} finally {
close();
setIsLoadingTranslate(false);
}
}}
className="px-3 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600"
>
{isLoadingTranslate
? "Translating..."
: "Translate to English"}
</button>
)}
</div>
{/* Editor Bahasa Indonesia */}
<div className="mt-2">
<Label className="text-sm font-semibold">
Indonesian Version
</Label>
<Controller
control={control}
name="descriptionOri"
render={({ field: { onChange, value } }) => (
<CustomEditor
onChange={onChange}
initialData={value}
/>
)}
/>
</div>
{/* Editor Bahasa Inggris (muncul setelah klik translate) */}
{translatedContent && (
<div className="mt-5">
<Label className="text-sm font-semibold">
English Version
</Label>
<CustomEditor
onChange={(val: any) => setTranslatedContent(val)}
initialData={translatedContent}
/>
</div>
)}
{errors.description?.message && (
<p className="text-red-400 text-sm">
{errors.description.message}
</p>
)}
</div>
{/* <div className="py-3 space-y-2">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<Label> <Label>
{t("description", { defaultValue: "Description" })} {t("description", { defaultValue: "Description" })}
@ -1253,7 +1430,6 @@ export default function FormVideo() {
)} )}
</div> </div>
{/* Pilihan bahasa untuk posting */}
<div className="flex items-center gap-4 mb-2"> <div className="flex items-center gap-4 mb-2">
<label className="flex items-center gap-2"> <label className="flex items-center gap-2">
<input <input
@ -1266,7 +1442,6 @@ export default function FormVideo() {
</label> </label>
</div> </div>
{/* Editor Bahasa Indonesia */}
<Controller <Controller
control={control} control={control}
name="descriptionOri" name="descriptionOri"
@ -1278,7 +1453,6 @@ export default function FormVideo() {
)} )}
/> />
{/* Editor Bahasa Inggris */}
{translatedContent && ( {translatedContent && (
<div className="mt-4"> <div className="mt-4">
<div className="flex flex-col"> <div className="flex flex-col">
@ -1291,7 +1465,7 @@ export default function FormVideo() {
value="en" value="en"
checked={selectedLang === "en"} checked={selectedLang === "en"}
onChange={() => setSelectedLang("en")} onChange={() => setSelectedLang("en")}
disabled={!translatedContent} // kalau belum translate, disable disabled={!translatedContent}
/> />
<span>Gunakan Bahasa Inggris</span> <span>Gunakan Bahasa Inggris</span>
</label> </label>
@ -1309,7 +1483,7 @@ export default function FormVideo() {
{errors.description.message} {errors.description.message}
</p> </p>
)} )}
</div> </div> */}
{/* <div className="py-3 space-y-2"> {/* <div className="py-3 space-y-2">
<Label> <Label>

View File

@ -227,6 +227,7 @@ export default function FormVideoUpdate() {
const [translatedContent, setTranslatedContent] = React.useState(""); const [translatedContent, setTranslatedContent] = React.useState("");
const [selectedLang, setSelectedLang] = React.useState<"id" | "en">("id"); const [selectedLang, setSelectedLang] = React.useState<"id" | "en">("id");
const roleId = getCookiesDecrypt("urie"); const roleId = getCookiesDecrypt("urie");
const [translatedTitle, setTranslatedTitle] = useState("");
const options: Option[] = [ const options: Option[] = [
{ id: "all", name: "SEMUA" }, { id: "all", name: "SEMUA" },
@ -1024,22 +1025,28 @@ export default function FormVideoUpdate() {
// // router.push("/en/contributor/content/video"); // // router.push("/en/contributor/content/video");
// // }); // // });
// }; // };
const save = async (data: VideoSchema) => { const save = async (data: VideoSchema) => {
loading(); loading();
const finalTags = tags.join(", "); const finalTags = tags.join(", ");
const descFinal = // const descFinal =
selectedLang === "en" && translatedContent // selectedLang === "en" && translatedContent
? translatedContent // ? translatedContent
: data.description; // : data.description;
const descFinal = translatedContent || data.description;
const finalTitle = translatedTitle || data.title;
const requestData = { const requestData = {
...data, ...data,
id: detail?.id, id: detail?.id,
title: data.title, // title: data.title,
description: htmlToString(descFinal), // description: htmlToString(descFinal),
// htmlDescription: descFinal,
title: finalTitle,
description: htmlToString(descFinal),
htmlDescription: descFinal, htmlDescription: descFinal,
fileTypeId, fileTypeId,
categoryId: selectedTarget, categoryId: selectedTarget,
@ -1325,6 +1332,81 @@ export default function FormVideoUpdate() {
<div className="gap-5 mb-5"> <div className="gap-5 mb-5">
{/* Input Title */} {/* Input Title */}
<div className="space-y-2 py-3"> <div className="space-y-2 py-3">
<div className="flex justify-between items-center">
<Label>{t("title", { defaultValue: "Title" })}</Label>
{roleId === "14" && (
<button
type="button"
onClick={async () => {
try {
loading();
setIsLoadingTranslate(true);
const res = await translateText({
text: getValues("title"),
sourceLang: "ID",
targetLang: "EN",
});
if (!res.error) {
const resultText =
res?.data?.data?.translations?.[0]?.text || "";
setTranslatedTitle(resultText);
}
} catch (err) {
close();
console.error("Translate title gagal:", err);
} finally {
close();
setIsLoadingTranslate(false);
}
}}
className="px-3 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600"
>
{isLoadingTranslate
? "Translating..."
: "Translate Title"}
</button>
)}
</div>
<Controller
control={control}
name="title"
render={({ field }) => (
<Input
size="md"
type="text"
value={field.value}
onChange={field.onChange}
placeholder="Enter Title"
/>
)}
/>
{/* English translated title appears below when available */}
{translatedTitle && (
<div className="mt-3">
<Label className="text-sm font-semibold">
English Version
</Label>
<Input
size="md"
type="text"
value={translatedTitle}
onChange={(e) => setTranslatedTitle(e.target.value)}
placeholder="Translated Title"
/>
</div>
)}
{errors.title?.message && (
<p className="text-red-400 text-sm">
{errors.title.message}
</p>
)}
</div>
{/* <div className="space-y-2 py-3">
<Label>{t("title", { defaultValue: "Title" })}</Label> <Label>{t("title", { defaultValue: "Title" })}</Label>
<Controller <Controller
control={control} control={control}
@ -1344,7 +1426,7 @@ export default function FormVideoUpdate() {
{errors.title.message} {errors.title.message}
</p> </p>
)} )}
</div> </div> */}
<div className="flex items-center"> <div className="flex items-center">
<div className="py-3 w-full space-y-2"> <div className="py-3 w-full space-y-2">
<Label>{t("category", { defaultValue: "Category" })}</Label> <Label>{t("category", { defaultValue: "Category" })}</Label>
@ -1407,7 +1489,7 @@ export default function FormVideoUpdate() {
if (!res.error) { if (!res.error) {
const resultText = const resultText =
res?.data?.data?.translations?.[0]?.text || ""; res?.data?.data?.translations?.[0]?.text || "";
// Overwrite data.description but still show both
setTranslatedContent(resultText); setTranslatedContent(resultText);
} }
} catch (err) { } catch (err) {
@ -1422,55 +1504,34 @@ export default function FormVideoUpdate() {
> >
{isLoadingTranslate {isLoadingTranslate
? "Translating..." ? "Translating..."
: "Translate to English"} : "Translate Description"}
</button> </button>
)} )}
</div> </div>
{/* Pilihan bahasa untuk posting */}
{roleId === "14" && (
<div className="flex items-center gap-4 mb-2">
<label className="flex items-center gap-2">
<input
type="radio"
value="id"
checked={selectedLang === "id"}
onChange={() => setSelectedLang("id")}
/>
<span>Gunakan Bahasa Indonesia</span>
</label>
</div>
)}
{/* Editor Bahasa Indonesia */} {/* Editor Bahasa Indonesia */}
<Controller <div className="mt-3">
control={control} <Label className="text-sm font-semibold">
name="description" Indonesian Version
render={({ field }) => ( </Label>
<CustomEditor <Controller
onChange={field.onChange} control={control}
initialData={field.value} name="description"
/> render={({ field }) => (
)} <CustomEditor
/> onChange={field.onChange}
initialData={field.value}
/>
)}
/>
</div>
{/* Editor Bahasa Inggris */} {/* English translated version muncul setelah translate */}
{translatedContent && ( {translatedContent && (
<div className="mt-4"> <div className="mt-5">
<div className="flex flex-col"> <Label className="text-sm font-semibold">
<Label className="text-[15px]">English Version</Label> English Version
<label className="flex items-center gap-2"> </Label>
<input
type="radio"
value="en"
checked={selectedLang === "en"}
onChange={() => setSelectedLang("en")}
disabled={!translatedContent}
/>
<span>Gunakan Bahasa Inggris</span>
</label>
</div>
<CustomEditor <CustomEditor
onChange={(val: any) => setTranslatedContent(val)} onChange={(val: any) => setTranslatedContent(val)}
initialData={translatedContent} initialData={translatedContent}
@ -1484,6 +1545,7 @@ export default function FormVideoUpdate() {
</p> </p>
)} )}
</div> </div>
{/* <div className="py-3 space-y-2"> {/* <div className="py-3 space-y-2">
<Label> <Label>
{t("description", { defaultValue: "Description" })} {t("description", { defaultValue: "Description" })}