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