From aa4c8f039932a6fd6fc0a08606408ffe5979ec60 Mon Sep 17 00:00:00 2001 From: Anang Yusman Date: Sat, 3 May 2025 18:39:30 +0800 Subject: [PATCH] [QUDO-8-QUDO-10] feat:Add Ask the expert kurasi konten koorkurator,Add Accept Assignment Kurasi Konten Kurator --- .../content/spit/table-spit/table-spit.tsx | 131 ++- .../mediahub/components/mediahub-table.tsx | 54 +- .../components/medsos-table.tsx | 54 +- .../schedule/event/components/event-table.tsx | 33 +- .../components/presscon-table.tsx | 33 +- .../components/pressrilis-table.tsx | 33 +- .../task-ta/upload-task/[id]/page.tsx | 2 +- .../task/components/task-table.tsx | 30 + .../components/collabroation-table.tsx | 84 +- .../components/escalation-table.tsx | 84 +- .../internal/components/internal-table.tsx | 84 +- .../contest/components/contest-table.tsx | 33 +- .../image/accept-assignment/[id]/page.tsx | 19 + .../image/ask-the-expert/[id]/page.tsx | 17 + .../image/do-it-yourself/[id]/page.tsx | 18 + .../giat-routine/image/image.tsx | 37 + .../giat-routine/video/audio-visual.tsx | 1 + app/[locale]/layout.tsx | 17 +- .../form/shared/accept-assignment-form.tsx | 1013 +++++++++++++++++ components/form/shared/ask-expert-form.tsx | 952 ++++++++++++++++ .../form/shared/do-it-yourself-form.tsx | 1013 +++++++++++++++++ .../form/task-ta/task-ta-detail-form.tsx | 156 +-- components/form/task-ta/task-ta-edit-form.tsx | 135 --- ...ask-ta-new.tsx => task-ta-upload-form.tsx} | 0 24 files changed, 3580 insertions(+), 453 deletions(-) create mode 100644 app/[locale]/(protected)/shared/curated-content/giat-routine/image/accept-assignment/[id]/page.tsx create mode 100644 app/[locale]/(protected)/shared/curated-content/giat-routine/image/ask-the-expert/[id]/page.tsx create mode 100644 app/[locale]/(protected)/shared/curated-content/giat-routine/image/do-it-yourself/[id]/page.tsx create mode 100644 components/form/shared/accept-assignment-form.tsx create mode 100644 components/form/shared/ask-expert-form.tsx create mode 100644 components/form/shared/do-it-yourself-form.tsx rename components/form/task-ta/{task-ta-new.tsx => task-ta-upload-form.tsx} (100%) diff --git a/app/[locale]/(protected)/contributor/content/spit/table-spit/table-spit.tsx b/app/[locale]/(protected)/contributor/content/spit/table-spit/table-spit.tsx index 791ea4f9..77fcfd17 100644 --- a/app/[locale]/(protected)/contributor/content/spit/table-spit/table-spit.tsx +++ b/app/[locale]/(protected)/contributor/content/spit/table-spit/table-spit.tsx @@ -43,6 +43,7 @@ import { InputGroup, InputGroupText } from "@/components/ui/input-group"; import columns from "./columns"; import { DropdownMenu, + DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; @@ -192,56 +193,86 @@ const TableSPIT = () => { /> -
- - - - - -
-

Filter

-
-
- - setDateFilter(e.target.value)} - className="max-w-sm" - /> -
- -
- handleStatusCheckboxChange(1)} - /> - -
-
- handleStatusCheckboxChange(2)} - /> - -
-
-
+
+
+ + + + + +
+

Filter

+
+
+ + setDateFilter(e.target.value)} + className="max-w-sm" + /> +
+ +
+ handleStatusCheckboxChange(1)} + /> + +
+
+ handleStatusCheckboxChange(2)} + /> + +
+
+
+
+
+ + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
diff --git a/app/[locale]/(protected)/contributor/planning/mediahub/components/mediahub-table.tsx b/app/[locale]/(protected)/contributor/planning/mediahub/components/mediahub-table.tsx index 9d3d6fc5..c1d72b98 100644 --- a/app/[locale]/(protected)/contributor/planning/mediahub/components/mediahub-table.tsx +++ b/app/[locale]/(protected)/contributor/planning/mediahub/components/mediahub-table.tsx @@ -26,6 +26,7 @@ import { } from "@/components/ui/table"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { + ChevronDown, ChevronLeft, ChevronRight, Eye, @@ -39,6 +40,7 @@ import { import { cn } from "@/lib/utils"; import { DropdownMenu, + DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, @@ -163,17 +165,47 @@ const MediahubTable = () => { /> -
- ) => - table.getColumn("status")?.setFilterValue(event.target.value) - } - className="max-w-sm " - /> +
+
+ ) => + table.getColumn("status")?.setFilterValue(event.target.value) + } + className="max-w-sm " + /> +
+
+ + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
diff --git a/app/[locale]/(protected)/contributor/planning/medsos-mediahub/components/medsos-table.tsx b/app/[locale]/(protected)/contributor/planning/medsos-mediahub/components/medsos-table.tsx index b909ffeb..ab41096f 100644 --- a/app/[locale]/(protected)/contributor/planning/medsos-mediahub/components/medsos-table.tsx +++ b/app/[locale]/(protected)/contributor/planning/medsos-mediahub/components/medsos-table.tsx @@ -26,6 +26,7 @@ import { } from "@/components/ui/table"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { + ChevronDown, ChevronLeft, ChevronRight, Eye, @@ -39,6 +40,7 @@ import { import { cn } from "@/lib/utils"; import { DropdownMenu, + DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, @@ -162,17 +164,47 @@ const MedsosTable = () => { /> -
- ) => - table.getColumn("status")?.setFilterValue(event.target.value) - } - className="max-w-sm " - /> +
+
+ ) => + table.getColumn("status")?.setFilterValue(event.target.value) + } + className="max-w-sm " + /> +
+
+ + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
diff --git a/app/[locale]/(protected)/contributor/schedule/event/components/event-table.tsx b/app/[locale]/(protected)/contributor/schedule/event/components/event-table.tsx index a4524e72..33ce62ae 100644 --- a/app/[locale]/(protected)/contributor/schedule/event/components/event-table.tsx +++ b/app/[locale]/(protected)/contributor/schedule/event/components/event-table.tsx @@ -36,6 +36,7 @@ import { Button } from "@/components/ui/button"; import useTableColumns from "./columns"; import { DropdownMenu, + DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuRadioGroup, DropdownMenuRadioItem, @@ -172,8 +173,8 @@ const EventTable = () => { /> -
-
+
+
+
+ + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
diff --git a/app/[locale]/(protected)/contributor/schedule/press-conference/components/presscon-table.tsx b/app/[locale]/(protected)/contributor/schedule/press-conference/components/presscon-table.tsx index c409f222..a52a9ed6 100644 --- a/app/[locale]/(protected)/contributor/schedule/press-conference/components/presscon-table.tsx +++ b/app/[locale]/(protected)/contributor/schedule/press-conference/components/presscon-table.tsx @@ -50,6 +50,7 @@ import { useTranslations } from "next-intl"; import useTableColumns from "./columns"; import { DropdownMenu, + DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuRadioGroup, DropdownMenuRadioItem, @@ -186,8 +187,8 @@ const PressConferenceTable = () => { /> -
-
+
+
+
+ + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
diff --git a/app/[locale]/(protected)/contributor/schedule/press-release/components/pressrilis-table.tsx b/app/[locale]/(protected)/contributor/schedule/press-release/components/pressrilis-table.tsx index 3315fc3e..68f90688 100644 --- a/app/[locale]/(protected)/contributor/schedule/press-release/components/pressrilis-table.tsx +++ b/app/[locale]/(protected)/contributor/schedule/press-release/components/pressrilis-table.tsx @@ -51,6 +51,7 @@ import { Link } from "@/i18n/routing"; import useTableColumns from "./columns"; import { DropdownMenu, + DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuRadioGroup, DropdownMenuRadioItem, @@ -187,8 +188,8 @@ const PressReleaseTable = () => { /> -
-
+
+
+
+ + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
diff --git a/app/[locale]/(protected)/contributor/task-ta/upload-task/[id]/page.tsx b/app/[locale]/(protected)/contributor/task-ta/upload-task/[id]/page.tsx index 96600bfe..5ee62efc 100644 --- a/app/[locale]/(protected)/contributor/task-ta/upload-task/[id]/page.tsx +++ b/app/[locale]/(protected)/contributor/task-ta/upload-task/[id]/page.tsx @@ -1,7 +1,7 @@ import { Card, CardContent } from "@/components/ui/card"; import SiteBreadcrumb from "@/components/site-breadcrumb"; import FormTaskTa from "@/components/form/task-ta/task-ta-form"; -import FormTaskTaNew from "@/components/form/task-ta/task-ta-new"; +import FormTaskTaNew from "@/components/form/task-ta/task-ta-upload-form"; const TaskTaUploadPage = () => { return ( diff --git a/app/[locale]/(protected)/contributor/task/components/task-table.tsx b/app/[locale]/(protected)/contributor/task/components/task-table.tsx index 50b45411..fdd7be32 100644 --- a/app/[locale]/(protected)/contributor/task/components/task-table.tsx +++ b/app/[locale]/(protected)/contributor/task/components/task-table.tsx @@ -40,6 +40,7 @@ import { import { cn } from "@/lib/utils"; import { DropdownMenu, + DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuRadioGroup, @@ -338,6 +339,35 @@ const TaskTable = () => { + +
+ + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
{/*
{ />
- - - - - - - - 1 - 10 Data - - - 1 - 20 Data - - - 1 - 25 Data - - - 1 - 50 Data - - - - +
+ + + + + + + + 1 - 10 Data + + + 1 - 20 Data + + + 1 - 25 Data + + + 1 - 50 Data + + + + +
+ + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
+
diff --git a/app/[locale]/(protected)/shared/communication/escalation/components/escalation-table.tsx b/app/[locale]/(protected)/shared/communication/escalation/components/escalation-table.tsx index f0f369c0..a2d87137 100644 --- a/app/[locale]/(protected)/shared/communication/escalation/components/escalation-table.tsx +++ b/app/[locale]/(protected)/shared/communication/escalation/components/escalation-table.tsx @@ -26,6 +26,7 @@ import { } from "@/components/ui/table"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { + ChevronDown, ChevronLeft, ChevronRight, Eye, @@ -39,6 +40,7 @@ import { import { cn, getCookiesDecrypt } from "@/lib/utils"; import { DropdownMenu, + DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuRadioGroup, @@ -186,32 +188,62 @@ const EscalationTable = () => { /> - - - - - - - - 1 - 10 Data - - - 1 - 20 Data - - - 1 - 25 Data - - - 1 - 50 Data - - - - +
+ + + + + + + + 1 - 10 Data + + + 1 - 20 Data + + + 1 - 25 Data + + + 1 - 50 Data + + + + +
+ + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
+
diff --git a/app/[locale]/(protected)/shared/communication/internal/components/internal-table.tsx b/app/[locale]/(protected)/shared/communication/internal/components/internal-table.tsx index 4f1ea31a..d7656e7d 100644 --- a/app/[locale]/(protected)/shared/communication/internal/components/internal-table.tsx +++ b/app/[locale]/(protected)/shared/communication/internal/components/internal-table.tsx @@ -26,6 +26,7 @@ import { } from "@/components/ui/table"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { + ChevronDown, ChevronLeft, ChevronRight, Eye, @@ -40,6 +41,7 @@ import { import { cn, getCookiesDecrypt } from "@/lib/utils"; import { DropdownMenu, + DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuRadioGroup, @@ -184,32 +186,62 @@ const InternalTable = () => { /> - - - - - - - - 1 - 10 Data - - - 1 - 20 Data - - - 1 - 25 Data - - - 1 - 50 Data - - - - +
+ + + + + + + + 1 - 10 Data + + + 1 - 20 Data + + + 1 - 25 Data + + + 1 - 50 Data + + + + +
+ + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
+
diff --git a/app/[locale]/(protected)/shared/contest/components/contest-table.tsx b/app/[locale]/(protected)/shared/contest/components/contest-table.tsx index e6576478..80ef2fcc 100644 --- a/app/[locale]/(protected)/shared/contest/components/contest-table.tsx +++ b/app/[locale]/(protected)/shared/contest/components/contest-table.tsx @@ -46,6 +46,7 @@ import { listContest } from "@/service/contest/contest"; import useTableColumns from "./columns"; import { DropdownMenu, + DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuRadioGroup, DropdownMenuRadioItem, @@ -172,8 +173,8 @@ const TaskTable = () => { /> -
-
+
+
+
+ + + + + + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + + column.toggleVisibility(!!value) + } + > + {column.id} + + ); + })} + + +
diff --git a/app/[locale]/(protected)/shared/curated-content/giat-routine/image/accept-assignment/[id]/page.tsx b/app/[locale]/(protected)/shared/curated-content/giat-routine/image/accept-assignment/[id]/page.tsx new file mode 100644 index 00000000..a9b8c706 --- /dev/null +++ b/app/[locale]/(protected)/shared/curated-content/giat-routine/image/accept-assignment/[id]/page.tsx @@ -0,0 +1,19 @@ +import { Card, CardContent } from "@/components/ui/card"; +import SiteBreadcrumb from "@/components/site-breadcrumb"; +import FormTaskTa from "@/components/form/task-ta/task-ta-form"; +import FormAskExpert from "@/components/form/shared/ask-expert-form"; +import FormDoItYourself from "@/components/form/shared/do-it-yourself-form"; +import FormAcceptAssignment from "@/components/form/shared/accept-assignment-form"; + +const AcceptAssignmentPage = () => { + return ( +
+ +
+ +
+
+ ); +}; + +export default AcceptAssignmentPage; diff --git a/app/[locale]/(protected)/shared/curated-content/giat-routine/image/ask-the-expert/[id]/page.tsx b/app/[locale]/(protected)/shared/curated-content/giat-routine/image/ask-the-expert/[id]/page.tsx new file mode 100644 index 00000000..9e117862 --- /dev/null +++ b/app/[locale]/(protected)/shared/curated-content/giat-routine/image/ask-the-expert/[id]/page.tsx @@ -0,0 +1,17 @@ +import { Card, CardContent } from "@/components/ui/card"; +import SiteBreadcrumb from "@/components/site-breadcrumb"; +import FormTaskTa from "@/components/form/task-ta/task-ta-form"; +import FormAskExpert from "@/components/form/shared/ask-expert-form"; + +const AskExpertCreatePage = () => { + return ( +
+ +
+ +
+
+ ); +}; + +export default AskExpertCreatePage; diff --git a/app/[locale]/(protected)/shared/curated-content/giat-routine/image/do-it-yourself/[id]/page.tsx b/app/[locale]/(protected)/shared/curated-content/giat-routine/image/do-it-yourself/[id]/page.tsx new file mode 100644 index 00000000..20c56cb2 --- /dev/null +++ b/app/[locale]/(protected)/shared/curated-content/giat-routine/image/do-it-yourself/[id]/page.tsx @@ -0,0 +1,18 @@ +import { Card, CardContent } from "@/components/ui/card"; +import SiteBreadcrumb from "@/components/site-breadcrumb"; +import FormTaskTa from "@/components/form/task-ta/task-ta-form"; +import FormAskExpert from "@/components/form/shared/ask-expert-form"; +import FormDoItYourself from "@/components/form/shared/do-it-yourself-form"; + +const DoItYourselfCreatePage = () => { + return ( +
+ +
+ +
+
+ ); +}; + +export default DoItYourselfCreatePage; diff --git a/app/[locale]/(protected)/shared/curated-content/giat-routine/image/image.tsx b/app/[locale]/(protected)/shared/curated-content/giat-routine/image/image.tsx index 5c392d42..7c1806da 100644 --- a/app/[locale]/(protected)/shared/curated-content/giat-routine/image/image.tsx +++ b/app/[locale]/(protected)/shared/curated-content/giat-routine/image/image.tsx @@ -1,5 +1,6 @@ "use client"; import { Link } from "@/components/navigation"; +import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { Carousel, @@ -8,6 +9,7 @@ import { CarouselNext, CarouselPrevious, } from "@/components/ui/carousel"; +import { getCookiesDecrypt } from "@/lib/utils"; import { listCuratedContent } from "@/service/curated-content/curated-content"; import { formatDateToIndonesian } from "@/utils/globals"; @@ -26,6 +28,7 @@ type ImageData = { const ImageSliderPage = () => { const router = useRouter(); + const roleId = Number(getCookiesDecrypt("urie")) || 0; const [imageData, setImageData] = useState([]); const [page, setPage] = useState(1); const [limit] = useState(10); @@ -88,6 +91,40 @@ const ImageSliderPage = () => { + {roleId === 11 && ( +
+ + + + + + +
+ )} + + {roleId === 12 && ( +
+ + + +
+ )} diff --git a/app/[locale]/(protected)/shared/curated-content/giat-routine/video/audio-visual.tsx b/app/[locale]/(protected)/shared/curated-content/giat-routine/video/audio-visual.tsx index e897a481..a18b8c41 100644 --- a/app/[locale]/(protected)/shared/curated-content/giat-routine/video/audio-visual.tsx +++ b/app/[locale]/(protected)/shared/curated-content/giat-routine/video/audio-visual.tsx @@ -1,5 +1,6 @@ "use client"; import { Link } from "@/components/navigation"; +import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { Carousel, diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index 72fd5a5c..6184d823 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -17,7 +17,7 @@ import LoadScript from "@/utils/globals"; export const metadata: Metadata = { title: "Media Hub | POLRI", - description: "created by codeshaper", + description: "", }; export default async function RootLayout({ @@ -33,15 +33,24 @@ export default async function RootLayout({ - - + + - {children} + + {children} + diff --git a/components/form/shared/accept-assignment-form.tsx b/components/form/shared/accept-assignment-form.tsx new file mode 100644 index 00000000..69527547 --- /dev/null +++ b/components/form/shared/accept-assignment-form.tsx @@ -0,0 +1,1013 @@ +"use client"; +import React, { useEffect, useRef, useState } from "react"; +import { useForm, Controller } from "react-hook-form"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { Label } from "@/components/ui/label"; +import { Card } from "@/components/ui/card"; +import { zodResolver } from "@hookform/resolvers/zod"; +import * as z from "zod"; +import Swal from "sweetalert2"; +import withReactContent from "sweetalert2-react-content"; +import { useParams, useRouter } from "next/navigation"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Checkbox } from "@/components/ui/checkbox"; +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; +import JoditEditor from "jodit-react"; +import { + createTask, + createTaskTa, + getTask, + getUserLevelForAssignments, +} from "@/service/task"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { CalendarIcon, ChevronDown, ChevronUp, Trash2 } from "lucide-react"; +import { AudioRecorder } from "react-audio-voice-recorder"; +import FileUploader from "@/components/form/shared/file-uploader"; +import { Upload } from "tus-js-client"; +import { error } from "@/config/swal"; +import { getCsrfToken } from "@/service/auth"; +import { loading } from "@/lib/swal"; +import { useTranslations } from "next-intl"; +import dynamic from "next/dynamic"; +import { cn } from "@/lib/utils"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { Calendar } from "@/components/ui/calendar"; +import { addDays, format, setDate } from "date-fns"; +import { DateRange } from "react-day-picker"; +import TimePicker from "react-time-picker"; +import "react-time-picker/dist/TimePicker.css"; +import "react-clock/dist/Clock.css"; +import { detailMedia } from "@/service/curated-content/curated-content"; + +const taskSchema = z.object({ + title: z.string().min(1, { message: "Judul diperlukan" }), + naration: z.string().min(2, { + message: "Narasi Penugasan harus lebih dari 2 karakter.", + }), + // url: z.string().min(1, { message: "Judul diperlukan" }), +}); + +interface FileWithPreview extends File { + preview: string; +} + +export type taskDetail = { + id: number; + title: string; + fileTypeOutput: string; + assignedToTopLevel: string; + assignedToLevel: string; + assignmentType: { + id: number; + name: string; + }; + assignmentMainType: { + id: number; + name: string; + }; + attachmentUrl: string; + taskType: string; + broadcastType: string; + narration: string; + is_active: string; +}; + +const CustomEditor = dynamic( + () => { + return import("@/components/editor/custom-editor"); + }, + { ssr: false } +); + +export default function FormAcceptAssignment() { + const MySwal = withReactContent(Swal); + const router = useRouter(); + const editor = useRef(null); + type TaskSchema = z.infer; + const { id } = useParams() as { id: string }; + console.log(id); + + // State for various form fields + const [expertise, setExpertiseOutput] = useState({ + semua: false, + komunikasi: false, + hukum: false, + bahasa: false, + ekonomi: false, + politik: false, + sosiologi: false, + ilmuadministrasipemerintah: false, + ti: false, + }); + const [expert, setExpertOutput] = useState({ + semua: false, + }); + + // const [assignmentType, setAssignmentType] = useState("mediahub"); + // const [assignmentCategory, setAssignmentCategory] = useState("publication"); + const [mainType, setMainType] = useState("1"); + const [taskType, setTaskType] = useState("atensi-khusus"); + const [broadcastType, setBroadcastType] = useState(""); + const [type, setType] = useState("1"); + const [selectedTarget, setSelectedTarget] = useState("3,4"); + const [detail, setDetail] = useState(); + const [refresh] = useState(false); + const [listDest, setListDest] = useState([]); + const [checkedLevels, setCheckedLevels] = useState(new Set()); + const [expandedPolda, setExpandedPolda] = useState([{}]); + const [isLoading, setIsLoading] = useState(false); + const [audioFile, setAudioFile] = useState(null); + const [isRecording, setIsRecording] = useState(false); + const [timer, setTimer] = useState(120); + + const t = useTranslations("Form"); + 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 [voiceNoteLink, setVoiceNoteLink] = useState(""); + const [date, setDate] = React.useState({ + from: new Date(2024, 0, 1), + }); + + const [platformTypeVisible, setPlatformTypeVisible] = useState(false); + const [unitSelection, setUnitSelection] = useState({ + semua: false, + mabes: false, + polda: false, + polres: false, + satker: false, + }); + + const [taskOutput, setTaskOutput] = useState({ + all: false, + video: false, + audio: false, + image: false, + text: false, + }); + + const [links, setLinks] = useState([""]); + + const { + register, + control, + setValue, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: zodResolver(taskSchema), + mode: "all", + }); + + // const handleRadioChange = (event: React.ChangeEvent) => { + // const selectedValue = Number(event.target.value); + // setMainType(selectedValue); + + // setPlatformTypeVisible(selectedValue === 2); + + useEffect(() => { + async function fetchPoldaPolres() { + setIsLoading(true); + try { + const response = await getUserLevelForAssignments(); + setListDest(response?.data?.data.list); + console.log("polda", response?.data?.data?.list); + const initialExpandedState = response?.data?.data.list.reduce( + (acc: any, polda: any) => { + acc[polda.id] = false; + return acc; + }, + {} + ); + setExpandedPolda(initialExpandedState); + console.log("polres", initialExpandedState); + } catch (error) { + console.error("Error fetching Polda/Polres data:", error); + } finally { + setIsLoading(false); + } + } + fetchPoldaPolres(); + }, []); + + // }; + const handleCheckboxChange = (levelId: number) => { + setCheckedLevels((prev) => { + const updatedLevels = new Set(prev); + if (updatedLevels.has(levelId)) { + updatedLevels.delete(levelId); + } else { + updatedLevels.add(levelId); + } + return updatedLevels; + }); + }; + + const handlePoldaPolresChange = () => { + return Array.from(checkedLevels).join(","); // Mengonversi Set ke string + }; + + const handleUnitChange = ( + key: keyof typeof unitSelection, + value: boolean + ) => { + if (key === "semua") { + const newState = { + semua: value, + mabes: value, + polda: value, + polres: value, + satker: value, + }; + setUnitSelection(newState); + } else { + const updatedSelection = { + ...unitSelection, + [key]: value, + }; + + const allChecked = ["mabes", "polda", "polres", "satker"].every( + (k) => updatedSelection[k as keyof typeof unitSelection] + ); + + updatedSelection.semua = allChecked; + + setUnitSelection(updatedSelection); + } + }; + + const handleExpertiseOutputChange = ( + key: keyof typeof expertise, + value: boolean + ) => { + if (key === "semua") { + const newState = { + semua: value, + komunikasi: value, + hukum: value, + bahasa: value, + ekonomi: value, + politik: value, + sosiologi: value, + ilmuadministrasipemerintah: value, + ti: value, + }; + setExpertiseOutput(newState); + } else { + const updated = { + ...expertise, + [key]: value, + }; + + const allChecked = ["video", "audio", "image", "text"].every( + (k) => updated[k as keyof typeof expertise] + ); + + updated.semua = allChecked; + setExpertiseOutput(updated); + } + }; + + const handleExpertOutputChange = ( + key: keyof typeof expert, + value: boolean + ) => { + if (key === "semua") { + const newState = { + semua: value, + }; + setExpertOutput(newState); + } else { + const updated = { + ...expert, + [key]: value, + }; + + const allChecked = ["video", "audio", "image", "text"].every( + (k) => updated[k as keyof typeof expert] + ); + + updated.semua = allChecked; + setExpertOutput(updated); + } + }; + + useEffect(() => { + async function initState() { + if (id) { + const response = await detailMedia(id); + const details = response?.data?.data; + + setDetail(details); + } + } + initState(); + }, [id, refresh]); + + const handleTaskOutputChange = ( + key: keyof typeof taskOutput, + value: boolean + ) => { + if (key === "all") { + const newState = { + all: value, + video: value, + audio: value, + image: value, + text: value, + }; + setTaskOutput(newState); + } else { + const updated = { + ...taskOutput, + [key]: value, + }; + + const allChecked = ["video", "audio", "image", "text"].every( + (k) => updated[k as keyof typeof taskOutput] + ); + + updated.all = allChecked; + setTaskOutput(updated); + } + }; + + const save = async (data: TaskSchema) => { + const fileTypeMapping = { + all: "1", + video: "2", + audio: "4", + image: "3", + text: "5", + }; + + const unitMapping = { + allUnit: "0", + mabes: "1", + polda: "2", + polres: "3", + satker: "4", + }; + const assignmentPurposeString = Object.keys(unitSelection) + .filter((key) => unitSelection[key as keyof typeof unitSelection]) + .map((key) => unitMapping[key as keyof typeof unitMapping]) + .join(","); + + const selectedOutputs = Object.keys(taskOutput) + .filter((key) => taskOutput[key as keyof typeof taskOutput]) + .map((key) => fileTypeMapping[key as keyof typeof fileTypeMapping]) + .join(","); + + const requestData: { + id?: number; + title: string; + assignedToLevel: any; + assignedToUsers: any; + assignmentTypeId: string; + fileTypeOutput: string; + narration: string; + platformType: string | null; + assignmentMainTypeId: any; + assignmentType: string; + assignedToRole: string; + broadcastType: string; + expertCompetencies: string; + attachmentUrl: string[]; + } = { + ...data, + // assignmentType, + // assignmentCategory, + assignedToLevel: handlePoldaPolresChange(), + assignedToUsers: assignmentPurposeString, + assignedToRole: selectedTarget, + assignmentType: taskType, + broadcastType: broadcastType, + assignmentMainTypeId: mainType, + assignmentTypeId: type, + fileTypeOutput: selectedOutputs, + narration: data.naration, + platformType: "", + expertCompetencies: "1,2,3", + title: data.title, + attachmentUrl: links, + }; + + const response = await createTaskTa(requestData); + + console.log("Form Data Submitted:", requestData); + console.log("response", response); + + const id = response?.data?.data.id; + loading(); + 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: 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 + ); + }); + }; + + const onSubmit = (data: TaskSchema) => { + MySwal.fire({ + title: "Simpan Data", + text: "Apakah Anda yakin ingin menyimpan data ini?", + icon: "warning", + showCancelButton: true, + cancelButtonColor: "#d33", + confirmButtonColor: "#3085d6", + confirmButtonText: "Simpan", + }).then((result) => { + if (result.isConfirmed) { + save(data); + } + }); + }; + + const toggleExpand = (poldaId: any) => { + setExpandedPolda((prev: any) => ({ + ...prev, + [poldaId]: !prev[poldaId], + })); + }; + + const onRecordingStart = () => { + setIsRecording(true); + + const countdown = setInterval(() => { + setTimer((prevTimer) => { + if (prevTimer <= 1) { + clearInterval(countdown); + return 0; + } + return prevTimer - 1; + }); + }, 1000); + + setTimeout(() => { + if (isRecording) { + handleStopRecording(); + } + }, 120000); + }; + + const handleStopRecording = () => { + setIsRecording(false); + setTimer(120); // Reset the timer to 2 minutes for the next recording + }; + + const addAudioElement = (blob: Blob) => { + const url = URL.createObjectURL(blob); + const audio = document.createElement("audio"); + audio.src = url; + audio.controls = true; + document.body.appendChild(audio); + + // 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 = (index: number) => { + setAudioFiles((prev) => prev.filter((_, idx) => idx !== index)); + }; + + 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}/assignment-expert/file/upload`, + headers: headers, + retryDelays: [0, 3000, 6000, 12_000, 24_000], + chunkSize: 20_000, + metadata: { + assignmentId: 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); + } 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/task-ta"); + } + } + + const successSubmit = (redirect: string) => { + MySwal.fire({ + title: "Sukses", + text: "Data berhasil disimpan.", + icon: "success", + confirmButtonColor: "#3085d6", + confirmButtonText: "OK", + }).then(() => { + router.push(redirect); + }); + }; + + const handleLinkChange = (index: number, value: string) => { + const updatedLinks = [...links]; + updatedLinks[index] = value; + setLinks(updatedLinks); + }; + + const handleAddRow = () => { + setLinks([...links, ""]); + }; + + // Remove a specific link row + const handleRemoveRow = (index: number) => { + const updatedLinks = links.filter((_: any, i: any) => i !== index); + setLinks(updatedLinks); + }; + + return ( + +
+

{t("form-task")}

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

{errors.title.message}

+ )} +
+ +
+ +
+ {Object.keys(taskOutput).map((key) => ( +
+ + handleTaskOutputChange( + key as keyof typeof taskOutput, + value as boolean + ) + } + /> + +
+ ))} +
+
+
+ +
+ {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) => ( + + ))} +
+ )} +
+ ))} +
+
+
+
+
+
+
+ + setType(value)} // Mengubah nilai state ketika pilihan berubah + className="flex flex-wrap gap-3" + > +
+ + +
+
+ + +
+
+ + +
+
+
+
+ + + + + + + + + +
+
+ + ( + + )} + /> + {errors.naration?.message && ( +

+ {errors.naration.message} +

+ )} +
+
+ +
+
+ + setVideoFiles(files)} + /> +
+
+ + setImageFiles(files)} + /> +
+
+ + setTextFiles(files)} + /> +
+
+ + + + setAudioFiles((prev) => [...prev, ...files]) + } + className="mt-2" + /> +
+ {audioFiles?.map((audio: any, idx: any) => ( +
+

{t("voice-note")}

+ +
+ ))} + {isRecording &&

Recording... {timer} seconds remaining

}{" "} + {/* Display remaining time */} +
+ + {links.map((link, index) => ( +
+ + handleLinkChange(index, e.target.value) + } + /> + {links.length > 1 && ( + + )} +
+ ))} + +
+
+
+
+ + {/* Submit Button */} +
+ +
+ + ) : ( + "" + )} +
+
+ ); +} diff --git a/components/form/shared/ask-expert-form.tsx b/components/form/shared/ask-expert-form.tsx new file mode 100644 index 00000000..1583813d --- /dev/null +++ b/components/form/shared/ask-expert-form.tsx @@ -0,0 +1,952 @@ +"use client"; +import React, { useEffect, useRef, useState } from "react"; +import { useForm, Controller } from "react-hook-form"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { Label } from "@/components/ui/label"; +import { Card } from "@/components/ui/card"; +import { zodResolver } from "@hookform/resolvers/zod"; +import * as z from "zod"; +import Swal from "sweetalert2"; +import withReactContent from "sweetalert2-react-content"; +import { useParams, useRouter } from "next/navigation"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Checkbox } from "@/components/ui/checkbox"; +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; +import JoditEditor from "jodit-react"; +import { + createTask, + createTaskTa, + getTask, + getUserLevelForAssignments, +} from "@/service/task"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { CalendarIcon, ChevronDown, ChevronUp, Trash2 } from "lucide-react"; +import { AudioRecorder } from "react-audio-voice-recorder"; +import FileUploader from "@/components/form/shared/file-uploader"; +import { Upload } from "tus-js-client"; +import { error } from "@/config/swal"; +import { getCsrfToken } from "@/service/auth"; +import { loading } from "@/lib/swal"; +import { useTranslations } from "next-intl"; +import dynamic from "next/dynamic"; +import { cn } from "@/lib/utils"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { Calendar } from "@/components/ui/calendar"; +import { addDays, format, setDate } from "date-fns"; +import { DateRange } from "react-day-picker"; +import TimePicker from "react-time-picker"; +import "react-time-picker/dist/TimePicker.css"; +import "react-clock/dist/Clock.css"; +import { detailMedia } from "@/service/curated-content/curated-content"; + +const taskSchema = z.object({ + title: z.string().min(1, { message: "Judul diperlukan" }), + naration: z.string().min(2, { + message: "Narasi Penugasan harus lebih dari 2 karakter.", + }), + // url: z.string().min(1, { message: "Judul diperlukan" }), +}); + +interface FileWithPreview extends File { + preview: string; +} + +export type taskDetail = { + id: number; + title: string; + fileTypeOutput: string; + assignedToTopLevel: string; + assignedToLevel: string; + assignmentType: { + id: number; + name: string; + }; + assignmentMainType: { + id: number; + name: string; + }; + attachmentUrl: string; + taskType: string; + broadcastType: string; + narration: string; + is_active: string; +}; + +const CustomEditor = dynamic( + () => { + return import("@/components/editor/custom-editor"); + }, + { ssr: false } +); + +export default function FormAskExpert() { + const MySwal = withReactContent(Swal); + const router = useRouter(); + const editor = useRef(null); + type TaskSchema = z.infer; + const { id } = useParams() as { id: string }; + console.log(id); + + // State for various form fields + const [expertise, setExpertiseOutput] = useState({ + semua: false, + komunikasi: false, + hukum: false, + bahasa: false, + ekonomi: false, + politik: false, + sosiologi: false, + ilmuadministrasipemerintah: false, + ti: false, + }); + const [expert, setExpertOutput] = useState({ + semua: false, + }); + + // const [assignmentType, setAssignmentType] = useState("mediahub"); + // const [assignmentCategory, setAssignmentCategory] = useState("publication"); + const [mainType, setMainType] = useState("1"); + const [taskType, setTaskType] = useState("atensi-khusus"); + const [broadcastType, setBroadcastType] = useState(""); + const [type, setType] = useState("1"); + const [selectedTarget, setSelectedTarget] = useState("3,4"); + const [detail, setDetail] = useState(); + const [refresh] = useState(false); + const [listDest, setListDest] = useState([]); + const [checkedLevels, setCheckedLevels] = useState(new Set()); + const [expandedPolda, setExpandedPolda] = useState([{}]); + const [isLoading, setIsLoading] = useState(false); + const [audioFile, setAudioFile] = useState(null); + const [isRecording, setIsRecording] = useState(false); + const [timer, setTimer] = useState(120); + + const t = useTranslations("Form"); + 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 [voiceNoteLink, setVoiceNoteLink] = useState(""); + const [date, setDate] = React.useState({ + from: new Date(2024, 0, 1), + }); + + const [platformTypeVisible, setPlatformTypeVisible] = useState(false); + const [unitSelection, setUnitSelection] = useState({ + semua: false, + mabes: false, + polda: false, + polres: false, + satker: false, + }); + const [taskOutput, setTaskOutput] = useState({ + all: false, + video: false, + audio: false, + image: false, + text: false, + }); + const [links, setLinks] = useState([""]); + + const { + register, + control, + setValue, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: zodResolver(taskSchema), + mode: "all", + }); + + // const handleRadioChange = (event: React.ChangeEvent) => { + // const selectedValue = Number(event.target.value); + // setMainType(selectedValue); + + // setPlatformTypeVisible(selectedValue === 2); + + useEffect(() => { + async function fetchPoldaPolres() { + setIsLoading(true); + try { + const response = await getUserLevelForAssignments(); + setListDest(response?.data?.data.list); + console.log("polda", response?.data?.data?.list); + const initialExpandedState = response?.data?.data.list.reduce( + (acc: any, polda: any) => { + acc[polda.id] = false; + return acc; + }, + {} + ); + setExpandedPolda(initialExpandedState); + console.log("polres", initialExpandedState); + } catch (error) { + console.error("Error fetching Polda/Polres data:", error); + } finally { + setIsLoading(false); + } + } + fetchPoldaPolres(); + }, []); + + // }; + const handleCheckboxChange = (levelId: number) => { + setCheckedLevels((prev) => { + const updatedLevels = new Set(prev); + if (updatedLevels.has(levelId)) { + updatedLevels.delete(levelId); + } else { + updatedLevels.add(levelId); + } + return updatedLevels; + }); + }; + + const handlePoldaPolresChange = () => { + return Array.from(checkedLevels).join(","); // Mengonversi Set ke string + }; + + const handleUnitChange = ( + key: keyof typeof unitSelection, + value: boolean + ) => { + if (key === "semua") { + const newState = { + semua: value, + mabes: value, + polda: value, + polres: value, + satker: value, + }; + setUnitSelection(newState); + } else { + const updatedSelection = { + ...unitSelection, + [key]: value, + }; + + const allChecked = ["mabes", "polda", "polres", "satker"].every( + (k) => updatedSelection[k as keyof typeof unitSelection] + ); + + updatedSelection.semua = allChecked; + + setUnitSelection(updatedSelection); + } + }; + + const handleExpertiseOutputChange = ( + key: keyof typeof expertise, + value: boolean + ) => { + if (key === "semua") { + const newState = { + semua: value, + komunikasi: value, + hukum: value, + bahasa: value, + ekonomi: value, + politik: value, + sosiologi: value, + ilmuadministrasipemerintah: value, + ti: value, + }; + setExpertiseOutput(newState); + } else { + const updated = { + ...expertise, + [key]: value, + }; + + const allChecked = [ + "komunikasi", + "hukum", + "bahasa", + "ekonomi", + "politik", + "sosiologi", + "ilmuadministrasipemerintah", + "ti", + ].every((k) => updated[k as keyof typeof expertise]); + + updated.semua = allChecked; + setExpertiseOutput(updated); + } + }; + + const handleExpertOutputChange = ( + key: keyof typeof expert, + value: boolean + ) => { + if (key === "semua") { + const newState = { + semua: value, + }; + setExpertOutput(newState); + } else { + const updated = { + ...expert, + [key]: value, + }; + + const allChecked = ["video", "audio", "image", "text"].every( + (k) => updated[k as keyof typeof expert] + ); + + updated.semua = allChecked; + setExpertOutput(updated); + } + }; + + useEffect(() => { + async function initState() { + if (id) { + const response = await detailMedia(id); + const details = response?.data?.data; + + setDetail(details); + } + } + initState(); + }, [id, refresh]); + + const handleTaskOutputChange = ( + key: keyof typeof taskOutput, + value: boolean + ) => { + if (key === "all") { + const newState = { + all: value, + video: value, + audio: value, + image: value, + text: value, + }; + setTaskOutput(newState); + } else { + const updated = { + ...taskOutput, + [key]: value, + }; + + const allChecked = ["video", "audio", "image", "text"].every( + (k) => updated[k as keyof typeof taskOutput] + ); + + updated.all = allChecked; + setTaskOutput(updated); + } + }; + + const save = async (data: TaskSchema) => { + const fileTypeMapping = { + all: "1", + video: "2", + audio: "4", + image: "3", + text: "5", + }; + + const areasMapping = { + semua: "0", + komunikasi: "1", + hukum: "2", + bahasa: "3", + ekonomi: "4", + politik: "5", + sosiologi: "6", + ilmuadministrasipemerintah: "7", + ti: "8", + }; + + const unitMapping = { + allUnit: "0", + mabes: "1", + polda: "2", + polres: "3", + satker: "4", + }; + const assignmentPurposeString = Object.keys(unitSelection) + .filter((key) => unitSelection[key as keyof typeof unitSelection]) + .map((key) => unitMapping[key as keyof typeof unitMapping]) + .join(","); + + const selectedOutputs = Object.keys(taskOutput) + .filter((key) => taskOutput[key as keyof typeof taskOutput]) + .map((key) => fileTypeMapping[key as keyof typeof fileTypeMapping]) + .join(","); + + const selectedAreaExpert = Object.keys(expertise) + .filter((key) => expertise[key as keyof typeof expertise]) + .map((key) => areasMapping[key as keyof typeof areasMapping]) + .join(","); + + const requestData: { + id?: number; + title: string; + assignedToLevel: any; + assignedToUsers: any; + assignmentTypeId: string; + fileTypeOutput: string; + areasExpertise: string; + narration: string; + platformType: string | null; + assignmentMainTypeId: any; + assignmentType: string; + assignedToRole: string; + broadcastType: string; + expertCompetencies: string; + attachmentUrl: string[]; + } = { + ...data, + // assignmentType, + // assignmentCategory, + assignedToLevel: handlePoldaPolresChange(), + assignedToUsers: assignmentPurposeString, + assignedToRole: selectedTarget, + assignmentType: taskType, + broadcastType: broadcastType, + assignmentMainTypeId: mainType, + areasExpertise: selectedAreaExpert, + assignmentTypeId: type, + fileTypeOutput: selectedOutputs, + narration: data.naration, + platformType: "", + expertCompetencies: "1,2,3", + title: data.title, + attachmentUrl: links, + }; + + const response = await createTaskTa(requestData); + + console.log("Form Data Submitted:", requestData); + console.log("response", response); + + const id = response?.data?.data.id; + loading(); + 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: 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 + ); + }); + }; + + const onSubmit = (data: TaskSchema) => { + MySwal.fire({ + title: "Simpan Data", + text: "Apakah Anda yakin ingin menyimpan data ini?", + icon: "warning", + showCancelButton: true, + cancelButtonColor: "#d33", + confirmButtonColor: "#3085d6", + confirmButtonText: "Simpan", + }).then((result) => { + if (result.isConfirmed) { + save(data); + } + }); + }; + + const toggleExpand = (poldaId: any) => { + setExpandedPolda((prev: any) => ({ + ...prev, + [poldaId]: !prev[poldaId], + })); + }; + + const onRecordingStart = () => { + setIsRecording(true); + + const countdown = setInterval(() => { + setTimer((prevTimer) => { + if (prevTimer <= 1) { + clearInterval(countdown); + return 0; + } + return prevTimer - 1; + }); + }, 1000); + + setTimeout(() => { + if (isRecording) { + handleStopRecording(); + } + }, 120000); + }; + + const handleStopRecording = () => { + setIsRecording(false); + setTimer(120); + }; + + const addAudioElement = (blob: Blob) => { + const url = URL.createObjectURL(blob); + const audio = document.createElement("audio"); + audio.src = url; + audio.controls = true; + document.body.appendChild(audio); + + // 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 = (index: number) => { + setAudioFiles((prev) => prev.filter((_, idx) => idx !== index)); + }; + + 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}/assignment-expert/file/upload`, + headers: headers, + retryDelays: [0, 3000, 6000, 12_000, 24_000], + chunkSize: 20_000, + metadata: { + assignmentId: 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); + } 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/task-ta"); + } + } + + const successSubmit = (redirect: string) => { + MySwal.fire({ + title: "Sukses", + text: "Data berhasil disimpan.", + icon: "success", + confirmButtonColor: "#3085d6", + confirmButtonText: "OK", + }).then(() => { + router.push(redirect); + }); + }; + + const handleLinkChange = (index: number, value: string) => { + const updatedLinks = [...links]; + updatedLinks[index] = value; + setLinks(updatedLinks); + }; + + const handleAddRow = () => { + setLinks([...links, ""]); + }; + + // Remove a specific link row + const handleRemoveRow = (index: number) => { + const updatedLinks = links.filter((_: any, i: any) => i !== index); + setLinks(updatedLinks); + }; + + return ( + +
+

{t("form-task")}

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

{errors.title.message}

+ )} +
+
+ +
+
+ +
+ {Object.keys(expertise).map((key) => ( +
+ + handleExpertiseOutputChange( + key as keyof typeof expertise, + value as boolean + ) + } + /> + +
+ ))} +
+
+
+ +
+ {Object.keys(expert).map((key) => ( +
+ + handleExpertOutputChange( + key as keyof typeof expert, + value as boolean + ) + } + /> + +
+ ))} +
+
+
+ + + + + + + + + +
+
+ +
+ {Object.keys(taskOutput).map((key) => ( +
+ + handleTaskOutputChange( + key as keyof typeof taskOutput, + value as boolean + ) + } + /> + +
+ ))} +
+
+
+ + ( + + )} + /> + {errors.naration?.message && ( +

+ {errors.naration.message} +

+ )} +
+
+ +
+
+ + setVideoFiles(files)} + /> +
+
+ + setImageFiles(files)} + /> +
+
+ + setTextFiles(files)} + /> +
+
+ + + + setAudioFiles((prev) => [...prev, ...files]) + } + className="mt-2" + /> +
+ {audioFiles?.map((audio: any, idx: any) => ( +
+

{t("voice-note")}

+ +
+ ))} + {isRecording &&

Recording... {timer} seconds remaining

}{" "} + {/* Display remaining time */} +
+ + {links.map((link, index) => ( +
+ + handleLinkChange(index, e.target.value) + } + /> + {links.length > 1 && ( + + )} +
+ ))} + +
+
+
+
+ + {/* Submit Button */} +
+ +
+ + ) : ( + "" + )} +
+
+ ); +} diff --git a/components/form/shared/do-it-yourself-form.tsx b/components/form/shared/do-it-yourself-form.tsx new file mode 100644 index 00000000..cdf21929 --- /dev/null +++ b/components/form/shared/do-it-yourself-form.tsx @@ -0,0 +1,1013 @@ +"use client"; +import React, { useEffect, useRef, useState } from "react"; +import { useForm, Controller } from "react-hook-form"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { Label } from "@/components/ui/label"; +import { Card } from "@/components/ui/card"; +import { zodResolver } from "@hookform/resolvers/zod"; +import * as z from "zod"; +import Swal from "sweetalert2"; +import withReactContent from "sweetalert2-react-content"; +import { useParams, useRouter } from "next/navigation"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Checkbox } from "@/components/ui/checkbox"; +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; +import JoditEditor from "jodit-react"; +import { + createTask, + createTaskTa, + getTask, + getUserLevelForAssignments, +} from "@/service/task"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { CalendarIcon, ChevronDown, ChevronUp, Trash2 } from "lucide-react"; +import { AudioRecorder } from "react-audio-voice-recorder"; +import FileUploader from "@/components/form/shared/file-uploader"; +import { Upload } from "tus-js-client"; +import { error } from "@/config/swal"; +import { getCsrfToken } from "@/service/auth"; +import { loading } from "@/lib/swal"; +import { useTranslations } from "next-intl"; +import dynamic from "next/dynamic"; +import { cn } from "@/lib/utils"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { Calendar } from "@/components/ui/calendar"; +import { addDays, format, setDate } from "date-fns"; +import { DateRange } from "react-day-picker"; +import TimePicker from "react-time-picker"; +import "react-time-picker/dist/TimePicker.css"; +import "react-clock/dist/Clock.css"; +import { detailMedia } from "@/service/curated-content/curated-content"; + +const taskSchema = z.object({ + title: z.string().min(1, { message: "Judul diperlukan" }), + naration: z.string().min(2, { + message: "Narasi Penugasan harus lebih dari 2 karakter.", + }), + // url: z.string().min(1, { message: "Judul diperlukan" }), +}); + +interface FileWithPreview extends File { + preview: string; +} + +export type taskDetail = { + id: number; + title: string; + fileTypeOutput: string; + assignedToTopLevel: string; + assignedToLevel: string; + assignmentType: { + id: number; + name: string; + }; + assignmentMainType: { + id: number; + name: string; + }; + attachmentUrl: string; + taskType: string; + broadcastType: string; + narration: string; + is_active: string; +}; + +const CustomEditor = dynamic( + () => { + return import("@/components/editor/custom-editor"); + }, + { ssr: false } +); + +export default function FormDoItYourself() { + const MySwal = withReactContent(Swal); + const router = useRouter(); + const editor = useRef(null); + type TaskSchema = z.infer; + const { id } = useParams() as { id: string }; + console.log(id); + + // State for various form fields + const [expertise, setExpertiseOutput] = useState({ + semua: false, + komunikasi: false, + hukum: false, + bahasa: false, + ekonomi: false, + politik: false, + sosiologi: false, + ilmuadministrasipemerintah: false, + ti: false, + }); + const [expert, setExpertOutput] = useState({ + semua: false, + }); + + // const [assignmentType, setAssignmentType] = useState("mediahub"); + // const [assignmentCategory, setAssignmentCategory] = useState("publication"); + const [mainType, setMainType] = useState("1"); + const [taskType, setTaskType] = useState("atensi-khusus"); + const [broadcastType, setBroadcastType] = useState(""); + const [type, setType] = useState("1"); + const [selectedTarget, setSelectedTarget] = useState("3,4"); + const [detail, setDetail] = useState(); + const [refresh] = useState(false); + const [listDest, setListDest] = useState([]); + const [checkedLevels, setCheckedLevels] = useState(new Set()); + const [expandedPolda, setExpandedPolda] = useState([{}]); + const [isLoading, setIsLoading] = useState(false); + const [audioFile, setAudioFile] = useState(null); + const [isRecording, setIsRecording] = useState(false); + const [timer, setTimer] = useState(120); + + const t = useTranslations("Form"); + 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 [voiceNoteLink, setVoiceNoteLink] = useState(""); + const [date, setDate] = React.useState({ + from: new Date(2024, 0, 1), + }); + + const [platformTypeVisible, setPlatformTypeVisible] = useState(false); + const [unitSelection, setUnitSelection] = useState({ + semua: false, + mabes: false, + polda: false, + polres: false, + satker: false, + }); + + const [taskOutput, setTaskOutput] = useState({ + all: false, + video: false, + audio: false, + image: false, + text: false, + }); + + const [links, setLinks] = useState([""]); + + const { + register, + control, + setValue, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: zodResolver(taskSchema), + mode: "all", + }); + + // const handleRadioChange = (event: React.ChangeEvent) => { + // const selectedValue = Number(event.target.value); + // setMainType(selectedValue); + + // setPlatformTypeVisible(selectedValue === 2); + + useEffect(() => { + async function fetchPoldaPolres() { + setIsLoading(true); + try { + const response = await getUserLevelForAssignments(); + setListDest(response?.data?.data.list); + console.log("polda", response?.data?.data?.list); + const initialExpandedState = response?.data?.data.list.reduce( + (acc: any, polda: any) => { + acc[polda.id] = false; + return acc; + }, + {} + ); + setExpandedPolda(initialExpandedState); + console.log("polres", initialExpandedState); + } catch (error) { + console.error("Error fetching Polda/Polres data:", error); + } finally { + setIsLoading(false); + } + } + fetchPoldaPolres(); + }, []); + + // }; + const handleCheckboxChange = (levelId: number) => { + setCheckedLevels((prev) => { + const updatedLevels = new Set(prev); + if (updatedLevels.has(levelId)) { + updatedLevels.delete(levelId); + } else { + updatedLevels.add(levelId); + } + return updatedLevels; + }); + }; + + const handlePoldaPolresChange = () => { + return Array.from(checkedLevels).join(","); // Mengonversi Set ke string + }; + + const handleUnitChange = ( + key: keyof typeof unitSelection, + value: boolean + ) => { + if (key === "semua") { + const newState = { + semua: value, + mabes: value, + polda: value, + polres: value, + satker: value, + }; + setUnitSelection(newState); + } else { + const updatedSelection = { + ...unitSelection, + [key]: value, + }; + + const allChecked = ["mabes", "polda", "polres", "satker"].every( + (k) => updatedSelection[k as keyof typeof unitSelection] + ); + + updatedSelection.semua = allChecked; + + setUnitSelection(updatedSelection); + } + }; + + const handleExpertiseOutputChange = ( + key: keyof typeof expertise, + value: boolean + ) => { + if (key === "semua") { + const newState = { + semua: value, + komunikasi: value, + hukum: value, + bahasa: value, + ekonomi: value, + politik: value, + sosiologi: value, + ilmuadministrasipemerintah: value, + ti: value, + }; + setExpertiseOutput(newState); + } else { + const updated = { + ...expertise, + [key]: value, + }; + + const allChecked = ["video", "audio", "image", "text"].every( + (k) => updated[k as keyof typeof expertise] + ); + + updated.semua = allChecked; + setExpertiseOutput(updated); + } + }; + + const handleExpertOutputChange = ( + key: keyof typeof expert, + value: boolean + ) => { + if (key === "semua") { + const newState = { + semua: value, + }; + setExpertOutput(newState); + } else { + const updated = { + ...expert, + [key]: value, + }; + + const allChecked = ["video", "audio", "image", "text"].every( + (k) => updated[k as keyof typeof expert] + ); + + updated.semua = allChecked; + setExpertOutput(updated); + } + }; + + useEffect(() => { + async function initState() { + if (id) { + const response = await detailMedia(id); + const details = response?.data?.data; + + setDetail(details); + } + } + initState(); + }, [id, refresh]); + + const handleTaskOutputChange = ( + key: keyof typeof taskOutput, + value: boolean + ) => { + if (key === "all") { + const newState = { + all: value, + video: value, + audio: value, + image: value, + text: value, + }; + setTaskOutput(newState); + } else { + const updated = { + ...taskOutput, + [key]: value, + }; + + const allChecked = ["video", "audio", "image", "text"].every( + (k) => updated[k as keyof typeof taskOutput] + ); + + updated.all = allChecked; + setTaskOutput(updated); + } + }; + + const save = async (data: TaskSchema) => { + const fileTypeMapping = { + all: "1", + video: "2", + audio: "4", + image: "3", + text: "5", + }; + + const unitMapping = { + allUnit: "0", + mabes: "1", + polda: "2", + polres: "3", + satker: "4", + }; + const assignmentPurposeString = Object.keys(unitSelection) + .filter((key) => unitSelection[key as keyof typeof unitSelection]) + .map((key) => unitMapping[key as keyof typeof unitMapping]) + .join(","); + + const selectedOutputs = Object.keys(taskOutput) + .filter((key) => taskOutput[key as keyof typeof taskOutput]) + .map((key) => fileTypeMapping[key as keyof typeof fileTypeMapping]) + .join(","); + + const requestData: { + id?: number; + title: string; + assignedToLevel: any; + assignedToUsers: any; + assignmentTypeId: string; + fileTypeOutput: string; + narration: string; + platformType: string | null; + assignmentMainTypeId: any; + assignmentType: string; + assignedToRole: string; + broadcastType: string; + expertCompetencies: string; + attachmentUrl: string[]; + } = { + ...data, + // assignmentType, + // assignmentCategory, + assignedToLevel: handlePoldaPolresChange(), + assignedToUsers: assignmentPurposeString, + assignedToRole: selectedTarget, + assignmentType: taskType, + broadcastType: broadcastType, + assignmentMainTypeId: mainType, + assignmentTypeId: type, + fileTypeOutput: selectedOutputs, + narration: data.naration, + platformType: "", + expertCompetencies: "1,2,3", + title: data.title, + attachmentUrl: links, + }; + + const response = await createTaskTa(requestData); + + console.log("Form Data Submitted:", requestData); + console.log("response", response); + + const id = response?.data?.data.id; + loading(); + 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: 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 + ); + }); + }; + + const onSubmit = (data: TaskSchema) => { + MySwal.fire({ + title: "Simpan Data", + text: "Apakah Anda yakin ingin menyimpan data ini?", + icon: "warning", + showCancelButton: true, + cancelButtonColor: "#d33", + confirmButtonColor: "#3085d6", + confirmButtonText: "Simpan", + }).then((result) => { + if (result.isConfirmed) { + save(data); + } + }); + }; + + const toggleExpand = (poldaId: any) => { + setExpandedPolda((prev: any) => ({ + ...prev, + [poldaId]: !prev[poldaId], + })); + }; + + const onRecordingStart = () => { + setIsRecording(true); + + const countdown = setInterval(() => { + setTimer((prevTimer) => { + if (prevTimer <= 1) { + clearInterval(countdown); + return 0; + } + return prevTimer - 1; + }); + }, 1000); + + setTimeout(() => { + if (isRecording) { + handleStopRecording(); + } + }, 120000); + }; + + const handleStopRecording = () => { + setIsRecording(false); + setTimer(120); // Reset the timer to 2 minutes for the next recording + }; + + const addAudioElement = (blob: Blob) => { + const url = URL.createObjectURL(blob); + const audio = document.createElement("audio"); + audio.src = url; + audio.controls = true; + document.body.appendChild(audio); + + // 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 = (index: number) => { + setAudioFiles((prev) => prev.filter((_, idx) => idx !== index)); + }; + + 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}/assignment-expert/file/upload`, + headers: headers, + retryDelays: [0, 3000, 6000, 12_000, 24_000], + chunkSize: 20_000, + metadata: { + assignmentId: 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); + } 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/task-ta"); + } + } + + const successSubmit = (redirect: string) => { + MySwal.fire({ + title: "Sukses", + text: "Data berhasil disimpan.", + icon: "success", + confirmButtonColor: "#3085d6", + confirmButtonText: "OK", + }).then(() => { + router.push(redirect); + }); + }; + + const handleLinkChange = (index: number, value: string) => { + const updatedLinks = [...links]; + updatedLinks[index] = value; + setLinks(updatedLinks); + }; + + const handleAddRow = () => { + setLinks([...links, ""]); + }; + + // Remove a specific link row + const handleRemoveRow = (index: number) => { + const updatedLinks = links.filter((_: any, i: any) => i !== index); + setLinks(updatedLinks); + }; + + return ( + +
+

{t("form-task")}

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

{errors.title.message}

+ )} +
+ +
+ +
+ {Object.keys(taskOutput).map((key) => ( +
+ + handleTaskOutputChange( + key as keyof typeof taskOutput, + value as boolean + ) + } + /> + +
+ ))} +
+
+
+ +
+ {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) => ( + + ))} +
+ )} +
+ ))} +
+
+
+
+
+
+
+ + setType(value)} // Mengubah nilai state ketika pilihan berubah + className="flex flex-wrap gap-3" + > +
+ + +
+
+ + +
+
+ + +
+
+
+
+ + + + + + + + + +
+
+ + ( + + )} + /> + {errors.naration?.message && ( +

+ {errors.naration.message} +

+ )} +
+
+ +
+
+ + setVideoFiles(files)} + /> +
+
+ + setImageFiles(files)} + /> +
+
+ + setTextFiles(files)} + /> +
+
+ + + + setAudioFiles((prev) => [...prev, ...files]) + } + className="mt-2" + /> +
+ {audioFiles?.map((audio: any, idx: any) => ( +
+

{t("voice-note")}

+ +
+ ))} + {isRecording &&

Recording... {timer} seconds remaining

}{" "} + {/* Display remaining time */} +
+ + {links.map((link, index) => ( +
+ + handleLinkChange(index, e.target.value) + } + /> + {links.length > 1 && ( + + )} +
+ ))} + +
+
+
+
+ + {/* Submit Button */} +
+ +
+ + ) : ( + "" + )} +
+
+ ); +} diff --git a/components/form/task-ta/task-ta-detail-form.tsx b/components/form/task-ta/task-ta-detail-form.tsx index 4a633432..dc69a11e 100644 --- a/components/form/task-ta/task-ta-detail-form.tsx +++ b/components/form/task-ta/task-ta-detail-form.tsx @@ -990,129 +990,7 @@ export default function FormTaskTaDetail() {

{errors.title.message}

)} -
-
- - -
-
- {Object.keys(unitSelection).map((key) => ( -
- - setUnitSelection({ ...unitSelection, [key]: value }) - } - /> - -
- ))} -
-
- - - - - - - - Daftar Wilayah Polda dan Polres - - -
- {listDest.map((polda: any) => ( -
- - {expandedPolda[polda.id] && ( -
- - {polda?.subDestination?.map((polres: any) => ( - - ))} -
- )} -
- ))} -
-
-
-
-
+ {/*
Medsos Mediahub
*/} -
- - setTaskType(String(value))} - className="flex flex-wrap gap-3" - > - - - - - -
@@ -1450,25 +1315,6 @@ export default function FormTaskTaDetail() { )} {isRecording &&

Recording... {timer} seconds remaining

}{" "} {/* Display remaining time */} -
- - {urlInputs.map((url: any, index: any) => ( -
- - // handleLinkChange(index, e.target.value) - // } - placeholder={`Masukkan link berita ${index + 1}`} - /> -
- ))} -
diff --git a/components/form/task-ta/task-ta-edit-form.tsx b/components/form/task-ta/task-ta-edit-form.tsx index f2e4d55b..2d06e489 100644 --- a/components/form/task-ta/task-ta-edit-form.tsx +++ b/components/form/task-ta/task-ta-edit-form.tsx @@ -805,141 +805,6 @@ export default function FormTaskTaEdit() {

{errors.title.message}

)} -
-
- - -
-
- {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) => ( - - ))} -
- )} -
- ))} -
-
-
-
-
-
- - setTaskType(String(value))} - className="flex flex-wrap gap-3" - > - - - - - -
diff --git a/components/form/task-ta/task-ta-new.tsx b/components/form/task-ta/task-ta-upload-form.tsx similarity index 100% rename from components/form/task-ta/task-ta-new.tsx rename to components/form/task-ta/task-ta-upload-form.tsx