diff --git a/app/(admin)/admin/advertise/page.tsx b/app/(admin)/admin/advertise/page.tsx index 378b1cd..a6a702c 100644 --- a/app/(admin)/admin/advertise/page.tsx +++ b/app/(admin)/admin/advertise/page.tsx @@ -41,7 +41,7 @@ const createArticleSchema = z.object({ }), }); -export default function BasicPage() { +export default function AdvertisePage() { const { isOpen, onOpen, onOpenChange, onClose } = useDisclosure(); const MySwal = withReactContent(Swal); diff --git a/app/(admin)/admin/comment/page.tsx b/app/(admin)/admin/comment/page.tsx new file mode 100644 index 0000000..64f9474 --- /dev/null +++ b/app/(admin)/admin/comment/page.tsx @@ -0,0 +1,84 @@ +"use client"; +import { AddIcon, CloudUploadIcon, TimesIcon } from "@/components/icons"; +import AdvertiseTable from "@/components/table/advertise/advertise-table"; + +import { + Button, + Card, + Chip, + Input, + Modal, + ModalBody, + ModalContent, + ModalFooter, + ModalHeader, + Switch, + Textarea, + useDisclosure, +} from "@heroui/react"; +import Link from "next/link"; +import { useRouter } from "next/navigation"; +import * as z from "zod"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { Controller, useForm } from "react-hook-form"; +import { Fragment, useEffect, useState } from "react"; +import Swal from "sweetalert2"; +import withReactContent from "sweetalert2-react-content"; + +import { useDropzone } from "react-dropzone"; +import { close, error, loading } from "@/config/swal"; +import Image from "next/image"; +import CommentTable from "@/components/table/comment/comment-table"; + +const createArticleSchema = z.object({ + title: z.string().min(2, { + message: "Judul harus diisi", + }), + url: z.string().min(2, { + message: "Link harus diisi", + }), + description: z.string().min(2, { + message: "Deskripsi harus diisi", + }), +}); + +export default function AdvertisePage() { + const { isOpen, onOpen, onOpenChange, onClose } = useDisclosure(); + const MySwal = withReactContent(Swal); + + const [refresh, setRefresh] = useState(false); + const [isHeader, setIsHeader] = useState(false); + + const [files, setFiles] = useState([]); + + const formOptions = { + resolver: zodResolver(createArticleSchema), + defaultValues: { title: "", description: "", url: "" }, + }; + + const { getRootProps, getInputProps } = useDropzone({ + onDrop: (acceptedFiles) => { + setFiles(acceptedFiles.map((file) => Object.assign(file))); + }, + maxFiles: 1, + accept: { + "image/*": [], + }, + }); + type UserSettingSchema = z.infer; + const { + control, + handleSubmit, + formState: { errors }, + } = useForm(formOptions); + + return ( +
+
+
+ +
+
+
+ ); +} diff --git a/app/(admin)/admin/comment/review/[id]/page.tsx b/app/(admin)/admin/comment/review/[id]/page.tsx new file mode 100644 index 0000000..e69de29 diff --git a/app/(admin)/admin/feedbacks/page.tsx b/app/(admin)/admin/feedbacks/page.tsx new file mode 100644 index 0000000..25004d7 --- /dev/null +++ b/app/(admin)/admin/feedbacks/page.tsx @@ -0,0 +1,13 @@ +import SuggestionsTable from "@/components/table/suggestions/suggestions-table"; + +export default function SuggestionsPage() { + return ( +
+
+
+ +
+
+
+ ); +} diff --git a/app/(admin)/admin/user-level/page.tsx b/app/(admin)/admin/user-level/page.tsx index 41ee9a3..43d615c 100644 --- a/app/(admin)/admin/user-level/page.tsx +++ b/app/(admin)/admin/user-level/page.tsx @@ -7,12 +7,6 @@ import React from "react"; const AdminMasterUserLevel = () => { return ( - //
- // - //
- //
- // - //
diff --git a/components/form/login.tsx b/components/form/login.tsx index ce6ab9f..3a503cd 100644 --- a/components/form/login.tsx +++ b/components/form/login.tsx @@ -11,7 +11,7 @@ import { useRouter } from "next/navigation"; import Swal from "sweetalert2"; import withReactContent from "sweetalert2-react-content"; import { saveActivity } from "@/service/activity-log"; -import Image from "next/image"; +import { Image } from "@heroui/react"; export default function Login() { const router = useRouter(); @@ -165,7 +165,7 @@ export default function Login() { height={480} alt="icon" src="/divhumas.png" - className="w-[120px]" + className="w-[240px] !h-[240px]" />
diff --git a/components/icons/sidebar-icon.tsx b/components/icons/sidebar-icon.tsx index c467a3f..2a8eee9 100644 --- a/components/icons/sidebar-icon.tsx +++ b/components/icons/sidebar-icon.tsx @@ -445,3 +445,43 @@ export const AddvertiseIcon = ({ /> ); +export const SuggestionsIcon = ({ + size, + height = 24, + width = 24, + fill = "currentColor", + ...props +}: IconSvgProps) => ( + + + +); +export const CommentIcon = ({ + size, + height = 24, + width = 24, + fill = "currentColor", + ...props +}: IconSvgProps) => ( + + + +); diff --git a/components/landing/HeaderNews.tsx b/components/landing/HeaderNews.tsx index 66aa053..f9da124 100644 --- a/components/landing/HeaderNews.tsx +++ b/components/landing/HeaderNews.tsx @@ -4,9 +4,9 @@ import { Card, CardFooter, CircularProgress, + Image, ScrollShadow, } from "@heroui/react"; -import Image from "next/image"; import { ChevronLeftIcon, ChevronRightIcon, EyeIcon } from "../icons"; import { Swiper, SwiperSlide, useSwiper } from "swiper/react"; import "swiper/css"; @@ -101,8 +101,6 @@ export default function HeaderNews() { className="border-none rounded-xl shadow-none" > headernews headernews("inside"); - const [modalPlacement, setModalPlacement] = React.useState("auto"); const t = useTranslations("Landing"); useEffect(() => { @@ -138,24 +137,6 @@ export default function PolriApps(props: { } }, [props.opened]); - // useEffect(() => { - // function updateLimitedData() { - // if (window.matchMedia("(max-width: 767px)").matches) { - // setLimitedData(list.slice(0, 2)); - // } else if (window.matchMedia("(min-width: 768px) and (max-width: 1023px)").matches) { - // setLimitedData(list.slice(0, 3)); - // } else { - // setLimitedData(list.slice(0, 5)); - // } - // } - - // updateLimitedData(); - // window.addEventListener('resize', updateLimitedData); - // return () => { - // window.removeEventListener('resize', updateLimitedData); - // }; - // }, [list]); - const changeNameToSlug = (name: string) => { const cleaned = name.replace("Polda ", "").trim().toLowerCase(); const slug = cleaned.replace(/\s+/g, "-"); @@ -164,16 +145,6 @@ export default function PolriApps(props: { return ( <> - {/*
- -
*/} { diff --git a/components/landing/banner-new.tsx b/components/landing/banner-new.tsx index d31f6e9..48b96e9 100644 --- a/components/landing/banner-new.tsx +++ b/components/landing/banner-new.tsx @@ -1,6 +1,5 @@ "use client"; import { useTranslations } from "next-intl"; -import Image from "next/image"; import React, { useEffect, useState } from "react"; import { ChevronDownIcon, @@ -35,6 +34,7 @@ import { PopoverContent, Accordion, AccordionItem, + Image, } from "@heroui/react"; import storedLanguage from "@/store/language-store"; import { ThemeSwitch } from "../theme-switch"; @@ -107,8 +107,6 @@ export default function BannerHumasNew() { > {withImage && ( logo {withImage && ( logo {`humasbanner-${index}`} @@ -221,13 +215,7 @@ export default function BannerHumasNew() { OBYEKTIF - DIPERCAYA - PARTISIPASI

- logo-humas + logo-humas
diff --git a/components/landing/digital-services.tsx b/components/landing/digital-services.tsx index b98cbb5..6e93ebf 100644 --- a/components/landing/digital-services.tsx +++ b/components/landing/digital-services.tsx @@ -1,11 +1,11 @@ "use client"; -import Image from "next/image"; import RegionalNews from "./RegionalNews"; import { useEffect, useState } from "react"; import CategorySatker from "./CategorySatker"; import PolriApps from "./PolriApps"; import Link from "next/link"; import SuggestionsModal from "./suggestions"; +import { Image } from "@heroui/react"; export default function DigitalServices() { const [isPoldaOpen, setIsPoldaOpen] = useState(false); @@ -27,8 +27,6 @@ export default function DigitalServices() { > indonesia @@ -45,8 +43,6 @@ export default function DigitalServices() { > satker @@ -63,8 +59,6 @@ export default function DigitalServices() { > presisi @@ -81,8 +75,6 @@ export default function DigitalServices() { > kritik-saran @@ -99,8 +91,6 @@ export default function DigitalServices() { > survey diff --git a/components/landing/suggestions.tsx b/components/landing/suggestions.tsx index ccf191c..9dc6fa0 100644 --- a/components/landing/suggestions.tsx +++ b/components/landing/suggestions.tsx @@ -25,6 +25,7 @@ import OTPInput from "react-otp-input"; import { otpRequest, otpValidation } from "@/service/master-user"; import Swal from "sweetalert2"; import withReactContent from "sweetalert2-react-content"; +import { createFeedback } from "@/service/feedbacks"; const createArticleSchema = z.object({ email: z.string().min(2, { @@ -71,26 +72,32 @@ export default function SuggestionsModal(props: { const onSubmit = async (values: z.infer) => { if (!needOtp) { loading(); - // const res = await otpRequest(values.email, values?.name); - // if (res?.error) { - // error(res.message); - // return false; - // } + const res = await otpRequest(values.email, values?.name); + if (res?.error) { + error(res.message); + return false; + } close(); setNeedOtp(true); } else { - // const validation = await otpValidation(values.email, otpValue); - // if (validation?.error) { - // error("OTP Tidak Sesuai"); - // return false; - // } + const validation = await otpValidation(values.email, otpValue); + if (validation?.error) { + error("OTP Tidak Sesuai"); + return false; + } const req = { - name: values.name, - description: values.description, - email: values.email, + commentFromName: values.name, + message: values.description, + commentFromEmail: values.email, }; + const res = await createFeedback(req); + if (res?.error) { + error(res?.message); + return false; + } + MySwal.fire({ title: "Berhasil Kirim", text: "", @@ -108,17 +115,6 @@ export default function SuggestionsModal(props: { props.modalStatus(!props.opened); onOpenChange(); } - - // setRefresh(!refresh); - // MySwal.fire({ - // title: "Sukses", - // icon: "success", - // confirmButtonColor: "#3085d6", - // confirmButtonText: "OK", - // }).then((result) => { - // if (result.isConfirmed) { - // } - // }); }; return ( diff --git a/components/layout/sidebar/sidebar-mobile.tsx b/components/layout/sidebar/sidebar-mobile.tsx index 67c25ef..efddecc 100644 --- a/components/layout/sidebar/sidebar-mobile.tsx +++ b/components/layout/sidebar/sidebar-mobile.tsx @@ -15,6 +15,7 @@ import { import { AddvertiseIcon, ArticleIcon, + CommentIcon, DashboardIcon, HomeIcon, InfoCircleIcon, @@ -25,6 +26,7 @@ import { MasterUsersIcon, MinusCircleIcon, StaticPageIcon, + SuggestionsIcon, TableIcon, } from "../../icons/sidebar-icon"; import { ThemeSwitch } from "../../theme-switch"; @@ -233,7 +235,34 @@ const sideBarDummyData = [ statusName: "Active", childModule: null, }, - + { + id: 34, + name: "Kritik & Saran", + moduleId: 652, + moduleName: "Apps", + modulePathUrl: "/admin/feedbacks", + parentId: -1, + icon: , + position: 1, + statusId: 1, + childMenu: [], + statusName: "Active", + childModule: null, + }, + { + id: 35, + name: "Komentar", + moduleId: 652, + moduleName: "Apps", + modulePathUrl: "/admin/comment", + parentId: -1, + icon: , + position: 1, + statusId: 1, + childMenu: [], + statusName: "Active", + childModule: null, + }, // { // id: 4, // name: "E-Magazine", diff --git a/components/layout/sidebar/sidebar.tsx b/components/layout/sidebar/sidebar.tsx index 3d4ab97..d69fc57 100644 --- a/components/layout/sidebar/sidebar.tsx +++ b/components/layout/sidebar/sidebar.tsx @@ -15,6 +15,7 @@ import { import { AddvertiseIcon, ArticleIcon, + CommentIcon, DashboardIcon, HomeIcon, InfoCircleIcon, @@ -25,6 +26,7 @@ import { MasterUsersIcon, MinusCircleIcon, StaticPageIcon, + SuggestionsIcon, TableIcon, } from "../../icons/sidebar-icon"; import { ThemeSwitch } from "../../theme-switch"; @@ -234,6 +236,34 @@ const sideBarDummyData = [ statusName: "Active", childModule: null, }, + { + id: 34, + name: "Kritik & Saran", + moduleId: 652, + moduleName: "Apps", + modulePathUrl: "/admin/feedbacks", + parentId: -1, + icon: , + position: 1, + statusId: 1, + childMenu: [], + statusName: "Active", + childModule: null, + }, + { + id: 35, + name: "Komentar", + moduleId: 652, + moduleName: "Apps", + modulePathUrl: "/admin/comment", + parentId: -1, + icon: , + position: 1, + statusId: 1, + childMenu: [], + statusName: "Active", + childModule: null, + }, // { // id: 4, diff --git a/components/main/dashboard/chart/suggestions-line-chart.tsx b/components/main/dashboard/chart/suggestions-line-chart.tsx new file mode 100644 index 0000000..513ce74 --- /dev/null +++ b/components/main/dashboard/chart/suggestions-line-chart.tsx @@ -0,0 +1,163 @@ +"use client"; +import React, { Component, useEffect, useState } from "react"; +import ReactApexChart from "react-apexcharts"; +import dummyData from "../../../../const/dummy.json"; +import { getStatisticMonthly } from "@/service/article"; + +type WeekData = { + week: number; + days: number[]; + total: number; +}; + +type RemainingDays = { + days: number[]; + total: number; +}; + +function processMonthlyData(count: number[]): { + weeks: WeekData[]; + remaining_days: RemainingDays; +} { + const weeks: WeekData[] = []; + let weekIndex = 1; + + for (let i = 0; i < count.length; i += 7) { + const weekData = count.slice(i, i + 7); + weeks.push({ + week: weekIndex, + days: weekData, + total: weekData.reduce((sum, day) => sum + day, 0), + }); + weekIndex++; + } + + const remainingDays: RemainingDays = { + days: count.length % 7 === 0 ? [] : count.slice(-count.length % 7), + total: count.slice(-count.length % 7).reduce((sum, day) => sum + day, 0), + }; + + return { + weeks, + remaining_days: remainingDays, + }; +} + +const SuggestionsChart = (props: { type: string; date: string }) => { + const { date, type } = props; + const [categories, setCategories] = useState([]); + const [seriesSuggestions, setSeriesSuggestions] = useState([]); + + useEffect(() => { + initFetch(); + }, [date, type]); + + function processYearlyData(data: any) { + const months = [ + "Januari", + "Februari", + "Maret", + "April", + "Mei", + "Juni", + "Juli", + "Agustus", + "September", + "Oktober", + "November", + "Desember", + ]; + const category = []; + const temp = []; + for (let i = 0; i < data.length; i++) { + const total = data[i].suggestions.reduce( + (sum: number, list: number) => sum + list, + 0 + ); + temp.push(total); + category.push(months[data[i].month - 1]); + } + return { categories: category, series: temp }; + } + + const initFetch = async () => { + const splitDate = date.split(" "); + // const res = await getStatisticMonthly(splitDate[1]); + // const data = res?.data?.data; + const data = dummyData.data; + if (type === "monthly") { + const getDatas = data?.filter( + (a: any) => a.year === Number(splitDate[1]) + ); + if (getDatas) { + const temp = processYearlyData(getDatas); + console.log("temp", temp); + setSeriesSuggestions(temp.series); + setCategories(temp.categories); + } else { + setSeriesSuggestions([]); + } + } else { + const getDatas = data?.find( + (a: any) => + a.month == Number(splitDate[0]) && a.year === Number(splitDate[1]) + ); + if (getDatas) { + const temp = processMonthlyData(getDatas?.suggestions); + if (type == "weekly") { + setSeriesSuggestions( + temp.weeks.map((list) => { + return list.total; + }) + ); + } else { + setSeriesSuggestions(getDatas.suggestions); + } + if (type === "weekly") { + const category = []; + for (let i = 1; i <= temp.weeks.length; i++) { + category.push(`Minggu ke-${i}`); + } + setCategories(category); + } + } else { + setSeriesSuggestions([]); + } + } + }; + + return ( +
+
+ +
+
+
+ ); +}; + +export default SuggestionsChart; diff --git a/components/main/dashboard/dashboard-container.tsx b/components/main/dashboard/dashboard-container.tsx index 368d8aa..0264047 100644 --- a/components/main/dashboard/dashboard-container.tsx +++ b/components/main/dashboard/dashboard-container.tsx @@ -13,6 +13,7 @@ import { Calendar, Checkbox, CheckboxGroup, + Image, Pagination, Popover, PopoverContent, @@ -138,7 +139,9 @@ export default function DashboardContainer() { }, [postContentDate]); async function fetchPostCount() { const getDate = (data: any) => { - return `${data.year}-${data.month}-${data.day}`; + return `${data.year}-${data.month < 10 ? `0${data.month}` : data.month}-${ + data.day < 10 ? `0${data.day}` : data.day + }`; }; const res = await getUserLevelDataStat( getDate(postContentDate.startDate), @@ -164,7 +167,6 @@ export default function DashboardContainer() { return date.month + " " + date.year; }; const getMonthYearName = (date: any) => { - console.log("dateee", date); const newDate = new Date(date); const months = [ @@ -249,14 +251,6 @@ export default function DashboardContainer() { Rekapitulasi Post Berita Polda/Polres Pada Website

- {/* setPostContentDate(e)} - inputClassName="z-50 w-full text-xs lg:text-sm bg-transparent border-1 border-gray-200 px-2 py-[6px] rounded-sm lg:rounded-lg h-[30px] lg:h-[40px] text-gray-600 dark:text-gray-300" - /> */} - thumbnail @@ -419,14 +414,14 @@ export default function DashboardContainer() {
diff --git a/components/table/comment/comment-table.tsx b/components/table/comment/comment-table.tsx new file mode 100644 index 0000000..274e014 --- /dev/null +++ b/components/table/comment/comment-table.tsx @@ -0,0 +1,422 @@ +"use client"; +import { + BannerIcon, + CloudUploadIcon, + CreateIconIon, + DeleteIcon, + DotsYIcon, + EyeIconMdi, + SearchIcon, + TimesIcon, +} from "@/components/icons"; +import { close, error, loading, success } from "@/config/swal"; +import { + deleteArticle, + getArticleByCategory, + getListArticle, +} from "@/service/article"; +import { Article } from "@/types/globals"; +import { convertDateFormat } from "@/utils/global"; +import { Button } from "@heroui/button"; +import { + Chip, + ChipProps, + Dropdown, + DropdownItem, + DropdownMenu, + DropdownTrigger, + Input, + Modal, + ModalBody, + ModalContent, + ModalFooter, + ModalHeader, + Pagination, + Select, + SelectItem, + Spinner, + Switch, + Table, + TableBody, + TableCell, + TableColumn, + TableHeader, + TableRow, + Textarea, + useDisclosure, +} from "@heroui/react"; +import Link from "next/link"; +import { Fragment, Key, useCallback, useEffect, useState } from "react"; +import Cookies from "js-cookie"; +import * as z from "zod"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { Controller, useForm } from "react-hook-form"; +import Swal from "sweetalert2"; +import withReactContent from "sweetalert2-react-content"; +import { useDropzone } from "react-dropzone"; +import Image from "next/image"; +import { useRouter } from "next/navigation"; + +const columns = [ + { name: "No", uid: "no" }, + { name: "Nama", uid: "name" }, + { name: "Email", uid: "email" }, + { name: "Komentar", uid: "comment" }, + { name: "Aksi", uid: "actions" }, +]; + +interface Category { + id: number; + title: string; +} + +const createArticleSchema = z.object({ + id: z.string().optional(), + title: z.string().min(2, { + message: "Judul harus diisi", + }), + url: z.string().min(2, { + message: "Link harus diisi", + }), + description: z.string().min(2, { + message: "Deskripsi harus diisi", + }), + file: z.string().optional(), +}); + +export default function CommentTable(props: { triggerRefresh: boolean }) { + const MySwal = withReactContent(Swal); + const { isOpen, onOpen, onOpenChange, onClose } = useDisclosure(); + const router = useRouter(); + const [page, setPage] = useState(1); + const [totalPage, setTotalPage] = useState(1); + const [article, setArticle] = useState([]); + const [showData, setShowData] = useState("10"); + const [search, setSearch] = useState(""); + const [categories, setCategoies] = useState([]); + const [selectedCategories, setSelectedCategories] = useState([]); + const [startDateValue, setStartDateValue] = useState({ + startDate: null, + endDate: null, + }); + + const [isHeader, setIsHeader] = useState(false); + + const [files, setFiles] = useState([]); + + const formOptions = { + resolver: zodResolver(createArticleSchema), + defaultValues: { title: "", description: "", url: "", file: "" }, + }; + + const { getRootProps, getInputProps } = useDropzone({ + onDrop: (acceptedFiles) => { + setFiles(acceptedFiles.map((file) => Object.assign(file))); + }, + maxFiles: 1, + accept: { + "image/*": [], + }, + }); + type UserSettingSchema = z.infer; + const { + control, + handleSubmit, + setValue, + formState: { errors }, + } = useForm(formOptions); + + useEffect(() => { + initState(); + }, [ + page, + showData, + startDateValue, + selectedCategories, + props.triggerRefresh, + ]); + + useEffect(() => { + getCategories(); + }, []); + + async function getCategories() { + const res = await getArticleByCategory(); + const data = res?.data?.data; + setCategoies(data); + } + + const handleRemoveFile = (file: File) => { + const uploadedFiles = files; + const filtered = uploadedFiles.filter((i) => i.name !== file.name); + setFiles([...filtered]); + }; + + async function initState() { + const req = { + limit: showData, + page: page, + search: search, + startDate: + startDateValue.startDate === null ? "" : startDateValue.startDate, + endDate: startDateValue.endDate === null ? "" : startDateValue.endDate, + category: Array.from(selectedCategories).join(","), + sort: "desc", + sortBy: "created_at", + }; + const res = await getListArticle(req); + getTableNumber(parseInt(showData), res.data?.data); + setTotalPage(res?.data?.meta?.totalPage); + } + + const getTableNumber = (limit: number, data: Article[]) => { + if (data) { + const startIndex = limit * (page - 1); + let iterate = 0; + const newData = data.map((value: any) => { + iterate++; + value.no = startIndex + iterate; + return value; + }); + setArticle(newData); + } + }; + + async function doDelete(id: any) { + // loading(); + const resDelete = await deleteArticle(id); + + if (resDelete?.error) { + error(resDelete.message); + return false; + } + close(); + success("Berhasil Hapus"); + initState(); + } + + const handleDelete = (id: any) => { + MySwal.fire({ + title: "Hapus Data", + icon: "warning", + showCancelButton: true, + cancelButtonColor: "#3085d6", + confirmButtonColor: "#d33", + confirmButtonText: "Hapus", + }).then((result) => { + if (result.isConfirmed) { + doDelete(id); + } + }); + }; + + const onSubmit = async (values: z.infer) => { + loading(); + const formData = { + title: values.title, + description: values.description, + isHeader: isHeader, + url: values.url, + }; + console.log("dataas", formData); + close(); + // setRefresh(!refresh); + // MySwal.fire({ + // title: "Sukses", + // icon: "success", + // confirmButtonColor: "#3085d6", + // confirmButtonText: "OK", + // }).then((result) => { + // if (result.isConfirmed) { + // } + // }); + }; + + const openModal = async (id: number) => { + // const res = await getCategoryById(Number(id)); + // const data = res?.data?.data; + // setValue("id", String(data?.id)); + // setValue("title", data?.title); + // setValue("description", data?.description); + // setValue("url", data?.url); + // setValue("file", data?.thumbnailUrl); + + onOpen(); + }; + + const renderCell = useCallback( + (comment: any, columnKey: Key) => { + const cellValue = comment[columnKey as keyof any]; + + switch (columnKey) { + case "url": + return ( + + https://www.google.com/ + + ); + + case "actions": + return ( +
+ + + + + + {/* + + + Detail + + */} + + router.push(`/admin/comment/review${comment.id}`) + } + > + + Review + + + handleDelete(article.id)} + > + + Delete + + + +
+ ); + + default: + return cellValue; + } + }, + [article] + ); + + let typingTimer: NodeJS.Timeout; + const doneTypingInterval = 1500; + + const handleKeyUp = () => { + clearTimeout(typingTimer); + typingTimer = setTimeout(doneTyping, doneTypingInterval); + }; + + const handleKeyDown = () => { + clearTimeout(typingTimer); + }; + + async function doneTyping() { + initState(); + } + + return ( + <> +
+
+
+
+

Pencarian

+ + } + type="text" + onChange={(e) => setSearch(e.target.value)} + onKeyUp={handleKeyUp} + onKeyDown={handleKeyDown} + /> +
+
+

Data

+ +
+
+ + + {(column) => ( + {column.name} + )} + + } + > + {(item: any) => ( + + {(columnKey) => ( + {renderCell(item, columnKey)} + )} + + )} + +
+
+ setPage(page)} + /> +
+
+
+ + ); +} diff --git a/components/table/master/master-user-level/master-user-level-table.tsx b/components/table/master/master-user-level/master-user-level-table.tsx index e729708..e8b844a 100644 --- a/components/table/master/master-user-level/master-user-level-table.tsx +++ b/components/table/master/master-user-level/master-user-level-table.tsx @@ -17,6 +17,7 @@ import { Divider, Chip, ChipProps, + Checkbox, } from "@heroui/react"; import { Button } from "@heroui/button"; import React, { Key, useCallback, useEffect, useMemo, useState } from "react"; @@ -32,6 +33,7 @@ import { import Link from "next/link"; import { getAllUserLevels } from "@/services/user-levels/user-levels-service"; import { close, loading } from "@/config/swal"; +import { stringify } from "querystring"; type UserObject = { id: number; @@ -45,10 +47,12 @@ type UserObject = { export default function MasterUserLevelTable() { const [totalPage, setTotalPage] = useState(1); - const [masterUserLevelTable, setmasterUserLevel] = useState([]); + const [masterUserLevelTable, setMasterUserLevel] = useState([]); const [search, setSearch] = useState(""); - - type TableRow = (typeof masterUserLevelTable)[0]; + const [doSetup, setDoSetup] = useState(false); + const [userLevelAll, setUserLevelAll] = useState([]); + const [selectAllLevel, setSelectAllLevel] = useState(false); + const [selectedLevel, setSelectedLevel] = useState([]); const columns = [ { name: "No", uid: "no" }, @@ -56,8 +60,15 @@ export default function MasterUserLevelTable() { { name: "User Name", uid: "aliasName" }, { name: "Level Number", uid: "levelNumber" }, { name: "Parent", uid: "parentLevelId" }, - // { name: "Province", uid: "province_id" }, - // { name: "Status", uid: "is_active" }, + { name: "Action", uid: "actions" }, + ]; + const columns2 = [ + { name: "No", uid: "no" }, + { name: "Setup", uid: "setup" }, + { name: "Name", uid: "name" }, + { name: "User Name", uid: "aliasName" }, + { name: "Level Number", uid: "levelNumber" }, + { name: "Parent", uid: "parentLevelId" }, { name: "Action", uid: "actions" }, ]; @@ -65,7 +76,11 @@ export default function MasterUserLevelTable() { useEffect(() => { fetchData(); - }, [page]); + }, [page, selectedLevel]); + + useEffect(() => { + fetchDataAll(); + }, []); async function fetchData() { loading(); @@ -80,6 +95,18 @@ export default function MasterUserLevelTable() { await initUserData(10, data); close(); } + async function fetchDataAll() { + loading(); + const request = { + page: 1, + limit: -1, + search: "", + }; + const res = await getAllUserLevels(request); + const data = res?.data?.data; + setUserLevelAll(data); + close(); + } async function initUserData(limit: number, data?: any) { if (data) { @@ -91,24 +118,10 @@ export default function MasterUserLevelTable() { value.no = startIndex + iterate; return value; }); - setmasterUserLevel(newData); + setMasterUserLevel(newData); } } - const findParentName = (data: string | number) => { - let name = "-"; - - for (let i = 9; i < masterUserLevelTable?.length; i++) { - const temp = masterUserLevelTable[i]; - if (temp.id === Number(data)) { - name = temp.name; - break; - } - } - - return name; - }; - let typingTimer: NodeJS.Timeout; const doneTypingInterval = 1500; @@ -125,13 +138,68 @@ export default function MasterUserLevelTable() { fetchData(); } - const renderCell = useCallback( - (masterUserLevel: TableRow, columnKey: Key) => { - const cellValue = masterUserLevel[columnKey as keyof UserObject]; + const doMapping = (status: boolean) => { + setSelectAllLevel(status); + if (status) { + const temp = []; + for (const element of userLevelAll) { + temp.push(String(element.id)); + } + setSelectedLevel(temp); + } else { + setSelectedLevel([]); + } + }; + const handleSelectedLevel = (id: string, checked: boolean) => { + console.log("change", id, checked); + const temp = [...selectedLevel]; + if (checked) { + temp.push(id); + setSelectedLevel(temp); + if (temp.length === userLevelAll.length) { + setSelectAllLevel(true); + } + } else { + const newTemp = temp.filter((a) => a !== id); + setSelectedLevel(newTemp); + if (newTemp.length !== userLevelAll.length) { + setSelectAllLevel(false); + } + } + }; + + const renderCell = useCallback( + (masterUserLevel: UserObject, columnKey: Key) => { + const findParentName = (data: string | number) => { + let name = "-"; + + for (let i = 9; i < userLevelAll?.length; i++) { + const temp = userLevelAll[i]; + if (temp.id === Number(data)) { + name = temp.name; + break; + } + } + + return name; + }; + + const cellValue = masterUserLevel[columnKey as keyof UserObject]; switch (columnKey) { case "parentLevelId": return

{findParentName(cellValue)}

; + + case "setup": + return ( + + handleSelectedLevel(String(masterUserLevel.id), e) + } + /> + ); case "actions": return (
@@ -173,38 +241,43 @@ export default function MasterUserLevelTable() { return cellValue; } }, - [masterUserLevelTable] + [selectedLevel, userLevelAll, masterUserLevelTable] ); return ( <>
- {/* - - */} -
-
-

Pencarian

- - } - type="text" - onChange={(e) => setSearch(e.target.value)} - onKeyUp={handleKeyUp} - onKeyDown={handleKeyDown} - /> +
+
+
+

Pencarian

+ + } + type="text" + onChange={(e) => setSearch(e.target.value)} + onKeyUp={handleKeyUp} + onKeyDown={handleKeyDown} + /> +
+ + {doSetup && ( + + )}
+ - + {(column) => ( - {column.name} + + {column.uid === "setup" ? ( + { + doMapping(e); + }} + > + ) : ( + column.name + )} + )}
-
+
([]); + const [showData, setShowData] = useState("10"); + const [search, setSearch] = useState(""); + const [isReply, setIsReply] = useState(false); + const [replyValue, setReplyValue] = useState(""); + + const formOptions = { + resolver: zodResolver(createArticleSchema), + defaultValues: { + commentFromName: "", + description: "", + commentFromEmail: "", + file: "", + }, + }; + + type UserSettingSchema = z.infer; + const { + control, + handleSubmit, + setValue, + formState: { errors }, + } = useForm(formOptions); + + useEffect(() => { + initState(); + }, [page, showData]); + + async function initState() { + const res = await getFeedbacks({ limit: showData, search: search }); + getTableNumber(parseInt(showData), res.data?.data); + setTotalPage(res?.data?.meta?.totalPage); + } + + const getTableNumber = (limit: number, data: Article[]) => { + if (data) { + const startIndex = limit * (page - 1); + let iterate = 0; + const newData = data.map((value: any) => { + iterate++; + value.no = startIndex + iterate; + return value; + }); + setArticle(newData); + } + }; + + async function doDelete(id: any) { + // loading(); + const resDelete = await deleteArticle(id); + + if (resDelete?.error) { + error(resDelete.message); + return false; + } + close(); + success("Berhasil Hapus"); + initState(); + } + + const handleDelete = (id: any) => { + MySwal.fire({ + title: "Hapus Data", + icon: "warning", + showCancelButton: true, + cancelButtonColor: "#3085d6", + confirmButtonColor: "#d33", + confirmButtonText: "Hapus", + }).then((result) => { + if (result.isConfirmed) { + doDelete(id); + } + }); + }; + + const onSubmit = async (values: z.infer) => { + loading(); + const formData = { + commentFromName: values.commentFromName, + message: values.description, + commentFromEmail: values.commentFromEmail, + }; + console.log("dataas", formData); + close(); + // setRefresh(!refresh); + // MySwal.fire({ + // title: "Sukses", + // icon: "success", + // confirmButtonColor: "#3085d6", + // confirmButtonText: "OK", + // }).then((result) => { + // if (result.isConfirmed) { + // } + // }); + }; + + const openModal = async (id: number) => { + const res = await getFeedbacksById(id); + const data = res?.data?.data; + setValue("id", String(data?.id)); + setValue("commentFromName", data?.commentFromName); + setValue("commentFromEmail", data?.commentFromEmail); + setValue("description", data?.message); + + onOpen(); + }; + + const getMonthYearName = (date: any) => { + const newDate = new Date(date); + + const months = [ + "Januari", + "Februari", + "Maret", + "April", + "Mei", + "Juni", + "Juli", + "Agustus", + "September", + "Oktober", + "November", + "Desember", + ]; + const year = newDate.getFullYear(); + + const month = months[newDate.getMonth()]; + return month + " " + year; + }; + + const renderCell = useCallback( + (suggestion: any, columnKey: Key) => { + const cellValue = suggestion[columnKey as keyof any]; + + switch (columnKey) { + case "commentFromEmail": + return ( + + https://www.google.com/ + + ); + + case "actions": + return ( +
+ + + + + + {/* + + + Detail + + */} + openModal(suggestion.id)} + > + + Edit + + + handleDelete(article.id)} + > + + Delete + + + +
+ ); + + default: + return cellValue; + } + }, + [article] + ); + + let typingTimer: NodeJS.Timeout; + const doneTypingInterval = 1500; + + const handleKeyUp = () => { + clearTimeout(typingTimer); + typingTimer = setTimeout(doneTyping, doneTypingInterval); + }; + + const handleKeyDown = () => { + clearTimeout(typingTimer); + }; + + async function doneTyping() { + initState(); + } + + const [startDateValue, setStartDateValue] = useState( + parseDate(convertDateFormatNoTimeV2(new Date())) + ); + const [typeDate, setTypeDate] = useState("monthly"); + + return ( + <> +
+ + + +
+ + + + + + + + +
+
+
+
+ +
+
+
+
+
+
+

Pencarian

+ + } + type="text" + onChange={(e) => setSearch(e.target.value)} + onKeyUp={handleKeyUp} + onKeyDown={handleKeyDown} + /> +
+
+

Data

+ +
+ +
+ + + {(column) => ( + {column.name} + )} + + } + > + {(item: any) => ( + + {(columnKey) => ( + {renderCell(item, columnKey)} + )} + + )} + +
+
+ setPage(page)} + /> +
+
+
+ + + {() => ( + <> + + Kritik Saran + + +
+
+

Nama

+ ( + + )} + /> + {errors?.commentFromName && ( +

+ {errors.commentFromName?.message} +

+ )} +
+
+

Email

+ ( + + )} + /> + {errors?.commentFromEmail && ( +

+ {errors.commentFromEmail?.message} +

+ )} +
+
+

Deskripsi

+ ( +