diff --git a/app/(admin)/settings/page.tsx b/app/(admin)/settings/page.tsx index 6b3e751..e9f9439 100644 --- a/app/(admin)/settings/page.tsx +++ b/app/(admin)/settings/page.tsx @@ -8,21 +8,21 @@ import { useEffect, useState } from "react"; export default function Settings() { const [profile, setProfile] = useState(); useEffect(() => { - const initFetch = async () => { - const profile = await getProfile(); - setProfile(profile?.data?.data); - }; initFetch(); }, []); + const initFetch = async () => { + const profile = await getProfile(); + setProfile(profile?.data?.data); + }; return (
-
+
- + initFetch()} /> - + initFetch()} />
diff --git a/app/layout.tsx b/app/layout.tsx index db3f8a7..0e3d497 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -64,13 +64,13 @@ export default function RootLayout({ children }: { children: ReactNode }) { -
{children}
+
{children}
diff --git a/app/setup-password/page.tsx b/app/setup-password/page.tsx index eb07c37..4edd919 100644 --- a/app/setup-password/page.tsx +++ b/app/setup-password/page.tsx @@ -5,7 +5,7 @@ import { Button } from "@nextui-org/button"; import Link from "next/link"; import Cookies from "js-cookie"; import { close, error, loading } from "@/config/swal"; -import { getProfile, postSignIn } from "@/service/master-user"; +import { getProfile, postSignIn, resetPassword } from "@/service/master-user"; import { useRouter, useSearchParams } from "next/navigation"; import Swal from "sweetalert2"; import withReactContent from "sweetalert2-react-content"; @@ -17,18 +17,37 @@ export default function Login() { const [isVisible, setIsVisible] = useState([false, false]); const searchParams = useSearchParams(); const userId = searchParams.get("id"); - + const code = searchParams.get("code"); const [passwordConf, setPasswordConf] = useState(""); const [password, setPassword] = useState(""); const [isValidPassword, setIsValidPassword] = useState(false); const onSubmit = async () => { const data = { + codeRequest: code, userId: String(userId), password: password, + confirmPassword: passwordConf, }; - console.log("data", data); + const res = await resetPassword(data); + if (res?.error) { + error(res?.message); + return false; + } + + MySwal.fire({ + title: "Sukses reset password", + + icon: "success", + cancelButtonColor: "#d33", + confirmButtonColor: "#3085d6", + confirmButtonText: "Oke", + }).then((result) => { + if (result.isConfirmed) { + router.push("/auth"); + } + }); }; const MySwal = withReactContent(Swal); diff --git a/components/form/login.tsx b/components/form/login.tsx index f86756e..8e808d0 100644 --- a/components/form/login.tsx +++ b/components/form/login.tsx @@ -6,7 +6,7 @@ import { Button } from "@nextui-org/button"; import Link from "next/link"; import Cookies from "js-cookie"; import { close, error, loading } from "@/config/swal"; -import { getProfile, postSignIn } from "@/service/master-user"; +import { checkUsernames, getProfile, postSignIn } from "@/service/master-user"; import { useRouter } from "next/navigation"; import Swal from "sweetalert2"; import withReactContent from "sweetalert2-react-content"; @@ -105,6 +105,11 @@ export default function Login() { const MySwal = withReactContent(Swal); const checkUsername = async () => { + const res = await checkUsernames(checkUsernameValue); + if (res?.error) { + error("Username tidak ditemukan"); + return false; + } MySwal.fire({ title: "", text: "", diff --git a/components/form/settings/password.tsx b/components/form/settings/password.tsx index 2223aee..94072ab 100644 --- a/components/form/settings/password.tsx +++ b/components/form/settings/password.tsx @@ -6,8 +6,10 @@ import { Input } from "@nextui-org/input"; import { EyeFilledIcon, EyeSlashFilledIcon } from "@/components/icons"; import { Button } from "@nextui-org/button"; import PasswordChecklist from "react-password-checklist"; +import { savePassword } from "@/service/master-user"; +import { close, error, loading } from "@/config/swal"; -export default function PasswordForm() { +export default function PasswordForm(props: { doFetch: () => void }) { const MySwal = withReactContent(Swal); const [isVisible, setIsVisible] = useState([false, false]); @@ -16,11 +18,27 @@ export default function PasswordForm() { const [isValidPassword, setIsValidPassword] = useState(false); const onSubmit = async () => { + loading(); const data = { password: password, + confirmPassword: passwordConf, }; - - console.log("data", data); + const res = await savePassword(data); + if (res?.error) { + error(res.message); + return false; + } + close(); + props.doFetch(); + MySwal.fire({ + title: "Sukses", + icon: "success", + confirmButtonColor: "#3085d6", + confirmButtonText: "OK", + }).then((result) => { + if (result.isConfirmed) { + } + }); }; const generatePassword = () => { @@ -152,7 +170,7 @@ export default function PasswordForm() { onChange={(isValid) => { setIsValidPassword(isValid); }} - className="text-white text-sm my-3" + className="text-black dark:text-white text-sm my-3" messages={{ minLength: "Password must be more than 8 characters", specialChar: "Password must include a special character", diff --git a/components/form/settings/profile.tsx b/components/form/settings/profile.tsx index 2e9056f..f7a994c 100644 --- a/components/form/settings/profile.tsx +++ b/components/form/settings/profile.tsx @@ -8,6 +8,8 @@ import withReactContent from "sweetalert2-react-content"; import { Input, Textarea } from "@nextui-org/input"; import { Button } from "@nextui-org/button"; import { Radio, RadioGroup } from "@nextui-org/react"; +import { updateProfile } from "@/service/master-user"; +import { close, error, loading } from "@/config/swal"; const formSchema = z.object({ fullname: z.string().min(1, { @@ -38,7 +40,10 @@ const formSchema = z.object({ }), }); -export default function ProfileForm(props: { profile: any }) { +export default function ProfileForm(props: { + profile: any; + doFetch: () => void; +}) { const MySwal = withReactContent(Swal); const { profile } = props; const formOptions = { @@ -46,15 +51,10 @@ export default function ProfileForm(props: { profile: any }) { }; type UserSettingSchema = z.infer; const { - register, control, handleSubmit, formState: { errors }, setValue, - getValues, - watch, - setError, - clearErrors, } = useForm(formOptions); useEffect(() => { @@ -68,7 +68,34 @@ export default function ProfileForm(props: { profile: any }) { }, [profile]); const onSubmit = async (values: z.infer) => { - console.log("values", values); + loading(); + const req = { + address: values.address, + fullname: values.fullname, + username: values.username, + email: values.email, + identityNumber: values.nrp, + phoneNumber: values.phoneNumber, + genderType: values.gender, + userLevelId: profile.userLevelId, + userRoleId: profile.userRoleId, + }; + const res = await updateProfile(req); + close(); + if (res?.error) { + error(res.message); + return false; + } + props.doFetch(); + MySwal.fire({ + title: "Sukses", + icon: "success", + confirmButtonColor: "#3085d6", + confirmButtonText: "OK", + }).then((result) => { + if (result.isConfirmed) { + } + }); }; return ( diff --git a/components/icons/dashboard-icon.tsx b/components/icons/dashboard-icon.tsx index b25165e..329ea16 100644 --- a/components/icons/dashboard-icon.tsx +++ b/components/icons/dashboard-icon.tsx @@ -69,7 +69,7 @@ export const DashboardMailboxIcon = ({ ); -export const DashboardArticle = ({ +export const DashboardShareIcon = ({ size, height = 48, width = 48, @@ -81,11 +81,11 @@ export const DashboardArticle = ({ height={size || height} width={size || width} {...props} - viewBox="0 0 20 20" + viewBox="0 0 512 512" > ); @@ -176,9 +176,39 @@ export const DashboardRightDownPointIcon = ({ > ); +export const DashboardCommentIcon = ({ + size, + height = 24, + width = 24, + fill = "currentColor", + ...props +}: IconSvgProps) => ( + + + + + + + + + + + +); diff --git a/components/layout/humas-layout.tsx b/components/layout/humas-layout.tsx index 68f821a..bf79e01 100644 --- a/components/layout/humas-layout.tsx +++ b/components/layout/humas-layout.tsx @@ -9,7 +9,7 @@ interface Props { export const HumasLayout = ({ children }: Props) => { return ( -
+
diff --git a/components/layout/navbar/NavbarHumas.tsx b/components/layout/navbar/NavbarHumas.tsx index 10bc0aa..4369e8c 100644 --- a/components/layout/navbar/NavbarHumas.tsx +++ b/components/layout/navbar/NavbarHumas.tsx @@ -18,7 +18,7 @@ import { } from "@nextui-org/react"; import Image from "next/image"; import Link from "next/link"; -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { ChevronDownIcon, ChevronRightIcon, @@ -104,15 +104,31 @@ export default function NavbarHumas(props: { size: string }) { /> ); + // useEffect(() => { + // const handleScroll = () => { + // setIsScrolled(window.scrollY > 0); + // }; + + // window.addEventListener("scroll", handleScroll); + + // return () => { + // window.removeEventListener("scroll", handleScroll); + // }; + // }, []); + useEffect(() => { + const mainElement = document.querySelector("main"); + const handleScroll = () => { - setIsScrolled(window.scrollY > 0); + if (mainElement) { + setIsScrolled(mainElement.scrollTop > 0); + } }; - window.addEventListener("scroll", handleScroll); + mainElement?.addEventListener("scroll", handleScroll); return () => { - window.removeEventListener("scroll", handleScroll); + mainElement?.removeEventListener("scroll", handleScroll); }; }, []); @@ -178,8 +194,8 @@ export default function NavbarHumas(props: { size: string }) { } >
- -
+ +

SP2HP

Pelayanan Surat Pemberitahuan Perkembangan Hasil @@ -190,8 +206,8 @@ export default function NavbarHumas(props: { size: string }) {

- -
+ +

Formulir Permohonan Informasi

@@ -210,8 +226,8 @@ export default function NavbarHumas(props: { size: string }) { } >
- -
+ +

Pelayanan SIM

Pelayanan Untuk Pendaftaran SIM dan Perpanjangan SIM @@ -224,8 +240,8 @@ export default function NavbarHumas(props: { size: string }) { onPress={() => window.open("https://erikkes.id/", "_blank")} >

- -
+ +

Pelayanan e-Rikkes SIM

@@ -240,8 +256,8 @@ export default function NavbarHumas(props: { size: string }) { onPress={() => window.open("https://eppsi.id/", "_blank")} >
- -
+ +

Pelayanan Test Psikologi SIM

@@ -260,7 +276,7 @@ export default function NavbarHumas(props: { size: string }) { } >
- +

Pelayanan e-Avis

@@ -275,7 +291,7 @@ export default function NavbarHumas(props: { size: string }) { } >

- +

Pelayanan Samsat Digital @@ -292,7 +308,7 @@ export default function NavbarHumas(props: { size: string }) { } >

- +

Pelayanan SKCK

@@ -310,7 +326,7 @@ export default function NavbarHumas(props: { size: string }) { } >

- +

Pelayanan Propam Presisi @@ -328,7 +344,7 @@ export default function NavbarHumas(props: { size: string }) { } >

- +

Pelayanan Dumas Presisi{" "} @@ -346,7 +362,7 @@ export default function NavbarHumas(props: { size: string }) { } >

- +

Pelayanan Binmas

@@ -365,7 +381,7 @@ export default function NavbarHumas(props: { size: string }) { } >

- +

Clean & Clear Polri{" "} @@ -669,7 +685,7 @@ export default function NavbarHumas(props: { size: string }) { >

-
+

Polri Super App

Aplikasi Layanan Perpanjangan SIM, Pembayaran STNK, @@ -688,7 +704,7 @@ export default function NavbarHumas(props: { size: string }) { >

-
+

Media Hub

Humas Polri dalam Data @@ -706,7 +722,7 @@ export default function NavbarHumas(props: { size: string }) { >

-
+

Polisiku

Membantu anggota Kepolisian untuk mengindetifikasi @@ -725,7 +741,7 @@ export default function NavbarHumas(props: { size: string }) { >

-
+

SP2HP

Surat Pemberitahuan Perkembangan Hasil Penyidikan Online @@ -743,7 +759,7 @@ export default function NavbarHumas(props: { size: string }) { >

-
+

Polri TV

Humas Polri dalam Audio Visual @@ -761,7 +777,7 @@ export default function NavbarHumas(props: { size: string }) { >

-
+

Polri Radio

Humas Polri dalam Audio @@ -792,7 +808,7 @@ export default function NavbarHumas(props: { size: string }) { >

-
+

e-Rikkes

Pemeriksaan Kesehatan Berbassi Teknologi Secara Online @@ -807,7 +823,7 @@ export default function NavbarHumas(props: { size: string }) { >

-
+

e-PPSI

Tes Psikologis SIM Secara Online{" "} @@ -990,7 +1006,7 @@ export default function NavbarHumas(props: { size: string }) { >

-
+

Sinar

Aplikasi dan Website Pelayanan Pendaftaran dan @@ -1089,12 +1105,24 @@ export default function NavbarHumas(props: { size: string }) {

- +
logo
+ + language === "id" ? setLanguage("en") : setLanguage("id") + } + > + {language === "id" ? : } + @@ -1134,7 +1162,7 @@ export default function NavbarHumas(props: { size: string }) { {item.label} ) : ( - {item.label} + {t(item.label)} )} {item.submenu && (dropdownOpen[item.key] ? ( @@ -1146,10 +1174,24 @@ export default function NavbarHumas(props: { size: string }) { )} {dropdownOpen[item.key] && item.submenu && ( -
- {item.submenu.map((subItem, subIndex) => ( +
+ {item.submenu.map((subItem: any, subIndex) => (
- {subItem.label} + + {subItem.img && ( + + )} + {subItem.multi + ? t(subItem.multi) + : subItem.label} +
))}
diff --git a/components/main/dashboard/chart/column-chart.tsx b/components/main/dashboard/chart/column-chart.tsx index b19185d..4a300c4 100644 --- a/components/main/dashboard/chart/column-chart.tsx +++ b/components/main/dashboard/chart/column-chart.tsx @@ -4,35 +4,16 @@ import ReactApexChart from "react-apexcharts"; import dummyData from "../../../../const/dummy.json"; type WeekData = { - week: number; // Minggu ke- - days: number[]; // Data jumlah view per hari dalam minggu tersebut - total: number; // Total jumlah view dalam minggu tersebut + week: number; + days: number[]; + total: number; }; -// Struktur untuk sisa hari type RemainingDays = { - days: number[]; // Data jumlah view untuk sisa hari - total: number; // Total jumlah view untuk sisa hari + days: number[]; + total: number; }; -// Struktur data per bulan setelah diolah -type ProcessedMonthlyData = { - id: string; // ID bulan - month: string; // Nama bulan - weeks: WeekData[]; // Data per minggu - remaining_days: RemainingDays; // Data sisa hari -}; - -// Struktur data input awal -type MonthlyDataInput = { - id: string; // ID bulan - month: string; // Nama bulan - count: number[]; // Jumlah view per hari selama 1 bulan -}; - -// Struktur array data awal -type MonthlyDataInputArray = MonthlyDataInput[]; - function processMonthlyData(count: number[]): { weeks: WeekData[]; remaining_days: RemainingDays; @@ -40,18 +21,16 @@ function processMonthlyData(count: number[]): { const weeks: WeekData[] = []; let weekIndex = 1; - // Kelompokkan data per 7 hari (minggu) for (let i = 0; i < count.length; i += 7) { - const weekData = count.slice(i, i + 7); // Ambil 7 hari + const weekData = count.slice(i, i + 7); weeks.push({ week: weekIndex, days: weekData, - total: weekData.reduce((sum, day) => sum + day, 0), // Total view per minggu + total: weekData.reduce((sum, day) => sum + day, 0), }); weekIndex++; } - // Cek sisa hari 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), @@ -63,10 +42,17 @@ function processMonthlyData(count: number[]): { }; } -const ApexChartColumn = (props: { type: string; date: string }) => { - const { date, type } = props; +const ApexChartColumn = (props: { + type: string; + date: string; + view: string[]; +}) => { + const { date, type, view } = props; const [categories, setCategories] = useState([]); - const [series, setSeries] = useState([]); + const [series, setSeries] = useState<{ name: string; data: number[] }[]>([]); + const [seriesVisit, setSeriesVisit] = useState([]); + const [seriesView, setSeriesView] = useState([]); + const [seriesShare, setSeriesShare] = useState([]); useEffect(() => { initFetch(); @@ -79,86 +65,88 @@ const ApexChartColumn = (props: { type: string; date: string }) => { (a) => a.month == splitDate[0] && a.year === splitDate[1] ); if (getDatas) { - const temp = processMonthlyData(getDatas?.count); - setSeries( - temp.weeks.map((list) => { - return list.total; - }) - ); + const temp1 = processMonthlyData(getDatas?.visit); + const temp2 = processMonthlyData(getDatas?.view); + const temp3 = processMonthlyData(getDatas?.share); + if (type == "weekly") { + setSeriesVisit( + temp1.weeks.map((list) => { + return list.total; + }) + ); + setSeriesView( + temp2.weeks.map((list) => { + return list.total; + }) + ); + setSeriesShare( + temp3.weeks.map((list) => { + return list.total; + }) + ); } else { - setSeries(getDatas.count); + console.log("sadadad", getDatas.visit, getDatas.view, getDatas.share); + setSeriesVisit(getDatas.visit); + setSeriesView(getDatas.view); + setSeriesShare(getDatas.share); } if (type === "weekly") { const category = []; - for (let i = 1; i <= temp.weeks.length; i++) { + for (let i = 1; i <= temp1.weeks.length; i++) { category.push(`Week ${i}`); } setCategories(category); } } else { - setSeries([]); + setSeriesVisit([]); } }; - const [state, setState] = useState({ - series: [ + useEffect(() => { + console.log("view", view); + const temp = [ { name: "Visit", - data: [ - 15, 23, 19, 14, 18, 20, 22, 17, 21, 19, 23, 16, 25, 20, 18, 19, 22, - 24, 15, 18, 21, 26, 28, 23, 17, 20, 19, 22, - ], + data: view.includes("visit") ? seriesVisit : [], }, - ], - options: { - chart: { - height: 350, - type: "line", - zoom: { - enabled: false, - }, + { + name: "View", + data: view.includes("view") ? seriesView : [], }, - dataLabels: { - enabled: false, - }, - stroke: { - curve: "straight", + { + name: "Share", + data: view.includes("share") ? seriesShare : [], }, + ]; - grid: { - row: { - colors: ["#f3f3f3", "transparent"], // takes an array which will be repeated on columns - opacity: 0.5, - }, - }, - }, - }); + console.log("temp", temp); + + setSeries(temp); + }, [view, seriesShare, seriesView, seriesVisit]); return ( -
-
+
+
diff --git a/components/main/dashboard/dashboard-container.tsx b/components/main/dashboard/dashboard-container.tsx index 7c1cdab..9e95a7d 100644 --- a/components/main/dashboard/dashboard-container.tsx +++ b/components/main/dashboard/dashboard-container.tsx @@ -1,10 +1,8 @@ "use client"; import { - DashboardArticle, - DashboardBriefcaseIcon, + DashboardCommentIcon, DashboardConnectIcon, - DashboardMailboxIcon, - DashboardRightDownPointIcon, + DashboardShareIcon, DashboardSpeecIcon, DashboardTopLeftPointIcon, DashboardUserIcon, @@ -12,6 +10,8 @@ import { import { Submenu1Icon } from "@/components/icons/sidebar-icon"; import { Button, + Checkbox, + CheckboxGroup, Pagination, Select, SelectItem, @@ -39,6 +39,11 @@ export default function DashboardContainer() { const [page, setPage] = useState(1); const [totalPage, setTotalPage] = useState(1); const [article, setArticle] = useState([]); + const [analyticsView, setAnalyticView] = useState([ + "visit", + "view", + "share", + ]); const [startDateValue, setStartDateValue] = useState({ startDate: new Date(), endDate: new Date(), @@ -88,7 +93,7 @@ export default function DashboardContainer() {
-
+

{fullname}

@@ -98,42 +103,66 @@ export default function DashboardContainer() {

- 4 Artikel + 4 Post Hari ini

- 2 Majalah + 12 Post Minggu ini

-
-
- -
-
Minggu ini
-
24
-
Total post
-
154
+
121
-
+
Total views
+
154
+
+
+
+ +
+
Total share
+
154
+
+
+
+ +
+
Total comment
530
-
+
Analytics - Views +
+ + + Visit + + + View + + + Share + + +
-
-
+
+
-
+

Recent Article

diff --git a/components/main/detail/comment.tsx b/components/main/detail/comment.tsx index 516cdcd..fff98b7 100644 --- a/components/main/detail/comment.tsx +++ b/components/main/detail/comment.tsx @@ -1,38 +1,190 @@ import { Button } from "@nextui-org/button"; -import { Textarea } from "@nextui-org/input"; -import React from "react"; +import { Input, Textarea } from "@nextui-org/input"; +import React, { useState } from "react"; +import { Controller, useForm } from "react-hook-form"; +import * as z from "zod"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { otpRequest, otpValidation } from "@/service/master-user"; +import { error } from "@/config/swal"; + +const commentSchema = z.object({ + name: z.string().min(1, { + message: "Judul harus diisi", + }), + email: z + .string() + .email({ + message: "Email tidak valid", + }) + .min(1, { + message: "Harus diisi", + }), + comment: z.string().min(1, { + message: "Deskripsi harus diisi", + }), +}); export default function Comment() { + const [needOtp, setNeedOtp] = useState(false); + const [otpValue, setOtpValue] = useState(""); + const formOptions = { + resolver: zodResolver(commentSchema), + }; + type UserSettingSchema = z.infer; + const { + control, + handleSubmit, + formState: { errors }, + setValue, + } = useForm(formOptions); + + const onSubmit = async (values: z.infer) => { + if (!needOtp) { + const res = await otpRequest(values.email); + if (res?.error) { + error(res.message); + return false; + } + setNeedOtp(true); + } else { + const validation = await otpValidation(values.email, otpValue); + if (validation?.error) { + error("OTP Tidak Sesuai"); + return false; + } + const req = { + email: values.email, + name: values.name, + comment: values.comment, + }; + console.log("req", req); + } + }; return ( -
-
- Berikan Komentar +
+ Tinggalkan balasan +

+ Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai{" "} + * +

+
+

Komentar

+ ( +