diff --git a/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx b/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx index 513c743f..00e002c8 100644 --- a/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx +++ b/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx @@ -1,10 +1,5 @@ // "use client"; -import React, { - useState, - useEffect, - useRef, - Fragment, -} from "react"; +import React, { useState, useEffect, useRef, Fragment } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; @@ -49,6 +44,7 @@ import { getUserLevelForAssignments } from "@/service/task"; import { AudioRecorder } from "react-audio-voice-recorder"; import FileUploader from "@/components/form/shared/file-uploader"; import { Upload } from "tus-js-client"; +import { getCsrfToken } from "@/service/auth"; const schema = z.object({ title: z.string().min(3, { message: "Required" }), @@ -75,9 +71,7 @@ const EventModal = ({ const [startDate, setStartDate] = useState(new Date()); const [endDate, setEndDate] = useState(new Date()); const [isPending, startTransition] = React.useTransition(); - const [agendaType, setAgendaType] = React.useState( - categories[0].value - ); + const [agendaType, setAgendaType] = React.useState(categories[0].value); const [listDest, setListDest] = useState([]); const [deleteModalOpen, setDeleteModalOpen] = useState(false); const [eventIdToDelete, setEventIdToDelete] = useState(null); @@ -97,6 +91,12 @@ const EventModal = ({ const [isVideoUploadFinish, setIsVideoUploadFinish] = useState(false); const [isTextUploadFinish, setIsTextUploadFinish] = useState(false); const [isAudioUploadFinish, setIsAudioUploadFinish] = useState(false); + let progressInfo: any = []; + let counterUpdateProgress = 0; + const [progressList, setProgressList] = useState([]); + let uploadPersen = 0; + const [isStartUpload, setIsStartUpload] = useState(false); + const [counterProgress, setCounterProgress] = useState(0); const { register, @@ -118,11 +118,17 @@ const EventModal = ({ const levelList = response?.data?.data.list; let listFiltered = []; if (agendaType == "polda") { - listFiltered = levelList.filter((level: any) => level.name != 'SATKER POLRI'); + listFiltered = levelList.filter( + (level: any) => level.name != "SATKER POLRI" + ); } else if (agendaType == "polres") { - listFiltered = levelList.filter((level: any) => level.name != 'SATKER POLRI'); + listFiltered = levelList.filter( + (level: any) => level.name != "SATKER POLRI" + ); } else if (agendaType == "satker") { - listFiltered = levelList.filter((level: any) => level.name == 'SATKER POLRI'); + listFiltered = levelList.filter( + (level: any) => level.name == "SATKER POLRI" + ); } setListDest(listFiltered); const initialExpandedState = listFiltered.reduce( @@ -181,52 +187,28 @@ const EventModal = ({ setIsImageUploadFinish(true); } imageFiles?.map(async (item: any, index: number) => { - await uploadResumableFile( - index, - String(id), - item, - "1", - "0" - ); + await uploadResumableFile(index, String(id), item, "1", "0"); }); if (videoFiles?.length == 0) { setIsVideoUploadFinish(true); } videoFiles?.map(async (item: any, index: number) => { - await uploadResumableFile( - index, - String(id), - item, - "2", - "0" - ); + await uploadResumableFile(index, String(id), item, "2", "0"); }); if (textFiles?.length == 0) { setIsTextUploadFinish(true); } textFiles?.map(async (item: any, index: number) => { - await uploadResumableFile( - index, - String(id), - item, - "3", - "0" - ); + await uploadResumableFile(index, String(id), item, "3", "0"); }); if (audioFiles?.length == 0) { setIsAudioUploadFinish(true); } audioFiles?.map(async (item: any, index: number) => { - await uploadResumableFile( - index, - String(id), - item, - "4", - "0" - ); + await uploadResumableFile(index, String(id), item, "4", "0"); }); // Optional: Use Swal for success feedback @@ -325,19 +307,24 @@ const EventModal = ({ }; async function uploadResumableFile( - idx: number, - id: string, - file: any, - fileTypeId: string, - duration: string + idx: number, + id: string, + file: any, + fileTypeId: string, + duration: string ) { console.log(idx, id, file, fileTypeId, 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}/agenda-settings/file/upload`, + headers: headers, retryDelays: [0, 3000, 6000, 12_000, 24_000], chunkSize: 20_000, metadata: { @@ -345,8 +332,12 @@ const EventModal = ({ filename: file.name, filetype: file.type, fileTypeId: fileTypeId, - duration: "", - isWatermark: "true", // hardcode + duration, + isWatermark: "false", // hardcode + }, + onBeforeRequest: function (req) { + var xhr = req.getUnderlyingObject(); + xhr.withCredentials = true; }, onError: async (e: any) => { console.log("Error upload :", e); @@ -358,27 +349,18 @@ const EventModal = ({ bytesTotal: any ) => { const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100); - // progressInfo[idx].percentage = uploadPersen; - // counterUpdateProgress++; - // console.log(counterUpdateProgress); - // setProgressList(progressInfo); - // setCounterProgress(counterUpdateProgress); + progressInfo[idx].percentage = uploadPersen; + counterUpdateProgress++; + console.log(counterUpdateProgress); + setProgressList(progressInfo); + setCounterProgress(counterUpdateProgress); }, onSuccess: async () => { - // uploadPersen = 100; - // progressInfo[idx].percentage = 100; - // counterUpdateProgress++; - // setCounterProgress(counterUpdateProgress); + uploadPersen = 100; + progressInfo[idx].percentage = 100; + counterUpdateProgress++; + setCounterProgress(counterUpdateProgress); successTodo(); - if (fileTypeId == '1'){ - setIsImageUploadFinish(true); - } else if (fileTypeId == '2'){ - setIsVideoUploadFinish(true); - } if (fileTypeId == '3'){ - setIsTextUploadFinish(true); - } if (fileTypeId == '4'){ - setIsAudioUploadFinish(true); - } }, }); @@ -387,14 +369,24 @@ const EventModal = ({ useEffect(() => { successTodo(); - }, [isImageUploadFinish, isVideoUploadFinish, isAudioUploadFinish, isTextUploadFinish]) + }, [ + isImageUploadFinish, + isVideoUploadFinish, + isAudioUploadFinish, + isTextUploadFinish, + ]); function successTodo() { - if (isImageUploadFinish && isVideoUploadFinish && isAudioUploadFinish && isTextUploadFinish) { + if ( + isImageUploadFinish && + isVideoUploadFinish && + isAudioUploadFinish && + isTextUploadFinish + ) { successSubmit("/in/contributor/agenda-setting"); } } - + const successSubmit = (redirect: string) => { MySwal.fire({ title: "Sukses", @@ -422,7 +414,9 @@ const EventModal = ({ > - {event?.length > 1 ? "Edit Agenda Setting" : "Create Agenda Setting"}{" "} + {event?.length > 1 + ? "Edit Agenda Setting" + : "Create Agenda Setting"}{" "} {event?.title} @@ -551,7 +545,9 @@ const EventModal = ({ )} /> - {(agendaType === "polda" || agendaType === "polres" || agendaType === "satker" )&& ( + {(agendaType === "polda" || + agendaType === "polres" || + agendaType === "satker") && (
@@ -588,50 +584,56 @@ const EventModal = ({ )} - {(agendaType == "polres" || agendaType == "satker") && expandedPolda[polda.id] && ( -
- - {polda?.subDestination?.map((polres: any) => ( -
+ )}
))} @@ -665,7 +667,7 @@ const EventModal = ({
-
-
- - - {event?.length > 1 ? "Updating..." : "Adding..."} + {event?.length > 1 ? "Updating..." : "Adding..."} - ) : event?.length > 1 ? ( + ) : event?.length > 1 ? ( "Update Agenda Setting" ) : ( "Simpan Agenda Setting" diff --git a/app/[locale]/(protected)/contributor/content/spit/table-spit/columns.tsx b/app/[locale]/(protected)/contributor/content/spit/table-spit/columns.tsx index ca5cf595..aa706fb3 100644 --- a/app/[locale]/(protected)/contributor/content/spit/table-spit/columns.tsx +++ b/app/[locale]/(protected)/contributor/content/spit/table-spit/columns.tsx @@ -112,12 +112,17 @@ const columns: ColumnDef[] = [ header: "Actions", enableHiding: false, cell: ({ row }) => { + const isDisabled = row.original.isPublish; // Check the isPublish value + return ( - + + )} +
+ ), }, + { accessorKey: "uniqueCode", header: "Code", diff --git a/app/[locale]/(protected)/contributor/task/forward/[id]/page.tsx b/app/[locale]/(protected)/contributor/task/forward/[id]/page.tsx new file mode 100644 index 00000000..2cb4a08c --- /dev/null +++ b/app/[locale]/(protected)/contributor/task/forward/[id]/page.tsx @@ -0,0 +1,18 @@ +import { Card, CardContent } from "@/components/ui/card"; +import SiteBreadcrumb from "@/components/site-breadcrumb"; +import FormTask from "@/components/form/task/task-form"; +import FormTaskDetail from "@/components/form/task/task-detail-form"; +import FormTaskForward from "@/components/form/task/task-forward-form"; + +const TaskForwardPage = async () => { + return ( +
+ +
+ +
+
+ ); +}; + +export default TaskForwardPage; diff --git a/app/[locale]/(protected)/shared/curated-content/giat-routine/image/detail/[id]/page.tsx b/app/[locale]/(protected)/shared/curated-content/giat-routine/image/detail/[id]/page.tsx index 9f776610..90858da6 100644 --- a/app/[locale]/(protected)/shared/curated-content/giat-routine/image/detail/[id]/page.tsx +++ b/app/[locale]/(protected)/shared/curated-content/giat-routine/image/detail/[id]/page.tsx @@ -21,8 +21,19 @@ import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import Cookies from "js-cookie"; import { postBlog } from "@/service/blog/blog"; import { Textarea } from "@/components/ui/textarea"; -import { DotSquare, InboxIcon, PaperclipIcon, SmileIcon } from "lucide-react"; -import { detailMedia } from "@/service/curated-content/curated-content"; +import { + DotSquare, + InboxIcon, + PaperclipIcon, + SmileIcon, + TrashIcon, +} from "lucide-react"; +import { + deleteMediaCurationMessage, + detailMedia, + getMediaCurationMessage, + saveMediaCurationMessage, +} from "@/service/curated-content/curated-content"; import { Swiper, SwiperSlide } from "swiper/react"; import "swiper/css"; import "swiper/css/free-mode"; @@ -34,6 +45,14 @@ import "swiper/css/navigation"; import { FreeMode, Navigation, Pagination, Thumbs } from "swiper/modules"; import { Avatar, AvatarImage } from "@/components/ui/avatar"; import { Badge } from "@/components/ui/badge"; +import { listData } from "@/service/landing/landing"; +import { + createAssignmentResponse, + deleteAssignmentResponse, + getAssignmentResponseList, +} from "@/service/task"; +import { getCookiesDecrypt } from "@/lib/utils"; +import { close, loading } from "@/lib/swal"; const detailSchema = z.object({ title: z.string().min(1, { message: "Judul diperlukan" }), @@ -50,6 +69,24 @@ type Category = { categoryName: string; }; +const formatDate = (dateString: string): string => { + const date = new Date(dateString); + + // Pastikan validitas tanggal + if (isNaN(date.getTime())) { + throw new Error("Invalid date format"); + } + + // Format tanggal + const day = date.getDate().toString().padStart(2, "0"); + const month = (date.getMonth() + 1).toString().padStart(2, "0"); + const year = date.getFullYear(); + // const hours = date.getHours().toString().padStart(2, "0"); + + // Gabungkan hasil format + return `${day}-${month}-${year} `; +}; + export type curationDetail = { id: number; title: string; @@ -108,22 +145,21 @@ export default function DetailImage() { console.log(id); const editor = useRef(null); type DetailSchema = z.infer; - + const userLevelNumber = getCookiesDecrypt("ulne"); + const userId = getCookiesDecrypt("uie"); const [selectedFiles, setSelectedFiles] = useState([]); const taskId = Cookies.get("taskId"); const scheduleId = Cookies.get("scheduleId"); const scheduleType = Cookies.get("scheduleType"); const [selectedTarget, setSelectedTarget] = useState(""); - // const [detail, setDetail] = useState({ - // title: null, - // tags: null, - // files: [], - // fileType: null, - // }); const [detail, setDetail] = useState(); const [refresh] = useState(false); const [detailThumb, setDetailThumb] = useState([]); const [thumbsSwiper, setThumbsSwiper] = useState(null); + const [showInput, setShowInput] = useState(false); + const [selectedFileId, setSelectedFileId] = useState(null); + const [listData, setListData] = useState([]); + const [message, setMessage] = useState(""); const { control, @@ -142,31 +178,93 @@ export default function DetailImage() { setReplyingTo(commentId); }; - const addReply = (commentId: number) => { - if (replyText.trim()) { - const newCommentData = commentsData.map((comment: any) => { - if (comment.id === commentId) { - return { - ...comment, - replies: [ - ...comment.replies, - { - text: replyText, - username: "You", - date: new Date().toLocaleString(), - }, - ], - }; - } - return comment; - }); + const handleInputChange = (e: React.ChangeEvent) => { + setMessage(e.target.value); + }; - setCommentsData(newCommentData); - setReplyText(""); + useEffect(() => { + async function initState() { + // loading(); + const response = await getMediaCurationMessage(selectedFileId); + console.log("data", response?.data?.data); + console.log("userLvl", userLevelNumber); + setListData(response?.data?.data); + close(); + } + + initState(); + }, [selectedFileId]); + + // const postData = () => { + // sendSuggestionParent(); + // }; + + const postData = async () => { + if (message?.length > 1 && selectedFileId) { + try { + const data = { + mediaUploadFileId: selectedFileId, + message, + parentId: null, + }; + + const response = await saveMediaCurationMessage(data); + console.log("Komentar terkirim:", response); + + const responseGet = await getMediaCurationMessage(selectedFileId); + setListData(responseGet?.data?.data); + + setMessage(""); + } catch (error) { + console.error("Error posting comment:", error); + } + } else { + console.log("Pesan atau file ID tidak valid."); + } + }; + + const sendReplyData = async (parentId: number) => { + const inputElement = document.querySelector( + `#input-comment-${parentId}` + ) as HTMLTextAreaElement; + + if (inputElement?.value?.length > 1 && selectedFileId) { + loading(); + const data = { + mediaUploadFileId: selectedFileId, + message: inputElement.value, + parentId, + }; + + console.log("Sending reply:", data); + const response = await saveMediaCurationMessage(data); + console.log(response); + + const responseGet = await getMediaCurationMessage(selectedFileId); + console.log("Updated comments:", responseGet?.data?.data); + setListData(responseGet?.data?.data); + + inputElement.value = ""; + close(); setReplyingTo(null); } }; + async function deleteDataSuggestion(dataId: any) { + loading(); + const response = await deleteMediaCurationMessage(dataId); + console.log(response); + const responseGet = await getMediaCurationMessage(selectedFileId); + console.log(responseGet?.data?.data); + setListData(responseGet?.data?.data); + close(); + } + + const deleteData = (dataId: any) => { + deleteDataSuggestion(dataId); + console.log(dataId); + }; + useEffect(() => { async function initState() { if (id) { @@ -174,16 +272,29 @@ export default function DetailImage() { const details = response?.data?.data; setDetail(details); + setSelectedFileId(details?.files[0]?.id); const filesData = details.files || []; - const fileUrls = filesData.map((file: { thumbnailFileUrl: string }) => - file.thumbnailFileUrl ? file.thumbnailFileUrl : "default-image.jpg" - ); + const fileUrls = filesData.map((file: any) => ({ + id: file.id, + thumbnailFileUrl: file.thumbnailFileUrl || "default-image.jpg", + })); setDetailThumb(fileUrls); } } initState(); }, [id, refresh]); + const handleFileClick = async (fileId: any) => { + setSelectedFileId(fileId); + try { + const response = await getMediaCurationMessage(fileId); + console.log("Data komentar:", response?.data?.data); + setListData(response?.data?.data); + } catch (error) { + console.error("Error fetching comments:", error); + } + }; + return (
{detail !== undefined ? ( @@ -336,11 +447,14 @@ export default function DetailImage() { className="w-full" > {detailThumb?.map((data: any) => ( - + handleFileClick(data.id)} + > {` ))} @@ -357,11 +471,14 @@ export default function DetailImage() { // className="mySwiper2" > {detailThumb?.map((data: any) => ( - + handleFileClick(data.id)} + > {` ))} @@ -432,112 +549,186 @@ export default function DetailImage() {
-
- -
- - - - - -
-
- - - -
-
-
- {commentsData.map((comment) => ( -
- - - -
- - {comment.username} - - - {comment.date} - -

{comment.text}

-
handleReply(comment.id)} - > - - Balas +
+ - -
- )}
diff --git a/app/[locale]/(protected)/shared/curated-content/giat-routine/image/image.tsx b/app/[locale]/(protected)/shared/curated-content/giat-routine/image/image.tsx index 937b95bf..96600dc7 100644 --- a/app/[locale]/(protected)/shared/curated-content/giat-routine/image/image.tsx +++ b/app/[locale]/(protected)/shared/curated-content/giat-routine/image/image.tsx @@ -8,7 +8,8 @@ import { CarouselNext, CarouselPrevious, } from "@/components/ui/carousel"; -import { getListContent } from "@/service/landing/landing"; +import { listCuratedContent } from "@/service/curated-content/curated-content"; + import { formatDateToIndonesian, generateLocalizedPath, @@ -35,10 +36,12 @@ const ImageSliderPage = () => { const [imageData, setImageData] = useState(); const [displayImage, setDisplayImage] = useState([]); const [page, setPage] = useState(1); + const [limit, setLimit] = React.useState(10); + const [search, setSearch] = React.useState(""); useEffect(() => { fetchData(); - }, [page]); + }, [page, limit, search]); useEffect(() => { if (imageData?.length > 0) { @@ -49,12 +52,7 @@ const ImageSliderPage = () => { }, [imageData]); const fetchData = async () => { - const response = await getListContent({ - page: page - 1, - size: 6, - sortBy: "createdAt", - contentTypeId: "1", - }); + const response = await listCuratedContent(search, limit, page - 1, 1, "1"); console.log(response); const data = response?.data?.data; diff --git a/app/[locale]/(protected)/shared/curated-content/giat-routine/video/audio-visual.tsx b/app/[locale]/(protected)/shared/curated-content/giat-routine/video/audio-visual.tsx index 523f860e..07070ec0 100644 --- a/app/[locale]/(protected)/shared/curated-content/giat-routine/video/audio-visual.tsx +++ b/app/[locale]/(protected)/shared/curated-content/giat-routine/video/audio-visual.tsx @@ -1,6 +1,7 @@ "use client"; import { Link } from "@/components/navigation"; import { Card, CardContent } from "@/components/ui/card"; +import { listCuratedContent } from "@/service/curated-content/curated-content"; import { getListContent } from "@/service/landing/landing"; import { formatDateToIndonesian } from "@/utils/globals"; import { Icon } from "@iconify/react/dist/iconify.js"; @@ -11,10 +12,12 @@ const VideoSliderPage = () => { const [allVideoData, setAllVideoData] = useState([]); const [displayVideos, setDisplayVideos] = useState([]); const [page, setPage] = useState(1); + const [limit, setLimit] = React.useState(10); + const [search, setSearch] = React.useState(""); useEffect(() => { - initFetch(); - }, []); + fetchData(); + }, [page, limit, search]); useEffect(() => { if (allVideoData?.length > 0) { @@ -24,14 +27,13 @@ const VideoSliderPage = () => { } }, [allVideoData]); - const initFetch = async () => { - const response = await getListContent({ - page: page - 1, - size: 12, - sortBy: "createdAt", - contentTypeId: "2", - }); - setAllVideoData(response?.data?.data?.content || []); + const fetchData = async () => { + const response = await listCuratedContent(search, limit, page - 1, 1, "2"); + console.log(response); + + const data = response?.data?.data; + const contentData = data?.content; + setAllVideoData(contentData); }; const shuffleAndSetVideos = () => { diff --git a/app/[locale]/(public)/content-management/download/page.tsx b/app/[locale]/(public)/content-management/download/page.tsx index 80483d72..f1ffbf31 100644 --- a/app/[locale]/(public)/content-management/download/page.tsx +++ b/app/[locale]/(public)/content-management/download/page.tsx @@ -278,10 +278,10 @@ const Galery = (props: any) => {
-

Galeri Saya

+

Galeri Saya

-
-
+
+
{
-
+
{selectedTab == "video" ? ( contentVideo?.length > 0 ? (
diff --git a/app/[locale]/(public)/content-management/rewrite/page.tsx b/app/[locale]/(public)/content-management/rewrite/page.tsx index 132c93ed..b49d5c96 100644 --- a/app/[locale]/(public)/content-management/rewrite/page.tsx +++ b/app/[locale]/(public)/content-management/rewrite/page.tsx @@ -157,42 +157,42 @@ const page = (props: any) => { } }; - async function shareToEmail() { - if (Number(userRoleId) < 1 || userRoleId == undefined) { - router.push("/auth/login"); - } else { - const data = { - mediaUploadId: id?.split("-")?.[0], - email: emailShareList || [emailShareInput], - message: emailMessageInput, - url: window.location.href, - }; - loading(); - const res = await sendMediaUploadToEmail(data); - if (res?.error) { - error(res.message); - return false; - } - close(); - successCallback("Konten Telah Dikirim"); + async function shareToEmail() { + if (Number(userRoleId) < 1 || userRoleId == undefined) { + router.push("/auth/login"); + } else { + const data = { + mediaUploadId: id?.split("-")?.[0], + email: emailShareList || [emailShareInput], + message: emailMessageInput, + url: window.location.href, + }; + loading(); + const res = await sendMediaUploadToEmail(data); + if (res?.error) { + error(res.message); + return false; } + close(); + successCallback("Konten Telah Dikirim"); } + } - const handleEmailList = (e: any) => { - const arrayEmail: any = []; - for (let i = 0; i < emailShareList?.length; i += 1) { - arrayEmail.push(emailShareList[i]); + const handleEmailList = (e: any) => { + const arrayEmail: any = []; + for (let i = 0; i < emailShareList?.length; i += 1) { + arrayEmail.push(emailShareList[i]); + } + if (e.which == 13) { + if (e.target.value) { + arrayEmail.push(e.target.value); + setEmailShareList(arrayEmail); + setEmailShareInput(""); } - if (e.which == 13) { - if (e.target.value) { - arrayEmail.push(e.target.value); - setEmailShareList(arrayEmail); - setEmailShareInput(""); - } - e.preventDefault(); - } - return false; - }; + e.preventDefault(); + } + return false; + }; return ( <> @@ -203,7 +203,7 @@ const page = (props: any) => {

Galeri Content Rewrite

-
+
{contentImage?.length > 0 ? (
{contentImage?.map((image: any) => ( diff --git a/app/[locale]/(public)/inbox/page.tsx b/app/[locale]/(public)/inbox/page.tsx index 8f330796..f7626ff3 100644 --- a/app/[locale]/(public)/inbox/page.tsx +++ b/app/[locale]/(public)/inbox/page.tsx @@ -24,14 +24,14 @@ const InboxSection = () => { useEffect(() => { async function getNotif() { - const response = await getUserNotifications(page, 2); + const response = await getUserNotifications(pages, 2); setNotifications(response?.data?.data?.content); setGetTotalData(response?.data?.data?.totalElements); setGetTotalPage(response?.data?.data?.totalPage); } async function getNotifUpdate() { - const response = await getUserNotifications(page, 3); + const response = await getUserNotifications(pages, 3); setNotifications(response?.data?.data?.content); setGetTotalData(response?.data?.data?.totalElements); setGetTotalPage(response?.data?.data?.totalPage); @@ -42,7 +42,7 @@ const InboxSection = () => { } else { getNotif(); } - }, [page]); + }, [pages]); return (
@@ -65,83 +65,85 @@ const InboxSection = () => { Update
-
-

List Notifikasi

- {notifications?.map((list: any) => ( - - {(() => { - switch (Number(list.notificationTypeId)) { - case 2: - return ( -
- {" "} - {" "} -
- ); +
+

List Notifikasi

+
+ {notifications?.map((list: any) => ( + + {(() => { + switch (Number(list.notificationTypeId)) { + case 2: + return ( +
+ {" "} + {" "} +
+ ); - case 3: - return ( -
- {" "} - {" "} -
- ); + case 3: + return ( +
+ {" "} + {" "} +
+ ); - case 4: - return ( -
- {" "} - {" "} -
- ); + case 4: + return ( +
+ {" "} + {" "} +
+ ); - case 5: - return ( -
- {" "} - {" "} -
- ); + case 5: + return ( +
+ {" "} + {" "} +
+ ); - case 6: - return ( -
- {" "} - {" "} -
- ); + case 6: + return ( +
+ {" "} + {" "} +
+ ); - case 7: - return ( -
- {" "} - {" "} -
- ); + case 7: + return ( +
+ {" "} + {" "} +
+ ); - case 8: - return ( -
- {" "} - {" "} -
- ); + case 8: + return ( +
+ {" "} + {" "} +
+ ); - default: - return ( -
- {" "} - {" "} -
- ); - } - })()} -
- {list.message} - {getTimestamp(new Date(list.createdAt))} -
-
- ))} + default: + return ( +
+ {" "} + {" "} +
+ ); + } + })()} +
+ {list.message} + {getTimestamp(new Date(list.createdAt))} +
+ + ))} +
diff --git a/app/[locale]/(public)/inbox/update/page.tsx b/app/[locale]/(public)/inbox/update/page.tsx index b8b14476..4f31e10a 100644 --- a/app/[locale]/(public)/inbox/update/page.tsx +++ b/app/[locale]/(public)/inbox/update/page.tsx @@ -9,13 +9,10 @@ import { useRouter } from "next/navigation"; import React, { useEffect, useState } from "react"; const UpdateSection = () => { - const router = useRouter(); const pathname = usePathname(); const isUpdate = pathname.includes("update"); const searchParams = useSearchParams(); - const page: any = searchParams?.get("page"); - const pages = page ? page - 1 : 0; const [notifications, setNotifications] = useState([]); @@ -24,25 +21,25 @@ const UpdateSection = () => { useEffect(() => { async function getNotif() { - const response = await getUserNotifications(page, 2); + const response = await getUserNotifications(pages, 2); setNotifications(response?.data?.data?.content); setGetTotalData(response?.data?.data?.totalElements); setGetTotalPage(response?.data?.data?.totalPage); } async function getNotifUpdate() { - const response = await getUserNotifications(page, 3); + const response = await getUserNotifications(pages, 3); setNotifications(response?.data?.data?.content); setGetTotalData(response?.data?.data?.totalElements); setGetTotalPage(response?.data?.data?.totalPage); - } + } if (isUpdate) { getNotifUpdate(); } else { getNotif(); } - }, [page]); + }, [pages]); return (
@@ -56,7 +53,7 @@ const UpdateSection = () => {

Update

-
+
@@ -65,83 +62,85 @@ const UpdateSection = () => {
Update
-
-

List Notifikasi

- {notifications?.map((list: any) => ( - - {(() => { - switch (Number(list.notificationTypeId)) { - case 2: - return ( -
- {" "} - {" "} -
- ); +
+

List Notifikasi

+
+ {notifications?.map((list: any) => ( + + {(() => { + switch (Number(list.notificationTypeId)) { + case 2: + return ( +
+ {" "} + {" "} +
+ ); - case 3: - return ( -
- {" "} - {" "} -
- ); + case 3: + return ( +
+ {" "} + {" "} +
+ ); - case 4: - return ( -
- {" "} - {" "} -
- ); + case 4: + return ( +
+ {" "} + {" "} +
+ ); - case 5: - return ( -
- {" "} - {" "} -
- ); + case 5: + return ( +
+ {" "} + {" "} +
+ ); - case 6: - return ( -
- {" "} - {" "} -
- ); + case 6: + return ( +
+ {" "} + {" "} +
+ ); - case 7: - return ( -
- {" "} - {" "} -
- ); + case 7: + return ( +
+ {" "} + {" "} +
+ ); - case 8: - return ( -
- {" "} - {" "} -
- ); + case 8: + return ( +
+ {" "} + {" "} +
+ ); - default: - return ( -
- {" "} - {" "} -
- ); - } - })()} -
- {list.message} - {getTimestamp(new Date(list.createdAt))} -
-
- ))} + default: + return ( +
+ {" "} + {" "} +
+ ); + } + })()} +
+ {list.message} + {getTimestamp(new Date(list.createdAt))} +
+ + ))} +
diff --git a/components/form/content/audio-detail-form.tsx b/components/form/content/audio-detail-form.tsx index 4eabe091..67b396d8 100644 --- a/components/form/content/audio-detail-form.tsx +++ b/components/form/content/audio-detail-form.tsx @@ -55,7 +55,7 @@ import { getCookiesDecrypt } from "@/lib/utils"; import { Icon } from "@iconify/react/dist/iconify.js"; import { error } from "@/lib/swal"; import dynamic from "next/dynamic"; -import ReactAudioPlayer from "react-audio-player"; +import WavesurferPlayer from "@wavesurfer/react"; const imageSchema = z.object({ title: z.string().min(1, { message: "Judul diperlukan" }), @@ -140,6 +140,16 @@ export default function FormAudioDetail() { const [isMabesApprover, setIsMabesApprover] = useState(false); const [audioPlaying, setAudioPlaying] = useState(null); + const waveSurferRef = useRef(null); + const [isPlaying, setIsPlaying] = useState(false); + + const onPlayPause = () => { + if (waveSurferRef.current) { + waveSurferRef.current.playPause(); + setIsPlaying(!isPlaying); + } + }; + let fileTypeId = "4"; const { @@ -245,9 +255,17 @@ export default function FormAudioDetail() { setSelectedTarget(details.categoryId); // Untuk dropdown const filesData = details.files || []; - const fileUrls = filesData.map((file: { secondaryUrl: string }) => - file.secondaryUrl ? file.secondaryUrl : "default-image.jpg" + 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" + ); + + console.log("audio", fileUrls); + setDetailThumb(fileUrls); } } @@ -430,60 +448,19 @@ export default function FormAudioDetail() {
- - {detailThumb?.map((data: any) => { - return ( - -
- - {/* */} -
-
- ); - })} -
- - {/*
- - {detailThumb?.map((data: any) => { - return ( - -
- - -
-
- ); - })} -
-
*/} +
+ {detailThumb?.map((url: any, index: number) => ( +
+ +
+ ))} +

{isPlaying ? "Pause" : "Play"}

+
diff --git a/components/form/content/image-detail-form.tsx b/components/form/content/image-detail-form.tsx index 86bd37de..0c2b48a9 100644 --- a/components/form/content/image-detail-form.tsx +++ b/components/form/content/image-detail-form.tsx @@ -334,7 +334,7 @@ export default function FormImageDetail() { return false; } - + const setupPlacement = ( index: number, placement: string, @@ -679,7 +679,7 @@ export default function FormImageDetail() {
- {isUserMabesApprover && + {isUserMabesApprover && (
- } + )}
)) diff --git a/components/form/content/spit-convert-form.tsx b/components/form/content/spit-convert-form.tsx index 33959df0..0d89a2f0 100644 --- a/components/form/content/spit-convert-form.tsx +++ b/components/form/content/spit-convert-form.tsx @@ -85,6 +85,17 @@ type Option = { label: string; }; +interface FileData { + contentId: number; + placement?: string[]; + [key: string]: any; // Extendable for additional properties +} + +interface PlacementData { + mediaFileId: number; + placements: string; +} + const CustomEditor = dynamic( () => { return import("@/components/editor/custom-editor"); @@ -118,7 +129,6 @@ export default function FormConvertSPIT() { const [thumbsSwiper, setThumbsSwiper] = useState(null); const [selectedAdvConfig, setSelectedAdvConfig] = useState(""); const [title, setTitle] = useState(""); - const roleId = getCookiesDecrypt("urie"); const [articleIds, setArticleIds] = useState([]); const [isGeneratedArticle, setIsGeneratedArticle] = useState(false); const [articleBody, setArticleBody] = useState(""); @@ -128,7 +138,10 @@ export default function FormConvertSPIT() { const [detailData, setDetailData] = useState(null); const [selectedFileType, setSelectedFileType] = useState("original"); const [isLoadingData, setIsLoadingData] = useState(false); - + const userLevelId = getCookiesDecrypt("ulie"); + const userLevelNumber = getCookiesDecrypt("ulne"); + const roleId = getCookiesDecrypt("urie"); + const [isMabesApprover, setIsMabesApprover] = useState(false); const [selectedTarget, setSelectedTarget] = useState(""); const [unitSelection, setUnitSelection] = useState({ allUnit: false, @@ -137,6 +150,7 @@ export default function FormConvertSPIT() { polres: false, }); const [publishedFor, setPublishedFor] = useState([]); + const [placementLength, setPlacementLength] = useState([]); const options: Option[] = [ { id: "all", label: "SEMUA" }, @@ -187,6 +201,17 @@ export default function FormConvertSPIT() { initState(); }, []); + useEffect(() => { + if ( + userLevelId != undefined && + roleId != undefined && + userLevelId == "216" && + roleId == "3" + ) { + setIsMabesApprover(true); + } + }, [userLevelId, roleId]); + const getCategories = async () => { try { const category = await listCategory(fileTypeId); @@ -247,26 +272,64 @@ export default function FormConvertSPIT() { })) ); - const handleCheckboxChangeFile = (mediaFileId: any, value: any) => { - setTempFile((prev: any) => - prev.map((file: any) => - file.contentId === mediaFileId - ? { - ...file, - placement: file.placement.includes(value) - ? file.placement.filter((item: any) => item !== value) // Hapus jika sudah ada - : [...file.placement, value], // Tambah jika belum ada - } - : file - ) - ); + const getPlacement = (): PlacementData[] => { + return tempFile + .filter((file: FileData) => (file.placement || []).length > 0) // Gunakan default array + .map((file: FileData) => ({ + mediaFileId: Number(file.contentId), + placements: (file.placement || []).join(","), // Gunakan default array + })); }; - const getPlacement = () => { - return tempFile.map((file: any) => ({ - mediaFileId: Number(file.contentId), - placements: file.placement.join(","), - })); + const setupPlacement = (value: string, id: number): void => { + const updatedFiles = tempFile.map((file: FileData) => { + if (file.contentId === id) { + const currentPlacement = file.placement || []; + if (currentPlacement.includes(value)) { + // Remove the placement value + file.placement = currentPlacement.filter((val) => val !== value); + } else { + // Add the placement value + file.placement = + value === "all" + ? ["all", "mabes", "polda", "international"] + : [...currentPlacement, value]; + if (file.placement.includes("all") && value !== "all") { + file.placement = file.placement.filter((val) => val !== "all"); + } + } + } + return file; + }); + + const placementLength = updatedFiles.reduce( + (acc: any, file: any) => acc + (file.placement?.length || 0), + 0 + ); + + setTempFile( + updatedFiles.sort((a: any, b: any) => a.contentId - b.contentId) + ); + setPlacementLength(placementLength); + + console.log("Updated Files:", updatedFiles); + }; + + const handleCheckboxChangeFile = (contentId: number, value: string) => { + setTempFile((prevTempFile: any) => { + return prevTempFile.map((file: any) => { + if (file.contentId === contentId) { + const isChecked = file.placement?.includes(value); + return { + ...file, + placement: isChecked + ? file.placement.filter((v: any) => v !== value) // Uncheck + : [...(file.placement || []), value], // Check + }; + } + return file; + }); + }); }; const handleCheckboxChange = (id: string): void => { @@ -296,11 +359,17 @@ export default function FormConvertSPIT() { } }; - const save = async (data: any) => { + const save = async (data: { + contentTitle: string; + contentDescription: string; + contentRewriteDescription: string; + contentCreator: string; + }): Promise => { const description = selectedFileType === "original" ? data.contentDescription : data.contentRewriteDescription; + const requestData = { spitId: id, title: data.contentTitle, @@ -310,7 +379,7 @@ export default function FormConvertSPIT() { categoryId: selectedCategoryId, publishedFor: publishedFor.join(","), creator: data.contentCreator, - files: getPlacement(), + files: getPlacement(), // Include placement data }; const response = await convertSPIT(requestData); @@ -630,18 +699,29 @@ export default function FormConvertSPIT() {
-
- - {detailThumb.map((data: any) => ( -
- {`Thumbnail -
- {["all", "mabes", "polda", "internasional"].map( - (value) => ( + {isMabesApprover ? ( +
+ + {detailThumb.map((data: any) => ( +
+ {`Thumbnail +
+ {[ + "all", + "mabes", + "polda", + "satker", + "internasional", + ].map((value) => ( - ) - )} + ))} +
-
- ))} -
+ ))} +
+ ) : ( + "" + )}
diff --git a/components/form/task/task-detail-form.tsx b/components/form/task/task-detail-form.tsx index caeb22d2..9ab91157 100644 --- a/components/form/task/task-detail-form.tsx +++ b/components/form/task/task-detail-form.tsx @@ -21,8 +21,14 @@ import { Checkbox } from "@/components/ui/checkbox"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import JoditEditor from "jodit-react"; import { + acceptAssignment, createAssignmentResponse, createTask, + deleteAssignmentResponse, + deleteTask, + finishTask, + getAcceptance, + getAcceptanceAssignmentStatus, getAssignmentResponseList, getTask, getUserLevelForAssignments, @@ -34,12 +40,14 @@ import { DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; -import { ChevronDown, ChevronUp } from "lucide-react"; +import { ChevronDown, ChevronUp, DotSquare, TrashIcon } from "lucide-react"; import dynamic from "next/dynamic"; import { Link } from "@/components/navigation"; import { Textarea } from "@/components/ui/textarea"; -import { loading } from "@/lib/swal"; +import { close, error, loading } from "@/lib/swal"; import { getCookiesDecrypt } from "@/lib/utils"; +import { Avatar, AvatarImage } from "@/components/ui/avatar"; +import { successCallback } from "@/config/swal"; const taskSchema = z.object({ uniqueCode: z.string().min(1, { message: "Judul diperlukan" }), @@ -64,6 +72,21 @@ export type taskDetail = { id: number; name: string; }; + createdBy: { + id: number; + fullname: string; + username: string | null; + email: string; + isActive: boolean; + isDefault: boolean; + isInternational: boolean; + userLevel: { + id: number; + name: string; + aliasName: string; + userGroupId: number; + }; + }; taskType: string; broadcastType: string; narration: string; @@ -85,6 +108,43 @@ const ViewEditor = dynamic( { ssr: false } ); +const formatDate = (dateString: string): string => { + const date = new Date(dateString); + + // Pastikan validitas tanggal + if (isNaN(date.getTime())) { + throw new Error("Invalid date format"); + } + + // Format tanggal + const day = date.getDate().toString().padStart(2, "0"); + const month = (date.getMonth() + 1).toString().padStart(2, "0"); + const year = date.getFullYear(); + // const hours = date.getHours().toString().padStart(2, "0"); + + // Gabungkan hasil format + return `${day}-${month}-${year} `; +}; + +interface AcceptanceData { + id: number; + acceptAt: string; + // Tambahkan properti lain sesuai dengan struktur data Anda +} + +interface AcceptanceData { + id: number; + userLevelId: number; + sentAt: string; + isAccept: boolean; + isSent: boolean; + userLevels: { + id: number; + name: string; + aliasName: string; + }; +} + export default function FormTaskDetail() { const MySwal = withReactContent(Swal); const router = useRouter(); @@ -123,6 +183,15 @@ export default function FormTaskDetail() { const [showInput, setShowInput] = useState(false); const [listData, setListData] = useState([]); const [message, setMessage] = useState(""); + const [sentAcceptance, setSentAcceptance] = useState([]); + const [acceptAcceptance, setAcceptAcceptance] = useState( + [] + ); + const [statusAcceptance, setStatusAcceptance] = useState(); + const [modalType, setModalType] = useState<"terkirim" | "diterima" | "">(""); + const [Isloading, setLoading] = useState(false); + const [refreshAcceptance, setRefreshAcceptance] = useState(false); + const [replyingTo, setReplyingTo] = useState(null); const [platformTypeVisible, setPlatformTypeVisible] = useState(false); const [unitSelection, setUnitSelection] = useState({ @@ -288,6 +357,19 @@ export default function FormTaskDetail() { }); }; + const successConfirm = () => { + MySwal.fire({ + title: "Sukses", + icon: "success", + confirmButtonColor: "#3085d6", + confirmButtonText: "OK", + }).then((result) => { + if (result.isConfirmed) { + router.push("/en/contributor/task"); + } + }); + }; + const handleCheckboxChange = (levelId: any) => { setCheckedLevels((prev: any) => { const updatedLevels = new Set(prev); @@ -307,10 +389,6 @@ export default function FormTaskDetail() { })); }; - // const handleToggleInput = () => { - // setShowInput((prev: any) => !prev); // Toggle visibility of input field - // }; - useEffect(() => { async function initState() { // loading(); @@ -324,11 +402,6 @@ export default function FormTaskDetail() { initState(); }, []); - // const handleSubmitResponse = () => { - // console.log("Response Submitted:", response); - // setShowInput(false); // Optionally hide the input after submission - // }; - const handleToggleInput = (): void => { setShowInput(!showInput); }; @@ -356,12 +429,39 @@ export default function FormTaskDetail() { sendSuggestionParent(); }; + const sendReplyData = async (parentId: number) => { + const inputElement = document.querySelector( + `#input-comment-${parentId}` + ) as HTMLTextAreaElement; + + if (inputElement?.value?.length > 1) { + loading(); + const data = { + assignmentId: id, + message: inputElement.value, + parentId, + }; + + console.log(data); + const response = await createAssignmentResponse(data); + console.log(response); + + const responseGet = await getAssignmentResponseList(id); + console.log(responseGet?.data?.data); + setListData(responseGet?.data?.data); + + inputElement.value = ""; + close(); + setReplyingTo(null); + } + }; + async function sendSuggestionParent() { if (message?.length > 1) { loading(); const data = { assignmentId: id, - message, // Gunakan response di sini + message, parentId: null, }; @@ -377,11 +477,253 @@ export default function FormTaskDetail() { } } + async function deleteDataSuggestion(dataId: any) { + loading(); + const response = await deleteAssignmentResponse(dataId); + console.log(response); + const responseGet = await getAssignmentResponseList(id); + console.log(responseGet?.data?.data); + setListData(responseGet?.data?.data); + close(); + } + + const deleteData = (dataId: any) => { + deleteDataSuggestion(dataId); + console.log(dataId); + }; + + // async function sendSuggestionChild(parentId: any) { + // const msg = document.querySelectorAll(`#input-comment-${parentId}`)[0] + // .value; + + // if (msg?.length > 1) { + // loading(); + // const data = { + // assignmentId: id, + // message: msg, + // parentId, + // }; + + // console.log(data); + // const response = await createAssignmentResponse(data); + // console.log(response); + // const responseGet = await getAssignmentResponseList(id); + // console.log(responseGet?.data?.data); + // setListData(responseGet?.data?.data); + // // $(":input").val(""); + // close(); + // } + // } + + async function getDataAcceptance() { + const response = await getAcceptanceAssignmentStatus(id); + setStatusAcceptance(response?.data?.data?.isAccept); + console.log("Status :", response?.data?.data?.isAccept); + } + + const handleAcceptAcceptance = async () => { + const isAccept = true; + loading(); + console.log("Id user :", userId); + const response = await acceptAssignment(id, !isAccept); + + if (response?.error) { + error(response?.message); + return false; + } + + successCallback(); + getDataAcceptance(); + return false; + }; + + async function getListAcceptance(): Promise { + const isAccept = true; + + try { + const resSent = await getAcceptance(id, !isAccept); + setSentAcceptance(resSent?.data?.data); + + const resAccept = await getAcceptance(id, isAccept); + + const acceptanceSort = resAccept?.data?.data?.sort( + (a: AcceptanceData, b: AcceptanceData) => + new Date(a.acceptAt).getTime() - new Date(b.acceptAt).getTime() + ); + + console.log("Data sort:", acceptanceSort); + setAcceptAcceptance(acceptanceSort); + } catch (error) { + console.error("Error fetching acceptance data:", error); + } + } + + useEffect(() => { + async function initState(): Promise { + await getListAcceptance(); + } + + initState(); + }, [refreshAcceptance]); + + const setFinishAcceptance = async ( + id: number, + isFinish: boolean + ): Promise => { + if (!isFinish) { + loading(); + + setRefreshAcceptance((prev) => !prev); + close(); + } + }; + + const handleReply = (id: any) => { + setReplyingTo(id); + }; + + const getModalContent = (type: "terkirim" | "diterima") => ( +
+ {Isloading ? ( +

Loading...

+ ) : ( + + + + + + + + + + {(type === "terkirim" ? sentAcceptance : acceptAcceptance).map( + (item) => ( + + + + + + ) + )} + +
WaktuUnitStatus
+ {new Date(item.sentAt).toLocaleString()} + {item.userLevels.name} + {type === "terkirim" ? "Terkirim" : "Diterima"} +
+ )} +
+ ); + + async function finishAssignment() { + const response = finishTask(id); + + // if (response.error) { + // error(response.message); + // return false; + // } + + successConfirm(); + } + + async function deleteAssignment() { + const response = deleteTask(id); + + // if (response.error) { + // error(response.message); + // return false; + // } + + successConfirm(); + } + + function handleForward() { + router.push(`/en/contributor/task/forward/${id}`); + } + + async function handleDeleteAssignment() { + MySwal.fire({ + title: "Apakah Anda yakin ingin menghapus data?", + text: "", + icon: "warning", + showCancelButton: true, + cancelButtonColor: "#d33", + confirmButtonColor: "#3085d6", + confirmButtonText: "Hapus", + }).then((result) => { + if (result.isConfirmed) { + deleteAssignment(); + } + }); + } + + async function handleAssignmentDone() { + MySwal.fire({ + title: "Apakah tugas sudah selesai?", + text: "", + icon: "warning", + showCancelButton: true, + cancelButtonColor: "#d33", + confirmButtonColor: "#3085d6", + confirmButtonText: "Ya", + cancelButtonText: "Tidak", + }).then((result) => { + if (result.isConfirmed) { + finishAssignment(); + } + }); + } + return ( -
-

Form Penugasan

- {detail !== undefined ? ( + {detail !== undefined ? ( +
+
+

Detail Penugasan

+
+
+ + + + + + + + + + + + Detail Status Penugasan + + + {modalType === "terkirim" && getModalContent("terkirim")} + {modalType === "diterima" && getModalContent("diterima")} + + +
+
+
+
@@ -546,7 +888,7 @@ export default function FormTaskDetail() {
setMainType(value)} // value={String(mainType)} // onValueChange={(value) => setMainType(Number(value))} @@ -575,8 +917,8 @@ export default function FormTaskDetail() {
setType(value)} // Mengubah nilai state ketika pilihan berubah + value={detail.assignmentType.id.toString()} + onValueChange={(value) => setType(value)} className="flex flex-wrap gap-3" >
@@ -661,7 +1003,17 @@ export default function FormTaskDetail() {
-
@@ -690,35 +1042,202 @@ export default function FormTaskDetail() {
)}
-

Tanggapan

+

Tanggapan

{listData?.map((item: any) => ( -
-
- {item.name} - - {item.timestamp} - -
-

{item.content}

-
- - +
+
+ + + +
+
+ + {item.suggestionFrom.username} + + + {formatDate(item.createdAt)} + +
+

{item.message}

+
+
handleReply(item.id)} + > + + Balas +
+
deleteData(item.id)} + > + + Delete +
+
+
+ {replyingTo === item.id && ( +
+