diff --git a/app/[locale]/(protected)/contributor/agenda-setting/calender-view.tsx b/app/[locale]/(protected)/contributor/agenda-setting/calender-view.tsx index 4e6995e1..fdc17169 100644 --- a/app/[locale]/(protected)/contributor/agenda-setting/calender-view.tsx +++ b/app/[locale]/(protected)/contributor/agenda-setting/calender-view.tsx @@ -10,7 +10,7 @@ import { Label } from "@/components/ui/label"; import ExternalDraggingevent from "./dragging-events"; import { Calendar } from "@/components/ui/calendar"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Plus } from "lucide-react"; +import { Book, CheckSquare2, CheckSquare2Icon, Plus } from "lucide-react"; import { Checkbox } from "@/components/ui/checkbox"; import { EventContentArg } from "@fullcalendar/core"; import EventModal from "./event-modal"; @@ -25,6 +25,13 @@ import { PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; const wait = () => new Promise((resolve) => setTimeout(resolve, 1000)); interface CalendarViewProps { @@ -46,7 +53,6 @@ export type CalendarEvent = { }; }; - const INITIAL_YEAR = dayjs().format("YYYY"); const INITIAL_MONTH = dayjs().format("M"); @@ -63,7 +69,7 @@ export interface AgendaSettingsAPIResponse { updatedAt: string; createdById: number | null; } - + interface YearlyData { january?: Event[]; february?: Event[]; @@ -86,8 +92,9 @@ interface MonthCardProps { interface ListItemProps { item: any; - text: string + text: string; createdBy: string; + isPublish: boolean; bgColor: string; } @@ -104,7 +111,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => { const [selectedEventDate, setSelectedEventDate] = useState(null); const roleId = Number(getCookiesDecrypt("urie")) || 0; const [apiEvents, setApiEvents] = useState([]); - const [loading, setLoading] = useState(false); + const [Isloading, setLoading] = useState(false); const [draggableInitialized, setDraggableInitialized] = useState(false); const t = useTranslations("CalendarApp"); @@ -117,10 +124,11 @@ const CalendarView = ({ categories }: CalendarViewProps) => { const TODAY = dayjs().format("yyyy-MM-dd"); const INITIAL_YEAR = dayjs().format("YYYY"); const INITIAL_MONTH = dayjs().format("M"); + const [open, setOpen] = useState(false); const [selectedYear, setSelectedYear] = useState(new Date()); const [selectedMonth, setSelectedMonth] = useState( - dayjs(new Date(Number(INITIAL_YEAR), Number(INITIAL_MONTH) - 1, 1)), + dayjs(new Date(Number(INITIAL_YEAR), Number(INITIAL_MONTH) - 1, 1)) ); const [dragEvents] = useState([ @@ -142,7 +150,11 @@ const CalendarView = ({ categories }: CalendarViewProps) => { const getCalendarEvents = async () => { console.log("View : ", activeView); - const res = await getAgendaSettingsList(selectedMonth?.format("YYYY") || INITIAL_YEAR, selectedMonth.format("M") || INITIAL_MONTH, ""); + const res = await getAgendaSettingsList( + selectedMonth?.format("YYYY") || INITIAL_YEAR, + selectedMonth.format("M") || INITIAL_MONTH, + "" + ); console.log("View : API Response:", res); if (res?.error) { @@ -175,13 +187,17 @@ const CalendarView = ({ categories }: CalendarViewProps) => { }; const getYearlyEvents = async () => { - const res = await getAgendaSettingsList(selectedMonth?.format("YYYY") || INITIAL_YEAR, '', ''); - if (res?.error) { - error(res.message); - return false; - } - setYearlyData(res?.data?.data); - } + const res = await getAgendaSettingsList( + selectedMonth?.format("YYYY") || INITIAL_YEAR, + "", + "" + ); + if (res?.error) { + error(res.message); + return false; + } + setYearlyData(res?.data?.data); + }; useEffect(() => { setSelectedCategory(categories?.map((c) => c.value)); @@ -218,21 +234,19 @@ const CalendarView = ({ categories }: CalendarViewProps) => { if (response?.data && Array.isArray(response?.data)) { // Transform API data to match CalendarEvent type - const eventsFromAPI: CalendarEvent[] = response?.data?.map( - (item) => ({ - id: item.id.toString(), - title: item.title, - createBy: "Mabes Polri - Approver", - createdByName: item.createdByName, - start: new Date(item.startDate), - end: new Date(item.endDate), - allDay: true, // Sesuaikan jika memang ada event sepanjang hari - extendedProps: { - calendar: item.agendaType, - description: item.description, - }, - }) - ); + const eventsFromAPI: CalendarEvent[] = response?.data?.map((item) => ({ + id: item.id.toString(), + title: item.title, + createBy: "Mabes Polri - Approver", + createdByName: item.createdByName, + start: new Date(item.startDate), + end: new Date(item.endDate), + allDay: true, // Sesuaikan jika memang ada event sepanjang hari + extendedProps: { + calendar: item.agendaType, + description: item.description, + }, + })); setApiEvents(eventsFromAPI); } else { @@ -321,11 +335,17 @@ const CalendarView = ({ categories }: CalendarViewProps) => { const renderEventContent = (eventInfo: any) => { const { title } = eventInfo.event; - const { createdByName } = eventInfo.event.extendedProps; // Akses dari extendedProps + const { isPublish } = eventInfo.event.extendedProps; + const { createdByName } = eventInfo.event.extendedProps; return ( <> -

{title}

+
+ {" "} + {isPublish == true && } +

{title}

+
+

Created By : {createdByName}

@@ -340,6 +360,8 @@ const CalendarView = ({ categories }: CalendarViewProps) => { return "bg-blue-400 border-none"; } else if (arg.event.extendedProps.calendar === "polres") { return "bg-slate-400 border-none"; + } else if (arg.event.extendedProps.calendar === "satker") { + return "bg-orange-500 border-none"; } else if (arg.event.extendedProps.calendar === "international") { return "bg-green-400 border-none"; } else { @@ -359,29 +381,30 @@ const CalendarView = ({ categories }: CalendarViewProps) => { const handleViewChange = (viewType: string) => { console.log("Change view : ", viewType); setActiveView(viewType); - }; + }; const months: Array<{ id: keyof YearlyData; label: string }> = [ - { id: 'january', label: 'Januari' }, - { id: 'february', label: 'Februari' }, - { id: 'march', label: 'Maret' }, - { id: 'april', label: 'April' }, - { id: 'may', label: 'Mei' }, - { id: 'june', label: 'Juni' }, - { id: 'july', label: 'Juli' }, - { id: 'august', label: 'Agustus' }, - { id: 'september', label: 'September' }, - { id: 'october', label: 'Oktober' }, - { id: 'november', label: 'November' }, - { id: 'december', label: 'Desember' } + { id: "january", label: "Januari" }, + { id: "february", label: "Februari" }, + { id: "march", label: "Maret" }, + { id: "april", label: "April" }, + { id: "may", label: "Mei" }, + { id: "june", label: "Juni" }, + { id: "july", label: "Juli" }, + { id: "august", label: "Agustus" }, + { id: "september", label: "September" }, + { id: "october", label: "Oktober" }, + { id: "november", label: "November" }, + { id: "december", label: "Desember" }, ]; - const getEventColor = (type: Event['type']): string => { - const colors: Record = { - mabes: 'bg-yellow-500', - polda: 'bg-blue-400', - polres: 'bg-slate-400', - international: 'bg-green-400' + const getEventColor = (type: Event["type"]): string => { + const colors: Record = { + mabes: "bg-yellow-500", + polda: "bg-blue-400", + polres: "bg-slate-400", + satker: "bg-orange-500", + international: "bg-green-400", }; return colors[type]; }; @@ -397,32 +420,42 @@ const CalendarView = ({ categories }: CalendarViewProps) => { allDay: true, extendedProps: { calendar: item.agendaType, - description: item.description - } + description: item.description, + }, }; const finalEvent: any = { - event: formattedEvent - } - + event: formattedEvent, + }; + console.log("Event click custom : ", finalEvent); setSelectedEventDate(null); setSheetOpen(true); setApiEvents(finalEvent); wait().then(() => (document.body.style.pointerEvents = "auto")); - } + }; - const ListItem: React.FC = ({ item, text, createdBy, bgColor }) => ( -
handleClickListItem(item)}> -

{text}

-

- Created By: {createdBy} -

+ const ListItem: React.FC = ({ + item, + text, + createdBy, + isPublish, + bgColor, + }) => ( +
handleClickListItem(item)} + > +
+ {isPublish == true && } +

{text}

+
+

Created By: {createdBy}

); const MonthCard: React.FC = (props: any) => { - const { monthId, label } = props; + const { monthId, label } = props; const events: any = yearlyData?.[monthId]; const displayedEvents = events?.slice(0, 3); const hasMoreEvents = events?.length > 3; @@ -431,7 +464,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => {

{label}

- +
{events?.length === 0 ? (
@@ -440,15 +473,16 @@ const CalendarView = ({ categories }: CalendarViewProps) => { ) : ( <> {displayedEvents?.map((event: any, index: number) => ( - ))} - + {hasMoreEvents && ( @@ -462,16 +496,19 @@ const CalendarView = ({ categories }: CalendarViewProps) => { > - {label} + + {label} +
{events.map((event: any, index: number) => ( - ))} @@ -488,22 +525,72 @@ const CalendarView = ({ categories }: CalendarViewProps) => { ); }; + const getModalContent = (type: "terkirim" | "diterima") => ( +
+ {Isloading ? ( +

Loading...

+ ) : ( + + + + + + + + + + + + + + + + + + + +
NoTicket NumberDate and TimeTitleStatus
{"1"}{"MIA - 001"}{"23/01/2025 13:00"}{"Daily Issue 25 Januari 2025 "} + {type === "terkirim" ? "Completed" : "Completed"} +
+ )} +
+ ); + return ( <>
- {roleId == 11 || roleId == 12 ? + {roleId == 11 || roleId == 2 || roleId == 12 ? ( : + + ) : ( "" - } + )} +
+ + + {roleId == 2 ? ( + + ) : null} + + + + Hasil Pantauan + + {getModalContent("terkirim")} + + +
@@ -536,7 +623,7 @@ const CalendarView = ({ categories }: CalendarViewProps) => { if (selectedCategory?.length === categories?.length) { setSelectedCategory([]); } else { - setSelectedCategory(categories.map((c) => c.value)); + setSelectedCategory(categories?.map((c) => c.value)); } }} /> @@ -597,35 +684,53 @@ const CalendarView = ({ categories }: CalendarViewProps) => { handleDateChange(info.view.currentStart, info.view.currentEnd); handleViewChange(info.view.type); }} - viewClassNames={activeView === "listYear" ? "hide-calendar-grid" : ""} + viewClassNames={ + activeView === "listYear" ? "hide-calendar-grid" : "" + } /> {activeView === "listYear" && (
- {months.slice(0, 3).map(month => ( - + {months.slice(0, 3).map((month) => ( + ))}
{/* Second Row */}
- {months.slice(3, 6).map(month => ( - + {months.slice(3, 6).map((month) => ( + ))}
{/* Third Row */}
- {months.slice(6, 9).map(month => ( - + {months.slice(6, 9).map((month) => ( + ))}
{/* Fourth Row */}
- {months.slice(9, 12).map(month => ( - + {months.slice(9, 12).map((month) => ( + ))}
diff --git a/app/[locale]/(protected)/contributor/agenda-setting/data.ts b/app/[locale]/(protected)/contributor/agenda-setting/data.ts index fddde01a..3cab4af9 100644 --- a/app/[locale]/(protected)/contributor/agenda-setting/data.ts +++ b/app/[locale]/(protected)/contributor/agenda-setting/data.ts @@ -125,7 +125,6 @@ export const categories = [ { label: "Polda", value: "polda", - className: "data-[state=checked]:bg-blue-400 data-[state=checked]:ring-blue-400", }, diff --git a/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx b/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx index 2ecd4174..e4abf2e9 100644 --- a/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx +++ b/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx @@ -4,7 +4,7 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { useForm, Controller } from "react-hook-form"; -import { cn } from "@/lib/utils"; +import { cn, getCookiesDecrypt } from "@/lib/utils"; import { format } from "date-fns"; import { Popover, @@ -21,7 +21,13 @@ import { import { Calendar } from "@/components/ui/calendar"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; -import { Loader2, CalendarIcon, ChevronUp, ChevronDown } from "lucide-react"; +import { + Loader2, + CalendarIcon, + ChevronUp, + ChevronDown, + Music, +} from "lucide-react"; import DeleteConfirmationDialog from "@/components/delete-confirmation-dialog"; import { CalendarCategory } from "./data"; import { @@ -36,9 +42,13 @@ import { error, loading, success } from "@/lib/swal"; import Cookies from "js-cookie"; import Swal from "sweetalert2"; import withReactContent from "sweetalert2-react-content"; -import { useRouter } from "next/navigation"; import { postSchedule } from "@/service/schedule/schedule"; -import { getAgendaSettingsById, saveAgendaSettings } from "@/service/agenda-setting/agenda-setting"; +import { + deleteAgendaSettings, + getAgendaSettingsById, + publishAgendaSettings, + 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"; @@ -47,10 +57,18 @@ import { Upload } from "tus-js-client"; import { getCsrfToken } from "@/service/auth"; import { Icon } from "@iconify/react"; import Image from "next/image"; +import { UnitMapping } from "./unit-mapping"; +import { useToast } from "@/components/ui/use-toast"; +import { usePathname, useRouter } from "next/navigation"; +import { Card } from "@/components/ui/card"; +import WavesurferPlayer from "@wavesurfer/react"; +import WaveSurfer from "wavesurfer.js"; const schema = z.object({ title: z.string().min(3, { message: "Required" }), - description: z.string().min(3, { message: "Required" }), + description: z + .string() + .min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }), }); interface FileWithPreview extends File { @@ -75,15 +93,17 @@ const EventModal = ({ event: any; selectedDate: any; }) => { + const roleId = Number(getCookiesDecrypt("urie")) || 0; + const [detail, setDetail] = useState(); 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 [listDest, setListDest] = useState([]); const [deleteModalOpen, setDeleteModalOpen] = useState(false); const [eventIdToDelete, setEventIdToDelete] = useState(null); const MySwal = withReactContent(Swal); const router = useRouter(); + const pathname = usePathname(); const [isLoading, setIsLoading] = useState(false); const [checkedLevels, setCheckedLevels] = useState(new Set()); const [expandedPolda, setExpandedPolda] = useState([{}]); @@ -95,20 +115,46 @@ const EventModal = ({ const [videoFiles, setVideoFiles] = useState([]); const [textFiles, setTextFiles] = useState([]); const [audioFiles, setAudioFiles] = useState([]); - const [imageUploadedFiles, setImageUploadedFiles] = useState([]); - const [videoUploadedFiles, setVideoUploadedFiles] = useState([]); - const [textUploadedFiles, setTextUploadedFiles] = useState([]); - const [audioUploadedFiles, setAudioUploadedFiles] = useState([]); + const [imageUploadedFiles, setImageUploadedFiles] = useState( + [] + ); + const [videoUploadedFiles, setVideoUploadedFiles] = useState( + [] + ); + const [textUploadedFiles, setTextUploadedFiles] = useState( + [] + ); + const [audioUploadedFiles, setAudioUploadedFiles] = useState( + [] + ); + const { toast } = useToast(); const [isImageUploadFinish, setIsImageUploadFinish] = useState(false); const [isVideoUploadFinish, setIsVideoUploadFinish] = useState(false); const [isTextUploadFinish, setIsTextUploadFinish] = useState(false); const [isAudioUploadFinish, setIsAudioUploadFinish] = useState(false); + const [unitData, setUnitData] = useState([]); + const [satkerData, setSatkerData] = useState([]); let progressInfo: any = []; let counterUpdateProgress = 0; const [progressList, setProgressList] = useState([]); let uploadPersen = 0; const [isStartUpload, setIsStartUpload] = useState(false); const [counterProgress, setCounterProgress] = useState(0); + const [wilayahPublish, setWilayahPublish] = React.useState({ + semua: false, + nasional: false, + polda: false, + polres: false, + satker: false, + international: false, + }); + const [agendaType, setAgendaType] = React.useState(""); // State untuk agendaType + const [selectedPolda, setSelectedPolda] = React.useState([]); // Untuk data Polda + const [selectedSatker, setSelectedSatker] = React.useState([]); + const [selectedPolres, setSelectedPolres] = React.useState([]); + const [wavesurfer, setWavesurfer] = useState(); + const [isPlaying, setIsPlaying] = useState(false); + const [isPublishing, setIsPublishing] = useState(false); const { register, @@ -123,59 +169,85 @@ const EventModal = ({ }); useEffect(() => { - async function fetchDetailData(){ + async function fetchDetailData() { const res = await getAgendaSettingsById(event?.event?.id); const detail = res?.data?.data; setDetailData(detail); + const description = res?.data?.data?.description; + console.log("description", res?.data?.data?.description); + + // Set nilai awal description ke form control + if (description) { + setValue("description", description); + } + const attachments = detail?.attachments; - setImageUploadedFiles(attachments?.filter((file: any) => file.fileTypeId == 1)); - setVideoUploadedFiles(attachments?.filter((file: any) => file.fileTypeId == 2)); - setTextUploadedFiles(attachments?.filter((file: any) => file.fileTypeId == 3)); - setAudioUploadedFiles(attachments?.filter((file: any) => file.fileTypeId == 4)); + setImageUploadedFiles( + attachments?.filter((file: any) => file.fileTypeId == 1) + ); + setVideoUploadedFiles( + attachments?.filter((file: any) => file.fileTypeId == 2) + ); + setTextUploadedFiles( + attachments?.filter((file: any) => file.fileTypeId == 3) + ); + setAudioUploadedFiles( + attachments?.filter((file: any) => file.fileTypeId == 4) + ); + + const agendaType = detail?.agendaType; + setWilayahPublish({ + semua: agendaType === "all", + nasional: agendaType === "mabes", + polda: agendaType === "polda", + polres: agendaType === "polres", + satker: agendaType === "satker", + international: agendaType === "international", + }); } fetchDetailData(); - }, [event]); + }, [event, setValue]); - useEffect(() => { - async function fetchPoldaPolres() { - setIsLoading(true); - try { - const response = await getUserLevelForAssignments(); - 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; - }, - {} - ); - setExpandedPolda(initialExpandedState); - } catch (error) { - console.error("Error fetching Polda/Polres data:", error); - } finally { - setIsLoading(false); - } - } - - fetchPoldaPolres(); - }, [agendaType]); + // useEffect(() => { + // async function fetchPoldaPolres() { + // setIsLoading(true); + // try { + // const response = await getUserLevelForAssignments(); + // 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; + // }, + // {} + // ); + // setExpandedPolda(initialExpandedState); + // } catch (error) { + // console.error("Error fetching Polda/Polres data:", error); + // } finally { + // setIsLoading(false); + // } + // } + + // fetchPoldaPolres(); + // }, [agendaType]); const handleCheckboxChange = (levelId: number) => { setCheckedLevels((prev) => { @@ -189,14 +261,55 @@ const EventModal = ({ }); }; - const save = async (data: any) => { - // const formData = new FormData(); - // formData.append("voiceNote", audioFile); + const toggleWilayah = (key: string) => { + setWilayahPublish((prev: any) => { + const newState = { ...prev, [key]: !prev[key] }; + + // Handle "semua" logic to check all options + if (key === "semua" && newState.semua) { + setAgendaType("all"); + return { + semua: true, + nasional: true, + polda: true, + polres: true, + satker: true, + international: true, + }; + } + + // Uncheck "semua" if any other option is selected + if (key !== "semua") { + newState.semua = false; + } + + // Set agendaType based on the selected checkbox + if (newState.nasional) setAgendaType("mabes"); + else if (newState.polda) setAgendaType("polda"); + else if (newState.polres) setAgendaType("polres"); + else if (newState.satker) setAgendaType("satker"); + else if (newState.international) setAgendaType("international"); + else setAgendaType(""); // Reset if no checkbox is selected + + return newState; + }); + }; + + const save = async (data: any, publish = false) => { + const publishTo = []; + if (wilayahPublish.semua) publishTo.push("all"); + if (wilayahPublish.nasional) publishTo.push("mabes"); + if (wilayahPublish.polda) publishTo.push(...selectedPolda); + if (wilayahPublish.polres) publishTo.push(...selectedPolres); + if (wilayahPublish.satker) publishTo.push(...selectedSatker); + if (wilayahPublish.international) publishTo.push("international"); const reqData = { + id: detailData?.id, title: data.title, description: data.description, - agendaType: agendaType, + agendaType, // Include agendaType in request + publishTo, startDate: format(startDate, "yyyy-MM-dd"), endDate: format(endDate, "yyyy-MM-dd"), }; @@ -210,23 +323,23 @@ const EventModal = ({ } const id = response?.data?.data.id; - + loading(); - if (imageFiles?.length == 0) { + 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) { + 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) { + if (textFiles?.length === 0) { setIsTextUploadFinish(true); } textFiles?.map(async (item: any, index: number) => { @@ -236,23 +349,41 @@ const EventModal = ({ if (audioFiles?.length == 0) { setIsAudioUploadFinish(true); } - audioFiles?.map(async (item: any, index: number) => { - await uploadResumableFile(index, String(id), item, "4", "0"); + audioFiles.map(async (item: FileWithPreview, index: number) => { + await uploadResumableFile( + index, + String(id), + item, // Use .file to access the actual File object + "4", + "0" // Optional: Replace with actual duration if available + ); }); + if (publish) { + setIsPublishing(true); + const publishResponse = await publishAgendaSettings(id); + setIsPublishing(false); - // 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"); - // }); + if (publishResponse?.error) { + error(publishResponse?.message); + return false; + } + + success("Agenda berhasil dipublikasikan!"); + } }; const onSubmit = (data: any) => { + if ( + (wilayahPublish.polda && selectedPolda.length === 0) || + (wilayahPublish.satker && selectedSatker.length === 0) || + (wilayahPublish.polres && selectedPolres.length === 0) + ) { + toast({ + title: "Pilih ID untuk Polda/Satker", + variant: "destructive", + }); + return; + } save(data); }; @@ -265,7 +396,7 @@ const EventModal = ({ setStartDate(event?.event?.start); setEndDate(event?.event?.end); const eventCalendar = event?.event?.extendedProps?.calendar; - setAgendaType(eventCalendar || categories[0].value); + setAgendaType(eventCalendar || categories?.length > 0 && categories[0].value); } setValue("title", event?.event?.title || ""); setValue("description", event?.event?.description || ""); @@ -324,11 +455,17 @@ const EventModal = ({ audio.controls = true; document.body.appendChild(audio); - // Convert Blob to File - const file = new File([blob], "voiceNote.webm", { type: "audio/webm" }); - setAudioFile(file); + // Convert Blob to File and add preview + const fileWithPreview: FileWithPreview = Object.assign( + new File([blob], "voiceNote.webm", { type: "audio/webm" }), + { preview: url } + ); + + // Add to state + setAudioFile(fileWithPreview); + setAudioFiles((prev) => [...prev, fileWithPreview]); }; - + const handleDeleteAudio = () => { // Remove the audio file by setting state to null setAudioFile(null); @@ -440,19 +577,71 @@ const EventModal = ({ }; const renderFilePreview = (url: string) => { - return ( - {"file - ); + return ( + {"file + ); }; - - const handleRemoveFile = (id: number) => { + const handleRemoveFile = (id: number) => {}; + + async function doDelete(id: any) { + loading(); + const resDelete = await deleteAgendaSettings(id); + if (resDelete?.error) { + error(resDelete.message); + return false; + } + close(); + successSubmitDelete("/in/contributor/agenda-setting"); + window.location.reload(); + } + + const handleDelete = (id: any) => { + MySwal.fire({ + title: "Hapus Data?", + text: "", + icon: "warning", + showCancelButton: true, + cancelButtonColor: "#3085d6", + confirmButtonColor: "#d33", + confirmButtonText: "Hapus", + }).then((result) => { + if (result.isConfirmed) { + doDelete(id); + } + }); + }; + + function successSubmitDelete(redirect: string) { + MySwal.fire({ + title: "Sukses", + icon: "success", + confirmButtonColor: "#3085d6", + confirmButtonText: "OK", + }).then(() => { + if (redirect === window.location.pathname) { + fetch(redirect, { method: "GET", cache: "reload" }).then(() => { + console.log("Data diperbarui."); + }); + } else { + window.location.href = redirect; + } + }); + } + + const onReady = (ws: any) => { + setWavesurfer(ws); + setIsPlaying(false); + }; + + const onPlayPause = () => { + wavesurfer && wavesurfer.playPause(); }; return ( @@ -476,6 +665,7 @@ const EventModal = ({ {event?.title} +
@@ -575,128 +765,98 @@ const EventModal = ({
- - ( - - )} - /> -
- {(agendaType === "polda" || - agendaType === "polres" || - agendaType === "satker") && ( -
- - - - - - - - Daftar Wilayah Polda dan Polres - - -
- {listDest.map((polda: any) => ( -
- - {(agendaType == "polres" || - agendaType == "satker") && - expandedPolda[polda.id] && ( -
- - {polda?.subDestination?.map( - (polres: any) => ( - - ) - )} -
- )} -
- ))} -
-
-
+ +
+
+ toggleWilayah("semua")} + /> + +
+
+ toggleWilayah("nasional")} + /> + +
+
+ toggleWilayah("polda")} + /> + + {wilayahPublish.polda && ( + + setSelectedPolda(data) + } + /> + )} +
+
+ toggleWilayah("polres")} + /> + + {wilayahPublish.polres && ( + + setSelectedPolres(data) + } + /> + )} +
+
+ toggleWilayah("satker")} + /> + + {wilayahPublish.satker && ( + + setSelectedSatker(data) + } + /> + )} +
+
+ toggleWilayah("international")} + /> + +
- )} +
+
( +
+ - -
- )}
diff --git a/app/[locale]/(protected)/shared/curated-content/giat-routine/document/all/page.tsx b/app/[locale]/(protected)/shared/curated-content/giat-routine/document/all/page.tsx new file mode 100644 index 00000000..8fab4905 --- /dev/null +++ b/app/[locale]/(protected)/shared/curated-content/giat-routine/document/all/page.tsx @@ -0,0 +1,120 @@ +import SiteBreadcrumb from "@/components/site-breadcrumb"; +import { Card, CardContent } from "@/components/ui/card"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Rows, Search, UploadIcon } from "lucide-react"; +import { InputGroup, InputGroupText } from "@/components/ui/input-group"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious, +} from "@/components/ui/carousel"; +import { Link } from "@/components/navigation"; +import { formatDateToIndonesian, generateLocalizedPath } from "@/utils/globals"; +import { Icon } from "@iconify/react/dist/iconify.js"; +import { locale } from "dayjs"; +import { useEffect, useState } from "react"; +import { getListContent } from "@/service/landing/landing"; +import ContestTable from "../../../../contest/components/contest-table"; +import AudioSliderPage from "../../audio/audio"; +import TeksSliderPage from "../../document/teks"; +import ImageSliderPage from "../../image/image"; +import VideoSliderPage from "../../video/audio-visual"; +import TeksAll from "./teks"; + +const DocumentAllPage = () => { + return ( +
+ +
+ + +

Konten Teks

+
+ +
+
+ +
+
+ + + + + + +
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+ +
+
+ + + + + + +
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+
+
+
+ + +
+ +
+
+
+
+
+
+ ); +}; + +export default DocumentAllPage; diff --git a/app/[locale]/(protected)/shared/curated-content/giat-routine/document/all/teks.tsx b/app/[locale]/(protected)/shared/curated-content/giat-routine/document/all/teks.tsx new file mode 100644 index 00000000..28ff6077 --- /dev/null +++ b/app/[locale]/(protected)/shared/curated-content/giat-routine/document/all/teks.tsx @@ -0,0 +1,116 @@ +"use client"; +import { Link } from "@/components/navigation"; +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious, +} from "@/components/ui/carousel"; +import { listCuratedContent } from "@/service/curated-content/curated-content"; +import { getListContent } from "@/service/landing/landing"; +import { + formatDateToIndonesian, + generateLocalizedPath, + textEllipsis, +} from "@/utils/globals"; +import { Icon } from "@iconify/react/dist/iconify.js"; +import { useParams, usePathname, useRouter } from "next/navigation"; +import React, { Component, useEffect, useState } from "react"; + +const TeksAll = () => { + const [documentData, setDocumentData] = useState(); + const [displayDocument, setDisplayDocument] = useState([]); + const [page, setPage] = useState(1); + const [limit, setLimit] = React.useState(10); + const [search, setSearch] = React.useState(""); + + useEffect(() => { + initFetch(); + }, [page, limit, search]); + + useEffect(() => { + if (documentData?.length > 0) { + shuffleAndSetVideos(); + const interval = setInterval(shuffleAndSetVideos, 5000); + return () => clearInterval(interval); // Cleanup interval on unmount + } + }, [documentData]); + + const initFetch = async () => { + const response = await listCuratedContent(search, limit, page - 1, 3, "1"); + console.log(response); + + const data = response?.data?.data; + const contentData = data?.content; + setDocumentData(contentData); + }; + const shuffleAndSetVideos = () => { + const shuffled = shuffleArray([...documentData]); + setDisplayDocument(shuffled.slice(0, 2)); + }; + + const shuffleArray = (array: any[]) => { + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [array[i], array[j]] = [array[j], array[i]]; + } + return array; + }; + + return ( +
+
+ {displayDocument?.map((document: any) => ( + +
+ + + +
+ +
+
+ {formatDateToIndonesian(new Date(document?.createdAt))}{" "} + {document?.timezone ? document?.timezone : "WIB"} |{" "} + 518 +
+
+ {document?.title} +
+
+ + + + Download Dokumen +
+
+ + ))} +
+
+ ); +}; + +export default TeksAll; diff --git a/app/[locale]/(protected)/shared/curated-content/giat-routine/document/detail/[id]/page.tsx b/app/[locale]/(protected)/shared/curated-content/giat-routine/document/detail/[id]/page.tsx index 187a3eab..abbd4b54 100644 --- a/app/[locale]/(protected)/shared/curated-content/giat-routine/document/detail/[id]/page.tsx +++ b/app/[locale]/(protected)/shared/curated-content/giat-routine/document/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"; @@ -35,6 +46,11 @@ import { FreeMode, Navigation, Pagination, Thumbs } from "swiper/modules"; import { Avatar, AvatarImage } from "@/components/ui/avatar"; import JoditEditor from "jodit-react"; import { Badge } from "@/components/ui/badge"; +import { Checkbox } from "@/components/ui/checkbox"; +import { htmlToString } from "@/utils/globals"; +import { loading } from "@/lib/swal"; +import { formatDate } from "@fullcalendar/core/index.js"; +import { getCookiesDecrypt } from "@/lib/utils"; const detailSchema = z.object({ title: z.string().min(1, { message: "Judul diperlukan" }), @@ -51,6 +67,13 @@ type Category = { categoryName: string; }; +type PublishedForObject = { + id: number; + name: string; + isInternal: boolean; + code: string; +}; + export type curationDetail = { id: number; title: string; @@ -71,6 +94,13 @@ export type curationDetail = { userGroupId: number; }; }; + publishedFor: string; // ID for selected radio button + publishedForObject: { + id: number; + name: string; + isInternal: boolean; + code: string; + }[]; tags: string; provinceId: string; is_active: string; @@ -109,7 +139,8 @@ export default function DetailDocument() { 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"); @@ -125,6 +156,7 @@ export default function DetailDocument() { const [refresh] = useState(false); const [detailThumb, setDetailThumb] = useState([]); const [thumbsSwiper, setThumbsSwiper] = useState(null); + const [selectedValue, setSelectedValue] = useState(""); const { control, @@ -138,11 +170,18 @@ export default function DetailDocument() { const [commentsData, setCommentsData] = useState(initialComments); const [replyText, setReplyText] = useState(""); const [replyingTo, setReplyingTo] = useState(null); + const [selectedFileId, setSelectedFileId] = useState(null); + const [listData, setListData] = useState([]); + const [message, setMessage] = useState(""); const handleReply = (commentId: number) => { setReplyingTo(commentId); }; + const handleInputChange = (e: React.ChangeEvent) => { + setMessage(e.target.value); + }; + const addReply = (commentId: number) => { if (replyText.trim()) { const newCommentData = commentsData.map((comment: any) => { @@ -168,6 +207,85 @@ export default function DetailDocument() { } }; + 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 = 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) { @@ -175,16 +293,37 @@ export default function DetailDocument() { const details = response?.data?.data; setDetail(details); + setSelectedValue(details?.publishedFor || ""); + 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, + url: file.secondaryUrl || "default-image.jpg", + format: file.format, + fileName: file.fileName, + placements: file.placements || "", + })); 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); + } + }; + + const handleValueChange = (value: string) => { + setSelectedValue(value); + }; + return (
{detail !== undefined ? ( @@ -192,8 +331,8 @@ export default function DetailDocument() {

Kurasi Detail

-
-
+
+
@@ -244,7 +383,7 @@ export default function DetailDocument() { name="description" render={({ field }) => ( +
+ - -
- )}
diff --git a/app/[locale]/(protected)/shared/curated-content/giat-routine/document/teks.tsx b/app/[locale]/(protected)/shared/curated-content/giat-routine/document/teks.tsx index 20e89711..a36d9269 100644 --- a/app/[locale]/(protected)/shared/curated-content/giat-routine/document/teks.tsx +++ b/app/[locale]/(protected)/shared/curated-content/giat-routine/document/teks.tsx @@ -7,6 +7,7 @@ import { CarouselNext, CarouselPrevious, } from "@/components/ui/carousel"; +import { listCuratedContent } from "@/service/curated-content/curated-content"; import { getListContent } from "@/service/landing/landing"; import { formatDateToIndonesian, @@ -21,94 +22,102 @@ const TeksSliderPage = () => { const [documentData, setDocumentData] = useState(); const [displayDocument, setDisplayDocument] = useState([]); const [page, setPage] = useState(1); + const [limit, setLimit] = React.useState(10); + const [search, setSearch] = React.useState(""); + const [hasData, setHasData] = useState(false); useEffect(() => { initFetch(); - }, []); + }, [page, limit, search]); - useEffect(() => { - if (documentData?.length > 0) { - shuffleAndSetVideos(); - const interval = setInterval(shuffleAndSetVideos, 5000); - return () => clearInterval(interval); // Cleanup interval on unmount - } - }, [documentData]); + // useEffect(() => { + // if (documentData?.length > 0) { + // shuffleAndSetVideos(); + // const interval = setInterval(shuffleAndSetVideos, 5000); + // return () => clearInterval(interval); // Cleanup interval on unmount + // } + // }, [documentData]); const initFetch = async () => { - const response = await getListContent({ - page: page - 1, - size: 12, - sortBy: "createdAt", - contentTypeId: "3", - }); + const response = await listCuratedContent(search, limit, page - 1, 3, "1"); console.log(response); - setDocumentData(response?.data?.data?.content); - }; - const shuffleAndSetVideos = () => { - const shuffled = shuffleArray([...documentData]); - setDisplayDocument(shuffled.slice(0, 2)); - }; - const shuffleArray = (array: any[]) => { - for (let i = array.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [array[i], array[j]] = [array[j], array[i]]; - } - return array; + const data = response?.data?.data; + const contentData = data?.content; + setHasData(displayDocument && displayDocument.length > 0); + setDisplayDocument(contentData); }; + // const shuffleAndSetVideos = () => { + // const shuffled = shuffleArray([...documentData]); + // setDisplayDocument(shuffled.slice(0, 2)); + // }; + + // const shuffleArray = (array: any[]) => { + // for (let i = array.length - 1; i > 0; i--) { + // const j = Math.floor(Math.random() * (i + 1)); + // [array[i], array[j]] = [array[j], array[i]]; + // } + // return array; + // }; return ( -
-
- {displayDocument?.map((document: any) => ( - -
- + + +
+ {displayDocument?.map((document: any) => ( + - - -
+
+ + + +
-
-
- {formatDateToIndonesian(new Date(document?.createdAt))}{" "} - {document?.timezone ? document?.timezone : "WIB"} |{" "} - 518 -
-
- {document?.title} -
-
- - - - Download Dokumen -
-
- - ))} -
-
+
+
+ {formatDateToIndonesian(new Date(document?.createdAt))}{" "} + {document?.timezone ? document?.timezone : "WIB"} |{" "} + 518 +
+
+ {document?.title} +
+
+ + + + Download Dokumen +
+
+ + ))} +
+ + + + + ); }; diff --git a/app/[locale]/(protected)/shared/curated-content/giat-routine/image/all/page.tsx b/app/[locale]/(protected)/shared/curated-content/giat-routine/image/all/page.tsx new file mode 100644 index 00000000..a65852bb --- /dev/null +++ b/app/[locale]/(protected)/shared/curated-content/giat-routine/image/all/page.tsx @@ -0,0 +1,72 @@ +import SiteBreadcrumb from "@/components/site-breadcrumb"; +import { Card, CardContent } from "@/components/ui/card"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Rows, Search, UploadIcon } from "lucide-react"; +import { InputGroup, InputGroupText } from "@/components/ui/input-group"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Carousel, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious, +} from "@/components/ui/carousel"; +import { Link } from "@/components/navigation"; +import { formatDateToIndonesian, generateLocalizedPath } from "@/utils/globals"; +import { Icon } from "@iconify/react/dist/iconify.js"; +import { locale } from "dayjs"; +import { useEffect, useState } from "react"; +import { getListContent } from "@/service/landing/landing"; +import ContestTable from "../../../../contest/components/contest-table"; +import AudioSliderPage from "../../audio/audio"; +import TeksSliderPage from "../../document/teks"; +import ImageSliderPage from "../../image/image"; +import VideoSliderPage from "../../video/audio-visual"; + +const ImageAllPage = () => { + return ( +
+ +
+ + +

Konten Image

+
+ +
+
+ +
+
+ + + + + + +
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+ ); +}; + +export default ImageAllPage; 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 90858da6..133d85c1 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 @@ -53,6 +53,8 @@ import { } from "@/service/task"; import { getCookiesDecrypt } from "@/lib/utils"; import { close, loading } from "@/lib/swal"; +import { Checkbox } from "@/components/ui/checkbox"; +import { htmlToString } from "@/utils/globals"; const detailSchema = z.object({ title: z.string().min(1, { message: "Judul diperlukan" }), @@ -107,6 +109,13 @@ export type curationDetail = { userGroupId: number; }; }; + publishedFor: string; // ID for selected radio button + publishedForObject: { + id: number; + name: string; + isInternal: boolean; + code: string; + }[]; tags: string; provinceId: string; is_active: string; @@ -173,6 +182,7 @@ export default function DetailImage() { const [commentsData, setCommentsData] = useState(initialComments); const [replyText, setReplyText] = useState(""); const [replyingTo, setReplyingTo] = useState(null); + const [selectedValue, setSelectedValue] = useState(""); const handleReply = (commentId: number) => { setReplyingTo(commentId); @@ -272,11 +282,13 @@ export default function DetailImage() { const details = response?.data?.data; setDetail(details); + setSelectedValue(details?.publishedFor || ""); setSelectedFileId(details?.files[0]?.id); const filesData = details.files || []; const fileUrls = filesData.map((file: any) => ({ id: file.id, thumbnailFileUrl: file.thumbnailFileUrl || "default-image.jpg", + placements: file.placements || "", })); setDetailThumb(fileUrls); } @@ -294,6 +306,9 @@ export default function DetailImage() { console.error("Error fetching comments:", error); } }; + const handleValueChange = (value: string) => { + setSelectedValue(value); + }; return (
@@ -302,8 +317,8 @@ export default function DetailImage() {

Kurasi Detail

-
-
+
+
@@ -354,7 +369,7 @@ export default function DetailImage() { name="description" render={({ field }) => ( -
-
- - - -
-
-
- {commentsData.map((comment) => ( -
- - - -
- - {comment.username} - - - {comment.date} - -

{comment.text}

-
handleReply(comment.id)} - > - - Balas +
+ - -
- )}
diff --git a/app/[locale]/(protected)/shared/curated-content/page.tsx b/app/[locale]/(protected)/shared/curated-content/page.tsx index 8f6e1514..6fe39f04 100644 --- a/app/[locale]/(protected)/shared/curated-content/page.tsx +++ b/app/[locale]/(protected)/shared/curated-content/page.tsx @@ -1,7 +1,7 @@ import SiteBreadcrumb from "@/components/site-breadcrumb"; import { Card, CardContent } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { Search, UploadIcon } from "lucide-react"; +import { ArrowRight, Rows, Search, UploadIcon } from "lucide-react"; import { InputGroup, InputGroupText } from "@/components/ui/input-group"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; @@ -73,19 +73,80 @@ const CuratedContentPage = () => {
- +
+ + +
+ + +
+
- +
+ +
+ + +
+
- +
+ +
+ + +
+
- +
+ +
+ + +
+
diff --git a/app/[locale]/(public)/audio/detail/[slug]/page.tsx b/app/[locale]/(public)/audio/detail/[slug]/page.tsx index 78e4b112..826cc7dc 100644 --- a/app/[locale]/(public)/audio/detail/[slug]/page.tsx +++ b/app/[locale]/(public)/audio/detail/[slug]/page.tsx @@ -1,24 +1,42 @@ "use client"; import { useParams, usePathname, useSearchParams } from "next/navigation"; -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { Icon } from "@iconify/react/dist/iconify.js"; import NewContent from "@/components/landing-page/new-content"; import { Link, useRouter } from "@/i18n/routing"; import { Textarea } from "@/components/ui/textarea"; import { BarWave } from "react-cssfx-loading"; import { useToast } from "@/components/ui/use-toast"; -import { checkWishlistStatus, deleteWishlist, getDetail, saveWishlist } from "@/service/landing/landing"; +import { checkWishlistStatus, createPublicSuggestion, deletePublicSuggestion, deleteWishlist, getDetail, getPublicSuggestionList, saveWishlist } from "@/service/landing/landing"; import { getCookiesDecrypt } from "@/lib/utils"; -import { close, error, loading, successCallback } from "@/config/swal"; +import { close, error, loading, successCallback, warning } from "@/config/swal"; import { sendMediaUploadToEmail } from "@/service/media-tracking/media-tracking"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; +import withReactContent from "sweetalert2-react-content"; +import Swal from "sweetalert2"; +import { checkMaliciousText, getPublicLocaleTimestamp } from "@/utils/globals"; +import parse from "html-react-parser"; +import $ from "jquery"; +import { useTranslations } from "next-intl"; + +const formWaveSurferOptions = (ref: any) => ({ + container: ref, + waveColor: "#eee", + progressColor: "OrangeRed", + cursorColor: "OrangeRed", + barWidth: 3, + barRadius: 3, + responsive: true, + height: 150, // If true, normalize by the maximum peak instead of 1.0. + normalize: true, // Use the PeakCache to improve rendering speed of large waveforms. + partialRender: true, +}); const DetailAudio = () => { const [selectedSize, setSelectedSize] = useState("L"); - const [selectedTab, setSelectedTab] = useState("video"); const router = useRouter(); const pathname = usePathname(); const params = useParams(); @@ -31,8 +49,6 @@ const DetailAudio = () => { const [downloadProgress, setDownloadProgress] = useState(0); const [isFromSPIT, setIsFromSPIT] = useState(false); const [main, setMain] = useState(); - const [resolutionSelected, setResolutionSelected] = useState("720"); - const [imageSizeSelected, setImageSizeSelected] = useState("l"); const userId = getCookiesDecrypt("uie"); const [emailShareList, setEmailShareList] = useState(); const [emailShareInput, setEmailShareInput] = useState(); @@ -42,8 +58,18 @@ const DetailAudio = () => { const [width, setWidth] = useState(); const [content, setContent] = useState([]); const userRoleId = getCookiesDecrypt("urie"); + const [playing, setPlaying] = useState(false); + const wavesurfer = useRef(null); + const waveformRef = useRef(null); + const [audioLoaded, setAudioLoaded] = useState(false); + const [volume, setVolume] = useState(0.5); + const [message, setMessage] = useState(""); + const [listSuggestion, setListSuggestion] = useState(); + const MySwal = withReactContent(Swal); + const [visibleInput, setVisibleInput] = useState(null); + const t = useTranslations("LandingPage"); - let typeString = "video"; + let typeString = "audio"; useEffect(() => { initFetch(); @@ -53,9 +79,12 @@ const DetailAudio = () => { const initFetch = async () => { const response = await getDetail(String(slug)); console.log("detailAudio", response); + const responseGet = await getPublicSuggestionList(slug?.split("-")?.[0]); + setIsFromSPIT(response?.data?.data?.isFromSPIT); setWidth(window.innerWidth); - setContent(response?.data.data); + setContent(response?.data?.data); + setListSuggestion(responseGet.data?.data); setMain({ id: response?.data?.data?.files[0]?.id, type: response?.data?.data?.fileType.name, @@ -224,11 +253,8 @@ const DetailAudio = () => { }; const sizes = [ - { label: "XL", value: "3198 x 1798 px" }, - { label: "L", value: "2399 x 1349 px" }, - { label: "M", value: "1599 x 899 px" }, - { label: "S", value: "1066 x 599 px" }, - { label: "XS", value: "800 x 450 px" }, + { label: "MP3", value: "... MB" }, + { label: "WAV", value: "... MB" }, ]; const handleShare = (type: any, url: any) => { @@ -268,6 +294,11 @@ const DetailAudio = () => { } } + const handlePlayPause = () => { + setPlaying(!playing); + wavesurfer?.current?.playPause(); + }; + const handleEmailList = (e: any) => { const arrayEmail: any = []; for (let i = 0; i < emailShareList?.length; i += 1) { @@ -284,6 +315,240 @@ const DetailAudio = () => { return false; }; + // useEffect(() => { + // function initState() { + // if (typeString == "audio" && main?.url != undefined) { + // const init = async () => { + // const { default: WaveSurfer } = await import("wavesurfer.js"); + + // setPlaying(false); + + // const formatTime = function (time: any) { + // return [ + // Math.floor((time % 3600) / 60), + // // minutes + // `00${Math.floor(time % 60)}`.slice(-2), // seconds + // ].join(":"); + // }; + // console.log("AUDIO", main?.url); + + // const options = formWaveSurferOptions(waveformRef.current); + + // wavesurfer.current = WaveSurfer.create(options); + + // wavesurfer.current.load(main?.url); + + // wavesurfer.current.on("ready", () => { + // // https://wavesurfer-js.org/docs/methods.html + // // wavesurfer.current.playPause(); + // // setPlaying(true); + // setAudioLoaded(true); + // // make sure object stillavailable when file loaded + // if (wavesurfer.current) { + // wavesurfer.current.setVolume(volume); + // let volumeNow = volume; + // setVolume(volumeNow); + // } + + // $(".waveform__duration").text(formatTime(wavesurfer.current.getDuration())); + // }); + + // // Show current time + // wavesurfer.current.on("audioprocess", () => { + // $(".waveform__counter").text(formatTime(wavesurfer.current.getCurrentTime())); + // }); + + // wavesurfer.current.on("finish", () => { + // setPlaying(false); + // }); + // }; + + // init(); + // // Removes events, elements and disconnects Web Audio nodes. + // // when component unmount + + // return () => wavesurfer?.current?.destroy(); + // } + // } + + // initState(); + // }, [main?.url]); + + useEffect(() => { + if (typeString === "audio" && main?.url) { + const init = async () => { + const { default: WaveSurfer } = await import("wavesurfer.js"); + + if (wavesurfer.current) { + wavesurfer.current.destroy(); // 🔥 Hapus instance lama sebelum membuat yang baru + } + + setPlaying(false); + + const formatTime = function (time: any) { + return [ + Math.floor((time % 3600) / 60), + // minutes + `00${Math.floor(time % 60)}`.slice(-2), // seconds + ].join(":"); + }; + + const options = formWaveSurferOptions(waveformRef.current); + wavesurfer.current = WaveSurfer.create(options); + wavesurfer.current.load(main?.url); + + wavesurfer.current.on("ready", () => { + setAudioLoaded(true); + wavesurfer.current.setVolume(volume); + $(".waveform__duration").text(formatTime(wavesurfer.current.getDuration())); + }); + + wavesurfer.current.on("audioprocess", () => { + $(".waveform__counter").text(formatTime(wavesurfer.current.getCurrentTime())); + }); + + wavesurfer.current.on("finish", () => { + setPlaying(false); + }); + }; + + init(); + + return () => { + if (wavesurfer.current) { + wavesurfer.current.destroy(); // 🔥 Hapus saat unmount + } + }; + } + }, [main?.url]); + + const onVolumeChange = (e: any) => { + const { target } = e; + const newVolume = +target?.value; + + if (newVolume) { + setVolume(newVolume); + wavesurfer?.current?.setVolume(newVolume || 1); + } + }; + + async function sendSuggestionChild(parentId: any) { + const inputElement = document.querySelector(`#input-comment-${parentId}`) as HTMLInputElement; + + if (inputElement && inputElement.value.length > 3) { + loading(); + const data = { + mediaUploadId: slug?.split("-")?.[0], + message: inputElement.value, + parentId, + }; + + console.log(data); + const response = await createPublicSuggestion(data); + console.log(response); + const responseGet: any = await getPublicSuggestionList(slug?.split("-")?.[0]); + console.log(responseGet.data?.data); + setListSuggestion(responseGet.data?.data); + + // Reset input field + inputElement.value = ""; + + // document.querySelector("#comment-id-" + parentId)?.style.display = "none"; + + close(); + } + } + async function deleteDataSuggestion(dataId: any) { + loading(); + const response = await deletePublicSuggestion(dataId); + console.log(response); + const responseGet = await getPublicSuggestionList(slug.split("-")?.[0]); + console.log(responseGet.data?.data); + setListSuggestion(responseGet.data?.data); + close(); + } + const deleteData = (dataId: any) => { + MySwal.fire({ + title: "Delete Comment", + icon: "warning", + showCancelButton: true, + cancelButtonColor: "#3085d6", + confirmButtonColor: "#d33", + confirmButtonText: "Delete", + }).then((result: any) => { + if (result.isConfirmed) { + deleteDataSuggestion(dataId); + console.log(dataId); + } + }); + }; + const showInput = (e: any) => { + console.log(document.querySelector(`#${e}`)?.classList); + document.querySelector(`#${e}`)?.classList.toggle("none"); + setVisibleInput(visibleInput === e ? null : e); + }; + + function addDefaultProfile(ev: any) { + ev.target.src = "/assets/avatar-profile.png"; + } + + async function sendSuggestionParent() { + if (message?.length > 3) { + loading(); + const data = { + mediaUploadId: slug?.split("-")?.[0], + message, + parentId: null, + }; + + const response = await createPublicSuggestion(data); + + console.log(response); + setMessage(""); + + const responseGet = await getPublicSuggestionList(slug?.split("-")?.[0]); + console.log(responseGet?.data?.data); + setListSuggestion(responseGet?.data?.data); + + // Hapus nilai semua input secara manual jika perlu + const inputs = document.querySelectorAll("input"); + inputs.forEach((input) => { + input.value = ""; + }); + + close(); + } + } + const getInputValue = (e: any) => { + const message = e.target.value; + console.log(message); + setMessage(message); + }; + const postData = () => { + const checkMessage = checkMaliciousText(message); + if (checkMessage == "") { + if (Number(userRoleId) < 1 || userRoleId == undefined) { + router.push("/auth"); + } else { + sendSuggestionParent(); + } + } else { + warning(checkMessage); + } + }; + const postDataChild = (id: any) => { + const checkMessage = checkMaliciousText(message); + if (checkMessage == "") { + if (Number(userRoleId) < 1 || userRoleId == undefined) { + router.push("/auth"); + } else { + sendSuggestionChild(id); + } + } else { + warning(checkMessage); + } + }; + return ( <>
@@ -291,25 +556,62 @@ const DetailAudio = () => {
{/* Bagian Kiri */}
-
- - -
+
+ +
+ +
+
+
+ + {/* */} + +
+ {/* Footer Informasi */} -
-

- oleh {detailDataAudio?.uploadedBy?.userLevel?.name} | Diupdate pada {detailDataAudio?.updatedAt} WIB |  - -   - {detailDataAudio?.clickCount} -

-

Kreator: {detailDataAudio?.creatorName}

+
+
+

+ {t("by")} {detailDataAudio?.uploadedBy?.userLevel?.name} +

+

+  | {t("updatedOn")} {detailDataAudio?.updatedAt} WIB  |  +

+

+ +   {detailDataAudio?.clickCount}   +

+
+
+

+ {t("creator")} {detailDataAudio?.creatorName} +

+
{/* Keterangan */} -
-

{detailDataAudio?.title}

-
+
+

{detailDataAudio?.title}

+
@@ -318,12 +620,12 @@ const DetailAudio = () => { {isSaved ? ( handleDeleteWishlist()} className="flex flex-col mb-3 items-center justify-center cursor-pointer"> -

Hapus

+

{t("delete")}

) : ( doBookmark()} className="flex flex-col mb-3 items-center justify-center cursor-pointer"> -

Simpan

+

{t("save")}

)} @@ -345,7 +647,7 @@ const DetailAudio = () => {
{/* Opsi Ukuran Foto */} -

Opsi Ukuran Audio

+

{t("audioSize")}

@@ -367,7 +669,7 @@ const DetailAudio = () => {
@@ -376,12 +678,12 @@ const DetailAudio = () => { - Download + {t("download")} {/* Tombol Bagikan */}
-

Bagikan

+

{t("share")}

handleShare("fb", `https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fmediahub.polri.go.id%2F${typeString}%2Fdetail%2F${content?.id}"e=${content?.title}`)}> @@ -399,13 +701,13 @@ const DetailAudio = () => {
-

Share Ke Email

+

{t("shareTo")}

-

Email Tujuan :

- setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder="Tekan Enter untuk input Email" /> +

{t("destinationEmail")}

+ setEmailShareInput(event.target.value)} onKeyPress={handleEmailList} type="email" placeholder={t("pressEnter")} />
@@ -417,10 +719,183 @@ const DetailAudio = () => {
{/* Comment */} -
-

Berikan Komentar

-