mediahub-fe/components/form/content/video-update-form.tsx

2155 lines
84 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import React, {
ChangeEvent,
Fragment,
useEffect,
useRef,
useState,
} from "react";
import { useForm, Controller } from "react-hook-form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Card } from "@/components/ui/card";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { useParams, useRouter } from "next/navigation";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Checkbox } from "@/components/ui/checkbox";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import {
Dialog,
DialogClose,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { register } from "module";
import { Switch } from "@/components/ui/switch";
import Cookies from "js-cookie";
import {
createMedia,
deleteFile,
getTagsBySubCategoryId,
listEnableCategory,
uploadThumbnail,
updateFilePlacements,
listEnableCategoryNew,
} from "@/service/content/content";
import { getUserLevelForAssignments } from "@/service/task";
import { detailMedia } from "@/service/curated-content/curated-content";
import { Badge } from "@/components/ui/badge";
import { CloudUpload, MailIcon } from "lucide-react";
import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css";
import "swiper/css/free-mode";
import "swiper/css/navigation";
import "swiper/css/pagination";
import "swiper/css/thumbs";
import "swiper/css";
import "swiper/css/navigation";
import { FreeMode, Navigation, Pagination, Thumbs } from "swiper/modules";
import { useDropzone } from "react-dropzone";
import Image from "next/image";
import { Icon } from "@iconify/react/dist/iconify.js";
import { Upload } from "tus-js-client";
import { getCsrfToken } from "@/service/auth";
import { error, loading } from "@/lib/swal";
import { useTranslations } from "next-intl";
import dynamic from "next/dynamic";
import { htmlToString } from "@/utils/globals";
import { v4 as uuidv4 } from "uuid";
import { getCookiesDecrypt } from "@/lib/utils";
import { translateText } from "@/service/content/ai";
import { close } from "@/config/swal";
const videoSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
description: z
.string()
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
creatorName: z.string().min(1, { message: "Creator diperlukan" }),
// tags: z.string().min(1, { message: "Judul diperlukan" }),
});
type Category = {
id: string;
name: string;
};
type Detail = {
id: string;
title: string;
description: string;
slug: string;
categoryId: number;
category: {
id: number;
name: string;
};
creatorName: string;
categoryName: string;
thumbnailLink: string;
tags: string;
};
interface FileWithPreview extends File {
id: string;
preview: string;
}
type Option = {
id: string;
name: string;
};
type PlacementType =
| "all"
| "mabes"
| "wilayah"
| "polda"
| "satker"
| "international";
interface FilePlacement {
id: string;
placements: PlacementType[];
}
interface TempFileItem {
id: string;
fileName: string;
url: string;
thumbnailFileUrl: string;
placements: string;
customLocationPlacements: string;
}
interface Destination {
id: number;
name: string;
levelNumber: number;
subDestination: SubDestination[];
}
interface SubDestination {
id: number;
name: string;
levelNumber: number;
}
const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
);
export default function FormVideoUpdate() {
const MySwal = withReactContent(Swal);
const router = useRouter();
const { id } = useParams() as { id: string };
console.log(id);
const editor = useRef(null);
type VideoSchema = z.infer<typeof videoSchema>;
let progressInfo: any = [];
let counterUpdateProgress = 0;
// const isDetailOfRegionShowed = false;
const [isDetailOfRegionShowed, setIsDetailOfRegionShowed] = useState(false);
const [progressList, setProgressList] = useState<any>([]);
let uploadPersen = 0;
const [isStartUpload, setIsStartUpload] = useState(false);
const [counterProgress, setCounterProgress] = useState(0);
const t = useTranslations("Form");
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId");
const scheduleId = Cookies.get("scheduleId");
const scheduleType = Cookies.get("scheduleType");
const [categories, setCategories] = useState<Category[]>([]);
const [selectedCategory, setSelectedCategory] = useState<any>();
const [tags, setTags] = useState<any[]>([]);
const [detail, setDetail] = useState<Detail>();
const [refresh, setRefresh] = useState(false);
const [selectedPublishers, setSelectedPublishers] = useState<number[]>([]);
const [detailVideo, setDetailVideo] = useState<any>([]);
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
const [files, setFiles] = useState<FileWithPreview[]>([]);
const [selectedTarget, setSelectedTarget] = useState("");
const [publishedFor, setPublishedFor] = useState<string[]>([]);
const inputRef = useRef<HTMLInputElement>(null);
const [selectedOptions, setSelectedOptions] = useState<{
[fileId: number]: string[];
}>({});
const [fileUnitSelections, setFileUnitSelections] = useState<{
[fileId: string]: {
semua: boolean;
nasional: boolean;
wilayah: boolean;
international: boolean;
polda: boolean;
satker: boolean;
};
}>({});
const [unitSelection, setUnitSelection] = useState({
semua: false,
nasional: false,
wilayah: false,
international: false,
polda: false,
satker: false,
});
const [checkedLevels, setCheckedLevels] = useState<Set<number>>(new Set());
const [listDest, setListDest] = useState<Destination[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [expandedPolda, setExpandedPolda] = useState<Set<number>>(new Set());
const [fileCheckedLevels, setFileCheckedLevels] = useState<{
[fileId: string]: Set<number>;
}>({});
const [isUpdatingFromMainCheckbox, setIsUpdatingFromMainCheckbox] =
useState(false);
const [mainCheckboxChangeType, setMainCheckboxChangeType] = useState<
string | null
>(null);
const [filePlacements, setFilePlacements] = useState<{
[fileId: string]: PlacementType[];
}>({});
const [tempFile, setTempFile] = useState<TempFileItem | null>(null);
const [isLoadingTranslate, setIsLoadingTranslate] = useState(false);
const [translatedContent, setTranslatedContent] = React.useState("");
const [selectedLang, setSelectedLang] = React.useState<"id" | "en">("id");
const roleId = getCookiesDecrypt("urie");
const [translatedTitle, setTranslatedTitle] = useState("");
const options: Option[] = [
{ id: "all", name: "SEMUA" },
{ id: "5", name: "UMUM" },
{ id: "6", name: "JOURNALIS" },
{ id: "7", name: "POLRI" },
{ id: "8", name: "KSP" },
];
let fileTypeId = "2";
const { getRootProps, getInputProps } = useDropzone({
onDrop: (acceptedFiles) => {
setFiles(
acceptedFiles.map((file) =>
Object.assign(file, {
id: uuidv4(),
preview: URL.createObjectURL(file),
})
)
);
},
accept: {
"video/*": [],
},
});
const {
control,
handleSubmit,
getValues,
setValue,
formState: { errors },
} = useForm<VideoSchema>({
resolver: zodResolver(videoSchema),
});
const handleImageChange = (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.files) {
const files = Array.from(event.target.files);
setSelectedFiles((prevImages: any) => [...prevImages, ...files]);
console.log("DATAFILE::", selectedFiles);
}
};
const handleRemoveImage = (index: number) => {
setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index));
};
useEffect(() => {
async function initState() {
getCategories();
}
initState();
}, []);
useEffect(() => {
const fetchPoldaPolres = async () => {
try {
setIsLoading(true);
const response = await getUserLevelForAssignments();
if (response?.data?.data) {
setListDest(response.data.data.list);
}
} catch (error) {
console.error("Error fetching user levels:", error);
} finally {
setIsLoading(false);
}
};
fetchPoldaPolres();
}, []);
useEffect(() => {
if (isUpdatingFromMainCheckbox && mainCheckboxChangeType) {
syncModalWithMainCheckbox();
setIsUpdatingFromMainCheckbox(false);
setMainCheckboxChangeType(null);
}
}, [isUpdatingFromMainCheckbox, mainCheckboxChangeType]);
useEffect(() => {
updateMainCheckboxFromModalLegacy();
}, [checkedLevels]);
const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter" && e.currentTarget.value.trim()) {
e.preventDefault();
const newTag = e.currentTarget.value.trim();
if (!tags.includes(newTag)) {
setTags((prevTags) => [...prevTags, newTag]); // Tambahkan tag baru
if (inputRef.current) {
inputRef.current.value = ""; // Kosongkan input
}
}
}
};
const handleRemoveTag = (index: number) => {
setTags((prevTags) => prevTags.filter((_, i) => i !== index));
};
const handleEditTag = (index: number, newValue: string) => {
setTags((prevTags) =>
prevTags.map((tag, i) => (i === index ? newValue : tag))
);
};
const getCategories = async () => {
try {
const category = await listEnableCategoryNew(fileTypeId);
const resCategory: Category[] = category?.data?.data?.content;
setCategories(resCategory);
console.log("data category", resCategory);
if (scheduleId && scheduleType === "3") {
const findCategory = resCategory.find((o) =>
o.name.toLowerCase().includes("pers rilis")
);
if (findCategory) {
// setValue("categoryId", findCategory.id);
setSelectedCategory(findCategory.id); // Set the selected category
const response = await getTagsBySubCategoryId(findCategory.id);
setTags(response?.data?.data);
}
}
} catch (error) {
console.error("Failed to fetch categories:", error);
}
};
useEffect(() => {
async function initState() {
if (id) {
const response = await detailMedia(id);
const details = response?.data?.data;
setDetail(details);
setSelectedTarget(String(details.category.id));
// Set form values immediately and then again after a delay to ensure editor is ready
setValue("title", details.title);
setValue("description", details.htmlDescription);
setValue("creatorName", details.creatorName);
// Set again after delay to ensure editor has loaded
setTimeout(() => {
setValue("title", details.title);
setValue("description", details.htmlDescription);
setValue("creatorName", details.creatorName);
}, 500);
if (details?.files) {
setFiles(details.files);
// Initialize filePlacements
const initialFilePlacements: { [key: string]: PlacementType[] } = {};
details.files.forEach((file: any) => {
if (file.placements) {
const placements = file.placements
.split(",")
.map((p: string) => p.trim() as PlacementType);
initialFilePlacements[file.id] = placements;
}
});
setFilePlacements(initialFilePlacements);
// Initialize fileCheckedLevels
const initialFileCheckedLevels: { [key: string]: Set<number> } = {};
details.files.forEach((file: any) => {
if (file.customLocationPlacements) {
const levelIds = file.customLocationPlacements
.split(",")
.map((id: string) => parseInt(id.trim()));
initialFileCheckedLevels[file.id] = new Set(levelIds);
}
});
setFileCheckedLevels(initialFileCheckedLevels);
// Initialize fileUnitSelections
const initialFileUnitSelections: { [key: string]: any } = {};
details.files.forEach((file: any) => {
if (file.placements) {
const placements = file.placements
.split(",")
.map((p: string) => p.trim());
initialFileUnitSelections[file.id] = {
semua: placements.includes("all"),
nasional: placements.includes("mabes"),
wilayah:
placements.includes("wilayah") ||
placements.includes("polda") ||
placements.includes("satker"),
international: placements.includes("international"),
polda: placements.includes("polda"),
satker: placements.includes("satker"),
};
}
});
setFileUnitSelections(initialFileUnitSelections);
}
if (details?.publishedFor) {
// Split string "7" to an array ["7"] if needed
setPublishedFor(details.publishedFor.split(","));
}
if (details?.tags) {
setTags(details.tags.split(",").map((tag: string) => tag.trim()));
}
// const matchingCategory = categories.find(
// (category) => category.id === details.categoryId
// );
// console.log("LALALALAL", matchingCategory)
// if (matchingCategory) {
// setSelectedTarget(String(matchingCategory.id));
// }
// setSelectedTarget(details.categoryId); // Untuk dropdown
const filesData = details.files || [];
const fileUrls = filesData.map((files: { url: string }) =>
files.url ? files.url : "default-image.jpg"
);
setDetailVideo(fileUrls);
}
}
initState();
}, [refresh, setValue]);
const updateMainCheckboxFromModalLegacy = () => {
const checkedPoldaCount = listDest.filter(
(item) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
checkedLevels.has(item.id)
).length;
const hasSelectedPolda = checkedPoldaCount > 0;
const allPoldaChecked =
checkedPoldaCount ===
listDest.filter(
(item) => item.levelNumber === 2 && item.name !== "SATKER POLRI"
).length;
const checkedSatkerCount = listDest.filter(
(item) =>
item.levelNumber === 2 &&
item.name === "SATKER POLRI" &&
checkedLevels.has(item.id)
).length;
const hasSelectedSatker = checkedSatkerCount > 0;
const allSatkerChecked =
checkedSatkerCount ===
listDest.filter(
(item) => item.levelNumber === 2 && item.name === "SATKER POLRI"
).length;
setUnitSelection((prev) => ({
...prev,
polda: hasSelectedPolda,
satker: hasSelectedSatker,
wilayah: hasSelectedPolda || hasSelectedSatker,
}));
};
const syncModalWithMainCheckbox = () => {
if (mainCheckboxChangeType === "wilayah_checked") {
const poldaIds = listDest
.filter(
(item) => item.levelNumber === 2 && item.name !== "SATKER POLRI"
)
.map((item) => item.id);
const satkerIds = listDest
.filter(
(item) => item.levelNumber === 2 && item.name === "SATKER POLRI"
)
.map((item) => item.id);
const allSatkerSubIds = listDest
.filter(
(item) => item.levelNumber === 2 && item.name === "SATKER POLRI"
)
.flatMap((item) => item.subDestination.map((sub) => sub.id));
setCheckedLevels((prev) => {
const newSet = new Set(prev);
[...poldaIds, ...satkerIds, ...allSatkerSubIds].forEach((id) =>
newSet.add(id)
);
return newSet;
});
} else if (mainCheckboxChangeType === "wilayah_unchecked") {
const poldaIds = listDest
.filter(
(item) => item.levelNumber === 2 && item.name !== "SATKER POLRI"
)
.map((item) => item.id);
const satkerIds = listDest
.filter(
(item) => item.levelNumber === 2 && item.name === "SATKER POLRI"
)
.map((item) => item.id);
const allSatkerSubIds = listDest
.filter(
(item) => item.levelNumber === 2 && item.name === "SATKER POLRI"
)
.flatMap((item) => item.subDestination.map((sub) => sub.id));
setCheckedLevels((prev) => {
const newSet = new Set(prev);
[...poldaIds, ...satkerIds, ...allSatkerSubIds].forEach((id) =>
newSet.delete(id)
);
return newSet;
});
} else if (mainCheckboxChangeType === "polda_checked") {
const poldaIds = listDest
.filter(
(item) => item.levelNumber === 2 && item.name !== "SATKER POLRI"
)
.map((item) => item.id);
setCheckedLevels((prev) => {
const newSet = new Set(prev);
poldaIds.forEach((id) => newSet.add(id));
return newSet;
});
} else if (mainCheckboxChangeType === "polda_unchecked") {
const poldaIds = listDest
.filter(
(item) => item.levelNumber === 2 && item.name !== "SATKER POLRI"
)
.map((item) => item.id);
setCheckedLevels((prev) => {
const newSet = new Set(prev);
poldaIds.forEach((id) => newSet.delete(id));
return newSet;
});
} else if (mainCheckboxChangeType === "satker_checked") {
const satkerIds = listDest
.filter(
(item) => item.levelNumber === 2 && item.name === "SATKER POLRI"
)
.map((item) => item.id);
const allSatkerSubIds = listDest
.filter(
(item) => item.levelNumber === 2 && item.name === "SATKER POLRI"
)
.flatMap((item) => item.subDestination.map((sub) => sub.id));
setCheckedLevels((prev) => {
const newSet = new Set(prev);
[...satkerIds, ...allSatkerSubIds].forEach((id) => newSet.add(id));
return newSet;
});
} else if (mainCheckboxChangeType === "satker_unchecked") {
const satkerIds = listDest
.filter(
(item) => item.levelNumber === 2 && item.name === "SATKER POLRI"
)
.map((item) => item.id);
const allSatkerSubIds = listDest
.filter(
(item) => item.levelNumber === 2 && item.name === "SATKER POLRI"
)
.flatMap((item) => item.subDestination.map((sub) => sub.id));
setCheckedLevels((prev) => {
const newSet = new Set(prev);
[...satkerIds, ...allSatkerSubIds].forEach((id) => newSet.delete(id));
return newSet;
});
}
};
const handleFileUnitChange = (
fileId: string,
key: string,
value: boolean
) => {
setFileUnitSelections((prev) => {
const currentSelection = prev[fileId] || {
semua: false,
nasional: false,
wilayah: false,
international: false,
polda: false,
satker: false,
};
const newSelection = { ...currentSelection, [key]: value };
if (key === "wilayah" && value) {
newSelection.polda = true;
newSelection.satker = true;
setIsDetailOfRegionShowed(value);
// Update fileCheckedLevels for wilayah
setFileCheckedLevels((prevLevels) => {
const currentFileLevels = prevLevels[fileId] || new Set<number>();
const newFileLevels = new Set(currentFileLevels);
// Add all POLDA items
listDest.forEach((item) => {
if (item.levelNumber === 2 && item.name !== "SATKER POLRI") {
newFileLevels.add(item.id);
}
});
// Add SATKER POLRI items and their sub-destinations
listDest.forEach((item) => {
if (item.levelNumber === 2 && item.name === "SATKER POLRI") {
newFileLevels.add(item.id);
item.subDestination.forEach((satkerItem) => {
newFileLevels.add(satkerItem.id);
});
}
});
return { ...prevLevels, [fileId]: newFileLevels };
});
} else if (key === "wilayah" && !value) {
newSelection.polda = false;
newSelection.satker = false;
// Clear fileCheckedLevels for wilayah
setFileCheckedLevels((prevLevels) => {
const newFileLevels = new Set<number>();
return { ...prevLevels, [fileId]: newFileLevels };
});
} else if (key === "semua") {
newSelection.nasional = value;
newSelection.wilayah = value;
newSelection.international = value;
newSelection.polda = value;
newSelection.satker = value;
}
return { ...prev, [fileId]: newSelection };
});
// Update filePlacements
setFilePlacements((prev) => {
const currentPlacements = prev[fileId] || [];
let newPlacements = [...currentPlacements];
if (key === "semua" && value) {
newPlacements = [
"all",
"mabes",
"wilayah",
"polda",
"satker",
"international",
];
} else if (key === "semua" && !value) {
newPlacements = [];
} else if (key === "nasional" && value) {
if (!newPlacements.includes("mabes")) newPlacements.push("mabes");
} else if (key === "nasional" && !value) {
newPlacements = newPlacements.filter((p) => p !== "mabes");
} else if (key === "wilayah" && value) {
if (!newPlacements.includes("wilayah")) newPlacements.push("wilayah");
if (!newPlacements.includes("polda")) newPlacements.push("polda");
if (!newPlacements.includes("satker")) newPlacements.push("satker");
} else if (key === "wilayah" && !value) {
newPlacements = newPlacements.filter(
(p) => !["wilayah", "polda", "satker"].includes(p)
);
} else if (key === "international" && value) {
if (!newPlacements.includes("international"))
newPlacements.push("international");
} else if (key === "international" && !value) {
newPlacements = newPlacements.filter((p) => p !== "international");
} else if (key === "polda" && value) {
if (!newPlacements.includes("polda")) newPlacements.push("polda");
} else if (key === "polda" && !value) {
newPlacements = newPlacements.filter((p) => p !== "polda");
} else if (key === "satker" && value) {
if (!newPlacements.includes("satker")) newPlacements.push("satker");
} else if (key === "satker" && !value) {
newPlacements = newPlacements.filter((p) => p !== "satker");
}
// Remove "all" if any individual option is unchecked
if (newPlacements.includes("all") && !value && key !== "semua") {
newPlacements = newPlacements.filter((p) => p !== "all");
}
return { ...prev, [fileId]: newPlacements };
});
};
const handleCheckboxChange = (id: string) => {
if (id === "all") {
// Select all options except "all"
const allOptions = options
.filter((opt) => opt.id !== "all")
.map((opt) => opt.id);
setPublishedFor(
publishedFor.length === allOptions.length ? [] : allOptions
);
} else {
// Toggle individual option
setPublishedFor((prev) =>
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
);
}
};
const handleFileCheckboxChangePlacement = (
fileId: string,
poldaItem: Destination,
isChecked: boolean
) => {
setFileCheckedLevels((prev) => {
const currentFileLevels = prev[fileId] || new Set<number>();
const newFileLevels = new Set(currentFileLevels);
if (isChecked) {
newFileLevels.add(poldaItem.id);
// Add all sub-destinations for SATKER POLRI
if (poldaItem.name === "SATKER POLRI") {
poldaItem.subDestination.forEach((satkerItem: SubDestination) => {
newFileLevels.add(satkerItem.id);
});
}
} else {
newFileLevels.delete(poldaItem.id);
// Remove all sub-destinations for SATKER POLRI
if (poldaItem.name === "SATKER POLRI") {
poldaItem.subDestination.forEach((satkerItem: SubDestination) => {
newFileLevels.delete(satkerItem.id);
});
}
}
return { ...prev, [fileId]: newFileLevels };
});
// Update fileUnitSelections based on checked levels
const updatedFileLevels = fileCheckedLevels[fileId] || new Set<number>();
const hasPolda = listDest.some(
(item) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
updatedFileLevels.has(item.id)
);
const hasSatker = listDest.some(
(item) =>
item.levelNumber === 2 &&
item.name === "SATKER POLRI" &&
updatedFileLevels.has(item.id)
);
setFileUnitSelections((prev) => ({
...prev,
[fileId]: {
...prev[fileId],
polda: hasPolda,
satker: hasSatker,
wilayah: hasPolda || hasSatker,
},
}));
};
const updateMainCheckboxFromModal = (fileId: string) => {
const fileLevels = fileCheckedLevels[fileId] || new Set<number>();
const hasPolda = listDest.some(
(item) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
fileLevels.has(item.id)
);
const hasSatker = listDest.some(
(item) =>
item.levelNumber === 2 &&
item.name === "SATKER POLRI" &&
fileLevels.has(item.id)
);
setFileUnitSelections((prev) => ({
...prev,
[fileId]: {
...prev[fileId],
polda: hasPolda,
satker: hasSatker,
wilayah: hasPolda || hasSatker,
},
}));
};
const toggleExpand = (poldaId: number) => {
setExpandedPolda((prev) => {
const newSet = new Set(prev);
if (newSet.has(poldaId)) {
newSet.delete(poldaId);
} else {
newSet.add(poldaId);
}
return newSet;
});
};
const handleSelectAllSubItems = (fileId: string, poldaItem: Destination) => {
const fileLevels = fileCheckedLevels[fileId] || new Set<number>();
const allSubItemsSelected = poldaItem.subDestination.every((subItem) =>
fileLevels.has(subItem.id)
);
setFileCheckedLevels((prev) => {
const currentFileLevels = prev[fileId] || new Set<number>();
const newFileLevels = new Set(currentFileLevels);
if (allSubItemsSelected) {
// Unselect all sub-items
poldaItem.subDestination.forEach((subItem) => {
newFileLevels.delete(subItem.id);
});
} else {
// Select all sub-items
poldaItem.subDestination.forEach((subItem) => {
newFileLevels.add(subItem.id);
});
}
return { ...prev, [fileId]: newFileLevels };
});
};
const getPlacement = () => {
const temp = [];
for (let i = 0; i < files.length; i++) {
const file = files[i] as any;
if (
file.id &&
filePlacements[file.id] &&
filePlacements[file.id].length > 0
) {
const now = filePlacements[file.id];
const normalizedNow = now.map((item): PlacementType => {
const value = String(item);
if (value === "nasional") return "mabes";
if (value === "wilayah") return "polda";
if (value === "semua") return "all";
return item as PlacementType;
});
const uniqueNow = Array.from(new Set(normalizedNow));
const nowArr = uniqueNow.join(",");
// Dapatkan checked levels untuk file ini
const currentFileCheckedLevels = fileCheckedLevels[file.id]
? Array.from(fileCheckedLevels[file.id])
: [];
const data = {
mediaFileId: file.id,
placements: nowArr,
customLocationPlacements: currentFileCheckedLevels.join(","),
};
temp.push(data);
}
}
return temp;
};
const setupPlacement = (
fileId: string,
placement: string,
isChecked: boolean
) => {
setFilePlacements((prev) => {
const currentPlacements = prev[fileId] || [];
let temp = { ...prev };
if (isChecked) {
if (placement === "all") {
temp[fileId] = [
"all",
"mabes",
"wilayah",
"polda",
"satker",
"international",
];
} else if (placement === "wilayah") {
temp[fileId] = [...currentPlacements, "wilayah", "polda", "satker"];
} else if (placement === "polda") {
temp[fileId] = [...currentPlacements, "polda"];
} else if (placement === "satker") {
temp[fileId] = [...currentPlacements, "satker"];
} else {
temp[fileId] = [...currentPlacements, placement as PlacementType];
}
} else {
if (placement === "all") {
temp[fileId] = [];
} else if (placement === "wilayah") {
temp[fileId] = currentPlacements.filter(
(p) => !["wilayah", "polda", "satker"].includes(p)
);
} else if (placement === "polda") {
temp[fileId] = currentPlacements.filter((p) => p !== "polda");
} else if (placement === "satker") {
temp[fileId] = currentPlacements.filter((p) => p !== "satker");
} else {
temp[fileId] = currentPlacements.filter((p) => p !== placement);
}
}
return temp;
});
};
const updateModalChecklistLevels = (fileId: string) => {
const fileLevels = fileCheckedLevels[fileId] || new Set<number>();
const hasPolda = listDest.some(
(item) =>
item.levelNumber === 2 &&
item.name !== "SATKER POLRI" &&
fileLevels.has(item.id)
);
const hasSatker = listDest.some(
(item) =>
item.levelNumber === 2 &&
item.name === "SATKER POLRI" &&
fileLevels.has(item.id)
);
setFileUnitSelections((prev) => ({
...prev,
[fileId]: {
...prev[fileId],
polda: hasPolda,
satker: hasSatker,
wilayah: hasPolda || hasSatker,
},
}));
};
// const save = async (data: VideoSchema) => {
// loading();
// const finalTags = tags.join(", ");
// const requestData = {
// ...data,
// id: detail?.id,
// title: data.title,
// description: data.description,
// htmlDescription: data.description,
// fileTypeId,
// categoryId: selectedTarget,
// subCategoryId: selectedTarget,
// uploadedBy: "2b7c8d83-d298-4b19-9f74-b07924506b58",
// statusId: "1",
// publishedFor: publishedFor.join(","),
// creatorName: data.creatorName,
// tags: finalTags,
// isYoutube: false,
// isInternationalMedia: false,
// };
// const response = await createMedia(requestData);
// console.log("Form Data Submitted:", requestData);
// const formMedia = new FormData();
// 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 });
// }
// progressInfo = progressInfoArr;
// setIsStartUpload(true);
// setProgressList(progressInfoArr);
// close();
// // showProgress();
// files.map(async (item: any, index: number) => {
// await uploadResumableFile(index, String(id), item, "0");
// });
// // MySwal.fire({
// // title: "Sukses",
// // text: "Data berhasil disimpan.",
// // icon: "success",
// // confirmButtonColor: "#3085d6",
// // confirmButtonText: "OK",
// // }).then(() => {
// // router.push("/en/contributor/content/video");
// // });
// };
const save = async (data: VideoSchema) => {
loading();
const finalTags = tags.join(", ");
// const descFinal =
// selectedLang === "en" && translatedContent
// ? translatedContent
// : data.description;
const descFinal = translatedContent || data.description;
const finalTitle = translatedTitle || data.title;
const requestData = {
...data,
id: detail?.id,
// title: data.title,
// description: htmlToString(descFinal),
// htmlDescription: descFinal,
title: finalTitle,
description: htmlToString(descFinal),
htmlDescription: descFinal,
fileTypeId,
categoryId: selectedTarget,
subCategoryId: selectedTarget,
uploadedBy: "2b7c8d83-d298-4b19-9f74-b07924506b58",
statusId: "1",
publishedFor: publishedFor.join(","),
creatorName: data.creatorName,
tags: finalTags,
isYoutube: false,
isInternationalMedia: false,
};
const response = await createMedia(requestData);
console.log("Form Data Submitted (Metadata):", requestData);
if (response?.error) {
error(response?.message);
return false;
}
// Update file placements
const responseFilePlacements = await updateFilePlacements(getPlacement());
if (responseFilePlacements?.error) {
error(responseFilePlacements?.message);
return false;
}
if (selectedFiles.length > 0) {
const thumbnail = selectedFiles[0];
const formMedia = new FormData();
formMedia.append("file", thumbnail);
const responseThumbnail = await uploadThumbnail(id, formMedia);
if (responseThumbnail?.error) {
error(responseThumbnail?.message);
return false;
}
console.log("Thumbnail uploaded:", responseThumbnail);
}
const newVideoFilesToUpload = files.filter((file) => file instanceof File);
if (newVideoFilesToUpload.length > 0) {
const progressInfoArr = newVideoFilesToUpload.map((item) => ({
percentage: 0,
fileName: item.name,
}));
progressInfo = progressInfoArr;
setIsStartUpload(true);
setProgressList(progressInfoArr);
newVideoFilesToUpload.map(async (item: File, index: number) => {
await uploadResumableFile(index, String(id), item, "0");
});
} else {
successSubmit("/in/contributor/content/video");
}
};
const onSubmit = (data: VideoSchema) => {
MySwal.fire({
title: "Simpan Data",
text: "Apakah Anda yakin ingin menyimpan data ini?",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Simpan",
}).then((result) => {
if (result.isConfirmed) {
save(data);
}
});
};
async function uploadResumableFile(
idx: number,
id: string,
file: any,
duration: string
) {
console.log(idx, id, file, duration);
// const placements = getPlacement(file.placements);
// console.log("Placementttt: : ", placements);
const resCsrf = await getCsrfToken();
const csrfToken = resCsrf?.data?.token;
console.log("CSRF TOKEN : ", csrfToken);
const headers = {
"X-XSRF-TOKEN": csrfToken,
};
const upload = new Upload(file, {
endpoint: `${process.env.NEXT_PUBLIC_API}/media/file/upload`,
headers: headers,
retryDelays: [0, 3000, 6000, 12_000, 24_000],
chunkSize: 20_000,
metadata: {
mediaid: id,
filename: file.name,
filetype: file.type,
duration,
isWatermark: "false", // hardcode
},
onBeforeRequest: function (req) {
var xhr = req.getUnderlyingObject();
xhr.withCredentials = true;
},
onError: async (e: any) => {
console.log("Error upload :", e);
error(e);
},
onChunkComplete: (
chunkSize: any,
bytesAccepted: any,
bytesTotal: any
) => {
const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100);
progressInfo[idx].percentage = uploadPersen;
counterUpdateProgress++;
console.log(counterUpdateProgress);
setProgressList(progressInfo);
setCounterProgress(counterUpdateProgress);
},
onSuccess: async () => {
uploadPersen = 100;
progressInfo[idx].percentage = 100;
counterUpdateProgress++;
setCounterProgress(counterUpdateProgress);
successTodo();
},
});
upload.start();
}
const successSubmit = (redirect: string) => {
MySwal.fire({
title: "Sukses",
text: "Data berhasil disimpan.",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push(redirect);
});
};
function successTodo() {
let counter = 0;
for (const element of progressInfo) {
if (element.percentage == 100) {
counter++;
}
}
if (counter == progressInfo.length) {
setIsStartUpload(false);
// hideProgress();
Cookies.remove("idCreate");
successSubmit("/in/contributor/content/video");
}
}
const handleRemoveAllFiles = () => {
setFiles([]);
};
const renderFilePreview = (file: FileWithPreview) => {
if (file?.type?.startsWith("image")) {
return (
<Image
width={48}
height={48}
alt={file.name}
src={URL.createObjectURL(file)}
className=" rounded border p-0.5"
/>
);
} else {
return <Icon icon="tabler:file-description" />;
}
};
const handleRemoveFile = (file: FileWithPreview) => {
const uploadedFiles = files;
const filtered = uploadedFiles.filter((i) => i.name !== file.name);
setFiles([...filtered]);
};
const fileList = files.map((file: any) => (
<div
key={file.id}
className="flex justify-between border px-3.5 py-3 my-6 rounded-md"
>
<div className="flex gap-3 items-center">
<div className="file-preview">{renderFilePreview(file)}</div>
<div>
<div className="text-sm text-card-foreground">
{file.fileName || file.name}
</div>
<div className="text-xs font-light text-muted-foreground">
{Math.round(file.size / 100) / 10 > 1000 ? (
<>{(Math.round(file.size / 100) / 10000).toFixed(1)}</>
) : (
<>{(Math.round(file.size / 100) / 10).toFixed(1)}</>
)}
{" kb"}
</div>
</div>
</div>
<Button
type="button"
size="icon"
color="destructive"
variant="outline"
className="border-none rounded-full"
onClick={() => handleDeleteFile(file.id)} // Kirim ID spesifik
>
<Icon icon="tabler:x" className="h-5 w-5" />
</Button>
</div>
));
function success() {
MySwal.fire({
title: "Sukses",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then((result) => {
if (result.isConfirmed) {
// window.location.reload();
}
});
}
const handleDeleteFile = (id: number) => {
MySwal.fire({
title: "Hapus file",
text: "Apakah Anda yakin ingin menghapus file ini?",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#3085d6",
confirmButtonColor: "#d33",
confirmButtonText: "Hapus",
}).then((result) => {
if (result.isConfirmed) {
doDelete(id);
}
});
};
async function doDelete(id: number) {
const data = { id };
try {
const response = await deleteFile(data);
if (response?.error) {
error(response.message);
return;
}
// Jika berhasil, hapus file dari state lokal
setFiles((prevFiles: any) =>
prevFiles.filter((file: any) => file.id !== id)
);
success();
} catch (err) {
error("Terjadi kesalahan saat menghapus file");
}
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
{detail !== undefined ? (
<div className="flex flex-col lg:flex-row gap-10">
<Card className="w-full lg:w-8/12">
<div className="px-6 py-6">
<p className="text-lg font-semibold mb-3">
{t("form-video", { defaultValue: "Form Video" })}
</p>
<div className="gap-5 mb-5">
{/* Input Title */}
<div className="space-y-2 py-3">
<div className="flex justify-between items-center">
<Label>{t("title", { defaultValue: "Title" })}</Label>
{roleId === "14" && (
<button
type="button"
onClick={async () => {
try {
loading();
setIsLoadingTranslate(true);
const res = await translateText({
text: getValues("title"),
sourceLang: "ID",
targetLang: "EN",
});
if (!res.error) {
const resultText =
res?.data?.data?.translations?.[0]?.text || "";
setTranslatedTitle(resultText);
}
} catch (err) {
close();
console.error("Translate title gagal:", err);
} finally {
close();
setIsLoadingTranslate(false);
}
}}
className="px-3 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600"
>
{isLoadingTranslate
? "Translating..."
: "Translate Title"}
</button>
)}
</div>
<Controller
control={control}
name="title"
render={({ field }) => (
<Input
size="md"
type="text"
value={field.value}
onChange={field.onChange}
placeholder="Enter Title"
/>
)}
/>
{/* English translated title appears below when available */}
{translatedTitle && (
<div className="mt-3">
<Label className="text-sm font-semibold">
English Version
</Label>
<Input
size="md"
type="text"
value={translatedTitle}
onChange={(e) => setTranslatedTitle(e.target.value)}
placeholder="Translated Title"
/>
</div>
)}
{errors.title?.message && (
<p className="text-red-400 text-sm">
{errors.title.message}
</p>
)}
</div>
{/* <div className="space-y-2 py-3">
<Label>{t("title", { defaultValue: "Title" })}</Label>
<Controller
control={control}
name="title"
render={({ field }) => (
<Input
size="md"
type="text"
value={field?.value}
onChange={field.onChange}
placeholder="Enter Title"
/>
)}
/>
{errors.title?.message && (
<p className="text-red-400 text-sm">
{errors.title.message}
</p>
)}
</div> */}
<div className="flex items-center">
<div className="py-3 w-full space-y-2">
<Label>{t("category", { defaultValue: "Category" })}</Label>
<Select
value={selectedTarget} // Nilai default berdasarkan detail
onValueChange={(id) => {
console.log("Selected Category:", id);
setSelectedTarget(id);
}}
>
<SelectTrigger size="md">
<SelectValue placeholder="Pilih" />
</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)
) && (
<SelectItem
key={String(detail.category.id)}
value={String(detail.category.id)}
>
{detail.category.name}
</SelectItem>
)}
{categories.map((category) => (
<SelectItem
key={String(category.id)}
value={String(category.id)}
>
{category.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
<div className="py-3 space-y-2">
<div className="flex justify-between items-center">
<Label>
{t("description", { defaultValue: "Description" })}
</Label>
{roleId === "14" && (
<button
type="button"
onClick={async () => {
try {
loading();
setIsLoadingTranslate(true);
const res = await translateText({
text: getValues("description"),
sourceLang: "ID",
targetLang: "EN",
});
if (!res.error) {
const resultText =
res?.data?.data?.translations?.[0]?.text || "";
// Overwrite data.description but still show both
setTranslatedContent(resultText);
}
} catch (err) {
close();
console.error("Translate gagal:", err);
} finally {
close();
setIsLoadingTranslate(false);
}
}}
className="px-3 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600"
>
{isLoadingTranslate
? "Translating..."
: "Translate Description"}
</button>
)}
</div>
{/* Editor Bahasa Indonesia */}
<div className="mt-3">
<Label className="text-sm font-semibold">
Indonesian Version
</Label>
<Controller
control={control}
name="description"
render={({ field }) => (
<CustomEditor
onChange={field.onChange}
initialData={field.value}
/>
)}
/>
</div>
{/* English translated version muncul setelah translate */}
{translatedContent && (
<div className="mt-5">
<Label className="text-sm font-semibold">
English Version
</Label>
<CustomEditor
onChange={(val: any) => setTranslatedContent(val)}
initialData={translatedContent}
/>
</div>
)}
{errors.description?.message && (
<p className="text-red-400 text-sm">
{errors.description.message}
</p>
)}
</div>
{/* <div className="py-3 space-y-2">
<Label>
{t("description", { defaultValue: "Description" })}
</Label>
<Controller
control={control}
name="description"
render={({ field: { onChange, value } }) => (
<CustomEditor onChange={onChange} initialData={value} />
)}
/>
{errors.description?.message && (
<p className="text-red-400 text-sm">
{errors.description.message}
</p>
)}
</div> */}
<div className="py-3 space-y-2">
<Label>
{t("select-file", { defaultValue: "Select File" })}
</Label>
{/* <Input
id="fileInput"
type="file"
onChange={handleImageChange}
/> */}
<Fragment>
<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">
{/* Drop files here or click to upload. */}
{t("drag-file", { defaultValue: "Drag File" })}
</h4>
<div className=" text-xs text-muted-foreground">
{t("upload-file-video-max", {
defaultValue: "Upload File Video Max",
})}
</div>
</div>
</div>
{files.length ? (
<Fragment>
<div>{fileList}</div>
<div className=" flex justify-between gap-2">
<div className="flex flex-row items-center gap-3 py-3">
<Label>
{t("watermark", { defaultValue: "Watermark" })}
</Label>
<div className="flex items-center gap-3">
<Switch defaultChecked color="primary" id="c2" />
</div>
</div>
{/* <Button
color="destructive"
onClick={handleRemoveAllFiles}
>
Remove All
</Button> */}
</div>
</Fragment>
) : null}
{files.length > 0 && (
<div className="mt-4">
<Label className="text-md font-semibold">
{" "}
{t("file-media", { defaultValue: "File Media" })}
</Label>
<div className="grid gap-4">
{files.map((file: any, index: any) => (
<div
key={file.id}
className="flex items-center border p-2 rounded-md"
>
<img
src={file.thumbnailFileUrl}
alt={file.fileName}
className="w-16 h-16 object-cover rounded-md mr-4"
/>
<div className="flex flex-wrap gap-3 items-center ">
<div className="flex-grow">
<p className="font-medium">{file.fileName}</p>
<a
href={file.url}
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 text-sm"
>
{t("view-file", {
defaultValue: "View File",
})}
</a>
</div>
<div className="bg-white rounded-md p-4 border">
{/* Checkbox Tingkat Utama */}
<div className="space-y-4">
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{[
{ key: "semua", label: "Semua" },
{
key: "nasional",
label: "Nasional",
},
{ key: "wilayah", label: "Wilayah" },
{
key: "international",
label: "Internasional",
},
].map((item, idx) => (
<div
key={item.key}
className="flex items-center gap-2 p-2 border border-gray-200 rounded-md hover:bg-gray-50"
>
<Checkbox
// id={`${item.key}-${index}`}
checked={
fileUnitSelections[file.id]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => {
handleFileUnitChange(
file.id,
item.key,
value as boolean
);
setupPlacement(
file.id,
item.key,
Boolean(value)
);
}}
/>
<Label
htmlFor={`${item.key}-${index}`}
className="text-sm font-medium cursor-pointer"
>
{item.label}
</Label>
</div>
))}
</div>
{/* Detail Wilayah */}
{fileUnitSelections[file.id]?.wilayah &&
isDetailOfRegionShowed && (
<div className="border-t border-gray-200 pt-2">
<p className="text-sm font-medium text-gray-700 mb-2">
Detail Wilayah:
</p>
{/* Checkbox Sub-kategori dengan tombol Kustom sejajar */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-3">
{[
{ key: "polda", label: "POLDA" },
{
key: "satker",
label: "SATKER",
},
].map((item, idx) => (
<div
key={item.key}
className="flex items-center gap-2 p-3 border border-gray-200 rounded-md hover:bg-gray-50"
>
<Checkbox
id={`${item.key}-${index}`}
checked={
fileUnitSelections[
file.id
]?.[
item.key as keyof typeof unitSelection
] || false
}
onCheckedChange={(value) => {
handleFileUnitChange(
file.id,
item.key,
value as boolean
);
setupPlacement(
file.id,
item.key,
Boolean(value)
);
}}
/>
<Label
htmlFor={`${item.key}-${index}`}
className="text-sm font-medium cursor-pointer"
>
{item.label}
</Label>
</div>
))}
{/* Tombol Kustom sejajar dengan checkbox */}
<div className="flex items-center justify-center p-3">
<Dialog>
<DialogTrigger asChild>
<Button
variant="outline"
size="sm"
className="gap-2"
>
<Icon
icon="material-symbols:tune"
width={16}
height={16}
/>
{t("custom", {
defaultValue: "Kustom",
})}
</Button>
</DialogTrigger>
<DialogContent className="max-w-[95vw] lg:max-w-[1400px] max-h-[90vh]">
<DialogHeader className="border-b border-gray-200 pb-4">
<DialogTitle className="text-lg font-semibold">
Daftar Wilayah POLDA dan
SATKER
</DialogTitle>
</DialogHeader>
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4 max-h-[70vh] overflow-y-auto p-1">
{listDest.map(
(polda: any) => (
<div
key={polda.id}
className="border border-gray-200 rounded-lg p-2 bg-white hover:shadow-sm transition-shadow"
>
{/* Header POLDA */}
<div className="flex items-center justify-between">
<Label className="flex items-center gap-3 flex-1 cursor-pointer">
<Checkbox
checked={
fileCheckedLevels[
file.id
]?.has(
Number(
polda.id
)
) || false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
file.id,
polda,
!fileCheckedLevels[
file.id
]?.has(
Number(
polda.id
)
)
)
}
/>
<span className="font-semibold text-gray-900 text-sm">
{polda.name}
</span>
</Label>
{/* Tombol expand hanya untuk SATKER POLRI */}
{polda.name ===
"SATKER POLRI" &&
polda.subDestination && (
<button
onClick={(
e
) => {
e.preventDefault();
e.stopPropagation();
toggleExpand(
polda.id
);
}}
className="p-1 hover:bg-gray-100 rounded-md transition-colors"
>
<Icon
icon={
expandedPolda.has(
polda.id
)
? "mdi:chevron-up"
: "mdi:chevron-down"
}
width={16}
height={16}
/>
</button>
)}
</div>
{/* Sub-items hanya untuk SATKER POLRI */}
{polda.name ===
"SATKER POLRI" &&
polda.subDestination &&
expandedPolda.has(
polda.id
) && (
<div className="max-h-[200px] overflow-y-auto border-t border-gray-100 pt-2">
{/* Tombol Pilih Semua untuk sub-items */}
<div className="mb-2 flex justify-start">
{(() => {
const allSubItemsChecked =
polda.subDestination?.every(
(
sub: any
) =>
fileCheckedLevels[
file
.id
]?.has(
Number(
sub.id
)
)
);
return (
<Button
size="sm"
variant="outline"
className="text-xs h-6 px-2"
onClick={() =>
handleSelectAllSubItems(
file.id,
polda
)
}
>
{allSubItemsChecked ? (
<>
<Icon
icon="material-symbols:check-indeterminate-small"
width={
12
}
height={
12
}
className="mr-1"
/>
Batal
Semua
</>
) : (
<>
<Icon
icon="material-symbols:check-all"
width={
12
}
height={
12
}
className="mr-1"
/>
Pilih
Semua
</>
)}
</Button>
);
})()}
</div>
<div className="space-y-1">
{polda.subDestination.map(
(
sub: any
) => (
<Label
key={
sub.id
}
className="flex items-center gap-2 p-2 rounded-md hover:bg-gray-50 transition-colors cursor-pointer text-xs"
>
<Checkbox
checked={
fileCheckedLevels[
file
.id
]?.has(
Number(
sub.id
)
) ||
false
}
onCheckedChange={() =>
handleFileCheckboxChangePlacement(
file.id,
polda,
!fileCheckedLevels[
file
.id
]?.has(
Number(
sub.id
)
)
)
}
/>
<span className="text-gray-700">
{
sub.name
}
</span>
</Label>
)
)}
</div>
</div>
)}
</div>
)
)}
</div>
<div className="flex justify-end gap-3 border-t border-gray-200 pt-4">
<DialogClose asChild>
<Button variant="outline">
{t("cancel", {
defaultValue: "Batal",
})}
</Button>
</DialogClose>
<DialogClose asChild>
<Button>Simpan</Button>
</DialogClose>
</div>
</DialogContent>
</Dialog>
</div>
</div>
</div>
)}
</div>
</div>
</div>
</div>
))}
</div>
</div>
)}
</Fragment>
</div>
</div>
</div>
</Card>
<div className="w-full lg:w-4/12">
<Card className="h-fit">
<div className="px-3 py-3">
<div className="space-y-2">
<Label>{t("creator", { defaultValue: "Creator" })}</Label>
<Controller
control={control}
name="creatorName"
render={({ field }) => (
<Input
size="md"
type="text"
value={field?.value}
onChange={field.onChange}
placeholder="Enter Title"
/>
)}
/>
{errors.creatorName?.message && (
<p className="text-red-400 text-sm">
{errors.creatorName.message}
</p>
)}
</div>
</div>
{/* <div className="mt-3 px-3 space-y-2">
<Label>{t("preview", { defaultValue: "Preview" })}</Label>
<Card className="mt-2">
<img
src={detail.thumbnailLink}
alt="Thumbnail Gambar Utama"
className="w-full h-auto rounded"
/>
</Card>
</div> */}
<div className="mt-3 px-3 space-y-2">
<Label>{t("preview", { defaultValue: "Preview" })}</Label>
<Input
type="file"
accept="image/*"
onChange={(e) => {
const file = e.target.files?.[0];
if (file) {
setSelectedFiles([file]);
}
}}
className="dark:border dark:border-gray-500 dark:rounded-lg"
/>
{selectedFiles.length > 0 ? (
<Card className="mt-2">
<img
src={URL.createObjectURL(selectedFiles[0])}
alt="Thumbnail Baru"
className="w-full h-auto rounded"
/>
</Card>
) : (
<Card className="mt-2">
<img
src={detail?.thumbnailLink}
alt="Thumbnail Lama"
className="w-full h-auto rounded"
/>
</Card>
)}
</div>
<div className="px-3 py-3">
<div className="space-y-2">
<Label>{t("tags", { defaultValue: "Tags" })}</Label>
<Input
type="text"
id="tags"
placeholder="Add a tag and press Enter"
onKeyDown={handleAddTag}
ref={inputRef}
/>
<div className="mt-3 flex flex-wrap gap-2">
{tags.map((tag, index) => (
<span
key={index}
className="flex items-center gap-2 px-2 py-1 rounded-lg bg-black text-white text-sm"
>
<input
type="text"
value={tag}
onChange={(e) => handleEditTag(index, e.target.value)}
className="bg-black text-white border-none focus:outline-none w-auto"
/>
<button
value={tag}
type="button"
onClick={() => handleRemoveTag(index)}
className="remove-tag-button text-white"
>
×
</button>
</span>
))}
</div>
</div>
</div>
<div className="px-3 py-3">
<div className="flex flex-col gap-6 space-y-2">
<Label>
{t("publish-target", { defaultValue: "Publish Target" })}
</Label>
{options.map((option: Option) => (
<div key={option.id} className="flex gap-2 items-center">
<Checkbox
id={option.id}
checked={
option.id === "all"
? publishedFor.length ===
options.filter((opt) => opt.id !== "all").length
: publishedFor.includes(option.id)
}
onCheckedChange={() => handleCheckboxChange(option.id)}
/>
<Label htmlFor={option.id}>{option.name}</Label>
</div>
))}
</div>
</div>
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
<MailIcon />
<p className="">
{t("suggestion-box", { defaultValue: "Suggestion Box" })} (0)
</p>
</div>
<div className="px-3 py-3">
<p>{t("information", { defaultValue: "Information" })}:</p>
{/* <p>{detail?.status}</p> */}
</div>
</Card>
<div className="flex flex-row justify-end gap-3">
<div className="mt-4">
<Button type="submit" color="primary">
{t("submit", { defaultValue: "Submit" })}
</Button>
</div>
<div className="mt-4">
<Button type="submit" color="primary" variant="outline">
{t("cancel", { defaultValue: "Cancel" })}
</Button>
</div>
</div>
</div>
</div>
) : (
""
)}
</form>
);
}