From 51da2b52062c5e378581641d9120ca7c8c8f53da Mon Sep 17 00:00:00 2001 From: Sabda Yagra Date: Tue, 30 Sep 2025 18:13:06 +0700 Subject: [PATCH] fix: all import in admin --- .../campaign-list/detail/[id]/page.tsx | 28 +- .../media-tracking/component/table.tsx | 23 - .../media-tracking/detail/component/table.tsx | 2 - .../tb-news/detail/component/table.tsx | 49 - .../shared/contest/components/columns.tsx | 2 +- .../giat-routine/audio/detail/[id]/page.tsx | 1 - .../document/detail/[id]/page.tsx | 1 - .../image/accept-assignment/[id]/page.tsx | 7 +- .../accept-assignment/detail/[id]/page.tsx | 1 - .../image/ask-the-expert/[id]/page.tsx | 2 +- .../giat-routine/image/detail/[id]/page.tsx | 1 - .../image/do-it-yourself/[id]/page.tsx | 3 +- .../giat-routine/video/detail/[id]/page.tsx | 1 - app/page.tsx | 5 - .../ecommarce/product-counter-button.tsx | 41 + components/editor/index.tsx | 182 +-- .../form/communication/internal-edit-form.tsx | 8 +- .../form/shared/accept-assignment-form.tsx | 1011 +++++++++++++++++ components/form/shared/ask-expert-form.tsx | 838 ++++++++++++++ .../form/shared/do-it-yourself-form.tsx | 892 +++++++++++++++ .../form/ticketing/ticketing-update-form.tsx | 12 +- .../landing-page/retracting-sidedar.tsx | 3 - components/landing-page/schedule.tsx | 1 - components/page-title.tsx | 6 +- components/partials/auth/login-form.tsx | 2 +- components/table/audio-table.tsx | 3 +- components/table/document-table.tsx | 3 +- components/table/image-table.tsx | 3 +- .../task-plan/social-media-modal/table.tsx | 28 - components/table/video-table.tsx | 3 +- components/ui/drawer.tsx | 118 -- components/ui/tooltip.tsx | 64 +- providers/theme-provider.tsx | 2 +- tailwind.config.ts | 2 +- 34 files changed, 2929 insertions(+), 419 deletions(-) delete mode 100644 app/page.tsx create mode 100644 components/ecommarce/product-counter-button.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 delete mode 100644 components/ui/drawer.tsx diff --git a/app/[locale]/(admin)/admin/broadcast/campaign-list/detail/[id]/page.tsx b/app/[locale]/(admin)/admin/broadcast/campaign-list/detail/[id]/page.tsx index a7d3057..a7aac87 100644 --- a/app/[locale]/(admin)/admin/broadcast/campaign-list/detail/[id]/page.tsx +++ b/app/[locale]/(admin)/admin/broadcast/campaign-list/detail/[id]/page.tsx @@ -2,7 +2,7 @@ import "react-datepicker/dist/react-datepicker.css"; import Link from "next/link"; -import { useRouter, usePathname } from "next/navigation"; +import { useRouter, usePathname, useParams, useSearchParams } from "next/navigation"; import { useEffect, useState } from "react"; import { useMediaQuery } from "react-responsive"; import { close, loading } from "@/config/swal"; @@ -55,31 +55,23 @@ interface PaginatedResponse { totalElements: number; } -interface PageProps { - params: { - id: string; - locale: string; - }; - searchParams: { - page?: string; - size?: string; - }; -} -export default function BroadcastCampaignDetail({ - params, - searchParams, -}: PageProps) { + +export default function BroadcastCampaignDetail() { const router = useRouter(); const pathname = usePathname(); - const { id, locale } = params; + const params = useParams(); + const id = params.id; + const locale = params.locale; const [getData, setGetData] = useState([]); const [totalPage, setTotalPage] = useState(0); const [totalData, setTotalData] = useState(0); const [activeTab, setActiveTab] = useState< "sent" | "schedule" | "account-list" >("sent"); - const { page, size } = searchParams; + const searchParams = useSearchParams(); + const page = searchParams.get("page") + const size = searchParams.get("size") const [calenderState, setCalenderState] = useState(false); const [typeFilter, setTypeFilter] = useState("email"); const [dateRange, setDateRange] = useState<[Date, Date]>([ @@ -124,7 +116,7 @@ export default function BroadcastCampaignDetail({ startDateString || "", endDateString || "", typeFilter, - id + id as string ); close(); diff --git a/app/[locale]/(admin)/admin/curator/media-tracking/component/table.tsx b/app/[locale]/(admin)/admin/curator/media-tracking/component/table.tsx index 18c6aa5..f946dbd 100644 --- a/app/[locale]/(admin)/admin/curator/media-tracking/component/table.tsx +++ b/app/[locale]/(admin)/admin/curator/media-tracking/component/table.tsx @@ -24,29 +24,6 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; -import { - ChevronLeft, - ChevronRight, - Eye, - MoreVertical, - Search, - SquarePen, - Trash2, - TrendingDown, - TrendingUp, -} from "lucide-react"; -import { cn } from "@/lib/utils"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; -import { Input } from "@/components/ui/input"; -import { InputGroup, InputGroupText } from "@/components/ui/input-group"; -import { paginationBlog } from "@/service/blog/blog"; -import { ticketingPagination } from "@/service/ticketing/ticketing"; import { Badge } from "@/components/ui/badge"; import { useRouter, useSearchParams } from "next/navigation"; import TablePagination from "@/components/table/table-pagination"; diff --git a/app/[locale]/(admin)/admin/media-tracking/detail/component/table.tsx b/app/[locale]/(admin)/admin/media-tracking/detail/component/table.tsx index b14b93a..e877562 100644 --- a/app/[locale]/(admin)/admin/media-tracking/detail/component/table.tsx +++ b/app/[locale]/(admin)/admin/media-tracking/detail/component/table.tsx @@ -14,8 +14,6 @@ import { getSortedRowModel, useReactTable, } from "@tanstack/react-table"; -import { Button } from "@/components/ui/button"; - import { Table, TableBody, diff --git a/app/[locale]/(admin)/admin/media-tracking/tb-news/detail/component/table.tsx b/app/[locale]/(admin)/admin/media-tracking/tb-news/detail/component/table.tsx index e7c6522..ac7f023 100644 --- a/app/[locale]/(admin)/admin/media-tracking/tb-news/detail/component/table.tsx +++ b/app/[locale]/(admin)/admin/media-tracking/tb-news/detail/component/table.tsx @@ -14,8 +14,6 @@ import { getSortedRowModel, useReactTable, } from "@tanstack/react-table"; -import { Button } from "@/components/ui/button"; - import { Table, TableBody, @@ -24,57 +22,10 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; -import { - ChevronLeft, - ChevronRight, - Eye, - MoreVertical, - Search, - SquarePen, - Trash2, - TrendingDown, - TrendingUp, - UserIcon, -} from "lucide-react"; -import { cn } from "@/lib/utils"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuRadioGroup, - DropdownMenuRadioItem, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; -import { Input } from "@/components/ui/input"; -import { InputGroup, InputGroupText } from "@/components/ui/input-group"; -import { paginationBlog } from "@/service/blog/blog"; -import { ticketingPagination } from "@/service/ticketing/ticketing"; -import { Badge } from "@/components/ui/badge"; import { useRouter, useSearchParams } from "next/navigation"; import TablePagination from "@/components/table/table-pagination"; import columns from "./column"; -import { getPlanningPagination } from "@/service/agenda-setting/agenda-setting"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover"; -import { listDataMedia } from "@/service/broadcast/broadcast"; import { listEnableCategory } from "@/service/content/content"; -import { Checkbox } from "@/components/ui/checkbox"; -import { close, loading } from "@/config/swal"; -import { Link } from "@/i18n/routing"; -import { Label } from "@/components/ui/label"; -import { - Dialog, - DialogContent, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; -import { link } from "fs"; const NewsDetailTable = () => { const router = useRouter(); diff --git a/app/[locale]/(admin)/admin/shared/contest/components/columns.tsx b/app/[locale]/(admin)/admin/shared/contest/components/columns.tsx index 405ce15..31762a0 100644 --- a/app/[locale]/(admin)/admin/shared/contest/components/columns.tsx +++ b/app/[locale]/(admin)/admin/shared/contest/components/columns.tsx @@ -17,8 +17,8 @@ import withReactContent from "sweetalert2-react-content"; import Swal from "sweetalert2"; import { error } from "@/lib/swal"; import { deleteMedia } from "@/service/content/content"; -import { deleteContest, publishContest } from "@/service/contest/contest"; import { useTranslations } from "next-intl"; +import { deleteContest, publishContest } from "@/service/service/contest/contest"; const useTableColumns = () => { const t = useTranslations("Table"); // Panggil di dalam hook diff --git a/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/audio/detail/[id]/page.tsx b/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/audio/detail/[id]/page.tsx index be92bda..d85ec45 100644 --- a/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/audio/detail/[id]/page.tsx +++ b/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/audio/detail/[id]/page.tsx @@ -19,7 +19,6 @@ import { } from "@/components/ui/select"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import Cookies from "js-cookie"; -import { postBlog } from "@/service/blog/blog"; import { Textarea } from "@/components/ui/textarea"; import { DotSquare, diff --git a/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/document/detail/[id]/page.tsx b/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/document/detail/[id]/page.tsx index 871594a..5337502 100644 --- a/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/document/detail/[id]/page.tsx +++ b/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/document/detail/[id]/page.tsx @@ -19,7 +19,6 @@ import { } from "@/components/ui/select"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import Cookies from "js-cookie"; -import { postBlog } from "@/service/blog/blog"; import { Textarea } from "@/components/ui/textarea"; import { DotSquare, diff --git a/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/accept-assignment/[id]/page.tsx b/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/accept-assignment/[id]/page.tsx index a9b8c70..be80ce7 100644 --- a/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/accept-assignment/[id]/page.tsx +++ b/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/accept-assignment/[id]/page.tsx @@ -1,9 +1,6 @@ -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"; +import SiteBreadcrumb from "@/components/site-breadcrumb"; + const AcceptAssignmentPage = () => { return ( diff --git a/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/accept-assignment/detail/[id]/page.tsx b/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/accept-assignment/detail/[id]/page.tsx index 16439cc..e9ac0a3 100644 --- a/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/accept-assignment/detail/[id]/page.tsx +++ b/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/accept-assignment/detail/[id]/page.tsx @@ -19,7 +19,6 @@ import { } from "@/components/ui/select"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import Cookies from "js-cookie"; -import { postBlog } from "@/service/blog/blog"; import { Textarea } from "@/components/ui/textarea"; import { DotSquare, diff --git a/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/ask-the-expert/[id]/page.tsx b/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/ask-the-expert/[id]/page.tsx index 9e11786..2efdd04 100644 --- a/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/ask-the-expert/[id]/page.tsx +++ b/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/ask-the-expert/[id]/page.tsx @@ -1,8 +1,8 @@ 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 (
diff --git a/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/detail/[id]/page.tsx b/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/detail/[id]/page.tsx index 53e3f21..b001ff8 100644 --- a/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/detail/[id]/page.tsx +++ b/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/detail/[id]/page.tsx @@ -19,7 +19,6 @@ import { } from "@/components/ui/select"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import Cookies from "js-cookie"; -import { postBlog } from "@/service/blog/blog"; import { Textarea } from "@/components/ui/textarea"; import { DotSquare, diff --git a/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/do-it-yourself/[id]/page.tsx b/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/do-it-yourself/[id]/page.tsx index 20c56cb..0bde542 100644 --- a/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/do-it-yourself/[id]/page.tsx +++ b/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/image/do-it-yourself/[id]/page.tsx @@ -1,9 +1,8 @@ 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 (
diff --git a/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/video/detail/[id]/page.tsx b/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/video/detail/[id]/page.tsx index a04a4f7..3e16a26 100644 --- a/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/video/detail/[id]/page.tsx +++ b/app/[locale]/(admin)/admin/shared/curated-content/giat-routine/video/detail/[id]/page.tsx @@ -19,7 +19,6 @@ import { } from "@/components/ui/select"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import Cookies from "js-cookie"; -import { postBlog } from "@/service/blog/blog"; import { Textarea } from "@/components/ui/textarea"; import { DotSquare, diff --git a/app/page.tsx b/app/page.tsx deleted file mode 100644 index 7bf6945..0000000 --- a/app/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import AutoRedirect from '@/components/auto-redirect'; - -export default function RootPage() { - return ; -} diff --git a/components/ecommarce/product-counter-button.tsx b/components/ecommarce/product-counter-button.tsx new file mode 100644 index 0000000..d363229 --- /dev/null +++ b/components/ecommarce/product-counter-button.tsx @@ -0,0 +1,41 @@ +'use client' +import React from 'react' +import { Icon } from "@/components/ui/icon" +import { cn } from "@/lib/utils" + +const ProductCounterButton = ({ className }: { className?: string }) => { + const [count, setCount] = React.useState(1) + const handleIncreaseQuantity = () => { + + if (count < 10) { + setCount(count + 1) + } + + } + const handleDecreaseQuantity = () => { + + if (count > 0) { + setCount(count - 1) + } + } + return ( +
+ + + {count} + + +
+ ) +} + +export default ProductCounterButton \ No newline at end of file diff --git a/components/editor/index.tsx b/components/editor/index.tsx index d98ed47..8ba0630 100644 --- a/components/editor/index.tsx +++ b/components/editor/index.tsx @@ -1,101 +1,101 @@ -'use client'; +// 'use client'; -import { useState, useEffect } from 'react'; -import dynamic from 'next/dynamic'; -import SimpleEditor from './simple-editor'; +// import { useState, useEffect } from 'react'; +// import dynamic from 'next/dynamic'; +// import SimpleEditor from './simple-editor'; -// Dynamic import untuk CKEditor dengan error handling -const CKEditorWrapper = dynamic( - () => import('./ckeditor-wrapper'), - { - ssr: false, - loading: () => ( -
-
Loading editor...
-
- ) - } -); +// // Dynamic import untuk CKEditor dengan error handling +// const CKEditorWrapper = dynamic( +// () => import('./ckeditor-wrapper'), +// { +// ssr: false, +// loading: () => ( +//
+//
Loading editor...
+//
+// ) +// } +// ); -interface EditorProps { - data?: string; - onChange?: (data: string) => void; - onReady?: (editor: any) => void; - onBlur?: (event: any, editor: any) => void; - onFocus?: (event: any, editor: any) => void; - config?: any; - disabled?: boolean; - className?: string; - fallbackToSimple?: boolean; -} +// interface EditorProps { +// data?: string; +// onChange?: (data: string) => void; +// onReady?: (editor: any) => void; +// onBlur?: (event: any, editor: any) => void; +// onFocus?: (event: any, editor: any) => void; +// config?: any; +// disabled?: boolean; +// className?: string; +// fallbackToSimple?: boolean; +// } -export default function Editor({ - data = '', - onChange, - onReady, - onBlur, - onFocus, - config = {}, - disabled = false, - className = '', - fallbackToSimple = true -}: EditorProps) { - const [useSimpleEditor, setUseSimpleEditor] = useState(false); - const [isMounted, setIsMounted] = useState(false); +// export default function Editor({ +// data = '', +// onChange, +// onReady, +// onBlur, +// onFocus, +// config = {}, +// disabled = false, +// className = '', +// fallbackToSimple = true +// }: EditorProps) { +// const [useSimpleEditor, setUseSimpleEditor] = useState(false); +// const [isMounted, setIsMounted] = useState(false); - useEffect(() => { - setIsMounted(true); - }, []); +// useEffect(() => { +// setIsMounted(true); +// }, []); - useEffect(() => { - // Check if CKEditor is available - const checkCKEditor = async () => { - try { - await import('@ckeditor/ckeditor5-react'); - await import('ckeditor5'); - setUseSimpleEditor(false); - } catch (error) { - console.warn('CKEditor not available, falling back to simple editor:', error); - if (fallbackToSimple) { - setUseSimpleEditor(true); - } - } - }; +// useEffect(() => { +// // Check if CKEditor is available +// const checkCKEditor = async () => { +// try { +// await import('@ckeditor/ckeditor5-react'); +// await import('ckeditor5'); +// setUseSimpleEditor(false); +// } catch (error) { +// console.warn('CKEditor not available, falling back to simple editor:', error); +// if (fallbackToSimple) { +// setUseSimpleEditor(true); +// } +// } +// }; - if (isMounted) { - checkCKEditor(); - } - }, [isMounted, fallbackToSimple]); +// if (isMounted) { +// checkCKEditor(); +// } +// }, [isMounted, fallbackToSimple]); - if (!isMounted) { - return ( -
-
Loading editor...
-
- ); - } +// if (!isMounted) { +// return ( +//
+//
Loading editor...
+//
+// ); +// } - if (useSimpleEditor) { - return ( - - ); - } +// if (useSimpleEditor) { +// return ( +// +// ); +// } - return ( - - ); -} +// return ( +// +// ); +// } diff --git a/components/form/communication/internal-edit-form.tsx b/components/form/communication/internal-edit-form.tsx index 34fc4dc..27b4580 100644 --- a/components/form/communication/internal-edit-form.tsx +++ b/components/form/communication/internal-edit-form.tsx @@ -18,16 +18,10 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { Avatar, AvatarImage } from "@/components/ui/avatar"; -import { - getTicketingInternalDetail, - getTicketingInternalDiscussion, - saveTicketing, - saveTicketInternalReply, -} from "@/service/communication/communication"; import { Textarea } from "@/components/ui/textarea"; import { htmlToString } from "@/utils/globals"; import { Icon } from "@iconify/react/dist/iconify.js"; +import { getTicketingInternalDetail, getTicketingInternalDiscussion, saveTicketing, saveTicketInternalReply } from "@/service/service/communication/communication"; const taskSchema = z.object({ // description: z.string().min(2, { diff --git a/components/form/shared/accept-assignment-form.tsx b/components/form/shared/accept-assignment-form.tsx new file mode 100644 index 0000000..60bb154 --- /dev/null +++ b/components/form/shared/accept-assignment-form.tsx @@ -0,0 +1,1011 @@ +"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 { + 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(); + + 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", { defaultValue: "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", { defaultValue: "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 0000000..3ee8f81 --- /dev/null +++ b/components/form/shared/ask-expert-form.tsx @@ -0,0 +1,838 @@ +"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 { + createTask, + createTaskTa, + getTask, + getUserLevelForAssignments, + getUserLevelForExpert, +} 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"; +import { getListCompetencies } from "@/service/management-user/management-user"; + +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 [userCompetencies, setUserCompetencies] = useState([]); + const [selectedCompetencies, setSelectedCompetencies] = useState>( + new Set() + ); + const [listExpert, setListExpert] = useState([]); + + 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(); + + const [links, setLinks] = useState([""]); + + const { + register, + control, + setValue, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: zodResolver(taskSchema), + mode: "all", + }); + + useEffect(() => { + getDataAdditional(); + }, []); + + async function getDataAdditional() { + const resCompetencies = await getListCompetencies(); + console.log("competency", resCompetencies); + setUserCompetencies(resCompetencies?.data?.data); + } + + useEffect(() => { + async function fetchListExpert() { + setIsLoading(true); + try { + const response = await getUserLevelForExpert(id); + setListExpert(response?.data?.data); + console.log("tenaga ahli", response?.data?.data); + } catch (error) { + console.error("Error fetching Polda/Polres data:", error); + } finally { + setIsLoading(false); + } + } + fetchListExpert(); + }, []); + + useEffect(() => { + async function initState() { + if (id) { + const response = await detailMedia(id); + const details = response?.data?.data; + + setDetail(details); + + if (details?.assignedToLevel) { + const levels: Set = new Set( + details.assignedToLevel.split(",").map((x: any) => Number(x)) + ); + setCheckedLevels(levels); + } + + if (details?.assignedToUsers) { + const userIds = details.assignedToUsers.split(",").map(Number); + setCheckedLevels(new Set(userIds)); + } + + if (details?.expertCompetencies) { + const compIds = details.expertCompetencies.split(",").map(Number); + setSelectedCompetencies(new Set(compIds)); + } + } + } + initState(); + }, [id, refresh]); + + useEffect(() => { + const fetchExpertsForCompetencies = async () => { + const allExperts: any[] = []; + + for (const compId of Array.from(selectedCompetencies)) { + const response = await getUserLevelForExpert(compId); + const experts = response?.data?.data || []; + allExperts.push(...experts); + } + + const uniqueExperts = Array.from( + new Map(allExperts.map((e) => [e.id, e])).values() + ); + + setListExpert(uniqueExperts); + }; + + if (selectedCompetencies.size > 0) { + fetchExpertsForCompetencies(); + } else { + setListExpert([]); + } + }, [selectedCompetencies]); + + // }; + 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 handleExpertChange = () => { + return Array.from(checkedLevels).join(","); + }; + + const handleCompetencyChange = async (competencyId: number) => { + setSelectedCompetencies((prev) => { + const updated = new Set(prev); + if (updated.has(competencyId)) { + updated.delete(competencyId); + } else { + updated.add(competencyId); + } + return updated; + }); + }; + + const save = async (data: TaskSchema) => { + const requestData: { + id?: number; + title: string; + assignedToUsers: any; + assignmentTypeId: string; + narration: string; + assignmentType: string; + expertCompetencies: string; + attachmentUrl: string[]; + } = { + ...data, + assignedToUsers: handleExpertChange(), + assignmentType: taskType, + assignmentTypeId: type, + narration: data.naration, + expertCompetencies: Array.from(selectedCompetencies).join(","), + 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-ta", { defaultValue: "Form Task Ta" })}

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

{errors.title.message}

+ )} +
+
+ + + + + + + + + +
+
+ +
+ {userCompetencies?.map((item: any) => ( +
+ handleCompetencyChange(item.id)} + /> + +
+ ))} +
+
+
+ +
+ + + + + + + Daftar Tenaga Ahli + +
+ {listExpert?.map((expert: any) => ( +
+ +
+ ))} +
+
+
+
+ {checkedLevels.size > 0 && ( +
+ +
+ {Array.from(checkedLevels).map((expertId) => { + const expert = listExpert?.find( + (exp: any) => exp.id === expertId + ); + return expert ? ( +
+ {expert.fullname} + +
+ ) : null; + })} +
+
+ )} +
+ +
+ + ( + + )} + /> + {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", { defaultValue: "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 0000000..834c560 --- /dev/null +++ b/components/form/shared/do-it-yourself-form.tsx @@ -0,0 +1,892 @@ +"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 { + 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(); + + 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; + assignedToUsers: any; + assignmentTypeId: string; + fileTypeOutput: string; + narration: string; + assignmentType: string; + expertCompetencies: string; + attachmentUrl: string[]; + } = { + ...data, + // assignmentType, + // assignmentCategory, + assignedToUsers: assignmentPurposeString, + assignmentType: taskType, + assignmentTypeId: type, + fileTypeOutput: selectedOutputs, + narration: data.naration, + 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-ta-do", { defaultValue: "Form Task Ta Do" })}

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

{errors.title.message}

+ )} +
+ +
+ +
+ {Object.keys(taskOutput).map((key) => ( +
+ + handleTaskOutputChange( + key as keyof typeof taskOutput, + value as boolean + ) + } + /> + +
+ ))} +
+
+ +
+ + 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", { defaultValue: "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/ticketing/ticketing-update-form.tsx b/components/form/ticketing/ticketing-update-form.tsx index 3ae98dd..8e6408b 100644 --- a/components/form/ticketing/ticketing-update-form.tsx +++ b/components/form/ticketing/ticketing-update-form.tsx @@ -18,17 +18,6 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { Avatar, AvatarImage } from "@/components/ui/avatar"; -import { - deleteTicket, - getTicketingDetail, - getTicketingInternalDetail, - getTicketingInternalDiscussion, - getTicketingReply, - saveTicketing, - saveTicketInternalReply, - saveTicketReply, -} from "@/service/communication/communication"; import { Icon } from "@iconify/react/dist/iconify.js"; import { list } from "postcss"; import { htmlToString } from "@/utils/globals"; @@ -44,6 +33,7 @@ import { } from "@/components/ui/popover"; import { ChevronDownIcon } from "lucide-react"; import { Description } from "@radix-ui/react-toast"; +import { deleteTicket, getTicketingDetail, getTicketingReply, saveTicketing, saveTicketReply } from "@/service/service/communication/communication"; const taskSchema = z.object({ title: z.string().min(1, { message: "Judul diperlukan" }), diff --git a/components/landing-page/retracting-sidedar.tsx b/components/landing-page/retracting-sidedar.tsx index 3943e4b..05167c4 100644 --- a/components/landing-page/retracting-sidedar.tsx +++ b/components/landing-page/retracting-sidedar.tsx @@ -7,13 +7,11 @@ import React, { useEffect, ReactNode, } from "react"; - import Image from "next/image"; import { Icon } from "@iconify/react"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { motion, AnimatePresence } from "framer-motion"; -import { useTheme } from "../layout/theme-context"; import Option from "./option"; import Cookies from "js-cookie"; @@ -217,7 +215,6 @@ const SidebarContent = ({ pathname: string; updateSidebarData: (newData: boolean) => void; }) => { - const { theme, toggleTheme } = useTheme(); const [expanded, setExpanded] = useState([]); const toggleExpand = (title: string) => { diff --git a/components/landing-page/schedule.tsx b/components/landing-page/schedule.tsx index 10c887a..3cacf68 100644 --- a/components/landing-page/schedule.tsx +++ b/components/landing-page/schedule.tsx @@ -287,7 +287,6 @@ export default function Schedule() {
{ if (otpValue.length === 6) { loading(); - const response = await verifyOTPByUsername(values.username, otpValue); + const response = await verifyOTPByUsername(values.username); if (response?.error) { error(response.message); diff --git a/components/table/audio-table.tsx b/components/table/audio-table.tsx index 18d4073..37877d7 100644 --- a/components/table/audio-table.tsx +++ b/components/table/audio-table.tsx @@ -14,7 +14,6 @@ import { id } from "date-fns/locale"; import { Badge } from "@/components/ui/badge"; import { listDataAudio, listDataImage } from "@/service/content"; import { Button } from "../ui/button"; -import page from "@/app/page"; import { getCookiesDecrypt } from "@/lib/utils"; import Swal from "sweetalert2"; import withReactContent from "sweetalert2-react-content"; @@ -107,7 +106,7 @@ export default function AudioTable() { case 3: return Trigger Error; default: - return Unknown; + return Unknown; } }; diff --git a/components/table/document-table.tsx b/components/table/document-table.tsx index af67574..a910f85 100644 --- a/components/table/document-table.tsx +++ b/components/table/document-table.tsx @@ -14,7 +14,6 @@ import { id } from "date-fns/locale"; import { Badge } from "@/components/ui/badge"; import { listDataImage, listDataTeks } from "@/service/content"; import { Button } from "../ui/button"; -import page from "@/app/page"; import { getCookiesDecrypt } from "@/lib/utils"; import Swal from "sweetalert2"; import withReactContent from "sweetalert2-react-content"; @@ -107,7 +106,7 @@ export default function DocumentTable() { case 3: return Trigger Error; default: - return Unknown; + return Unknown; } }; diff --git a/components/table/image-table.tsx b/components/table/image-table.tsx index ecdc48e..259c582 100644 --- a/components/table/image-table.tsx +++ b/components/table/image-table.tsx @@ -14,7 +14,6 @@ import { id } from "date-fns/locale"; import { Badge } from "@/components/ui/badge"; import { listDataImage } from "@/service/content"; import { Button } from "../ui/button"; -import page from "@/app/page"; import { getCookiesDecrypt } from "@/lib/utils"; import Swal from "sweetalert2"; import withReactContent from "sweetalert2-react-content"; @@ -123,7 +122,7 @@ export default function ImageTable() { case 3: return Trigger Error; default: - return Unknown; + return Unknown; } }; diff --git a/components/table/task-plan/social-media-modal/table.tsx b/components/table/task-plan/social-media-modal/table.tsx index d6d1374..83e7009 100644 --- a/components/table/task-plan/social-media-modal/table.tsx +++ b/components/table/task-plan/social-media-modal/table.tsx @@ -2,7 +2,6 @@ import * as React from "react"; import { - ColumnDef, ColumnFiltersState, PaginationState, SortingState, @@ -14,8 +13,6 @@ import { getSortedRowModel, useReactTable, } from "@tanstack/react-table"; -import { Button } from "@/components/ui/button"; - import { Table, TableBody, @@ -24,36 +21,11 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; -import { - ChevronLeft, - ChevronRight, - Eye, - MoreVertical, - Search, - SquarePen, - Trash2, - TrendingDown, - TrendingUp, -} from "lucide-react"; -import { cn } from "@/lib/utils"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; -import { Input } from "@/components/ui/input"; -import { InputGroup, InputGroupText } from "@/components/ui/input-group"; -import { paginationBlog } from "@/service/blog/blog"; -import { ticketingPagination } from "@/service/ticketing/ticketing"; -import { Badge } from "@/components/ui/badge"; import { useRouter, useSearchParams } from "next/navigation"; import TablePagination from "@/components/table/table-pagination"; import columns from "./column"; import { getPlanningDailyMedsosByPlatform, - getPlanningPagination, } from "@/service/agenda-setting/agenda-setting"; const TaskPlanningSocialMediaTable = (props: { diff --git a/components/table/video-table.tsx b/components/table/video-table.tsx index 1f661ee..05e2e5f 100644 --- a/components/table/video-table.tsx +++ b/components/table/video-table.tsx @@ -14,7 +14,6 @@ import { id } from "date-fns/locale"; import { Badge } from "@/components/ui/badge"; import { listDataImage, listDataVideo } from "@/service/content"; import { Button } from "../ui/button"; -import page from "@/app/page"; import { getCookiesDecrypt } from "@/lib/utils"; import Swal from "sweetalert2"; import withReactContent from "sweetalert2-react-content"; @@ -106,7 +105,7 @@ export default function VideoTable() { case 3: return Trigger Error; default: - return Unknown; + return Unknown; } }; diff --git a/components/ui/drawer.tsx b/components/ui/drawer.tsx deleted file mode 100644 index 6a0ef53..0000000 --- a/components/ui/drawer.tsx +++ /dev/null @@ -1,118 +0,0 @@ -"use client" - -import * as React from "react" -import { Drawer as DrawerPrimitive } from "vaul" - -import { cn } from "@/lib/utils" - -const Drawer = ({ - shouldScaleBackground = true, - ...props -}: React.ComponentProps) => ( - -) -Drawer.displayName = "Drawer" - -const DrawerTrigger = DrawerPrimitive.Trigger - -const DrawerPortal = DrawerPrimitive.Portal - -const DrawerClose = DrawerPrimitive.Close - -const DrawerOverlay = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName - -const DrawerContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - -
- {children} - - -)) -DrawerContent.displayName = "DrawerContent" - -const DrawerHeader = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-) -DrawerHeader.displayName = "DrawerHeader" - -const DrawerFooter = ({ - className, - ...props -}: React.HTMLAttributes) => ( -
-) -DrawerFooter.displayName = "DrawerFooter" - -const DrawerTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DrawerTitle.displayName = DrawerPrimitive.Title.displayName - -const DrawerDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DrawerDescription.displayName = DrawerPrimitive.Description.displayName - -export { - Drawer, - DrawerPortal, - DrawerOverlay, - DrawerTrigger, - DrawerClose, - DrawerContent, - DrawerHeader, - DrawerFooter, - DrawerTitle, - DrawerDescription, -} diff --git a/components/ui/tooltip.tsx b/components/ui/tooltip.tsx index f50e930..e22424f 100644 --- a/components/ui/tooltip.tsx +++ b/components/ui/tooltip.tsx @@ -8,7 +8,7 @@ import { cva, type VariantProps } from "class-variance-authority"; import { color } from "@/lib/type"; const tooltipVariants = cva( - "z-50 overflow-hidden rounded-md px-3 py-1.5 text-sm shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ", + "z-50 overflow-hidden rounded-md px-3 py-1.5 text-sm shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", { variants: { color: { @@ -27,46 +27,42 @@ const tooltipVariants = cva( } ); -interface ToolTipProps extends React.ComponentPropsWithoutRef, VariantProps { - color?: color +interface ToolTipProps + extends React.ComponentPropsWithoutRef, + VariantProps { + color?: color; } -const Tooltip = TooltipPrimitive.Root -const TooltipTrigger = TooltipPrimitive.Trigger + +const Tooltip = TooltipPrimitive.Root; +const TooltipTrigger = TooltipPrimitive.Trigger; const TooltipArrow = TooltipPrimitive.Arrow; -const TooltipProvider = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->( - ({ delayDuration = 0, ...props }, ref) => ( - - ) +const TooltipProvider = ({ + delayDuration = 0, + ...props +}: React.ComponentPropsWithoutRef) => ( + ); -TooltipProvider.displayName = TooltipPrimitive.Provider.displayName; const TooltipContent = React.forwardRef< React.ElementRef, ToolTipProps ->( - ({ className, sideOffset = 4, color, children, ...props }, ref) => ( - - {children} - - ) -); +>(({ className, sideOffset = 4, color, children, ...props }, ref) => ( + + {children} + +)); TooltipContent.displayName = TooltipPrimitive.Content.displayName; -export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider, TooltipArrow } - - - - +export { + Tooltip, + TooltipTrigger, + TooltipContent, + TooltipProvider, + TooltipArrow, +}; diff --git a/providers/theme-provider.tsx b/providers/theme-provider.tsx index e90aa8c..4722908 100644 --- a/providers/theme-provider.tsx +++ b/providers/theme-provider.tsx @@ -2,7 +2,7 @@ import * as React from "react" import { ThemeProvider as NextThemesProvider } from "next-themes" -import { type ThemeProviderProps } from "next-themes/dist/types" +import type { ThemeProviderProps } from "next-themes" export function ThemeProvider({ children, ...props }: ThemeProviderProps) { return {children} diff --git a/tailwind.config.ts b/tailwind.config.ts index 8586acd..91e9909 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,7 +1,7 @@ import type { Config } from "tailwindcss"; const config = { - darkMode: ["class"], + darkMode: ["class", "html"], content: [ "./pages/**/*.{ts,tsx}", "./components/**/*.{js,ts,tsx}",