diff --git a/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx b/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx index 0010f772..9853795f 100644 --- a/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx +++ b/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx @@ -1,5 +1,10 @@ // "use client"; -import React, { useState, useEffect } 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"; @@ -32,7 +37,7 @@ import { DialogTrigger, } from "@/components/ui/dialog"; import { Textarea } from "@/components/ui/textarea"; -import { error, loading } from "@/lib/swal"; +import { error, loading, success } from "@/lib/swal"; import Cookies from "js-cookie"; import Swal from "sweetalert2"; import withReactContent from "sweetalert2-react-content"; @@ -42,12 +47,18 @@ import { saveAgendaSettings } from "@/service/agenda-setting/agenda-setting"; import { Checkbox } from "@/components/ui/checkbox"; import { getUserLevelForAssignments } from "@/service/task"; import { AudioRecorder } from "react-audio-voice-recorder"; +import FileUploaderMultiple from "@/components/form/shared/file-uploaded"; +import { Upload } from "tus-js-client"; const schema = z.object({ title: z.string().min(3, { message: "Required" }), description: z.string().min(3, { message: "Required" }), }); +interface FileWithPreview extends File { + preview: string; +} + const EventModal = ({ open, onClose, @@ -64,7 +75,7 @@ const EventModal = ({ const [startDate, setStartDate] = useState(new Date()); const [endDate, setEndDate] = useState(new Date()); const [isPending, startTransition] = React.useTransition(); - const [calendarProps, setCalendarProps] = React.useState( + const [agendaType, setAgendaType] = React.useState( categories[0].value ); const [listDest, setListDest] = useState([]); @@ -78,6 +89,14 @@ const EventModal = ({ const [audioFile, setAudioFile] = useState(null); const [isRecording, setIsRecording] = useState(false); const [timer, setTimer] = useState(120); + const [imageFiles, setImageFiles] = useState([]); + const [videoFiles, setVideoFiles] = useState([]); + const [textFiles, setTextFiles] = useState([]); + const [audioFiles, setAudioFiles] = useState([]); + const [isImageUploadFinish, setIsImageUploadFinish] = useState(false); + const [isVideoUploadFinish, setIsVideoUploadFinish] = useState(false); + const [isTextUploadFinish, setIsTextUploadFinish] = useState(false); + const [isAudioUploadFinish, setIsAudioUploadFinish] = useState(false); const { register, @@ -96,8 +115,17 @@ const EventModal = ({ setIsLoading(true); try { const response = await getUserLevelForAssignments(); - setListDest(response?.data?.data.list); - const initialExpandedState = response?.data?.data.list.reduce( + const levelList = response?.data?.data.list; + let listFiltered = []; + if (agendaType == "polda") { + listFiltered = levelList.filter((level: any) => level.name != 'SATKER POLRI'); + } else if (agendaType == "polres") { + listFiltered = levelList.filter((level: any) => level.name != 'SATKER POLRI'); + } else if (agendaType == "satker") { + listFiltered = levelList.filter((level: any) => level.name == 'SATKER POLRI'); + } + setListDest(listFiltered); + const initialExpandedState = listFiltered.reduce( (acc: any, polda: any) => { acc[polda.id] = false; return acc; @@ -105,7 +133,6 @@ const EventModal = ({ {} ); setExpandedPolda(initialExpandedState); - console.log("polres", initialExpandedState); } catch (error) { console.error("Error fetching Polda/Polres data:", error); } finally { @@ -113,7 +140,8 @@ const EventModal = ({ } } fetchPoldaPolres(); - }, []); + console.log("Event", event); + }, [agendaType]); const handleCheckboxChange = (levelId: number) => { setCheckedLevels((prev) => { @@ -128,18 +156,15 @@ const EventModal = ({ }; const save = async (data: any) => { - if (!audioFile) return; - - const formData = new FormData(); - formData.append("voiceNote", audioFile); + // const formData = new FormData(); + // formData.append("voiceNote", audioFile); const reqData = { title: data.title, description: data.description, - agendaType: calendarProps, + agendaType: agendaType, startDate: format(startDate, "yyyy-MM-dd"), endDate: format(endDate, "yyyy-MM-dd"), - voiceNote: formData, }; console.log("Submitted Data:", reqData); @@ -150,20 +175,70 @@ const EventModal = ({ return false; } - Cookies.set("AgendaSetting", response?.data?.data.id, { - expires: 1, + const id = response?.data?.data.id; + + if (imageFiles?.length == 0) { + setIsImageUploadFinish(true); + } + imageFiles?.map(async (item: any, index: number) => { + 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" + ); + }); + + if (textFiles?.length == 0) { + setIsTextUploadFinish(true); + } + textFiles?.map(async (item: any, index: number) => { + 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" + ); }); // Optional: Use Swal for success feedback - MySwal.fire({ - title: "Sukses", - text: "Data berhasil disimpan.", - icon: "success", - confirmButtonColor: "#3085d6", - confirmButtonText: "OK", - }).then(() => { - router.push("en/contributor/agenda-setting"); - }); + // MySwal.fire({ + // title: "Sukses", + // text: "Data berhasil disimpan.", + // icon: "success", + // confirmButtonColor: "#3085d6", + // confirmButtonText: "OK", + // }).then(() => { + // router.push("en/contributor/agenda-setting"); + // }); }; const onSubmit = (data: any) => { @@ -179,7 +254,7 @@ const EventModal = ({ setStartDate(event?.event?.start); setEndDate(event?.event?.end); const eventCalendar = event?.event?.extendedProps?.calendar; - setCalendarProps(eventCalendar || categories[0].value); + setAgendaType(eventCalendar || categories[0].value); } setValue("title", event?.event?.title || ""); setValue("description", event?.event?.description || ""); @@ -197,6 +272,7 @@ const EventModal = ({ }; const toggleExpand = (poldaId: any) => { + console.log("Toogle : ", expandedPolda); setExpandedPolda((prev: any) => ({ ...prev, [poldaId]: !prev[poldaId], @@ -248,6 +324,89 @@ const EventModal = ({ audioElements.forEach((audio) => audio.remove()); }; + async function uploadResumableFile( + 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 upload = new Upload(file, { + endpoint: `${process.env.NEXT_PUBLIC_API}/agenda-settings/file/upload`, + retryDelays: [0, 3000, 6000, 12_000, 24_000], + chunkSize: 20_000, + metadata: { + agendaSettingId: id, + filename: file.name, + filetype: file.type, + fileTypeId: fileTypeId, + duration: "", + isWatermark: "true", // hardcode + }, + 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(); + if (fileTypeId == '1'){ + setIsImageUploadFinish(true); + } else if (fileTypeId == '2'){ + setIsVideoUploadFinish(true); + } if (fileTypeId == '3'){ + setIsTextUploadFinish(true); + } if (fileTypeId == '4'){ + setIsAudioUploadFinish(true); + } + }, + }); + + upload.start(); + } + + useEffect(() => { + successTodo(); + }, [isImageUploadFinish, isVideoUploadFinish, isAudioUploadFinish, isTextUploadFinish]) + + function successTodo() { + if (isImageUploadFinish && isVideoUploadFinish && isAudioUploadFinish && isTextUploadFinish) { + successSubmit("/in/contributor/agenda-setting"); + } + } + + const successSubmit = (redirect: string) => { + MySwal.fire({ + title: "Sukses", + text: "Data berhasil disimpan.", + icon: "success", + confirmButtonColor: "#3085d6", + confirmButtonText: "OK", + }).then(() => { + router.push(redirect); + }); + }; + return ( <> - {event ? "Edit Agenda Setting" : "Create Agenda Setting"}{" "} + {event?.length > 1 ? "Edit Agenda Setting" : "Create Agenda Setting"}{" "} {event?.title} @@ -366,14 +525,14 @@ const EventModal = ({
- + ( setImageFiles(files)} />
- setImageFiles(files)} />
- setTextFiles(files)} />
@@ -542,6 +708,16 @@ const EventModal = ({ downloadOnSavePress={true} downloadFileExtension="webm" /> + setAudioFiles(files)} + className="mt-2" + />
{audioFile && (
@@ -568,15 +744,15 @@ const EventModal = ({ {isPending ? ( <> - {event ? "Updating..." : "Adding..."} + {event?.length > 1 ? "Updating..." : "Adding..."} - ) : event ? ( + ) : event?.length > 1 ? ( "Update Agenda Setting" ) : ( "Simpan Agenda Setting" )} - {event && ( + {event?.length > 1 && ( +
+ )); + + return ( + +
+ +
+ +

+ {/* Drop files here or click to upload. */} + Tarik file disini atau klik untuk upload. +

+
+ ( {label} + Ukuran maksimal {maxSize} MB.) +
+
+
+ {files.length ? ( + +
{fileList}
+
+ +
+
+ ) : null} +
+ ); +} + + +export default FileUploaderMultiple; \ No newline at end of file diff --git a/components/form/task/task-form.tsx b/components/form/task/task-form.tsx index 5f5f22f4..40746c9a 100644 --- a/components/form/task/task-form.tsx +++ b/components/form/task/task-form.tsx @@ -34,6 +34,9 @@ import { } from "@/components/ui/dialog"; import { ChevronDown, ChevronUp } from "lucide-react"; import { AudioRecorder } from "react-audio-voice-recorder"; +import FileUploaderMultiple from "@/components/form/shared/file-uploaded"; +import { Upload } from "tus-js-client"; +import { error } from "@/config/swal"; const taskSchema = z.object({ title: z.string().min(1, { message: "Judul diperlukan" }), @@ -42,6 +45,10 @@ const taskSchema = z.object({ }), }); +interface FileWithPreview extends File { + preview: string; +} + export type taskDetail = { id: number; title: string; @@ -96,6 +103,15 @@ export default function FormTask() { const [isRecording, setIsRecording] = useState(false); const [timer, setTimer] = useState(120); + const [imageFiles, setImageFiles] = useState([]); + const [videoFiles, setVideoFiles] = useState([]); + const [textFiles, setTextFiles] = useState([]); + const [audioFiles, setAudioFiles] = useState([]); + const [isImageUploadFinish, setIsImageUploadFinish] = useState(false); + const [isVideoUploadFinish, setIsVideoUploadFinish] = useState(false); + const [isTextUploadFinish, setIsTextUploadFinish] = useState(false); + const [isAudioUploadFinish, setIsAudioUploadFinish] = useState(false); + const [platformTypeVisible, setPlatformTypeVisible] = useState(false); const [unitSelection, setUnitSelection] = useState({ allUnit: false, @@ -222,15 +238,69 @@ export default function FormTask() { console.log("Form Data Submitted:", requestData); console.log("response", response); - MySwal.fire({ - title: "Sukses", - text: "Data berhasil disimpan.", - icon: "success", - confirmButtonColor: "#3085d6", - confirmButtonText: "OK", - }).then(() => { - router.push("/en/contributor/task"); + const id = response?.data?.data.id; + + if (imageFiles?.length == 0) { + setIsImageUploadFinish(true); + } + imageFiles?.map(async (item: any, index: number) => { + 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" + ); + }); + + if (textFiles?.length == 0) { + setIsTextUploadFinish(true); + } + textFiles?.map(async (item: any, index: number) => { + 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" + ); + }); + + // MySwal.fire({ + // title: "Sukses", + // text: "Data berhasil disimpan.", + // icon: "success", + // confirmButtonColor: "#3085d6", + // confirmButtonText: "OK", + // }).then(() => { + // router.push("/en/contributor/task"); + // }); }; const onSubmit = (data: TaskSchema) => { @@ -301,6 +371,90 @@ export default function FormTask() { audioElements.forEach((audio) => audio.remove()); }; + async function uploadResumableFile( + 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 upload = new Upload(file, { + endpoint: `${process.env.NEXT_PUBLIC_API}/assignment/file/upload`, + retryDelays: [0, 3000, 6000, 12_000, 24_000], + chunkSize: 20_000, + metadata: { + assignmentid: id, + filename: file.name, + filetype: file.type, + fileTypeId: fileTypeId, + duration: "", + isWatermark: "true", // hardcode + }, + 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(); + if (fileTypeId == '1'){ + setIsImageUploadFinish(true); + } else if (fileTypeId == '2'){ + setIsVideoUploadFinish(true); + } if (fileTypeId == '3'){ + setIsTextUploadFinish(true); + } if (fileTypeId == '4'){ + setIsAudioUploadFinish(true); + } + }, + }); + + upload.start(); + } + + useEffect(() => { + successTodo(); + }, [isImageUploadFinish, isVideoUploadFinish, isAudioUploadFinish, isTextUploadFinish]) + + function successTodo() { + if (isImageUploadFinish && isVideoUploadFinish && isAudioUploadFinish && isTextUploadFinish) { + successSubmit("/in/contributor/agenda-setting"); + } + } + + const successSubmit = (redirect: string) => { + MySwal.fire({ + title: "Sukses", + text: "Data berhasil disimpan.", + icon: "success", + confirmButtonColor: "#3085d6", + confirmButtonText: "OK", + }).then(() => { + router.push(redirect); + }); + }; + + return (
@@ -550,34 +704,41 @@ export default function FormTask() {

)}
-
+
- setImageFiles(files)} />
- setImageFiles(files)} />
- setTextFiles(files)} />
@@ -591,6 +752,16 @@ export default function FormTask() { downloadOnSavePress={true} downloadFileExtension="webm" /> + setAudioFiles(files)} + className="mt-2" + />
{audioFile && (
@@ -605,7 +776,9 @@ export default function FormTask() {
)} - {isRecording &&

Recording... {timer} seconds remaining

}{" "} + {isRecording && ( +

Recording... {timer} seconds remaining

+ )}{" "} {/* Display remaining time */}
diff --git a/service/content/content.ts b/service/content/content.ts index 9db1eb05..95caaeba 100644 --- a/service/content/content.ts +++ b/service/content/content.ts @@ -59,24 +59,7 @@ export async function listDataTeks(limit: any, page: any, isForSelf: any, isAppr ); } -<<<<<<< HEAD -export async function listDataAudio( - limit: any, - page: any, - isForSelf: any, - isApproval: any, - categoryFilter: any, - statusFilter: any, - needApprovalFromLevel: any, - creator: any, - source: any, - startDate: any, - endDate: any, - title: string = "" -) { -======= export async function listDataAudio(page: any, limit: any, isForSelf: any, isApproval: any, categoryFilter: any, statusFilter: any, needApprovalFromLevel: any, creator: any, source: any, startDate: any, endDate: any, title: string = "") { ->>>>>>> 247f9af4015f7f700a54f09bbe1c1f99b91c71fd return await httpGetInterceptor( `media/list?enablePage=1&sortBy=createdAt&sort=desc&size=${limit}&page=${page}&typeId=4&isForSelf=${isForSelf}&isApproval=${isApproval}&categoryId=${categoryFilter}&statusId=${statusFilter}&needApprovalFromLevel=${needApprovalFromLevel}&creatorUserLevelName=${source}&creatorName=${creator}&startDate=${startDate}&endDate=${endDate}&title=${title}` ); diff --git a/service/http-config/http-interceptor-service.ts b/service/http-config/http-interceptor-service.ts index 8f929ac7..90979859 100644 --- a/service/http-config/http-interceptor-service.ts +++ b/service/http-config/http-interceptor-service.ts @@ -3,6 +3,7 @@ import axiosInterceptorInstance from "./axios-interceptor-instance"; import Cookies from "js-cookie"; export async function httpGetInterceptor(pathUrl: any) { + const pathname = window.location.pathname; const response = await axiosInterceptorInstance .get(pathUrl) .catch((error) => error.response); @@ -17,7 +18,9 @@ export async function httpGetInterceptor(pathUrl: any) { Object.keys(Cookies.get()).forEach((cookieName) => { Cookies.remove(cookieName); }); - // window.location.href = "/"; + if (pathname?.includes("/contributor/") || pathname?.includes("/admin/") || pathname?.includes("/supervisor/")) { + window.location.href = "/"; + } } else { return { error: true,