fix landing navbar, dashboard, reset & forgot password
This commit is contained in:
parent
c4c0b20bc6
commit
270fa658a7
|
|
@ -8,21 +8,21 @@ import { useEffect, useState } from "react";
|
|||
export default function Settings() {
|
||||
const [profile, setProfile] = useState<any>();
|
||||
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 (
|
||||
<div className="w-full lg:w-[60%] p-5">
|
||||
<div className="flex flex-col bg-gray-100 dark:bg-stone-900 text-black dark:text-white rounded-md p-5">
|
||||
<div className="flex flex-col bg-gray-50 shadow-md dark:bg-stone-900 text-black dark:text-white rounded-md p-5">
|
||||
<Tabs aria-label="Tabs radius" radius="sm">
|
||||
<Tab key="profile" title="Profile">
|
||||
<ProfileForm profile={profile} />
|
||||
<ProfileForm profile={profile} doFetch={() => initFetch()} />
|
||||
</Tab>
|
||||
<Tab key="music" title="Password">
|
||||
<PasswordForm />
|
||||
<PasswordForm doFetch={() => initFetch()} />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -64,13 +64,13 @@ export default function RootLayout({ children }: { children: ReactNode }) {
|
|||
</head>
|
||||
<body
|
||||
className={clsx(
|
||||
"bg-background font-sans antialiased overflow-hidden",
|
||||
"bg-background font-sans antialiased",
|
||||
fontSans.variable
|
||||
)}
|
||||
>
|
||||
<NextIntlClientProvider locale={localeNow} messages={messages}>
|
||||
<Providers themeProps={{ attribute: "class", defaultTheme: "dark" }}>
|
||||
<main className="overflow-y-auto">{children}</main>
|
||||
<main className="">{children}</main>
|
||||
</Providers>
|
||||
</NextIntlClientProvider>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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: "",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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<typeof formSchema>;
|
||||
const {
|
||||
register,
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
setValue,
|
||||
getValues,
|
||||
watch,
|
||||
setError,
|
||||
clearErrors,
|
||||
} = useForm<UserSettingSchema>(formOptions);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -68,7 +68,34 @@ export default function ProfileForm(props: { profile: any }) {
|
|||
}, [profile]);
|
||||
|
||||
const onSubmit = async (values: z.infer<typeof formSchema>) => {
|
||||
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 (
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ export const DashboardMailboxIcon = ({
|
|||
<rect width="8" height="3" x="9" y="20" fill="#3dd9eb" />
|
||||
</svg>
|
||||
);
|
||||
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"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M3 17a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2zM15 5h-5V4h5zm0 2h-5V6h5zm0 2h-5V8h5zM5 14h10v1H5zm0-2h10v1H5zm0-2h10v1H5zm0-6h4v5H5z"
|
||||
d="M503.691 189.836L327.687 37.851C312.281 24.546 288 35.347 288 56.015v80.053C127.371 137.907 0 170.1 0 322.326c0 61.441 39.581 122.309 83.333 154.132c13.653 9.931 33.111-2.533 28.077-18.631C66.066 312.814 132.917 274.316 288 272.085V360c0 20.7 24.3 31.453 39.687 18.164l176.004-152c11.071-9.562 11.086-26.753 0-36.328"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
|
@ -176,9 +176,39 @@ export const DashboardRightDownPointIcon = ({
|
|||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
fillRule="evenodd"
|
||||
d="M5.47 5.47a.75.75 0 0 1 1.06 0l10.72 10.72V9a.75.75 0 0 1 1.5 0v9a.75.75 0 0 1-.75.75H9a.75.75 0 0 1 0-1.5h7.19L5.47 6.53a.75.75 0 0 1 0-1.06"
|
||||
clip-rule="evenodd"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export const DashboardCommentIcon = ({
|
||||
size,
|
||||
height = 24,
|
||||
width = 24,
|
||||
fill = "currentColor",
|
||||
...props
|
||||
}: IconSvgProps) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height={size || height}
|
||||
width={size || width}
|
||||
{...props}
|
||||
viewBox="0 0 48 48"
|
||||
>
|
||||
<defs>
|
||||
<mask id="ipSComment0">
|
||||
<g
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="4"
|
||||
>
|
||||
<path fill="#fff" stroke="#fff" d="M44 6H4v30h9v5l10-5h21z" />
|
||||
<path stroke="#000" d="M14 19.5v3m10-3v3m10-3v3" />
|
||||
</g>
|
||||
</mask>
|
||||
</defs>
|
||||
<path fill="currentColor" d="M0 0h48v48H0z" mask="url(#ipSComment0)" />
|
||||
</svg>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ interface Props {
|
|||
|
||||
export const HumasLayout = ({ children }: Props) => {
|
||||
return (
|
||||
<section className="flex flex-col overflow-auto">
|
||||
<section className="flex flex-col">
|
||||
<NavbarHumas size="sm" />
|
||||
<NavbarHumas size="lg" />
|
||||
<NewsTicker />
|
||||
|
|
|
|||
|
|
@ -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 }) {
|
|||
}
|
||||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="at4.png" />
|
||||
<div className="flex flex-col px-2">
|
||||
<img src="at4.png" className="w-[35px]" />
|
||||
<div className="flex flex-col ">
|
||||
<p className="text-[16px] font-bold">SP2HP</p>
|
||||
<p className="text-ellipsis text-[12px] font-light overflow-hidden">
|
||||
Pelayanan Surat Pemberitahuan Perkembangan Hasil
|
||||
|
|
@ -190,8 +206,8 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
</DropdownItem>
|
||||
<DropdownItem>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="pm2.png" />
|
||||
<div className="flex flex-col px-2">
|
||||
<img src="pm2.png" className="w-[35px]" />
|
||||
<div className="flex flex-col ">
|
||||
<p className="text-[16px] font-bold">
|
||||
Formulir Permohonan Informasi
|
||||
</p>
|
||||
|
|
@ -210,8 +226,8 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
}
|
||||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="pm3.png" />
|
||||
<div className="flex flex-col px-2">
|
||||
<img src="pm3.png" className="w-[35px]" />
|
||||
<div className="flex flex-col ">
|
||||
<p className="text-[16px] font-bold">Pelayanan SIM</p>
|
||||
<p className="text-ellipsis text-[12px] font-light overflow-hidden">
|
||||
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")}
|
||||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="pm4.png" />
|
||||
<div className="flex flex-col px-2">
|
||||
<img src="pm4.png" className="w-[35px]" />
|
||||
<div className="flex flex-col ">
|
||||
<p className="text-[16px] font-bold">
|
||||
Pelayanan e-Rikkes SIM
|
||||
</p>
|
||||
|
|
@ -240,8 +256,8 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
onPress={() => window.open("https://eppsi.id/", "_blank")}
|
||||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="pm5.png" />
|
||||
<div className="flex flex-col px-2">
|
||||
<img src="pm5.png" className="w-[35px]" />
|
||||
<div className="flex flex-col ">
|
||||
<p className="text-[16px] font-bold">
|
||||
Pelayanan Test Psikologi SIM
|
||||
</p>
|
||||
|
|
@ -260,7 +276,7 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
}
|
||||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="pm6.png" />
|
||||
<img src="pm6.png" className="w-[35px]" />
|
||||
<div className="flex flex-col">
|
||||
<p className="text-[16px] font-bold">Pelayanan e-Avis</p>
|
||||
<p className="text-ellipsis text-[12px] font-light overflow-hidden">
|
||||
|
|
@ -275,7 +291,7 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
}
|
||||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="pm7.png" />
|
||||
<img src="pm7.png" className="w-[35px]" />
|
||||
<div className="flex flex-col">
|
||||
<p className="text-[16px] font-bold">
|
||||
Pelayanan Samsat Digital
|
||||
|
|
@ -292,7 +308,7 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
}
|
||||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="pm8.png" />
|
||||
<img src="pm8.png" className="w-[35px]" />
|
||||
<div className="flex flex-col">
|
||||
<p className="text-[16px] font-bold">Pelayanan SKCK</p>
|
||||
<p className="text-ellipsis text-[12px] font-light overflow-hidden">
|
||||
|
|
@ -310,7 +326,7 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
}
|
||||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="pm9.png" />
|
||||
<img src="pm9.png" className="w-[35px]" />
|
||||
<div className="flex flex-col">
|
||||
<p className="text-[16px] font-bold">
|
||||
Pelayanan Propam Presisi
|
||||
|
|
@ -328,7 +344,7 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
}
|
||||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="pm10.png" />
|
||||
<img src="pm10.png" className="w-[35px]" />
|
||||
<div className="flex flex-col">
|
||||
<p className="text-[16px] font-bold">
|
||||
Pelayanan Dumas Presisi{" "}
|
||||
|
|
@ -346,7 +362,7 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
}
|
||||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="pm11.png" />
|
||||
<img src="pm11.png" className="w-[35px]" />
|
||||
<div className="flex flex-col">
|
||||
<p className="text-[16px] font-bold">Pelayanan Binmas </p>
|
||||
<p className="text-ellipsis text-[12px] font-light overflow-hidden">
|
||||
|
|
@ -365,7 +381,7 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
}
|
||||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="pm12.png" />
|
||||
<img src="pm12.png" className="w-[35px]" />
|
||||
<div className="flex flex-col">
|
||||
<p className="text-[16px] font-bold">
|
||||
Clean & Clear Polri{" "}
|
||||
|
|
@ -669,7 +685,7 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="at1.png" />
|
||||
<div className="flex flex-col px-2">
|
||||
<div className="flex flex-col ">
|
||||
<p className="text-[16px] font-bold">Polri Super App</p>
|
||||
<p className="text-ellipsis text-[12px] font-light overflow-hidden">
|
||||
Aplikasi Layanan Perpanjangan SIM, Pembayaran STNK,
|
||||
|
|
@ -688,7 +704,7 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="at2.png" />
|
||||
<div className="flex flex-col px-2">
|
||||
<div className="flex flex-col ">
|
||||
<p className="text-[16px] font-bold">Media Hub</p>
|
||||
<p className="text-ellipsis text-[12px] font-light overflow-hidden">
|
||||
Humas Polri dalam Data
|
||||
|
|
@ -706,7 +722,7 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="at3.png" />
|
||||
<div className="flex flex-col px-2">
|
||||
<div className="flex flex-col ">
|
||||
<p className="text-[16px] font-bold">Polisiku</p>
|
||||
<p className="text-ellipsis text-[12px] font-light overflow-hidden">
|
||||
Membantu anggota Kepolisian untuk mengindetifikasi
|
||||
|
|
@ -725,7 +741,7 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="at4.png" />
|
||||
<div className="flex flex-col px-2">
|
||||
<div className="flex flex-col ">
|
||||
<p className="text-[16px] font-bold">SP2HP</p>
|
||||
<p className="text-ellipsis text-[12px] font-light overflow-hidden">
|
||||
Surat Pemberitahuan Perkembangan Hasil Penyidikan Online
|
||||
|
|
@ -743,7 +759,7 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="at5.png" />
|
||||
<div className="flex flex-col px-2">
|
||||
<div className="flex flex-col ">
|
||||
<p className="text-[16px] font-bold">Polri TV</p>
|
||||
<p className="text-ellipsis text-[12px] font-light overflow-hidden">
|
||||
Humas Polri dalam Audio Visual
|
||||
|
|
@ -761,7 +777,7 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="at6.png" />
|
||||
<div className="flex flex-col px-2">
|
||||
<div className="flex flex-col ">
|
||||
<p className="text-[16px] font-bold">Polri Radio</p>
|
||||
<p className="text-ellipsis text-[12px] font-light overflow-hidden">
|
||||
Humas Polri dalam Audio
|
||||
|
|
@ -792,7 +808,7 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="pm4.png" />
|
||||
<div className="flex flex-col px-2">
|
||||
<div className="flex flex-col ">
|
||||
<p className="text-[16px] font-bold">e-Rikkes</p>
|
||||
<p className="text-ellipsis text-[12px] font-light overflow-hidden">
|
||||
Pemeriksaan Kesehatan Berbassi Teknologi Secara Online
|
||||
|
|
@ -807,7 +823,7 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="pm5.png" />
|
||||
<div className="flex flex-col px-2">
|
||||
<div className="flex flex-col ">
|
||||
<p className="text-[16px] font-bold">e-PPSI</p>
|
||||
<p className="text-ellipsis text-[12px] font-light overflow-hidden">
|
||||
Tes Psikologis SIM Secara Online{" "}
|
||||
|
|
@ -990,7 +1006,7 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
>
|
||||
<div className="flex flex-row gap-2">
|
||||
<img src="pm3.png" />
|
||||
<div className="flex flex-col px-2">
|
||||
<div className="flex flex-col ">
|
||||
<p className="text-[16px] font-bold">Sinar</p>
|
||||
<p className="text-ellipsis text-[12px] font-light overflow-hidden">
|
||||
Aplikasi dan Website Pelayanan Pendaftaran dan
|
||||
|
|
@ -1089,12 +1105,24 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<NavbarContent className="lg:hidden px-2">
|
||||
<NavbarContent
|
||||
className={`px-2 lg:hidden transition-all duration-300 ease-in-out backdrop-opacity-10 ${
|
||||
isScrolled ? "bg-white dark:bg-[#1F1A17] " : "bg-opacity-50"
|
||||
}`}
|
||||
>
|
||||
<div className="flex justify-between w-full">
|
||||
<Link href={"/"}>
|
||||
<img src="/logohumas.png" alt="logo" className="w-[78px]" />
|
||||
</Link>
|
||||
<div className="flex flex-row gap-3 items-center">
|
||||
<a
|
||||
className="cursor-pointer"
|
||||
onClick={() =>
|
||||
language === "id" ? setLanguage("en") : setLanguage("id")
|
||||
}
|
||||
>
|
||||
{language === "id" ? <IdnIcon /> : <UKIcon />}
|
||||
</a>
|
||||
<ThemeSwitch />
|
||||
<NavbarMenuToggle />
|
||||
<NavbarMenu>
|
||||
|
|
@ -1134,7 +1162,7 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
<span>{item.label}</span>
|
||||
</Link>
|
||||
) : (
|
||||
<span>{item.label}</span>
|
||||
<span>{t(item.label)}</span>
|
||||
)}
|
||||
{item.submenu &&
|
||||
(dropdownOpen[item.key] ? (
|
||||
|
|
@ -1146,10 +1174,24 @@ export default function NavbarHumas(props: { size: string }) {
|
|||
)}
|
||||
</NavbarMenuItem>
|
||||
{dropdownOpen[item.key] && item.submenu && (
|
||||
<div className="pl-2">
|
||||
{item.submenu.map((subItem, subIndex) => (
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{item.submenu.map((subItem: any, subIndex) => (
|
||||
<div key={subIndex}>
|
||||
<Link href={subItem.href}>{subItem.label}</Link>
|
||||
<Link
|
||||
href={subItem.href}
|
||||
target={subItem.blank ? "_black" : ""}
|
||||
className="flex flex-row gap-2 items-center text-sm"
|
||||
>
|
||||
{subItem.img && (
|
||||
<img
|
||||
src={subItem.img}
|
||||
className="w-[35px] h-[35px] object-cover"
|
||||
/>
|
||||
)}
|
||||
{subItem.multi
|
||||
? t(subItem.multi)
|
||||
: subItem.label}
|
||||
</Link>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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<string[]>([]);
|
||||
const [series, setSeries] = useState<number[]>([]);
|
||||
const [series, setSeries] = useState<{ name: string; data: number[] }[]>([]);
|
||||
const [seriesVisit, setSeriesVisit] = useState<number[]>([]);
|
||||
const [seriesView, setSeriesView] = useState<number[]>([]);
|
||||
const [seriesShare, setSeriesShare] = useState<number[]>([]);
|
||||
|
||||
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 (
|
||||
<div>
|
||||
<div id="chart">
|
||||
<div className="h-full">
|
||||
<div id="chart" className="h-full">
|
||||
<ReactApexChart
|
||||
options={{
|
||||
chart: {
|
||||
height: 350,
|
||||
type: "line",
|
||||
zoom: {
|
||||
enabled: false,
|
||||
},
|
||||
height: "100%",
|
||||
type: "area",
|
||||
},
|
||||
stroke: {
|
||||
curve: "smooth",
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
xaxis: {
|
||||
categories: type == "weekly" ? categories : [],
|
||||
},
|
||||
}}
|
||||
series={[
|
||||
{
|
||||
name: "Visit",
|
||||
data: series,
|
||||
},
|
||||
]}
|
||||
type="line"
|
||||
height={350}
|
||||
series={series}
|
||||
type="area"
|
||||
height="100%"
|
||||
/>
|
||||
</div>
|
||||
<div id="html-dist"></div>
|
||||
|
|
|
|||
|
|
@ -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<ArticleData[]>([]);
|
||||
const [analyticsView, setAnalyticView] = useState<string[]>([
|
||||
"visit",
|
||||
"view",
|
||||
"share",
|
||||
]);
|
||||
const [startDateValue, setStartDateValue] = useState({
|
||||
startDate: new Date(),
|
||||
endDate: new Date(),
|
||||
|
|
@ -88,7 +93,7 @@ export default function DashboardContainer() {
|
|||
<div className="p-8 flex justify-center">
|
||||
<div className="w-full flex flex-col gap-6">
|
||||
<div className="w-full flex flex-col md:flex-row gap-6 justify-center">
|
||||
<div className="px-8 py-4 justify-between w-full md:w-[35%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col rounded-lg">
|
||||
<div className="px-8 py-4 justify-between w-full md:w-[37%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col rounded-lg">
|
||||
<div className="flex justify-between w-full items-center">
|
||||
<div className="flex flex-col gap-2">
|
||||
<p className="font-bold text-xl ">{fullname}</p>
|
||||
|
|
@ -98,42 +103,66 @@ export default function DashboardContainer() {
|
|||
</div>
|
||||
<div className="flex flex-row gap-5">
|
||||
<p className="text-lg font-semibold">
|
||||
4 <span className="text-sm font-normal">Artikel</span>
|
||||
4 Post <span className="text-sm font-normal">Hari ini</span>
|
||||
</p>
|
||||
<p className="text-lg font-semibold">
|
||||
2 <span className="text-sm font-normal">Majalah</span>
|
||||
12 Post <span className="text-sm font-normal">Minggu ini</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full md:w-[25%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col justify-center items-center rounded-lg">
|
||||
<div className="h-1/2 flex items-center justify-center">
|
||||
<DashboardArticle />
|
||||
</div>
|
||||
<div className="">Minggu ini</div>
|
||||
<div className="font-semibold text-lg">24</div>
|
||||
</div>
|
||||
<div className="w-full md:w-[20%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col justify-center items-center rounded-lg">
|
||||
<div className="h-1/2 flex items-center justify-center">
|
||||
<DashboardSpeecIcon />
|
||||
</div>
|
||||
<div className="">Total post</div>
|
||||
<div className="font-semibold text-lg">154</div>
|
||||
<div className="font-semibold text-lg">121</div>
|
||||
</div>
|
||||
<div className="w-full md:w-[20%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col justify-center items-center rounded-lg">
|
||||
<div className="w-full md:w-[15%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col justify-center items-center rounded-lg">
|
||||
<div className="h-1/2 flex items-center justify-center">
|
||||
<DashboardConnectIcon />
|
||||
</div>
|
||||
<div className="">Total views</div>
|
||||
<div className="font-semibold text-lg">154</div>
|
||||
</div>
|
||||
<div className="w-full md:w-[15%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col justify-center items-center rounded-lg">
|
||||
<div className="h-1/2 flex items-center justify-center">
|
||||
<DashboardShareIcon />
|
||||
</div>
|
||||
<div className="">Total share</div>
|
||||
<div className="font-semibold text-lg">154</div>
|
||||
</div>
|
||||
<div className="w-full md:w-[15%] h-[160px] shadow-md bg-white dark:bg-[#18181b] flex flex-col justify-center items-center rounded-lg">
|
||||
<div className="h-1/2 flex items-center justify-center">
|
||||
<DashboardCommentIcon size={50} />
|
||||
</div>
|
||||
<div className="">Total comment</div>
|
||||
<div className="font-semibold text-lg">530</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full flex flex-row gap-6 justify-center ">
|
||||
<div className="border-1 shadow-sm w-full rounded-lg md:w-[60%] p-6 flex flex-col">
|
||||
<div className="border-1 shadow-sm w-full rounded-lg md:w-[55%] p-6 flex flex-col">
|
||||
<div className="flex justify-between mb-3">
|
||||
<div className="font-semibold flex flex-col">
|
||||
Analytics
|
||||
<span className="font-normal text-xs text-gray-600">Views</span>
|
||||
<div className="font-normal text-xs text-gray-600 flex flex-row gap-2">
|
||||
<CheckboxGroup
|
||||
label=""
|
||||
value={analyticsView}
|
||||
orientation="horizontal"
|
||||
onValueChange={setAnalyticView}
|
||||
>
|
||||
<Checkbox size="sm" value="visit">
|
||||
Visit
|
||||
</Checkbox>
|
||||
<Checkbox size="sm" value="view" color="success">
|
||||
View
|
||||
</Checkbox>
|
||||
<Checkbox size="sm" value="share" color="warning">
|
||||
Share
|
||||
</Checkbox>
|
||||
</CheckboxGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row gap-2">
|
||||
<Button
|
||||
|
|
@ -162,16 +191,17 @@ export default function DashboardContainer() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row w-full">
|
||||
<div className="w-full">
|
||||
<div className="flex flex-row w-full h-full">
|
||||
<div className="w-full h-full">
|
||||
<ApexChartColumn
|
||||
type={typeDate}
|
||||
date={getMonthYear(startDateValue.startDate)}
|
||||
view={analyticsView}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col w-[40%] gap-6 shadow-md bg-white dark:bg-[#18181b] rounded-lg p-8">
|
||||
<div className="flex flex-col w-[45%] gap-6 shadow-md bg-white dark:bg-[#18181b] rounded-lg p-8">
|
||||
<div className="flex justify-between">
|
||||
<p>Recent Article</p>
|
||||
<Link href="/admin/article/create">
|
||||
|
|
|
|||
|
|
@ -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<typeof commentSchema>;
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
setValue,
|
||||
} = useForm<UserSettingSchema>(formOptions);
|
||||
|
||||
const onSubmit = async (values: z.infer<typeof commentSchema>) => {
|
||||
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 (
|
||||
<div className="p-3 lg:p-10 space-y-2 md:spacey-3 lg:space-y-5">
|
||||
<div>
|
||||
<b>Berikan Komentar</b>
|
||||
<form className="p-3 flex flex-col gap-3" onSubmit={handleSubmit(onSubmit)}>
|
||||
<b>Tinggalkan balasan</b>
|
||||
<p className="text-xs">
|
||||
Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai{" "}
|
||||
<span className="text-red-600">*</span>
|
||||
</p>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm">Komentar</p>
|
||||
<Controller
|
||||
control={control}
|
||||
name="comment"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Textarea
|
||||
type="text"
|
||||
id="comment"
|
||||
placeholder=""
|
||||
label=""
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
labelPlacement="outside"
|
||||
className="w-full "
|
||||
classNames={{
|
||||
inputWrapper: [
|
||||
"border-1 rounded-lg",
|
||||
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
||||
],
|
||||
}}
|
||||
variant="bordered"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors?.comment && (
|
||||
<p className="text-red-400 text-sm mb-3">{errors.comment?.message}</p>
|
||||
)}
|
||||
</div>
|
||||
<Textarea
|
||||
variant="bordered"
|
||||
placeholder="Masukkan Komentar Anda di sini.."
|
||||
className="text-black"
|
||||
classNames={{
|
||||
input: ["w-full", "bg-transparent", "!text-black"],
|
||||
mainWrapper: ["w-full", "bg-transparent"],
|
||||
innerWrapper: ["bg-transparent"],
|
||||
inputWrapper: [
|
||||
"bg-transparent",
|
||||
"dark:bg-transparent",
|
||||
"!border-1 border-gray-300",
|
||||
"hover:bg-transparent",
|
||||
"dark:hover:bg-transparent",
|
||||
"group-data-[focused=true]:bg-transparent",
|
||||
"dark:group-data-[focused=true]:bg-transaparent",
|
||||
"group-data-[focused=false]:bg-transparent",
|
||||
"focus-within:!bg-transparent",
|
||||
"border-1",
|
||||
],
|
||||
}}
|
||||
/>
|
||||
<Button className="bg-[#DD8306] text-white" radius="none">
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm">
|
||||
Nama <span className="text-red-600">*</span>
|
||||
</p>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="name"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Input
|
||||
type="text"
|
||||
id="name"
|
||||
placeholder=""
|
||||
label=""
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
labelPlacement="outside"
|
||||
className="w-full "
|
||||
classNames={{
|
||||
inputWrapper: [
|
||||
"border-1 rounded-lg",
|
||||
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
||||
],
|
||||
}}
|
||||
variant="bordered"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors?.name && (
|
||||
<p className="text-red-400 text-sm mb-3">{errors.name?.message}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm">
|
||||
Email <span className="text-red-600">*</span>
|
||||
</p>
|
||||
<Controller
|
||||
control={control}
|
||||
name="email"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<Input
|
||||
type="email"
|
||||
id="email"
|
||||
placeholder=""
|
||||
label=""
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
labelPlacement="outside"
|
||||
className="w-full "
|
||||
classNames={{
|
||||
inputWrapper: [
|
||||
"border-1 rounded-lg",
|
||||
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
||||
],
|
||||
}}
|
||||
variant="bordered"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors?.email && (
|
||||
<p className="text-red-400 text-sm mb-3">{errors.email?.message}</p>
|
||||
)}
|
||||
</div>
|
||||
{needOtp && (
|
||||
<div className="flex flex-col gap-1">
|
||||
<p>OTP</p>
|
||||
<Input
|
||||
type="number"
|
||||
id="otp"
|
||||
placeholder=""
|
||||
label=""
|
||||
value={otpValue}
|
||||
onValueChange={setOtpValue}
|
||||
labelPlacement="outside"
|
||||
className="w-[100px] "
|
||||
classNames={{
|
||||
inputWrapper: [
|
||||
"border-1 rounded-lg",
|
||||
"dark:group-data-[focused=false]:bg-transparent !border-1 dark:!border-gray-400",
|
||||
],
|
||||
}}
|
||||
variant="bordered"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Button className="bg-[#DD8306] text-white" radius="md" type="submit">
|
||||
Kirim
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
294
config/site.ts
294
config/site.ts
|
|
@ -27,100 +27,294 @@ export const siteConfig = {
|
|||
},
|
||||
],
|
||||
humasMenuItems: [
|
||||
{
|
||||
key: "home",
|
||||
label: "Beranda",
|
||||
href: "/",
|
||||
},
|
||||
{
|
||||
key: "about",
|
||||
label: "Tentang",
|
||||
submenu: [
|
||||
{
|
||||
label: "Tentang Humas POLRI",
|
||||
href: "/tentang-humas-polri",
|
||||
},
|
||||
{
|
||||
label: "Profile Pimpinan POLRI",
|
||||
href: "/profile-pimpinan-polri",
|
||||
},
|
||||
{
|
||||
label: "Struktur Organisasi",
|
||||
href: "/struktur-organisasi",
|
||||
},
|
||||
{
|
||||
label: "Visi dan Misi",
|
||||
href: "/visi-misi",
|
||||
},
|
||||
{
|
||||
label: "Tugas dan Fungsi",
|
||||
href: "/tugas-dan-fungsi",
|
||||
},
|
||||
{
|
||||
label: "Logo",
|
||||
href: "#",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "ppid",
|
||||
label: "Portal PPID",
|
||||
href: "/portal-ppid",
|
||||
},
|
||||
{
|
||||
key: "service",
|
||||
label: "Pelayanan Masyarakat",
|
||||
label: "pelayananMasyarakat",
|
||||
submenu: [
|
||||
{
|
||||
label: "SP2HP",
|
||||
href: "https://sp2hp.bareskrim.polri.go.id/",
|
||||
img: "at4.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Formulir Permohonan Informasi",
|
||||
href: "/form-permohonan-informasi",
|
||||
href: "#",
|
||||
img: "pm2.png",
|
||||
blank: false,
|
||||
},
|
||||
{
|
||||
label: "Pelayanan SIM",
|
||||
href: "https://www.digitalkorlantas.id/sim/",
|
||||
img: "pm3.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Pelayanan e-Rikkes SIM",
|
||||
href: "https://erikkes.id/",
|
||||
img: "pm4.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Pelayanan Tes Psikologi SIM",
|
||||
label: "Pelayanan Test Psikologi SIM",
|
||||
href: "https://eppsi.id/",
|
||||
img: "pm5.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Pelayanan e-Avis",
|
||||
href: "https://e-avis.korlantas.polri.go.id/",
|
||||
img: "pm6.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Pelayanan Samsat Digital",
|
||||
href: "https://samsatdigital.id/",
|
||||
img: "pm7.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Pelayanan SKCK",
|
||||
href: "https://play.google.com/store/apps/details?id=superapps.polri.presisi.presisi&hl=en_US",
|
||||
href: "https://skck.polri.go.id/",
|
||||
img: "pm8.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Pelayanan Propam Presisi",
|
||||
href: "https://play.google.com/store/apps/details?id=com.stk.pengaduanpropam",
|
||||
href: "https://play.google.com/store/apps/details?id=com.stk.pengaduanpropam&pli=1",
|
||||
img: "pm9.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Pelayanan Dumas Presisi",
|
||||
href: "https://dumaspresisi.polri.go.id/",
|
||||
img: "pm10.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Pelayanan Binmas",
|
||||
href: "https://bos.polri.go.id/login",
|
||||
img: "pm11.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Wistle Blower System",
|
||||
href: "https://play.google.com/store/apps/details?id=id.go.ssdmpolri.pengaduanappsbarupolri2",
|
||||
label: "Clean & Clear Polri",
|
||||
href: "#",
|
||||
img: "pm12.png",
|
||||
blank: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "contact",
|
||||
label: "Kontak",
|
||||
href: "/kontak-kami",
|
||||
key: "public-information",
|
||||
label: "informasi_publik",
|
||||
submenu: [
|
||||
{
|
||||
label: "Humas Polri",
|
||||
href: "/static/humas-polri",
|
||||
},
|
||||
{
|
||||
label: "Profil Pimpinan Polri",
|
||||
href: "/static/profile-kapolri",
|
||||
},
|
||||
{
|
||||
label: "Struktur Organisasi",
|
||||
href: "/static/struktur-mabes",
|
||||
},
|
||||
{
|
||||
label: "Visi & Misi",
|
||||
href: "/static/visi-misi-polri",
|
||||
},
|
||||
{
|
||||
label: "Tugas & Fungsi",
|
||||
href: "/static/tugas-dan-fungsi-polri",
|
||||
},
|
||||
{
|
||||
label: "Logo",
|
||||
href: "/static/logo",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "publication",
|
||||
label: "publikasi",
|
||||
submenu: [
|
||||
{
|
||||
label: "Humas Polri",
|
||||
href: "/static/humas-polri",
|
||||
},
|
||||
{
|
||||
label: "Profil Pimpinan Polri",
|
||||
href: "/static/profile-kapolri",
|
||||
},
|
||||
{
|
||||
label: "Struktur Organisasi",
|
||||
href: "/static/struktur-mabes",
|
||||
},
|
||||
{
|
||||
label: "Visi & Misi",
|
||||
href: "/static/visi-misi-polri",
|
||||
},
|
||||
{
|
||||
label: "Tugas & Fungsi",
|
||||
href: "/static/tugas-dan-fungsi-polri",
|
||||
},
|
||||
{
|
||||
label: "Logo",
|
||||
href: "/static/logo",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "about",
|
||||
label: "tentang",
|
||||
submenu: [
|
||||
{
|
||||
label: "Humas Polri",
|
||||
href: "/static/humas-polri",
|
||||
},
|
||||
{
|
||||
label: "Profil Pimpinan Polri",
|
||||
href: "/static/profile-kapolri",
|
||||
},
|
||||
{
|
||||
label: "Struktur Organisasi",
|
||||
href: "/static/struktur-mabes",
|
||||
},
|
||||
{
|
||||
label: "Visi & Misi",
|
||||
href: "/static/visi-misi-polri",
|
||||
},
|
||||
{
|
||||
label: "Tugas & Fungsi",
|
||||
href: "/static/tugas-dan-fungsi-polri",
|
||||
},
|
||||
{
|
||||
label: "Logo",
|
||||
href: "/static/logo",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
key: "related-app",
|
||||
label: "aplikasi_terkait",
|
||||
submenu: [
|
||||
{
|
||||
label: "Polri Super App",
|
||||
href: "https://play.google.com/store/apps/details?id=superapps.polri.presisi.presisi&hl=en_US&gl=US",
|
||||
img: "at1.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Media Hub",
|
||||
href: "https://play.google.com/store/apps/details?id=com.mediahub.mediahub_mobile",
|
||||
img: "at2.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Polisiku",
|
||||
href: "https://play.google.com/store/apps/details?id=id.co.qlue.polisiku&hl=id&gl=ID",
|
||||
img: "at3.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "SP2HP",
|
||||
href: "https://sp2hp.bareskrim.polri.go.id/",
|
||||
img: "at4.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Polri TV",
|
||||
href: "https://play.google.com/store/apps/details?id=com.polritv",
|
||||
img: "at5.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Polri Radio",
|
||||
href: "https://play.google.com/store/apps/details?id=com.polritv",
|
||||
img: "at6.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "e-Avis",
|
||||
href: "https://e-avis.korlantas.polri.go.id/",
|
||||
img: "pm6.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "e-Rikkes",
|
||||
href: "https://erikkes.id/",
|
||||
img: "pm4.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "e-PPSI",
|
||||
href: "https://eppsi.id/",
|
||||
img: "pm5.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "BOS",
|
||||
href: "https://bos.polri.go.id/login",
|
||||
img: "pm11.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Signal",
|
||||
href: "https://play.google.com/store/apps/details?id=app.signal.id",
|
||||
img: "pm7.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "SKCK Online",
|
||||
href: "https://skck.polri.go.id/",
|
||||
img: "pm8.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Propam Presisi",
|
||||
href: "https://play.google.com/store/apps/details?id=com.stk.pengaduanpropam",
|
||||
img: "pm9.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Monitoring Presisi",
|
||||
href: "https://play.google.com/store/apps/details?id=com.stk.pengaduanpropam",
|
||||
img: "at14.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "SDM - Penerimaan POLRI",
|
||||
href: "https://penerimaan.polri.go.id/",
|
||||
img: "at15.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Whistle Blowing System",
|
||||
href: "https://pengaduan-penerimaan.polri.go.id/",
|
||||
img: "at16.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Dumas Presisi",
|
||||
href: "https://play.google.com/store/apps/details?id=com.admasolusi.monitoringpresisi",
|
||||
img: "pm10.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Clean & Clear Polri",
|
||||
href: "#",
|
||||
img: "pm12.png",
|
||||
blank: true,
|
||||
},
|
||||
{
|
||||
label: "Sinar",
|
||||
href: "https://www.digitalkorlantas.id/sim/",
|
||||
img: "pm3.png",
|
||||
blank: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "dashboard",
|
||||
|
|
|
|||
|
|
@ -4,27 +4,51 @@
|
|||
"id": "1",
|
||||
"year": "2024",
|
||||
"month": "november",
|
||||
"count": [
|
||||
"visit": [
|
||||
14, 32, 10, 21, 15, 18, 24, 30, 12, 25, 19, 28, 14, 17, 22, 31, 27, 13,
|
||||
20, 24, 29, 18, 21, 26, 23, 14, 19, 17, 28, 22
|
||||
],
|
||||
"view": [
|
||||
20, 24, 29, 18, 21, 26, 23, 14, 19, 17, 28, 22, 14, 32, 10, 21, 15, 18,
|
||||
24, 30, 12, 25, 19, 28, 14, 17, 22, 31, 27, 13
|
||||
],
|
||||
"share": [
|
||||
24, 30, 12, 25, 19, 28, 14, 2, 31, 27, 13, 17, 22, 31, 27, 13, 18, 21,
|
||||
26, 23, 14, 18, 24, 30, 12, 25, 19, 28, 14, 17
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"year": "2024",
|
||||
"month": "december",
|
||||
"count": [
|
||||
"visit": [
|
||||
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, 22, 42, 32
|
||||
],
|
||||
"view": [
|
||||
20, 24, 29, 18, 21, 26, 23, 14, 19, 17, 28, 22, 14, 32, 10, 21, 15, 18,
|
||||
24, 30, 12, 25, 19, 28, 14, 17, 22, 31, 27, 13, 21
|
||||
],
|
||||
"share": [
|
||||
14, 32, 10, 21, 15, 18, 24, 30, 12, 25, 19, 28, 14, 17, 22, 31, 27, 13,
|
||||
20, 24, 29, 18, 21, 26, 23, 14, 19, 17, 28, 22, 32
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"year": "2025",
|
||||
"month": "january",
|
||||
"count": [
|
||||
"visit": [
|
||||
21, 24, 19, 27, 29, 23, 18, 20, 26, 22, 24, 30, 25, 19, 17, 21, 27, 23,
|
||||
29, 25, 22
|
||||
],
|
||||
"view": [
|
||||
21, 24, 19, 25, 19, 17, 21, 27, 23, 29, 25, 22, 27, 29, 23, 18, 20, 26,
|
||||
22, 24, 30
|
||||
],
|
||||
"share": [
|
||||
22, 24, 30, 25, 19, 17, 21, 27, 23, 29, 25, 22, 21, 24, 19, 27, 29, 23,
|
||||
18, 20, 26
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@ import {
|
|||
httpDeleteInterceptor,
|
||||
httpGet,
|
||||
httpPost,
|
||||
httpPut,
|
||||
} from "./http-config/axios-base-service";
|
||||
import Cookies from "js-cookie";
|
||||
|
||||
const token = Cookies.get("access_token");
|
||||
const id = Cookies.get("uie");
|
||||
|
||||
export async function listMasterUsers(data: any) {
|
||||
const headers = {
|
||||
|
|
@ -41,3 +43,46 @@ export async function getProfile(code?: string) {
|
|||
};
|
||||
return await httpGet(`/users/info`, headers);
|
||||
}
|
||||
|
||||
export async function updateProfile(data: any) {
|
||||
const headers = {
|
||||
"content-type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
};
|
||||
return await httpPut(`/users/${id}`, headers, data);
|
||||
}
|
||||
export async function savePassword(data: any) {
|
||||
const headers = {
|
||||
"content-type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
};
|
||||
return await httpPost(`/users/save-password`, headers, data);
|
||||
}
|
||||
|
||||
export async function resetPassword(data: any) {
|
||||
const headers = {
|
||||
"content-type": "application/json",
|
||||
};
|
||||
return await httpPost(`/users/reset-password`, headers, data);
|
||||
}
|
||||
|
||||
export async function checkUsernames(username: string) {
|
||||
const headers = {
|
||||
"content-type": "application/json",
|
||||
};
|
||||
return await httpPost(`/users/forgot-password`, headers, { username });
|
||||
}
|
||||
|
||||
export async function otpRequest(email: string) {
|
||||
const headers = {
|
||||
"content-type": "application/json",
|
||||
};
|
||||
return await httpPost(`/users/otp-request`, headers, { email });
|
||||
}
|
||||
|
||||
export async function otpValidation(email: string, otpCode: string) {
|
||||
const headers = {
|
||||
"content-type": "application/json",
|
||||
};
|
||||
return await httpPost(`/users/otp-validation`, headers, { email, otpCode });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,11 +84,11 @@
|
|||
}
|
||||
|
||||
body {
|
||||
height: 100%; /* Pastikan tinggi body penuh */
|
||||
overflow: hidden; /* Nonaktifkan scrolling global */
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
main {
|
||||
height: 100vh; /* Pastikan tinggi main sesuai viewport */
|
||||
overflow-y: auto; /* Aktifkan scrolling hanya di elemen main */
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue