fixing
This commit is contained in:
parent
00ddca22b9
commit
978c8b364f
|
|
@ -48,12 +48,12 @@ const AudioPlayer: React.FC<AudioPlayerProps> = ({ urlAudio, fileName }) => {
|
|||
<div className="mt-2">
|
||||
<h2 className="text-lg font-semibold">{fileName}</h2>
|
||||
<audio ref={audioRef} src={urlAudio} controls className="mt-1 w-full" />
|
||||
{/* <div className="mt-2 space-x-2">
|
||||
<div className="mt-2 space-x-2">
|
||||
<button onClick={playAudio}>▶️ Play</button>
|
||||
<button onClick={pauseAudio}>⏸️ Pause</button>
|
||||
<button onClick={stopAudio}>⏹️ Stop</button>
|
||||
</div>
|
||||
<div className="mt-1 text-sm text-gray-500">{formatTime(currentTime)}</div> */}
|
||||
<div className="mt-1 text-sm text-gray-500">{formatTime(currentTime)}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -290,7 +290,6 @@ export default function FormAudioDetail() {
|
|||
setSelectedPublishers(publisherIds);
|
||||
}
|
||||
|
||||
// Set the selected target to the category ID from details
|
||||
setSelectedTarget(String(details.category.id));
|
||||
|
||||
const filesData = details?.files || [];
|
||||
|
|
@ -298,11 +297,11 @@ export default function FormAudioDetail() {
|
|||
(file: any) =>
|
||||
file.contentType &&
|
||||
(file.contentType.startsWith("audio/") ||
|
||||
file.contentType.includes("webm"))
|
||||
file.contentType.includes("mpeg"))
|
||||
);
|
||||
|
||||
const fileUrls = audioFiles.map((file: { secondaryUrl: string }) =>
|
||||
file.secondaryUrl ? file.secondaryUrl : "default-audio.mp3"
|
||||
const fileUrls = audioFiles.map((file: { url: string }) =>
|
||||
file.url ? file.url : ""
|
||||
);
|
||||
console.log("Audio file URLs:", fileUrls);
|
||||
|
||||
|
|
@ -523,7 +522,11 @@ export default function FormAudioDetail() {
|
|||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{/* Show the category from details if it doesn't exist in categories list */}
|
||||
{detail && !categories.find(cat => String(cat.id) === String(detail.category.id)) && (
|
||||
{detail &&
|
||||
!categories.find(
|
||||
(cat) =>
|
||||
String(cat.id) === String(detail.category.id)
|
||||
) && (
|
||||
<SelectItem
|
||||
key={String(detail.category.id)}
|
||||
value={String(detail.category.id)}
|
||||
|
|
@ -532,7 +535,10 @@ export default function FormAudioDetail() {
|
|||
</SelectItem>
|
||||
)}
|
||||
{categories.map((category) => (
|
||||
<SelectItem key={String(category.id)} value={String(category.id)}>
|
||||
<SelectItem
|
||||
key={String(category.id)}
|
||||
value={String(category.id)}
|
||||
>
|
||||
{category.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -211,6 +211,9 @@ export default function FormAudio() {
|
|||
tags: z
|
||||
.array(z.string().min(1))
|
||||
.min(1, { message: "Wajib isi minimal 1 tag" }),
|
||||
publishedFor: z
|
||||
.array(z.string())
|
||||
.min(1, { message: "Minimal 1 target publish harus dipilih." }),
|
||||
});
|
||||
|
||||
const {
|
||||
|
|
@ -227,6 +230,7 @@ export default function FormAudio() {
|
|||
rewriteDescription: "",
|
||||
category: "",
|
||||
tags: [],
|
||||
publishedFor: [],
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -479,10 +483,8 @@ export default function FormAudio() {
|
|||
const handleCheckboxChange = (id: string): void => {
|
||||
if (id === "all") {
|
||||
if (publishedFor.includes("all")) {
|
||||
// Uncheck all checkboxes
|
||||
setPublishedFor([]);
|
||||
} else {
|
||||
// Select all checkboxes
|
||||
setPublishedFor(
|
||||
options
|
||||
.filter((opt: any) => opt.id !== "all")
|
||||
|
|
@ -493,8 +495,6 @@ export default function FormAudio() {
|
|||
const updatedPublishedFor = publishedFor.includes(id)
|
||||
? publishedFor.filter((item) => item !== id)
|
||||
: [...publishedFor, id];
|
||||
|
||||
// Remove "all" if any checkbox is unchecked
|
||||
if (publishedFor.includes("all") && id !== "all") {
|
||||
setPublishedFor(updatedPublishedFor.filter((item) => item !== "all"));
|
||||
} else {
|
||||
|
|
@ -505,7 +505,6 @@ export default function FormAudio() {
|
|||
|
||||
useEffect(() => {
|
||||
if (articleBody) {
|
||||
// Set ke dua field jika rewrite juga aktif
|
||||
setValue("description", articleBody);
|
||||
setValue("rewriteDescription", articleBody);
|
||||
}
|
||||
|
|
@ -513,13 +512,21 @@ export default function FormAudio() {
|
|||
|
||||
const save = async (data: AudioSchema) => {
|
||||
loading();
|
||||
|
||||
if (files.length === 0) {
|
||||
MySwal.fire("Error", "Minimal 1 file harus diunggah.", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
const finalTags = tags.join(", ");
|
||||
const finalTitle = isSwitchOn ? title : data.title;
|
||||
// const finalDescription = articleBody || data.description;
|
||||
const finalDescription = isSwitchOn
|
||||
? data.description
|
||||
: selectedFileType === "rewrite"
|
||||
? data.rewriteDescription
|
||||
: data.descriptionOri;
|
||||
|
||||
if (!finalDescription?.trim()) {
|
||||
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
|
||||
return;
|
||||
|
|
@ -567,40 +574,36 @@ export default function FormAudio() {
|
|||
const response = await createMedia(requestData);
|
||||
console.log("Form Data Submitted:", requestData);
|
||||
|
||||
if (response?.error) {
|
||||
MySwal.fire("Error", response?.message, "error");
|
||||
return;
|
||||
}
|
||||
Cookies.set("idCreate", response?.data?.data, { expires: 1 });
|
||||
id = response?.data?.data;
|
||||
|
||||
const formMedia = new FormData();
|
||||
console.log("Thumbnail : ", files[0]);
|
||||
formMedia.append("file", files[0]);
|
||||
const thumbnail = files[0];
|
||||
formMedia.append("file", thumbnail);
|
||||
const responseThumbnail = await uploadThumbnail(id, formMedia);
|
||||
if (responseThumbnail?.error == true) {
|
||||
error(responseThumbnail?.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const progressInfoArr = [];
|
||||
for (const item of files) {
|
||||
progressInfoArr.push({ percentage: 0, fileName: item.name });
|
||||
}
|
||||
const progressInfoArr = files.map((item) => ({
|
||||
percentage: 0,
|
||||
fileName: item.name,
|
||||
}));
|
||||
progressInfo = progressInfoArr;
|
||||
setIsStartUpload(true);
|
||||
setProgressList(progressInfoArr);
|
||||
|
||||
close();
|
||||
// showProgress();
|
||||
files.map(async (item: any, index: number) => {
|
||||
await uploadResumableFile(index, String(id), item, "0");
|
||||
await uploadResumableFile(
|
||||
index,
|
||||
String(id),
|
||||
item,
|
||||
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0"
|
||||
);
|
||||
});
|
||||
|
||||
Cookies.remove("idCreate");
|
||||
|
||||
// MySwal.fire("Sukses", "Data berhasil disimpan.", "success");
|
||||
};
|
||||
|
||||
const onSubmit = (data: AudioSchema) => {
|
||||
|
|
@ -1413,29 +1416,73 @@ export default function FormAudio() {
|
|||
)}
|
||||
</div>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="publishedFor"
|
||||
render={({ field }) => (
|
||||
<div className="px-3 py-3">
|
||||
<div className="flex flex-col gap-3 space-y-2">
|
||||
<Label>
|
||||
{t("publish-target", { defaultValue: "Publish Target" })}
|
||||
</Label>
|
||||
{options.map((option) => (
|
||||
<div key={option.id} className="flex gap-2 items-center">
|
||||
|
||||
{options.map((option) => {
|
||||
const isAllChecked =
|
||||
field.value.length ===
|
||||
options.filter((opt: any) => opt.id !== "all").length;
|
||||
|
||||
const isChecked =
|
||||
option.id === "all"
|
||||
? isAllChecked
|
||||
: field.value.includes(option.id);
|
||||
|
||||
const handleChange = () => {
|
||||
let updated: string[] = [];
|
||||
|
||||
if (option.id === "all") {
|
||||
updated = isAllChecked
|
||||
? []
|
||||
: options
|
||||
.filter((opt: any) => opt.id !== "all")
|
||||
.map((opt: any) => opt.id);
|
||||
} else {
|
||||
updated = isChecked
|
||||
? field.value.filter((val) => val !== option.id)
|
||||
: [...field.value, option.id];
|
||||
|
||||
if (isAllChecked && option.id !== "all") {
|
||||
updated = updated.filter((val) => val !== "all");
|
||||
}
|
||||
}
|
||||
|
||||
field.onChange(updated);
|
||||
setPublishedFor(updated);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
key={option.id}
|
||||
className="flex gap-2 items-center"
|
||||
>
|
||||
<Checkbox
|
||||
id={option.id}
|
||||
checked={
|
||||
option.id === "all"
|
||||
? publishedFor.length ===
|
||||
options.filter((opt: any) => opt.id !== "all")
|
||||
.length
|
||||
: publishedFor.includes(option.id)
|
||||
}
|
||||
onCheckedChange={() => handleCheckboxChange(option.id)}
|
||||
checked={isChecked}
|
||||
onCheckedChange={handleChange}
|
||||
/>
|
||||
<Label htmlFor={option.id}>{option.label}</Label>
|
||||
</div>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
|
||||
{errors.publishedFor && (
|
||||
<p className="text-red-500 text-sm">
|
||||
{errors.publishedFor.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</Card>
|
||||
<div className="flex flex-row justify-end gap-3">
|
||||
<div className="mt-4">
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ import { getCsrfToken } from "@/service/auth";
|
|||
import { Link } from "@/i18n/routing";
|
||||
import { request } from "http";
|
||||
import { useLocale, useTranslations } from "next-intl";
|
||||
import { toast } from "sonner";
|
||||
|
||||
interface FileWithPreview extends File {
|
||||
preview: string;
|
||||
|
|
@ -153,27 +154,77 @@ export default function FormImage() {
|
|||
{ id: "8", label: "KSP" },
|
||||
];
|
||||
|
||||
type FileWithPreview = File & {
|
||||
preview: string;
|
||||
};
|
||||
|
||||
const MAX_FILE_SIZE = 100 * 1024 * 1024;
|
||||
|
||||
const { getRootProps, getInputProps } = useDropzone({
|
||||
onDrop: (acceptedFiles) => {
|
||||
setFiles(acceptedFiles.map((file) => Object.assign(file)));
|
||||
},
|
||||
accept: {
|
||||
"image/*": [],
|
||||
"image/jpeg": [],
|
||||
"image/png": [],
|
||||
"image/jpg": [],
|
||||
},
|
||||
onDrop: (acceptedFiles) => {
|
||||
const validFiles = acceptedFiles
|
||||
.filter(
|
||||
(file) =>
|
||||
["image/jpeg", "image/png", "image/jpg"].includes(file.type) &&
|
||||
file.size <= MAX_FILE_SIZE
|
||||
)
|
||||
.map((file) =>
|
||||
Object.assign(file, {
|
||||
preview: URL.createObjectURL(file),
|
||||
})
|
||||
);
|
||||
|
||||
if (validFiles.length === 0) {
|
||||
toast.error(
|
||||
"File tidak valid. Hanya .jpg, .jpeg, .png maksimal 100MB yang diperbolehkan."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setFiles(validFiles);
|
||||
setValue("files", validFiles);
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
files.forEach((file) => URL.revokeObjectURL(file.preview));
|
||||
};
|
||||
}, [files]);
|
||||
|
||||
const imageSchema = z.object({
|
||||
title: z.string().min(1, { message: t("titleRequired") }),
|
||||
description: z.string().optional(),
|
||||
descriptionOri: z.string().optional(),
|
||||
rewriteDescription: z.string().optional(),
|
||||
creatorName: z.string().min(1, { message: t("creatorRequired") }),
|
||||
categoryId: z.string().min(1, { message: "Kategori diperlukan" }),
|
||||
tags: z.array(z.string()).min(1, { message: "Minimal 1 tag diperlukan" }),
|
||||
files: z
|
||||
.array(z.any())
|
||||
.min(1, { message: "Minimal 1 file harus diunggah." })
|
||||
.refine(
|
||||
(files) =>
|
||||
files.every(
|
||||
(file: File) =>
|
||||
["image/jpeg", "image/png", "image/jpg"].includes(file.type) &&
|
||||
file.size <= 100 * 1024 * 1024
|
||||
),
|
||||
{
|
||||
message:
|
||||
"Hanya file .jpg, .jpeg, .png, maksimal 100MB yang diperbolehkan.",
|
||||
}
|
||||
),
|
||||
categoryId: z.string().min(1, { message: "Kategori wajib dipilih." }),
|
||||
tags: z
|
||||
.array(z.string())
|
||||
.min(1, { message: "Minimal 1 tag harus ditambahkan." }),
|
||||
publishedFor: z
|
||||
.array(z.string())
|
||||
.min(1, { message: "Pilih target publish" }),
|
||||
files: z.array(z.any()).min(1, { message: "File harus diupload" }),
|
||||
.min(1, { message: "Minimal 1 target publish harus dipilih." }),
|
||||
});
|
||||
|
||||
const {
|
||||
|
|
@ -186,15 +237,15 @@ export default function FormImage() {
|
|||
} = useForm<ImageSchema>({
|
||||
resolver: zodResolver(imageSchema),
|
||||
defaultValues: {
|
||||
title: "",
|
||||
description: "",
|
||||
descriptionOri: "",
|
||||
rewriteDescription: "",
|
||||
title: "",
|
||||
creatorName: "",
|
||||
files: [],
|
||||
categoryId: "",
|
||||
tags: [],
|
||||
publishedFor: [],
|
||||
files: [],
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -395,7 +446,9 @@ export default function FormImage() {
|
|||
e.preventDefault();
|
||||
const newTag = e.currentTarget.value.trim();
|
||||
if (!tags.includes(newTag)) {
|
||||
setTags((prevTags) => [...prevTags, newTag]);
|
||||
const updatedTags = [...tags, newTag];
|
||||
setTags(updatedTags);
|
||||
setValue("tags", updatedTags);
|
||||
if (inputRef.current) {
|
||||
inputRef.current.value = "";
|
||||
}
|
||||
|
|
@ -404,20 +457,15 @@ export default function FormImage() {
|
|||
};
|
||||
|
||||
const handleRemoveTag = (index: number) => {
|
||||
setTags((prevTags) => prevTags.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
const handleRemoveImage = (index: number) => {
|
||||
setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index));
|
||||
const updatedTags = tags.filter((_, i) => i !== index);
|
||||
setTags(updatedTags);
|
||||
setValue("tags", updatedTags);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
async function initState() {
|
||||
getCategories();
|
||||
// setVideoActive(fileTypeId == '2');
|
||||
// getRoles();
|
||||
}
|
||||
|
||||
initState();
|
||||
}, []);
|
||||
|
||||
|
|
@ -478,6 +526,12 @@ export default function FormImage() {
|
|||
|
||||
const save = async (data: ImageSchema) => {
|
||||
loading();
|
||||
|
||||
if (files.length === 0) {
|
||||
MySwal.fire("Error", "Minimal 1 file harus diunggah.", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
const finalTags = tags.join(", ");
|
||||
const finalTitle = isSwitchOn ? title : data.title;
|
||||
// const finalDescription = articleBody || data.description;
|
||||
|
|
@ -817,10 +871,10 @@ export default function FormImage() {
|
|||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center">
|
||||
{/* <div className="flex items-center">
|
||||
<div className="py-3 space-y-2 w-full">
|
||||
<Label>{t("category", { defaultValue: "Category" })}</Label>
|
||||
{/* <Select
|
||||
<Select
|
||||
value={selectedCategory}
|
||||
onValueChange={(id) => {
|
||||
console.log("Selected Category ID:", id);
|
||||
|
|
@ -840,35 +894,46 @@ export default function FormImage() {
|
|||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select> */}
|
||||
</Select>
|
||||
</div>
|
||||
</div> */}
|
||||
<Controller
|
||||
control={control}
|
||||
name="categoryId"
|
||||
render={({ field }) => (
|
||||
<div className="w-full">
|
||||
<Label>{t("category", { defaultValue: "Category" })}</Label>
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
onValueChange={(value) => {
|
||||
field.onChange(value);
|
||||
setSelectedCategory(value);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectTrigger size="md">
|
||||
<SelectValue placeholder="Pilih" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{categories.map((cat) => (
|
||||
<SelectItem key={cat.id} value={cat.id.toString()}>
|
||||
{cat.name}
|
||||
{categories.map((category) => (
|
||||
<SelectItem
|
||||
key={category.id}
|
||||
value={category.id.toString()}
|
||||
>
|
||||
{category.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
{errors.categoryId?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.categoryId?.message}
|
||||
|
||||
{errors.categoryId && (
|
||||
<p className="text-sm text-red-500 mt-1">
|
||||
{errors.categoryId.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex flex-row items-center gap-3 py-3 ">
|
||||
<Label>
|
||||
{t("ai-assistance", { defaultValue: "Ai Assistance" })}
|
||||
|
|
@ -1232,170 +1297,6 @@ export default function FormImage() {
|
|||
</RadioGroup>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<Controller
|
||||
control={control}
|
||||
name="files"
|
||||
render={({ field }) => {
|
||||
const maxSize = 100 * 1024 * 1024;
|
||||
const [previews, setPreviews] = useState<string[]>([]);
|
||||
|
||||
const { getRootProps, getInputProps, fileRejections } =
|
||||
useDropzone({
|
||||
accept: {
|
||||
"image/jpeg": [".jpeg", ".jpg"],
|
||||
"image/png": [".png"],
|
||||
},
|
||||
maxSize,
|
||||
multiple: true, // <- Set true jika ingin lebih dari satu
|
||||
onDrop: (acceptedFiles) => {
|
||||
const currentFiles = [
|
||||
...(field.value || []),
|
||||
...acceptedFiles,
|
||||
];
|
||||
field.onChange(currentFiles);
|
||||
|
||||
const newPreviews = acceptedFiles.map((file) =>
|
||||
URL.createObjectURL(file)
|
||||
);
|
||||
setPreviews((prev) => [...prev, ...newPreviews]);
|
||||
},
|
||||
});
|
||||
|
||||
// Clean up URL on unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
previews.forEach((url) => URL.revokeObjectURL(url));
|
||||
};
|
||||
}, [previews]);
|
||||
|
||||
const handleRemoveFile = (indexToRemove: number) => {
|
||||
const updatedFiles = [...field.value];
|
||||
updatedFiles.splice(indexToRemove, 1);
|
||||
|
||||
const updatedPreviews = [...previews];
|
||||
URL.revokeObjectURL(updatedPreviews[indexToRemove]); // Clean up
|
||||
updatedPreviews.splice(indexToRemove, 1);
|
||||
|
||||
field.onChange(updatedFiles);
|
||||
setPreviews(updatedPreviews);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>
|
||||
{t("select-file", { defaultValue: "Select File" })}
|
||||
</Label>
|
||||
|
||||
<div {...getRootProps({ className: "dropzone" })}>
|
||||
<input {...getInputProps()} />
|
||||
<div className="w-full text-center border-dashed border border-default-200 dark:border-default-300 rounded-md py-[52px] flex items-center flex-col">
|
||||
<CloudUpload className="text-default-300 w-10 h-10" />
|
||||
<h4 className="text-2xl font-medium mb-1 mt-3 text-card-foreground/80">
|
||||
{t("drag-file", { defaultValue: "Drag File" })}
|
||||
</h4>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{t("upload-file-max", {
|
||||
defaultValue:
|
||||
"Upload file max 100MB (.jpg, .jpeg, .png)",
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{field.value && field.value.length > 0 && (
|
||||
<div className="mt-3 space-y-3">
|
||||
{field.value.map((file: File, idx: number) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="flex items-center gap-4 border p-2 rounded-md relative"
|
||||
>
|
||||
<img
|
||||
src={previews[idx]}
|
||||
alt={`preview-${idx}`}
|
||||
className="w-24 h-24 object-cover rounded border"
|
||||
/>
|
||||
<div className="flex-1 text-sm">
|
||||
<div className="font-medium">{file.name}</div>
|
||||
<div className="text-muted-foreground text-xs">
|
||||
{(file.size / (1024 * 1024)).toFixed(2)} MB
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="absolute top-1 right-1 text-muted-foreground hover:text-red-500"
|
||||
onClick={() => handleRemoveFile(idx)}
|
||||
>
|
||||
<X className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
type="button"
|
||||
variant="default"
|
||||
onClick={() => {
|
||||
field.onChange([]);
|
||||
previews.forEach((url) =>
|
||||
URL.revokeObjectURL(url)
|
||||
);
|
||||
setPreviews([]);
|
||||
}}
|
||||
>
|
||||
{t("remove-all", {
|
||||
defaultValue: "Remove All",
|
||||
})}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{errors.files?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.files.message}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{fileRejections.length > 0 && (
|
||||
<div className="text-red-400 text-sm space-y-1 mt-2">
|
||||
{fileRejections.map(({ file, errors }, index) => (
|
||||
<div key={index}>
|
||||
<p>{file.name}:</p>
|
||||
<ul className="ml-4 list-disc">
|
||||
{errors.map((e, idx) => (
|
||||
<li key={idx}>
|
||||
{e.code === "file-too-large" &&
|
||||
t("size", {
|
||||
defaultValue:
|
||||
"File too large. Max 100MB.",
|
||||
})}
|
||||
{e.code === "file-invalid-type" &&
|
||||
t("only", {
|
||||
defaultValue:
|
||||
"Invalid file type. Only .jpg, .jpeg, .png allowed.",
|
||||
})}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/* <Controller
|
||||
name="files"
|
||||
control={control}
|
||||
rules={{
|
||||
required: t("file-required", {
|
||||
defaultValue: "File is required",
|
||||
}),
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<div className="py-3 space-y-2">
|
||||
<Label>
|
||||
{t("select-file", { defaultValue: "Select File" })}
|
||||
|
|
@ -1430,14 +1331,12 @@ export default function FormImage() {
|
|||
</>
|
||||
) : null}
|
||||
|
||||
{errors.files?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.files && (
|
||||
<p className="text-red-500 text-sm mt-1">
|
||||
{errors.files.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/> */}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
|
@ -1467,10 +1366,12 @@ export default function FormImage() {
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* <div className="px-3 py-3 space-y-2">
|
||||
<Label htmlFor="tags">
|
||||
{t("tags", { defaultValue: "Tags" })}
|
||||
</Label>
|
||||
|
||||
<Input
|
||||
type="text"
|
||||
id="tags"
|
||||
|
|
@ -1501,29 +1402,22 @@ export default function FormImage() {
|
|||
{t("tags", { defaultValue: "Tags" })}
|
||||
</Label>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="tags"
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<Input
|
||||
type="text"
|
||||
id="tags"
|
||||
placeholder="Add a tag and press Enter"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" && e.currentTarget.value.trim()) {
|
||||
e.preventDefault();
|
||||
field.onChange([
|
||||
...field.value,
|
||||
e.currentTarget.value.trim(),
|
||||
]);
|
||||
e.currentTarget.value = "";
|
||||
}
|
||||
}}
|
||||
onKeyDown={handleAddTag}
|
||||
ref={inputRef}
|
||||
/>
|
||||
|
||||
{errors.tags && (
|
||||
<p className="text-sm text-red-500 mt-1">
|
||||
{errors.tags.message}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="mt-3">
|
||||
{field.value.map((tag: string, index: number) => (
|
||||
{tags.map((tag, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="px-1 py-1 rounded-lg bg-black text-white mr-2 text-sm font-sans"
|
||||
|
|
@ -1531,12 +1425,7 @@ export default function FormImage() {
|
|||
{tag}{" "}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const updatedTags = field.value.filter(
|
||||
(_, i) => i !== index
|
||||
);
|
||||
field.onChange(updatedTags);
|
||||
}}
|
||||
onClick={() => handleRemoveTag(index)}
|
||||
className="remove-tag-button"
|
||||
>
|
||||
×
|
||||
|
|
@ -1544,14 +1433,6 @@ export default function FormImage() {
|
|||
</span>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Tampilkan error */}
|
||||
{errors.tags?.message && (
|
||||
<p className="text-red-400 text-sm">{errors.tags.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* <div className="px-3 py-3">
|
||||
|
|
@ -1577,63 +1458,76 @@ export default function FormImage() {
|
|||
))}
|
||||
</div>
|
||||
</div> */}
|
||||
<Controller
|
||||
control={control}
|
||||
name="publishedFor"
|
||||
render={({ field }) => (
|
||||
<div className="px-3 py-3">
|
||||
<div className="flex flex-col gap-3 space-y-2">
|
||||
<Label>
|
||||
{t("publish-target", { defaultValue: "Publish Target" })}
|
||||
</Label>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="publishedFor"
|
||||
render={({ field }) => (
|
||||
<>
|
||||
{options.map((option) => (
|
||||
{options.map((option) => {
|
||||
const isAllChecked =
|
||||
field.value.length ===
|
||||
options.filter((opt: any) => opt.id !== "all").length;
|
||||
|
||||
const isChecked =
|
||||
option.id === "all"
|
||||
? isAllChecked
|
||||
: field.value.includes(option.id);
|
||||
|
||||
const handleChange = () => {
|
||||
let updated: string[] = [];
|
||||
|
||||
if (option.id === "all") {
|
||||
updated = isAllChecked
|
||||
? []
|
||||
: options
|
||||
.filter((opt: any) => opt.id !== "all")
|
||||
.map((opt: any) => opt.id);
|
||||
} else {
|
||||
updated = isChecked
|
||||
? field.value.filter((val) => val !== option.id)
|
||||
: [...field.value, option.id];
|
||||
|
||||
if (isAllChecked && option.id !== "all") {
|
||||
updated = updated.filter((val) => val !== "all");
|
||||
}
|
||||
}
|
||||
|
||||
field.onChange(updated);
|
||||
setPublishedFor(updated);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
key={option.id}
|
||||
className="flex gap-2 items-center"
|
||||
>
|
||||
<Checkbox
|
||||
id={option.id}
|
||||
checked={field.value.includes(option.id)}
|
||||
onCheckedChange={(checked: boolean) => {
|
||||
let updated: string[] = [...field.value];
|
||||
if (checked) {
|
||||
updated.push(option.id);
|
||||
} else {
|
||||
updated = updated.filter(
|
||||
(id) => id !== option.id
|
||||
);
|
||||
}
|
||||
|
||||
if (option.id === "all") {
|
||||
if (checked) {
|
||||
updated = options
|
||||
.filter((opt) => opt.id !== "all")
|
||||
.map((opt) => opt.id);
|
||||
} else {
|
||||
updated = [];
|
||||
}
|
||||
}
|
||||
field.onChange(updated);
|
||||
}}
|
||||
checked={isChecked}
|
||||
onCheckedChange={handleChange}
|
||||
/>
|
||||
<Label htmlFor={option.id}>{option.label}</Label>
|
||||
</div>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Tampilkan error */}
|
||||
{errors.publishedFor?.message && (
|
||||
<p className="text-red-400 text-sm">
|
||||
{errors.publishedFor && (
|
||||
<p className="text-red-500 text-sm">
|
||||
{errors.publishedFor.message}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* button submit */}
|
||||
<div className="flex flex-row justify-end gap-3">
|
||||
<div className="mt-4">
|
||||
<Button type="submit" color="primary">
|
||||
|
|
|
|||
|
|
@ -83,24 +83,20 @@ export default function FormTeks() {
|
|||
const router = useRouter();
|
||||
const editor = useRef(null);
|
||||
type TeksSchema = z.infer<typeof teksSchema>;
|
||||
|
||||
const params = useParams();
|
||||
const locale = params?.locale;
|
||||
|
||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||
const taskId = Cookies.get("taskId");
|
||||
const scheduleId = Cookies.get("scheduleId");
|
||||
const scheduleType = Cookies.get("scheduleType");
|
||||
const roleId = getCookiesDecrypt("urie");
|
||||
const [selectedFileType, setSelectedFileType] = useState("original");
|
||||
|
||||
const [categories, setCategories] = useState<Category[]>([]);
|
||||
const [selectedCategory, setSelectedCategory] = useState<any>();
|
||||
const [tags, setTags] = useState<any[]>([]);
|
||||
const [thumbnail, setThumbnail] = useState<File | null>(null);
|
||||
const [preview, setPreview] = useState<string | null>(null);
|
||||
const [selectedLanguage, setSelectedLanguage] = useState("");
|
||||
|
||||
const [selectedSEO, setSelectedSEO] = useState<string>("");
|
||||
const [title, setTitle] = useState<string>("");
|
||||
const [selectedAdvConfig, setSelectedAdvConfig] = useState<string>("");
|
||||
|
|
@ -109,9 +105,8 @@ export default function FormTeks() {
|
|||
const [isLoadingData, setIsLoadingData] = useState<boolean>(false);
|
||||
const [selectedWritingStyle, setSelectedWritingStyle] =
|
||||
useState("professional");
|
||||
const [editorContent, setEditorContent] = useState(""); // Untuk original editor
|
||||
const [editorContent, setEditorContent] = useState("");
|
||||
const [rewriteEditorContent, setRewriteEditorContent] = useState("");
|
||||
|
||||
const [articleIds, setArticleIds] = useState<string[]>([]);
|
||||
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
|
||||
const [articleBody, setArticleBody] = useState<string>("");
|
||||
|
|
@ -192,8 +187,8 @@ export default function FormTeks() {
|
|||
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
|
||||
category: z.string().min(1, { message: "Kategori harus dipilih" }),
|
||||
tags: z
|
||||
.array(z.string().min(1))
|
||||
.min(1, { message: "Minimal 1 tag diperlukan" }),
|
||||
.array(z.string())
|
||||
.min(1, { message: "Minimal 1 tag harus ditambahkan." }),
|
||||
files: z
|
||||
.array(
|
||||
z
|
||||
|
|
@ -220,6 +215,9 @@ export default function FormTeks() {
|
|||
description: z.string().optional(),
|
||||
descriptionOri: z.string().optional(),
|
||||
rewriteDescription: z.string().optional(),
|
||||
publishedFor: z
|
||||
.array(z.string())
|
||||
.min(1, { message: "Minimal 1 target publish harus dipilih." }),
|
||||
});
|
||||
|
||||
const {
|
||||
|
|
@ -228,6 +226,7 @@ export default function FormTeks() {
|
|||
getValues,
|
||||
setValue,
|
||||
formState: { errors },
|
||||
trigger,
|
||||
} = useForm<TeksSchema>({
|
||||
resolver: zodResolver(teksSchema),
|
||||
defaultValues: {
|
||||
|
|
@ -239,9 +238,14 @@ export default function FormTeks() {
|
|||
description: "",
|
||||
descriptionOri: "",
|
||||
rewriteDescription: "",
|
||||
publishedFor: [],
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setValue("publishedFor", publishedFor);
|
||||
}, [publishedFor, setValue]);
|
||||
|
||||
const doGenerateMainKeyword = async () => {
|
||||
console.log(selectedMainKeyword);
|
||||
if (selectedMainKeyword?.length > 1) {
|
||||
|
|
@ -434,22 +438,25 @@ export default function FormTeks() {
|
|||
}
|
||||
};
|
||||
|
||||
const handleAddTag = (
|
||||
e: React.KeyboardEvent<HTMLInputElement>,
|
||||
field: any
|
||||
) => {
|
||||
if (e.key === "Enter") {
|
||||
const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === "Enter" && e.currentTarget.value.trim()) {
|
||||
e.preventDefault();
|
||||
const value = e.currentTarget.value.trim();
|
||||
if (value && !field.value.includes(value)) {
|
||||
field.onChange([...field.value, value]);
|
||||
e.currentTarget.value = "";
|
||||
const newTag = e.currentTarget.value.trim();
|
||||
if (!tags.includes(newTag)) {
|
||||
const updatedTags = [...tags, newTag];
|
||||
setTags(updatedTags);
|
||||
setValue("tags", updatedTags);
|
||||
if (inputRef.current) {
|
||||
inputRef.current.value = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleRemoveTag = (index: number, field: any) => {
|
||||
const newTags = field.value.filter((_: any, i: number) => i !== index);
|
||||
field.onChange(newTags);
|
||||
|
||||
const handleRemoveTag = (index: number) => {
|
||||
const updatedTags = tags.filter((_, i) => i !== index);
|
||||
setTags(updatedTags);
|
||||
setValue("tags", updatedTags);
|
||||
};
|
||||
|
||||
const handleRemoveImage = (index: number) => {
|
||||
|
|
@ -528,9 +535,15 @@ export default function FormTeks() {
|
|||
|
||||
const save = async (data: TeksSchema) => {
|
||||
loading();
|
||||
|
||||
if (files.length === 0) {
|
||||
MySwal.fire("Error", "Minimal 1 file harus diunggah.", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
const finalTags = tags.join(", ");
|
||||
const finalTitle = isSwitchOn ? title : data.title;
|
||||
|
||||
// const finalDescription = articleBody || data.description;
|
||||
const finalDescription = isSwitchOn
|
||||
? data.description
|
||||
: selectedFileType === "rewrite"
|
||||
|
|
@ -541,6 +554,7 @@ export default function FormTeks() {
|
|||
MySwal.fire("Error", "Deskripsi tidak boleh kosong.", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
let requestData: {
|
||||
title: string;
|
||||
description: string;
|
||||
|
|
@ -555,7 +569,7 @@ export default function FormTeks() {
|
|||
tags: string;
|
||||
isYoutube: boolean;
|
||||
isInternationalMedia: boolean;
|
||||
attachFromScheduleId?: number; // ✅ Tambahkan properti ini
|
||||
attachFromScheduleId?: number;
|
||||
} = {
|
||||
...data,
|
||||
title: finalTitle,
|
||||
|
|
@ -576,49 +590,43 @@ export default function FormTeks() {
|
|||
let id = Cookies.get("idCreate");
|
||||
|
||||
if (scheduleId !== undefined) {
|
||||
requestData.attachFromScheduleId = Number(scheduleId); // ✅ Tambahkan nilai ini
|
||||
requestData.attachFromScheduleId = Number(scheduleId);
|
||||
}
|
||||
|
||||
if (id == undefined) {
|
||||
const response = await createMedia(requestData);
|
||||
console.log("Form Data Submitted:", requestData);
|
||||
|
||||
if (response?.error) {
|
||||
MySwal.fire("Error", response?.message, "error");
|
||||
return;
|
||||
}
|
||||
Cookies.set("idCreate", response?.data?.data, { expires: 1 });
|
||||
id = response?.data?.data;
|
||||
|
||||
// Upload Thumbnail
|
||||
const formMedia = new FormData();
|
||||
console.log("Thumbnail : ", files[0]);
|
||||
formMedia.append("file", files[0]);
|
||||
const thumbnail = files[0];
|
||||
formMedia.append("file", thumbnail);
|
||||
const responseThumbnail = await uploadThumbnail(id, formMedia);
|
||||
if (responseThumbnail?.error == true) {
|
||||
error(responseThumbnail?.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Upload File
|
||||
const progressInfoArr = [];
|
||||
for (const item of files) {
|
||||
progressInfoArr.push({ percentage: 0, fileName: item.name });
|
||||
}
|
||||
const progressInfoArr = files.map((item) => ({
|
||||
percentage: 0,
|
||||
fileName: item.name,
|
||||
}));
|
||||
progressInfo = progressInfoArr;
|
||||
setIsStartUpload(true);
|
||||
setProgressList(progressInfoArr);
|
||||
|
||||
close();
|
||||
// showProgress();
|
||||
files.map(async (item: any, index: number) => {
|
||||
await uploadResumableFile(index, String(id), item, "0");
|
||||
await uploadResumableFile(
|
||||
index,
|
||||
String(id),
|
||||
item,
|
||||
fileTypeId == "2" || fileTypeId == "4" ? item.duration : "0"
|
||||
);
|
||||
});
|
||||
|
||||
Cookies.remove("idCreate");
|
||||
|
||||
// MySwal.fire("Sukses", "Data berhasil disimpan.", "success");
|
||||
};
|
||||
|
||||
const onSubmit = (data: TeksSchema) => {
|
||||
|
|
@ -796,7 +804,6 @@ export default function FormTeks() {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Jika input title kosong, isi dengan hasil generate title
|
||||
if (!getValues("title") && title) {
|
||||
setValue("title", title);
|
||||
}
|
||||
|
|
@ -810,7 +817,7 @@ export default function FormTeks() {
|
|||
lang: "id",
|
||||
contextType: "text",
|
||||
urlContext: null,
|
||||
context: editorContent, // Ambil isi editor original
|
||||
context: editorContent,
|
||||
createdBy: roleId,
|
||||
sentiment: "Humorous",
|
||||
clientId: "7QTW8cMojyayt6qnhqTOeJaBI70W4EaQ",
|
||||
|
|
@ -1353,20 +1360,23 @@ export default function FormTeks() {
|
|||
<Label htmlFor="tags">
|
||||
{t("tags", { defaultValue: "Tags" })}
|
||||
</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="tags"
|
||||
render={({ field }) => (
|
||||
<>
|
||||
|
||||
<Input
|
||||
type="text"
|
||||
id="tags"
|
||||
placeholder="Add a tag and press Enter"
|
||||
onKeyDown={(e) => handleAddTag(e, field)} // pass field ke fungsi
|
||||
onKeyDown={handleAddTag}
|
||||
ref={inputRef}
|
||||
/>
|
||||
|
||||
{errors.tags && (
|
||||
<p className="text-sm text-red-500 mt-1">
|
||||
{errors.tags.message}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="mt-3">
|
||||
{field.value.map((tag, index) => (
|
||||
{tags.map((tag, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="px-1 py-1 rounded-lg bg-black text-white mr-2 text-sm font-sans"
|
||||
|
|
@ -1374,7 +1384,7 @@ export default function FormTeks() {
|
|||
{tag}{" "}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveTag(index, field)}
|
||||
onClick={() => handleRemoveTag(index)}
|
||||
className="remove-tag-button"
|
||||
>
|
||||
×
|
||||
|
|
@ -1382,15 +1392,9 @@ export default function FormTeks() {
|
|||
</span>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
{errors.tags?.message && (
|
||||
<p className="text-red-400 text-sm">{errors.tags.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="px-3 py-3">
|
||||
{/* <div className="px-3 py-3">
|
||||
<div className="flex flex-col gap-3 space-y-2">
|
||||
<Label>
|
||||
{t("publish-target", { defaultValue: "Publish Target" })}
|
||||
|
|
@ -1412,7 +1416,74 @@ export default function FormTeks() {
|
|||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div> */}
|
||||
<Controller
|
||||
control={control}
|
||||
name="publishedFor"
|
||||
render={({ field }) => (
|
||||
<div className="px-3 py-3">
|
||||
<div className="flex flex-col gap-3 space-y-2">
|
||||
<Label>
|
||||
{t("publish-target", { defaultValue: "Publish Target" })}
|
||||
</Label>
|
||||
|
||||
{options.map((option) => {
|
||||
const isAllChecked =
|
||||
field.value.length ===
|
||||
options.filter((opt: any) => opt.id !== "all").length;
|
||||
|
||||
const isChecked =
|
||||
option.id === "all"
|
||||
? isAllChecked
|
||||
: field.value.includes(option.id);
|
||||
|
||||
const handleChange = () => {
|
||||
let updated: string[] = [];
|
||||
|
||||
if (option.id === "all") {
|
||||
updated = isAllChecked
|
||||
? []
|
||||
: options
|
||||
.filter((opt: any) => opt.id !== "all")
|
||||
.map((opt: any) => opt.id);
|
||||
} else {
|
||||
updated = isChecked
|
||||
? field.value.filter((val) => val !== option.id)
|
||||
: [...field.value, option.id];
|
||||
|
||||
if (isAllChecked && option.id !== "all") {
|
||||
updated = updated.filter((val) => val !== "all");
|
||||
}
|
||||
}
|
||||
|
||||
field.onChange(updated);
|
||||
setPublishedFor(updated);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
key={option.id}
|
||||
className="flex gap-2 items-center"
|
||||
>
|
||||
<Checkbox
|
||||
id={option.id}
|
||||
checked={isChecked}
|
||||
onCheckedChange={handleChange}
|
||||
/>
|
||||
<Label htmlFor={option.id}>{option.label}</Label>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
{errors.publishedFor && (
|
||||
<p className="text-red-500 text-sm">
|
||||
{errors.publishedFor.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</Card>
|
||||
<div className="flex flex-row justify-end gap-3">
|
||||
<div className="mt-4">
|
||||
|
|
|
|||
|
|
@ -85,7 +85,6 @@ export default function FormVideo() {
|
|||
type VideoSchema = z.infer<typeof videoSchema>;
|
||||
const params = useParams();
|
||||
const locale = params?.locale;
|
||||
|
||||
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||
const taskId = Cookies.get("taskId");
|
||||
const scheduleId = Cookies.get("scheduleId");
|
||||
|
|
@ -116,7 +115,9 @@ export default function FormVideo() {
|
|||
null
|
||||
);
|
||||
const [selectedMainKeyword, setSelectedMainKeyword] = useState("");
|
||||
// const [selectedWritingStyle, setSelectedWritingStyle] = useState("");
|
||||
const [publishedForError, setPublishedForError] = useState<string | null>(
|
||||
null
|
||||
);
|
||||
const [selectedSize, setSelectedSize] = useState("");
|
||||
const [detailData, setDetailData] = useState<any>(null);
|
||||
const [articleImages, setArticleImages] = useState<string[]>([]);
|
||||
|
|
@ -211,6 +212,9 @@ export default function FormVideo() {
|
|||
(files) => files.every((file: File) => file.size <= MAX_FILE_SIZE),
|
||||
{ message: "Ukuran file maksimal 100 MB" }
|
||||
),
|
||||
publishedFor: z
|
||||
.array(z.string())
|
||||
.min(1, { message: "Minimal 1 target publish harus dipilih." }),
|
||||
});
|
||||
|
||||
const {
|
||||
|
|
@ -228,9 +232,14 @@ export default function FormVideo() {
|
|||
category: "",
|
||||
files: [],
|
||||
tags: [],
|
||||
publishedFor: [],
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setValue("publishedFor", publishedFor);
|
||||
}, [publishedFor, setValue]);
|
||||
|
||||
const doGenerateMainKeyword = async () => {
|
||||
console.log(selectedMainKeyword);
|
||||
if (selectedMainKeyword?.length > 1) {
|
||||
|
|
@ -511,6 +520,12 @@ export default function FormVideo() {
|
|||
}, [articleBody, setValue]);
|
||||
|
||||
const save = async (data: VideoSchema) => {
|
||||
if (publishedFor.length === 0) {
|
||||
setPublishedForError("Minimal 1 target publish harus dipilih.");
|
||||
return;
|
||||
} else {
|
||||
setPublishedForError(null);
|
||||
}
|
||||
loading();
|
||||
const finalTags = data.tags.join(", ");
|
||||
const finalTitle = isSwitchOn ? title : data.title;
|
||||
|
|
@ -600,8 +615,6 @@ export default function FormVideo() {
|
|||
});
|
||||
|
||||
Cookies.remove("idCreate");
|
||||
|
||||
// MySwal.fire("Sukses", "Data berhasil disimpan.", "success");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -1427,29 +1440,73 @@ export default function FormVideo() {
|
|||
<p className="text-red-400 text-sm">{errors.tags.message}</p>
|
||||
)}
|
||||
</div>
|
||||
<Controller
|
||||
control={control}
|
||||
name="publishedFor"
|
||||
render={({ field }) => (
|
||||
<div className="px-3 py-3">
|
||||
<div className="flex flex-col gap-3 space-y-2">
|
||||
<Label>
|
||||
{t("publish-target", { defaultValue: "Publish Target" })}
|
||||
</Label>
|
||||
{options.map((option) => (
|
||||
<div key={option.id} className="flex gap-2 items-center">
|
||||
|
||||
{options.map((option) => {
|
||||
const isAllChecked =
|
||||
field.value.length ===
|
||||
options.filter((opt: any) => opt.id !== "all").length;
|
||||
|
||||
const isChecked =
|
||||
option.id === "all"
|
||||
? isAllChecked
|
||||
: field.value.includes(option.id);
|
||||
|
||||
const handleChange = () => {
|
||||
let updated: string[] = [];
|
||||
|
||||
if (option.id === "all") {
|
||||
updated = isAllChecked
|
||||
? []
|
||||
: options
|
||||
.filter((opt: any) => opt.id !== "all")
|
||||
.map((opt: any) => opt.id);
|
||||
} else {
|
||||
updated = isChecked
|
||||
? field.value.filter((val) => val !== option.id)
|
||||
: [...field.value, option.id];
|
||||
|
||||
if (isAllChecked && option.id !== "all") {
|
||||
updated = updated.filter((val) => val !== "all");
|
||||
}
|
||||
}
|
||||
|
||||
field.onChange(updated);
|
||||
setPublishedFor(updated);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
key={option.id}
|
||||
className="flex gap-2 items-center"
|
||||
>
|
||||
<Checkbox
|
||||
id={option.id}
|
||||
checked={
|
||||
option.id === "all"
|
||||
? publishedFor.length ===
|
||||
options.filter((opt: any) => opt.id !== "all")
|
||||
.length
|
||||
: publishedFor.includes(option.id)
|
||||
}
|
||||
onCheckedChange={() => handleCheckboxChange(option.id)}
|
||||
checked={isChecked}
|
||||
onCheckedChange={handleChange}
|
||||
/>
|
||||
<Label htmlFor={option.id}>{option.label}</Label>
|
||||
</div>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
|
||||
{errors.publishedFor && (
|
||||
<p className="text-red-500 text-sm">
|
||||
{errors.publishedFor.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</Card>
|
||||
<div className="flex flex-row justify-end gap-3">
|
||||
<div className="mt-4">
|
||||
|
|
|
|||
Loading…
Reference in New Issue