diff --git a/app/[locale]/(protected)/admin/settings/iklan/component/table.tsx b/app/[locale]/(protected)/admin/settings/iklan/component/table.tsx index 4fc2216c..48ee4e8c 100644 --- a/app/[locale]/(protected)/admin/settings/iklan/component/table.tsx +++ b/app/[locale]/(protected)/admin/settings/iklan/component/table.tsx @@ -36,6 +36,7 @@ import { Trash2, TrendingDown, TrendingUp, + UploadIcon, UserIcon, } from "lucide-react"; import { cn } from "@/lib/utils"; @@ -208,7 +209,15 @@ const AdvertisementsList = () => { return ( <>
- +
+ + + +
+ {/* */}
{ + return ( +
+ +
+ +
+
+ ); +}; + +export default AdvertisementsCreatePage; diff --git a/app/[locale]/(protected)/contributor/agenda-setting/calender-view.tsx b/app/[locale]/(protected)/contributor/agenda-setting/calender-view.tsx index 7a63a970..1861e18b 100644 --- a/app/[locale]/(protected)/contributor/agenda-setting/calender-view.tsx +++ b/app/[locale]/(protected)/contributor/agenda-setting/calender-view.tsx @@ -110,7 +110,7 @@ interface ListItemProps { interface APIResponse { error: boolean; message: any; - data: AgendaSettingsAPIResponse[] | null; // `data` bisa berupa array atau null + data: AgendaSettingsAPIResponse[] | null; } const CalendarView = ({ categories }: CalendarViewProps) => { @@ -218,15 +218,21 @@ const CalendarView = ({ categories }: CalendarViewProps) => { setSelectedCategory(categories?.map((c) => c.value)); }, [categories]); - const filteredEvents = calendarEvents?.filter((event) => - selectedCategory?.includes(event.extendedProps.calendar) - ); + const filteredEvents = calendarEvents?.filter((event) => { + const eventCategories = event.extendedProps.calendar + ?.split(",") + .map((val: string) => val.trim()); // agar "1, 2" tetap dianggap benar + + return eventCategories?.some((cat: string) => + selectedCategory?.includes(cat) + ); + }); const displayedEvents = filteredEvents?.length > 1 ? filteredEvents : apiEvents; useEffect(() => { - setSelectedCategory(categories?.map((c) => c.value)); + setSelectedCategory(categories?.map((c: any) => c.value)); }, [categories]); useEffect(() => { @@ -369,15 +375,15 @@ const CalendarView = ({ categories }: CalendarViewProps) => { }; const handleClassName = (arg: EventContentArg) => { - if (arg.event.extendedProps.calendar === "mabes") { + if (arg.event.extendedProps.calendar === "1") { return "bg-yellow-500 border-none"; - } else if (arg.event.extendedProps.calendar === "polda") { + } else if (arg.event.extendedProps.calendar === "2") { return "bg-blue-400 border-none"; - } else if (arg.event.extendedProps.calendar === "polres") { + } else if (arg.event.extendedProps.calendar === "3") { return "bg-slate-400 border-none"; - } else if (arg.event.extendedProps.calendar === "satker") { + } else if (arg.event.extendedProps.calendar === "4") { return "bg-orange-500 border-none"; - } else if (arg.event.extendedProps.calendar === "international") { + } else if (arg.event.extendedProps.calendar === "5") { return "bg-green-400 border-none"; } else { return "primary"; @@ -415,11 +421,11 @@ const CalendarView = ({ categories }: CalendarViewProps) => { 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", + 1: "bg-yellow-500", + 2: "bg-blue-400", + 3: "bg-slate-400", + 4: "bg-orange-500", + 5: "bg-green-400", }; return colors[type]; }; diff --git a/app/[locale]/(protected)/contributor/agenda-setting/data.ts b/app/[locale]/(protected)/contributor/agenda-setting/data.ts index 3cab4af9..837b2136 100644 --- a/app/[locale]/(protected)/contributor/agenda-setting/data.ts +++ b/app/[locale]/(protected)/contributor/agenda-setting/data.ts @@ -22,7 +22,7 @@ export const calendarEvents = [ allDay: false, //className: "warning", extendedProps: { - calendar: "polda", + calendar: "2", }, }, { @@ -33,7 +33,7 @@ export const calendarEvents = [ allDay: true, //className: "success", extendedProps: { - calendar: "national", + calendar: "1", }, }, { @@ -44,7 +44,7 @@ export const calendarEvents = [ end: new Date(date.getFullYear(), date.getMonth() + 1, -7), // className: "info", extendedProps: { - calendar: "polres", + calendar: "3", }, }, { @@ -55,7 +55,7 @@ export const calendarEvents = [ allDay: true, //className: "primary", extendedProps: { - calendar: "polres", + calendar: "3", }, }, { @@ -66,7 +66,7 @@ export const calendarEvents = [ allDay: true, // className: "danger", extendedProps: { - calendar: "polda", + calendar: "2", }, }, { @@ -77,7 +77,7 @@ export const calendarEvents = [ allDay: true, //className: "primary", extendedProps: { - calendar: "international", + calendar: "5", }, }, ]; @@ -85,31 +85,31 @@ export const calendarEvents = [ export const calendarCategories = [ { label: "Nasional", - value: "mabes", + value: "1", activeClass: "ring-primary-500 bg-primary-500", className: "group-hover:border-blue-500", }, { label: "Polda", - value: "polda", + value: "2", activeClass: "ring-success-500 bg-success-500", className: " group-hover:border-green-500", }, { label: "Polres", - value: "polres", + value: "3", activeClass: "ring-danger-500 bg-danger-500", className: " group-hover:border-red-500", }, { label: "Satker", - value: "satker", + value: "4", activeClass: "ring-yellow-500 bg-yellow-500", className: " group-hover:border-red-500", }, { label: "Internasional", - value: "international", + value: "5", activeClass: "ring-info-500 bg-info-500", className: " group-hover:border-cyan-500", }, @@ -118,31 +118,31 @@ export const calendarCategories = [ export const categories = [ { label: "Nasional", - value: "mabes", + value: "1", className: "data-[state=checked]:bg-yellow-500 data-[state=checked]:ring-yellow-500", }, { label: "Polda", - value: "polda", + value: "2", className: "data-[state=checked]:bg-blue-400 data-[state=checked]:ring-blue-400", }, { label: "Polres", - value: "polres", + value: "3", className: "data-[state=checked]:bg-slate-400 data-[state=checked]:ring-slate-400 ", }, { label: "Satker", - value: "satker", + value: "4", className: "data-[state=checked]:bg-warning data-[state=checked]:ring-warning ", }, { label: "Internasional", - value: "international", + value: "5", className: "data-[state=checked]:bg-green-500 data-[state=checked]:ring-green-500 ", }, diff --git a/app/[locale]/(protected)/contributor/agenda-setting/page.tsx b/app/[locale]/(protected)/contributor/agenda-setting/page.tsx index 4f88d946..010f3ead 100644 --- a/app/[locale]/(protected)/contributor/agenda-setting/page.tsx +++ b/app/[locale]/(protected)/contributor/agenda-setting/page.tsx @@ -1,4 +1,4 @@ -"use client" +"use client"; import { getEvents, getCategories } from "./utils"; import { calendarEvents, Category } from "./data"; @@ -8,7 +8,6 @@ import { useEffect, useState } from "react"; import { CalendarCategory } from "./data"; const CalenderPage = () => { - const [categories, setCategories] = useState([]); const userLevelNumber = Number(getCookiesDecrypt("ulne")) || 0; const userLevelId = Number(getCookiesDecrypt("ulie")) || 0; @@ -20,32 +19,32 @@ const CalenderPage = () => { async function initData() { const events = await getEvents(); const categories = await getCategories(); - let valueShowed : string[] = []; + let valueShowed: string[] = []; if (userLevelNumber == 1) { - valueShowed = ['mabes', 'polda', 'polres', 'satker', 'international']; + valueShowed = ["1", "2", "3", "4", "5"]; } else if (userLevelNumber == 2 && userLevelId != 761) { - valueShowed = ['polda', 'polres']; - } else if ((userLevelNumber == 2 && userLevelId == 761) || (userLevelNumber == 3 && userParentLevelId == 761)) { - valueShowed = ['satker']; + valueShowed = ["2", "3"]; + } else if ( + (userLevelNumber == 2 && userLevelId == 761) || + (userLevelNumber == 3 && userParentLevelId == 761) + ) { + valueShowed = ["4"]; } else if (userLevelNumber == 3 && userParentLevelId != 761) { - valueShowed = ['polres']; + valueShowed = ["3"]; } - - const formattedCategories = categories.filter((category: Category) => valueShowed.includes(category.value)) - .map((category: Category) => ({ - ...category, - activeClass: "", - })); + + const formattedCategories = categories + .filter((category: Category) => valueShowed.includes(category.value)) + .map((category: Category) => ({ + ...category, + activeClass: "", + })); console.log(formattedCategories); setCategories(formattedCategories); } }, []); - return ( -
- {categories && } -
- ); + return
{categories && }
; }; export default CalenderPage; diff --git a/components/form/content/image-form.tsx b/components/form/content/image-form.tsx index 743dfc29..669abf69 100644 --- a/components/form/content/image-form.tsx +++ b/components/form/content/image-form.tsx @@ -633,7 +633,7 @@ export default function FormImage() { setIsStartUpload(false); // hideProgress(); Cookies.remove("idCreate"); - successSubmit("in/contributor/content/image/"); + successSubmit("/in/contributor/content/image"); } } diff --git a/components/form/schedule/form-calendar-polri-detail.tsx b/components/form/schedule/form-calendar-polri-detail.tsx index dc51013c..0cdc7b7f 100644 --- a/components/form/schedule/form-calendar-polri-detail.tsx +++ b/components/form/schedule/form-calendar-polri-detail.tsx @@ -103,6 +103,41 @@ export function CalendarPolriAddDetail() { to: parseISO(details.endDate), }); } + + if (details?.assignedTo) { + const assignedToArray = details.assignedTo.split(","); + const newUnitSelection = { ...unitSelection }; + assignedToArray.forEach((val: any) => { + switch (val) { + case "0": + newUnitSelection.semua = true; + break; + case "1": + newUnitSelection.mabes = true; + break; + case "2": + newUnitSelection.polda = true; + break; + case "3": + newUnitSelection.satker = true; + break; + case "4": + newUnitSelection.internasional = true; + break; + default: + break; + } + }); + setUnitSelection(newUnitSelection); + } + + // 2. Set checkbox wilayah + if (details?.assignedToLevel) { + const levelIds = details.assignedToLevel + .split(",") + .map((id: string) => parseInt(id)); + setCheckedLevels(new Set(levelIds)); + } } } initState(); diff --git a/components/form/schedule/form-calendar-polri-update.tsx b/components/form/schedule/form-calendar-polri-update.tsx index 4ab0f21a..b4aaacb8 100644 --- a/components/form/schedule/form-calendar-polri-update.tsx +++ b/components/form/schedule/form-calendar-polri-update.tsx @@ -13,7 +13,13 @@ import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; -import { CalendarIcon, ChevronDown, ChevronUp, Plus } from "lucide-react"; +import { + CalendarIcon, + ChevronDown, + ChevronUp, + CloudUpload, + Plus, +} from "lucide-react"; import { useTranslations } from "next-intl"; import DatePicker from "react-datepicker"; import { id } from "date-fns/locale"; @@ -22,8 +28,8 @@ import { zodResolver } from "@hookform/resolvers/zod"; import router from "next/router"; import { Controller, useForm } from "react-hook-form"; import { date, z } from "zod"; -import { error } from "@/lib/swal"; -import { postCalendar } from "@/service/schedule/schedule"; +import { error, loading } from "@/lib/swal"; +import { detailCalendar, postCalendar } from "@/service/schedule/schedule"; import { DateRange } from "react-day-picker"; import Cookies from "js-cookie"; import withReactContent from "sweetalert2-react-content"; @@ -35,18 +41,41 @@ import { PopoverTrigger, } from "@/components/ui/popover"; import { Calendar } from "@/components/ui/calendar"; -import { format } from "date-fns"; +import { format, parseISO } from "date-fns"; import { cn } from "@/lib/utils"; import { getUserLevelForAssignments } from "@/service/task"; import { Card } from "@/components/ui/card"; +import { useDropzone } from "react-dropzone"; +import { Icon } from "@/components/ui/icon"; +import Image from "next/image"; +import FileUploader from "../shared/file-uploader"; +import { getCsrfToken } from "@/service/auth"; +import { Upload } from "tus-js-client"; +import { useParams } from "next/navigation"; const calendarSchema = z.object({ title: z.string().min(1, { message: "Judul diperlukan" }), description: z.string().min(1, { message: "Judul diperlukan" }), }); +interface FileWithPreview extends File { + preview: string; +} + +interface FileUploaded { + id: number; + url: string; +} + +interface Detail { + id: number; + title: string; + description: string; +} + export function CalendarPolriAddUpdate() { const MySwal = withReactContent(Swal); + const { id } = useParams() as { id: string }; const [open, setOpen] = React.useState(false); const t = useTranslations("Schedule"); type CalendarSchema = z.infer; @@ -55,9 +84,17 @@ export function CalendarPolriAddUpdate() { const [checkedLevels, setCheckedLevels] = React.useState(new Set()); const [expandedPolda, setExpandedPolda] = React.useState([{}]); const [isLoading, setIsLoading] = React.useState(false); + const [isImageUploadFinish, setIsImageUploadFinish] = React.useState(false); + const [files, setFiles] = React.useState([]); + const [imageUploadedFiles, setImageUploadedFiles] = React.useState< + FileUploaded[] + >([]); + const [detail, setDetail] = React.useState(); + const [imageFiles, setImageFiles] = React.useState([]); const [date, setDate] = React.useState({ from: new Date(2025, 0, 1), }); + const [refresh, setRefresh] = React.useState(false); const [unitSelection, setUnitSelection] = React.useState({ semua: false, @@ -79,6 +116,62 @@ export function CalendarPolriAddUpdate() { }, }); + React.useEffect(() => { + async function initState() { + if (id) { + const response = await detailCalendar(id); + const details = response?.data?.data; + + setDetail(details); + + if (details) { + setDate({ + from: parseISO(details.startDate), + to: parseISO(details.endDate), + }); + + // 1. Set unit selection + if (details?.assignedTo) { + const assignedToArray = details.assignedTo.split(","); + const newUnitSelection = { ...unitSelection }; + assignedToArray.forEach((val: any) => { + switch (val) { + case "0": + newUnitSelection.semua = true; + break; + case "1": + newUnitSelection.mabes = true; + break; + case "2": + newUnitSelection.polda = true; + break; + case "3": + newUnitSelection.satker = true; + break; + case "4": + newUnitSelection.internasional = true; + break; + default: + break; + } + }); + setUnitSelection(newUnitSelection); + } + + // 2. Set checkbox wilayah + if (details?.assignedToLevel) { + const levelIds = details.assignedToLevel + .split(",") + .map((id: string) => parseInt(id)); + setCheckedLevels(new Set(levelIds)); + } + } + } + } + + initState(); + }, [refresh, setValue]); + React.useEffect(() => { async function fetchPoldaPolres() { setIsLoading(true); @@ -169,6 +262,7 @@ export function CalendarPolriAddUpdate() { .map((key) => unitMapping[key as keyof typeof unitMapping]) .join(","); const requestData = { + id: detail?.id, title: data.title, startDate: date?.from ? format(date.from, "yyyy-MM-dd") : null, endDate: date?.to ? format(date.to, "yyyy-MM-dd") : null, @@ -189,6 +283,89 @@ export function CalendarPolriAddUpdate() { expires: 1, }); + loading(); + if (imageFiles?.length === 0) { + setIsImageUploadFinish(true); + } + imageFiles?.map(async (item: any, index: number) => { + await uploadResumableFile(index, String(id), item, "1", "0"); + }); + }; + + async function uploadResumableFile( + idx: number, + id: string, + file: any, + fileTypeId: string, + duration: string + ) { + console.log(idx, id, file, fileTypeId, duration); + + 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: { + calendarId: id, + filename: file.name, + contentType: file.type, + fileTypeId: fileTypeId, + duration, + }, + onBeforeRequest: function (req) { + var xhr = req.getUnderlyingObject(); + xhr.withCredentials = true; + }, + onError: async (e: any) => { + console.log("Error upload :", e); + error(e); + }, + onChunkComplete: ( + chunkSize: any, + bytesAccepted: any, + bytesTotal: any + ) => { + // const uploadPersen = Math.floor((bytesAccepted / bytesTotal) * 100); + // progressInfo[idx].percentage = uploadPersen; + // counterUpdateProgress++; + // console.log(counterUpdateProgress); + // setProgressList(progressInfo); + // setCounterProgress(counterUpdateProgress); + }, + onSuccess: async () => { + // uploadPersen = 100; + // progressInfo[idx].percentage = 100; + // counterUpdateProgress++; + // setCounterProgress(counterUpdateProgress); + successTodo(); + if (fileTypeId == "1") { + setIsImageUploadFinish(true); + } + }, + }); + + upload.start(); + } + + React.useEffect(() => { + successTodo(); + }, [isImageUploadFinish]); + + function successTodo() { + if (isImageUploadFinish) { + successSubmit("/in/contributor/agenda-setting"); + } + } + + const successSubmit = (redirect: string) => { MySwal.fire({ title: "Sukses", text: "Data berhasil disimpan.", @@ -196,7 +373,7 @@ export function CalendarPolriAddUpdate() { confirmButtonColor: "#3085d6", confirmButtonText: "OK", }).then(() => { - router.reload(); + router.push(redirect); }); }; @@ -216,237 +393,291 @@ export function CalendarPolriAddUpdate() { }); }; + const renderFilePreview = (url: string) => { + return ( + {"file + ); + }; + + const handleRemoveFile = (id: number) => {}; + return (
-
-
-
-

Tanggal Acara

-
- - - - - - - - -
-
-
-

Publish Area

-
-
- {Object.keys(unitSelection).map((key) => ( -
- - handleUnitChange( - key as keyof typeof unitSelection, - value as boolean - ) - } + {detail !== undefined ? ( +
+ +
+
+

Tanggal Acara

+
+ + + + + + + + +
+
+
+

Publish Area

+
+
+ {Object.keys(unitSelection).map((key) => ( +
+ + handleUnitChange( + key as keyof typeof unitSelection, + value as boolean + ) + } + /> + +
+ ))} +
+
+ + + + + + + + Daftar Wilayah Polda dan Polres + + +
+ {listDest.map((polda: any) => ( +
+ + {expandedPolda[polda.id] && ( +
+ + {polda?.subDestination?.map( + (polres: any) => ( + + ) + )} +
+ )} +
+ ))} +
+
+
+
+
+
+ +
+

Nama Acara

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

+ {errors.title.message} +

+ )} +
+ +
+ + setImageFiles(files)} + /> + {imageUploadedFiles?.map((file: any, index: number) => ( +
+ + Thumbnail Gambar Utama + +
+
+
+ {renderFilePreview(file.url)} +
+
+
+ {file.fileName} +
+
+
+ + +
))}
-
- - - - - - - - Daftar Wilayah Polda dan Polres - - -
- {listDest.map((polda: any) => ( -
- - {expandedPolda[polda.id] && ( -
- - {polda?.subDestination?.map((polres: any) => ( - - ))} -
- )} -
- ))} -
-
-
+ +
+

Deskripsi

+ ( +