diff --git a/app/[locale]/(protected)/admin/add-experts/component/column.tsx b/app/[locale]/(protected)/admin/add-experts/component/column.tsx index c2281486..dad8eee6 100644 --- a/app/[locale]/(protected)/admin/add-experts/component/column.tsx +++ b/app/[locale]/(protected)/admin/add-experts/component/column.tsx @@ -11,6 +11,12 @@ import { } from "@/components/ui/dropdown-menu"; import { Button } from "@/components/ui/button"; import { Link } from "@/components/navigation"; +import { useToast } from "@/components/ui/use-toast"; +import withReactContent from "sweetalert2-react-content"; +import Swal from "sweetalert2"; +import { useRouter } from "next/navigation"; +import { deleteUser } from "@/service/management-user/management-user"; +import { stringify } from "querystring"; const columns: ColumnDef[] = [ { @@ -79,6 +85,37 @@ const columns: ColumnDef[] = [ header: "Actions", enableHiding: false, cell: ({ row }) => { + const { toast } = useToast(); + const MySwal = withReactContent(Swal); + const router = useRouter(); + const doDelete = async (id: number) => { + const response = await deleteUser(id); + if (response?.error) { + toast({ + title: stringify(response?.message), + variant: "destructive", + }); + } + toast({ + title: "Success delete", + }); + + router.push("?dataChange=true"); + }; + + const handleDelete = (id: number) => { + MySwal.fire({ + title: "Apakah anda ingin menghapus data user?", + showCancelButton: true, + confirmButtonColor: "#dc3545", + confirmButtonText: "Iya", + cancelButtonText: "Tidak", + }).then((result) => { + if (result.isConfirmed) { + doDelete(id); + } + }); + }; return ( @@ -104,7 +141,7 @@ const columns: ColumnDef[] = [ handleDeleteMedia(row.original.id)} + onClick={() => handleDelete(row.original.userKeycloakId)} className="p-2 border-b text-destructive bg-destructive/30 focus:bg-destructive focus:text-destructive-foreground rounded-none" > diff --git a/app/[locale]/(protected)/contributor/task-ta/components/columns.tsx b/app/[locale]/(protected)/contributor/task-ta/components/columns.tsx index 0213be3c..ad0a8864 100644 --- a/app/[locale]/(protected)/contributor/task-ta/components/columns.tsx +++ b/app/[locale]/(protected)/contributor/task-ta/components/columns.tsx @@ -159,7 +159,7 @@ const useTableColumns = () => { - {(roleId == 11 || roleId == 12) && ( + {(roleId == 11 || roleId == 12 || roleId == 19) && ( @@ -175,7 +175,7 @@ const useTableColumns = () => { )} - {roleId == 12 && ( + {(roleId == 11 || roleId == 12 || roleId == 19) && ( diff --git a/app/[locale]/(protected)/contributor/task-ta/detail-upload/image/[id]/page.tsx b/app/[locale]/(protected)/contributor/task-ta/detail-upload/image/[id]/page.tsx new file mode 100644 index 00000000..4713ac39 --- /dev/null +++ b/app/[locale]/(protected)/contributor/task-ta/detail-upload/image/[id]/page.tsx @@ -0,0 +1,15 @@ +import FormImageDetail from "@/components/form/content/task-ta/image-detail-form"; +import SiteBreadcrumb from "@/components/site-breadcrumb"; + +const ImageDetailPage = async () => { + return ( +
+ +
+ +
+
+ ); +}; + +export default ImageDetailPage; diff --git a/components/form/content/task-ta/audio-detail-form.tsx b/components/form/content/task-ta/audio-detail-form.tsx new file mode 100644 index 00000000..d3f28fb7 --- /dev/null +++ b/components/form/content/task-ta/audio-detail-form.tsx @@ -0,0 +1,926 @@ +"use client"; +import React, { ChangeEvent, 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 JoditEditor from "jodit-react"; +import { register } from "module"; +import { Switch } from "@/components/ui/switch"; +import Cookies from "js-cookie"; +import { + createMedia, + getTagsBySubCategoryId, + listEnableCategory, + rejectFiles, + submitApproval, +} from "@/service/content/content"; +import { + detailMedia, + getDataApprovalByMediaUpload, +} from "@/service/curated-content/curated-content"; +import { Badge } from "@/components/ui/badge"; +import { MailIcon, Music } 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 { + DialogHeader, + DialogFooter, + Dialog, + DialogContent, + DialogTitle, +} from "@/components/ui/dialog"; +import { Textarea } from "@/components/ui/textarea"; +import { loading } from "@/config/swal"; +import { getCookiesDecrypt } from "@/lib/utils"; +import { Icon } from "@iconify/react/dist/iconify.js"; +import { error } from "@/lib/swal"; +import dynamic from "next/dynamic"; +import WavesurferPlayer from "@wavesurfer/react"; +import WaveSurfer from "wavesurfer.js"; +import { useTranslations } from "next-intl"; +import SuggestionModal from "@/components/modal/suggestions-modal"; +import { formatDateToIndonesian } from "@/utils/globals"; +import ApprovalHistoryModal from "@/components/modal/approval-history-modal"; + +const imageSchema = 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 FileType = { + id: number; + url: string; + thumbnailFileUrl: string; + fileName: string; +}; + +type Detail = { + id: string; + title: string; + description: string; + slug: string; + category: { + id: number; + name: string; + }; + categoryName: string; + creatorName: string; + thumbnailLink: string; + tags: string; + statusName: string; + isPublish: boolean; + needApprovalFromLevel: number; + files: FileType[]; + uploadedById: number; +}; + +const ViewEditor = dynamic( + () => { + return import("@/components/editor/view-editor"); + }, + { ssr: false } +); + +export default function FormAudioDetail() { + const MySwal = withReactContent(Swal); + const router = useRouter(); + const userId = getCookiesDecrypt("uie"); + const userLevelId = getCookiesDecrypt("ulie"); + const roleId = getCookiesDecrypt("urie"); + + const [modalOpen, setModalOpen] = useState(false); + const { id } = useParams() as { id: string }; + console.log(id); + const editor = useRef(null); + type ImageSchema = z.infer; + + const [selectedFiles, setSelectedFiles] = useState([]); + const taskId = Cookies.get("taskId"); + const scheduleId = Cookies.get("scheduleId"); + const scheduleType = Cookies.get("scheduleType"); + const [status, setStatus] = useState(""); + const [categories, setCategories] = useState([]); + const [selectedCategory, setSelectedCategory] = useState(); + const [tags, setTags] = useState([]); + const [detail, setDetail] = useState(); + const [refresh, setRefresh] = useState(false); + const [selectedPublishers, setSelectedPublishers] = useState([]); + const [description, setDescription] = useState(""); + const [main, setMain] = useState([]); + const [detailThumb, setDetailThumb] = useState([]); + const [thumbsSwiper, setThumbsSwiper] = useState(null); + + const t = useTranslations("Form"); + const [selectedTarget, setSelectedTarget] = useState(""); + const [files, setFiles] = useState([]); + const [rejectedFiles, setRejectedFiles] = useState([]); + const [isUserMabesApprover, setIsUserMabesApprover] = useState(false); + const [audioPlaying, setAudioPlaying] = useState(null); + const [filePlacements, setFilePlacements] = useState([]); + + const waveSurferRef = useRef(null); + + const [wavesurfer, setWavesurfer] = useState(); + const [isPlaying, setIsPlaying] = useState(false); + const [approval, setApproval] = useState(); + + const onReady = (ws: any) => { + setWavesurfer(ws); + setIsPlaying(false); + }; + + const onPlayPause = () => { + wavesurfer && wavesurfer.playPause(); + }; + + let fileTypeId = "4"; + + const { + control, + handleSubmit, + setValue, + formState: { errors }, + } = useForm({ + resolver: zodResolver(imageSchema), + }); + + // const handleKeyDown = (e: any) => { + // const newTag = e.target.value.trim(); // Ambil nilai input + // if (e.key === "Enter" && newTag) { + // e.preventDefault(); // Hentikan submit form + // if (!tags.includes(newTag)) { + // setTags((prevTags) => [...prevTags, newTag]); // Tambah tag baru + // setValue("tags", ""); // Kosongkan input + // } + // } + // }; + + useEffect(() => { + if ( + userLevelId != undefined && + roleId != undefined && + userLevelId == "216" && + roleId == "3" + ) { + setIsUserMabesApprover(true); + } + }, [userLevelId, roleId]); + + const handleCheckboxChange = (id: number) => { + setSelectedPublishers((prev) => + prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id] + ); + }; + + useEffect(() => { + async function initState() { + getCategories(); + } + + initState(); + }, []); + + const getCategories = async () => { + try { + const category = await listEnableCategory(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); + } + }; + + const setupPlacementCheck = (length: number) => { + const temp = []; + for (let i = 0; i < length; i++) { + temp.push([]); + } + setFilePlacements(temp); + }; + + useEffect(() => { + async function initState() { + if (id) { + const response = await detailMedia(id); + const details = response?.data?.data; + console.log("detail", details); + setFiles(details?.files); + setDetail(details); + setMain({ + type: details?.fileType.name, + url: details?.files[0]?.url, + names: details?.files[0]?.fileName, + format: details?.files[0]?.format, + }); + setupPlacementCheck(details?.files?.length); + + if (details.publishedForObject) { + const publisherIds = details.publishedForObject.map( + (obj: any) => obj.id + ); + setSelectedPublishers(publisherIds); + } + + const matchingCategory = categories.find( + (category) => category.id === details.categoryId + ); + + if (matchingCategory) { + setSelectedTarget(matchingCategory.name); + } + + setSelectedTarget(details.categoryId); // Untuk dropdown + + const filesData = details.files || []; + const audioFiles = filesData.filter( + (file: any) => + file.contentType && file.contentType.startsWith("video/webm") + ); + + const fileUrls = audioFiles.map((file: { secondaryUrl: string }) => + file.secondaryUrl ? file.secondaryUrl : "default-audio.mp3" + ); + + setDetailThumb(fileUrls); + + const approvals = await getDataApprovalByMediaUpload(details?.id); + setApproval(approvals?.data?.data); + } + } + initState(); + }, [refresh, setValue]); + + const actionApproval = (e: string) => { + const temp = []; + for (const element of detail.files) { + temp.push([]); + } + setFilePlacements(temp); + setStatus(e); + setFiles(detail.files); + + setDescription(""); + setModalOpen(true); + }; + + const submit = async () => { + if ( + (description?.length > 1 && Number(status) == 3) || + Number(status) == 2 || + Number(status) == 4 + ) { + save(); + + // MySwal.fire({ + // title: "Simpan Approval", + // text: "", + // icon: "warning", + // showCancelButton: true, + // cancelButtonColor: "#d33", + // confirmButtonColor: "#3085d6", + // confirmButtonText: "Simpan", + // }).then((result) => { + // if (result.isConfirmed) { + // } + // }); + } + }; + + const getPlacement = () => { + console.log("getPlaa", filePlacements); + const temp = []; + for (let i = 0; i < filePlacements?.length; i++) { + if (filePlacements[i].length !== 0) { + const now = filePlacements[i].filter((a) => a !== "all"); + const data = { mediaFileId: files[i].id, placements: now.join(",") }; + temp.push(data); + } + } + return temp; + }; + + async function save() { + const data = { + mediaUploadId: id, + statusId: status, + message: description, + // files: [], + files: isUserMabesApprover ? getPlacement() : [], + }; + setModalOpen(false); + loading(); + const response = await submitApproval(data); + + if (response?.error) { + error(response.message); + return false; + } + + const dataReject = { + listFiles: rejectedFiles, + }; + + const resReject = await rejectFiles(dataReject); + + if (resReject?.error) { + error(resReject.message); + return false; + } + + close(); + submitApprovalSuccesss(); + + return false; + } + + function handleDeleteFileApproval(id: number) { + const selectedFiles = files.filter((file) => file.id != id); + setFiles(selectedFiles); + const rejects = rejectedFiles; + rejects.push(id); + setRejectedFiles(rejects); + } + + const setupPlacement = ( + index: number, + placement: string, + checked: boolean + ) => { + let temp = [...filePlacements]; + if (checked) { + if (placement === "all") { + temp[index] = ["all", "mabes", "polda", "international"]; + } else { + const now = temp[index]; + now.push(placement); + if (now.length === 3 && !now.includes("all")) { + now.push("all"); + } + temp[index] = now; + } + } else { + if (placement === "all") { + temp[index] = []; + } else { + const now = temp[index].filter((a) => a !== placement); + console.log("now", now); + temp[index] = now; + if (now.length === 3 && now.includes("all")) { + const newData = now.filter((b) => b !== "all"); + temp[index] = newData; + } + } + } + setFilePlacements(temp); + }; + + const handleMain = ( + type: string, + url: string, + names: string, + format: string + ) => { + console.log("Test 3 :", type, url, names, format); + setMain({ + type, + url, + names, + format, + }); + return false; + }; + + const handleAudioPlayPause = (audioSrc: string) => { + if (audioPlaying === audioSrc) { + setAudioPlaying(null); // Pause if the same audio is clicked + } else { + setAudioPlaying(audioSrc); // Play the new audio + } + }; + + const submitApprovalSuccesss = () => { + MySwal.fire({ + title: "Sukses", + text: "Data berhasil disimpan.", + icon: "success", + confirmButtonColor: "#3085d6", + confirmButtonText: "OK", + }).then(() => { + router.push("/in/contributor/content/audio"); + }); + }; + + return ( +
+ {detail !== undefined ? ( +
+ +
+

{t("form-audio")}

+
+ {/* Input Title */} +
+ + ( + + )} + /> + {errors.title?.message && ( +

+ {errors.title.message} +

+ )} +
+
+
+ + +
+
+ +
+ + ( + + )} + /> + {errors.description?.message && ( +

+ {errors.description.message} +

+ )} +
+ + +
+
+ {detailThumb?.map((url: any, index: number) => ( +
+ setIsPlaying(true)} + onPause={() => setIsPlaying(false)} + /> +
+ ))} + +
+
+
+
+
+
+ +
+
+ + ( + + )} + /> + {errors.creatorName?.message && ( +

+ {errors.creatorName.message} +

+ )} +
+
+ +
+
+ +
+ {detail?.tags + ?.split(",") + .map((tag: string, index: number) => ( + + {tag.trim()} + + ))} +
+
+
+
+
+ +
+ handleCheckboxChange(5)} + /> + +
+
+ handleCheckboxChange(6)} + /> + +
+
+ handleCheckboxChange(7)} + /> + +
+
+ handleCheckboxChange(8)} + /> + +
+
+
+ +
+

{t("information")}:

+

{detail?.statusName}

+

Komentar

+

{approval?.message}

+

+ {" "} + {approval?.approvalBy?.fullname} |{" "} + {formatDateToIndonesian(approval?.approvalDate)} +

+ +
+ {/* {detail?.isPublish == false ? ( +
+ +
+ ) : ( + "" + )} */} + + + + + {t("leave-comment")} + + {status == "2" + ? files?.map((file, index) => ( +
+ {/* */} + + + {" "} +
+ + {isUserMabesApprover && ( +
+
+ + setupPlacement(index, "all", Boolean(e)) + } + /> + +
+
+ + setupPlacement(index, "mabes", Boolean(e)) + } + /> + +
+
+ + setupPlacement(index, "polda", Boolean(e)) + } + /> + +
+ +
+ + setupPlacement( + index, + "international", + Boolean(e) + ) + } + /> + +
+
+ )} +
+
+ )) + : ""} +
+