feat:login button, auth, fix:admin - article page

This commit is contained in:
Rama Priyanto 2024-11-08 16:01:21 +07:00
parent e7482b346d
commit d18bbfe568
12 changed files with 736 additions and 900 deletions

View File

@ -1,15 +1,103 @@
"use client"; "use client";
import { Input } from "@nextui-org/input"; import { Input } from "@nextui-org/input";
import React from "react"; import React, { useState } from "react";
import { EyeFilledIcon, EyeSlashFilledIcon } from "../icons"; import { EyeFilledIcon, EyeSlashFilledIcon } from "../icons";
import { Button } from "@nextui-org/button"; import { Button } from "@nextui-org/button";
import Link from "next/link"; 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 { useRouter } from "next/navigation";
export default function Login() { export default function Login() {
const router = useRouter();
const [isVisible, setIsVisible] = React.useState(false); const [isVisible, setIsVisible] = React.useState(false);
const toggleVisibility = () => setIsVisible(!isVisible); const toggleVisibility = () => setIsVisible(!isVisible);
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const onSubmit = async () => {
const data = {
username: username,
password: password,
};
if (!username || !password) {
error("Username & Password Wajib Diisi !");
} else {
loading();
const response = await postSignIn(data);
if (response?.error) {
error("Username / Password Tidak Sesuai");
} else {
const access_token: any = response?.data?.data?.id_token;
const refresh_token: any = response?.data?.data?.refresh_token;
const dateTime: any = new Date();
const newTime: any = dateTime.getTime() + 10 * 60 * 1000;
Cookies.set("access_token", access_token, {
expires: 1,
});
Cookies.set("refresh_token", refresh_token, {
expires: 1,
});
Cookies.set("time_refresh", newTime, {
expires: 1,
});
Cookies.set("is_first_login", "true", {
secure: true,
sameSite: "strict",
});
const profile = await getProfile();
console.log("PROFILE : ", profile?.data);
Cookies.set("profile_picture", profile?.data?.data?.profilePictureUrl, {
expires: 1,
});
Cookies.set("uie", profile?.data?.data?.id, {
expires: 1,
});
Cookies.set("ufne", profile?.data?.data?.fullname, {
expires: 1,
});
Cookies.set("username", profile?.data?.data?.username, {
expires: 1,
});
Cookies.set("urie", profile?.data?.data?.roleId, {
expires: 1,
});
Cookies.set("roleName", profile?.data?.data?.roleName, {
expires: 1,
});
Cookies.set("masterPoldaId", profile?.data?.data?.masterPoldaId, {
expires: 1,
});
Cookies.set("ulne", profile?.data?.data?.roleLevelNumber, {
expires: 1,
});
Cookies.set("urce", profile?.data?.data?.roleCode, {
expires: 1,
});
Cookies.set("email", profile?.data?.data?.email, {
expires: 1,
});
close();
router.push("/admin/dashboard");
Cookies.set("status", "login", {
expires: 1,
});
}
}
// }
};
const setValUsername = (e: any) => {
const uname = e.replaceAll(/[^\w.-]/g, "");
setUsername(uname.toLowerCase());
};
return ( return (
<div className="bg-white text-black md:flex px-0 md:px-2 lg:px-5"> <div className="bg-white text-black md:flex px-0 md:px-2 lg:px-5">
<div className="w-auto md:w-1/2 p-2 md:p-5 lg:p-10 space-y-5"> <div className="w-auto md:w-1/2 p-2 md:p-5 lg:p-10 space-y-5">
@ -39,6 +127,15 @@ export default function Login() {
label="Username" label="Username"
placeholder="Masukkan username anda!" placeholder="Masukkan username anda!"
variant="underlined" variant="underlined"
onChange={(e: any) => {
setValUsername(e.target.value.trim());
}}
onPaste={(e: any) => {
setValUsername(e.target.value.trim());
}}
onCopy={(e: any) => {
setValUsername(e.target.value.trim());
}}
/> />
</div> </div>
<div> <div>
@ -77,50 +174,15 @@ export default function Login() {
label="Password" label="Password"
placeholder="Masukkan password anda" placeholder="Masukkan password anda"
variant="underlined" variant="underlined"
onChange={(event) => setPassword(event.target.value)}
/> />
</div> </div>
<div>
<Input
classNames={{
input: ["w-full", "bg-transparent", "!text-black"],
mainWrapper: ["w-full", "bg-transparent"],
innerWrapper: ["bg-transparent"],
label: ["!text-black", "font-semibold"],
inputWrapper: [
"bg-transparent",
"dark:bg-transparent",
"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",
],
}}
isRequired
endContent={
<button
className="focus:outline-none"
type="button"
onClick={toggleVisibility}
>
{isVisible ? (
<EyeSlashFilledIcon className="text-2xl text-default-400 pointer-events-none" />
) : (
<EyeFilledIcon className="text-2xl text-default-400 pointer-events-none" />
)}
</button>
}
type={isVisible ? "text" : "password"}
label="Konfirmasi Password"
placeholder="Masukkan password anda"
variant="underlined"
/>
</div>
<div> <div>
<Button <Button
size="lg" size="lg"
className="w-full bg-[#DD8306] rounded-md font-semibold" className="w-full bg-[#DD8306] rounded-md font-semibold"
onPress={onSubmit}
> >
Login Login
</Button> </Button>

View File

@ -14,7 +14,9 @@ export default function ENewsPolri() {
useEffect(() => { useEffect(() => {
async function getArticle() { async function getArticle() {
const response = await getListArticle(); const req = { page: 1, search: "", limit: "10" };
const response = await getListArticle(req);
console.log("res", response?.data?.data); console.log("res", response?.data?.data);
setArticle(response?.data?.data); setArticle(response?.data?.data);
} }

View File

@ -17,7 +17,8 @@ export default function HeaderNews() {
useEffect(() => { useEffect(() => {
async function getArticle() { async function getArticle() {
const response = await getListArticle(); const req = { page: 1, search: "", limit: "10" };
const response = await getListArticle(req);
console.log("res", response?.data?.data); console.log("res", response?.data?.data);
setArticle(response?.data?.data); setArticle(response?.data?.data);
} }

View File

@ -363,6 +363,9 @@ export default function NavbarHumas() {
<div> <div>
<ThemeSwitch /> <ThemeSwitch />
</div> </div>
<Link href="/auth">
<Button className="bg-[#DD8306]">Login</Button>
</Link>
</div> </div>
</div> </div>
</div> </div>
@ -375,6 +378,11 @@ export default function NavbarHumas() {
{siteConfig.humasMenuItems.map((item) => ( {siteConfig.humasMenuItems.map((item) => (
<div key={item.key} className="relative"> <div key={item.key} className="relative">
<NavbarMenuItem> <NavbarMenuItem>
{item.key === "login" ? (
<Link href="/auth" className="my-3">
<Button className="bg-[#DD8306]">Login</Button>
</Link>
) : (
<div <div
onClick={() => toggleDropdown(item.key)} onClick={() => toggleDropdown(item.key)}
className="flex items-end gap-2" className="flex items-end gap-2"
@ -393,6 +401,7 @@ export default function NavbarHumas() {
<ChevronDownIcon /> <ChevronDownIcon />
))} ))}
</div> </div>
)}
</NavbarMenuItem> </NavbarMenuItem>
{dropdownOpen[item.key] && item.submenu && ( {dropdownOpen[item.key] && item.submenu && (
<div className="pl-2"> <div className="pl-2">
@ -410,219 +419,5 @@ export default function NavbarHumas() {
</NavbarContent> </NavbarContent>
</div> </div>
</Navbar> </Navbar>
// <Navbar
// shouldHideOnScroll
// position='static'
// maxWidth='full' isBlurred={true} className='h-28 md:h-36 dark:bg-[#1F1A17]'>
// <div className='w-1/12 min-w-max'>
// <img src="/logohumas.png" alt="" />
// </div>
// <div className='w-11/12 lg:w-8/12 font-semibold hidden md:block '>
// <div className='flex justify-around items-center'>
// <Link href={'/'}>
// <div>Beranda</div>
// </Link>
// <div>
// <Dropdown className=' dark:bg-[#1F1A17]'>
// <NavbarItem>
// <DropdownTrigger>
// <Button
// disableRipple
// className="p-0 bg-transparent data-[hover=true]:bg-transparent text-medium font font-semibold"
// radius="sm"
// variant="light"
// endContent={<ChevronDownIcon className="pt-1" />}
// >
// Tentang
// </Button>
// </DropdownTrigger>
// </NavbarItem>
// <DropdownMenu
// // aria-label="tentang"
// // title='Tentang'
// className="pt-4"
// classNames={{
// // base: "flex",
// // list: "border-2 gap-2 flex flex-row flex-wrap"
// list: "gap-2 flex flex-row flex-wrap"
// }}
// itemClasses={{
// // base: "border border-red-700 w-[350px]"
// base: "w-[350px]"
// }}
// >
// <DropdownItem
// endContent={<ChevronRightWhite />}
// >
// <Link href='/tentang-humas-polri'>
// Tentang Humas POLRI
// </Link>
// </DropdownItem>
// <DropdownItem
// endContent={<ChevronRightWhite />}
// >
// <Link href='/profile-pimpinan-polri'>
// Profile Pimpinan POLRI
// </Link>
// </DropdownItem>
// <DropdownItem
// endContent={<ChevronRightWhite />}>
// <Link href='/struktur-organisasi'>
// Struktur Organisasi
// </Link>
// </DropdownItem>
// <DropdownItem
// endContent={<ChevronRightWhite />}>
// <Link href='/visi-misi'>
// Visi & Misi
// </Link>
// </DropdownItem>
// <DropdownItem
// endContent={<ChevronRightWhite />}>
// <Link href='/tugas-dan-fungsi'>
// Tugas & Fungsi
// </Link>
// </DropdownItem>
// <DropdownItem
// endContent={<ChevronRightWhite />}>
// Logo
// </DropdownItem>
// </DropdownMenu>
// </Dropdown>
// </div>
// <div><Link href="/portal-ppid">Portal PPID</Link></div>
// <div>
// <Dropdown className='dark:bg-[#1F1A17]'>
// <NavbarItem>
// <DropdownTrigger>
// <Button
// disableRipple
// className="p-0 bg-transparent data-[hover=true]:bg-transparent text-medium font font-semibold"
// radius="sm"
// variant="light"
// endContent={<ChevronDownIcon className="pt-1" />}
// >
// Pelayanan Masyarakat
// </Button>
// </DropdownTrigger>
// </NavbarItem>
// <DropdownMenu
// aria-label="pelayanan-masyarakat"
// title='pelayanan-masyarakat'
// className="pt-4"
// classNames={{
// list: "gap-2 flex flex-row flex-wrap"
// }}
// itemClasses={{
// base: "w-[350px]"
// }}
// >
// <DropdownItem
// endContent={<ChevronRightWhite />}
// >
// <Link href={'/form-permohonan-informasi'}>
// Formulir Permohonan Informasi
// </Link>
// </DropdownItem>
// <DropdownItem
// endContent={<ChevronRightWhite />}
// >
// <Link href='https://www.digitalkorlantas.id/sim/' target="_blank">
// Pelayanan SIM
// </Link>
// </DropdownItem>
// <DropdownItem
// endContent={<ChevronRightWhite />}>
// <Link href='https://erikkes.id/' target="_blank">
// Pelayanan e-Rikkes SIM
// </Link>
// </DropdownItem>
// <DropdownItem
// endContent={<ChevronRightWhite />}>
// <Link href='https://eppsi.id/' target="_blank">
// Pelayanan Test Psikologi SIM
// </Link>
// </DropdownItem>
// <DropdownItem
// endContent={<ChevronRightWhite />}>
// <Link href="https://e-avis.korlantas.polri.go.id/" target='_blank'>
// Pelayanan e-Arvis
// </Link>
// </DropdownItem>
// <DropdownItem
// endContent={<ChevronRightWhite />}>
// <Link href="https://samsatdigital.id/" target='_blank'>
// Pelayanan Samsat Digital
// </Link>
// </DropdownItem>
// <DropdownItem
// endContent={<ChevronRightWhite />}>
// <Link href='https://play.google.com/store/apps/details?id=superapps.polri.presisi.presisi&hl=en_US' target='_blank'>
// Pelayanan SKCK
// </Link>
// </DropdownItem>
// <DropdownItem
// endContent={<ChevronRightWhite />}>
// <Link href='https://play.google.com/store/apps/details?id=com.stk.pengaduanpropam' target='_blank'>
// Pelayanan Propam Presisi
// </Link>
// </DropdownItem>
// <DropdownItem
// endContent={<ChevronRightWhite />}>
// <Link href='https://dumaspresisi.polri.go.id/' target='_blank'>
// Pelayanan Dumas Presisi
// </Link>
// </DropdownItem>
// <DropdownItem
// endContent={<ChevronRightWhite />}>
// <Link href='https://bos.polri.go.id/login' target='_blank'>
// Pelayanan Binmas
// </Link>
// </DropdownItem>
// <DropdownItem
// endContent={<ChevronRightWhite />}>
// <Link href='https://play.google.com/store/apps/details?id=id.go.ssdmpolri.pengaduanappsbarupolri2' target='_blank'>
// Whistle Blower System
// </Link>
// </DropdownItem>
// </DropdownMenu>
// </Dropdown>
// </div>
// <div>
// <Link href={'/kontak-kami'}>Kontak</Link></div>
// <div><ThemeSwitch /></div>
// </div>
// </div>
// <div className='w-3/12 hidden lg:block'>
// <div className='flex items-center justify-between'>
// <div className="flex gap-1 lg:gap-3 items-center">
// <Link href='https://www.facebook.com/DivHumasPolri' target='_blank'>
// <FbIcon />
// </Link>
// <Link href='https://www.instagram.com/divisihumaspolri/' target='_blank'>
// <IgIcon />
// </Link>
// <Link href='https://www.youtube.com/user/pidhumaspolri' target='_blank'>
// <YtIcon />
// </Link>
// <Link href='https://twitter.com/DivHumas_Polri' target='_blank'>
// <TwIcon />
// </Link>
// <Link href='https://www.tiktok.com/@divhumas_polri' target='_blank'>
// <TtIcon />
// </Link>
// </div>
// <div className=''><IdnIcon /></div>
// </div>
// <div className='pb-10 pt-3'>
// {searchInput}
// </div>
// </div>
// <NavbarContent className="sm:hidden basis-1 pl-4" justify="end">
// <ThemeSwitch />
// <NavbarMenuToggle />
// </NavbarContent>
// </Navbar>
); );
} }

View File

@ -1,7 +1,7 @@
import { SidebarMenuTask } from "@/types/globals"; import { SidebarMenuTask } from "@/types/globals";
import { Tooltip } from "@nextui-org/react"; import { Tooltip } from "@nextui-org/react";
import Link from "next/link"; import Link from "next/link";
import { usePathname } from "next/navigation"; import { usePathname, useRouter } from "next/navigation";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { import {
ChevronLeftIcon, ChevronLeftIcon,
@ -24,6 +24,8 @@ import { SidebarCollapseItems } from "./sidebar-collapse-items";
import { SidebarCollapseSubItems } from "./sidebar-collapse-sub-items"; import { SidebarCollapseSubItems } from "./sidebar-collapse-sub-items";
import { useSidebar } from "./sidebar-context"; import { useSidebar } from "./sidebar-context";
import { SidebarMenu } from "./sidebar-menu"; import { SidebarMenu } from "./sidebar-menu";
import Image from "next/image";
import Cookies from "js-cookie";
interface SubMenuItems { interface SubMenuItems {
id: number; id: number;
@ -176,20 +178,7 @@ const sideBarDummyData = [
statusName: "Active", statusName: "Active",
childModule: null, childModule: null,
}, },
// {
// id: 9,
// name: "Master User Level",
// moduleId: 655,
// moduleName: "Form User Level",
// modulePathUrl: "/admin/master-user-level",
// parentId: -1,
// icon: <FormLayoutIcon />,
// position: 1,
// statusId: 1,
// childMenu: [],
// statusName: "Active",
// childModule: null,
// },
{ {
id: 10, id: 10,
name: "Master User Role", name: "Master User Role",
@ -204,108 +193,32 @@ const sideBarDummyData = [
statusName: "Active", statusName: "Active",
childModule: null, childModule: null,
}, },
// {
// id: 11,
// name: "Form Wizard",
// moduleId: 657,
// moduleName: "Form Wizard",
// modulePathUrl: "/admin/form-wizard",
// parentId: -1,
// icon: <FormWizardIcon />,
// position: 1,
// statusId: 1,
// childMenu: [],
// statusName: "Active",
// childModule: null,
// },
// {
// id: 13,
// name: "Others",
// moduleId: 658,
// moduleName: "Others",
// modulePathUrl: "/admin/basic",
// isGroup: true,
// parentId: -1,
// icon: "table",
// position: 1,
// statusId: 1,
// childMenu: [],
// statusName: "Active",
// childModule: null,
// },
// {
// id: 13,
// name: "Menu 1",
// moduleId: 652,
// moduleName: "Menu 1",
// modulePathUrl: "/admin/menu1",
// parentId: -1,
// icon: <HomeIcon />,
// position: 1,
// statusId: 1,
// statusName: "Active",
// childMenu: [
// {
// id: 3,
// name: "SubMenu 1",
// moduleId: 653,
// moduleName: "SubMenu 1",
// modulePathUrl: "/admin/menu1/submenu1",
// parentId: 702,
// icon: <Submenu1Icon size={16} />,
// position: 2,
// statusId: 1,
// statusName: "Active",
// childMenu: [],
// childModule: null,
// },
// {
// id: 4,
// name: "SubMenu 2",
// moduleId: 653,
// moduleName: "SubMenu 2",
// modulePathUrl: "/admin/menu1/submenu2",
// parentId: 702,
// icon: <Submenu2Icon size={16} />,
// position: 2,
// statusId: 1,
// statusName: "Active",
// childMenu: [],
// childModule: null,
// }
// ],
// childModule: null,
// },
]; ];
const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => { const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
const pathname = usePathname(); const pathname = usePathname();
const router = useRouter();
const [sidebarMenu, setSidebarMenu] = useState<SidebarMenuTask[]>(); const [sidebarMenu, setSidebarMenu] = useState<SidebarMenuTask[]>();
const { isOpen, toggleSidebar } = useSidebar(); const { isOpen, toggleSidebar } = useSidebar();
const token = Cookies.get("access_token");
const closeSidebar = () => { useEffect(() => {
if (isOpen) { if (!token) {
toggleSidebar(); onLogout();
} }
}, [token]);
const onLogout = () => {
Object.keys(Cookies.get()).forEach((cookieName) => {
Cookies.remove(cookieName);
});
router.push("/auth");
}; };
useEffect(() => { useEffect(() => {
updateSidebarData(isOpen); updateSidebarData(isOpen);
}, [isOpen]); }, [isOpen]);
const renderIcon = (icon: string) => {
switch (icon) {
case "dashboard":
return <DashboardIcon />;
case "menu1":
return <HomeIcon />;
case "table":
return <TableIcon />;
default:
return null;
}
};
return ( return (
<div className={`flex h-full ${isOpen ? "min-w-[290px]" : "min-w-[80px]"}`}> <div className={`flex h-full ${isOpen ? "min-w-[290px]" : "min-w-[80px]"}`}>
<div <div
@ -383,6 +296,7 @@ const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
? sideBarDummyData?.map((list: any, index: number) => ? sideBarDummyData?.map((list: any, index: number) =>
list.isGroup ? ( list.isGroup ? (
<p <p
key={list}
className={`font-bold mr-4 ${!isOpen ? "text-center" : ""}`} className={`font-bold mr-4 ${!isOpen ? "text-center" : ""}`}
> >
{isOpen ? list.name : "..."} {isOpen ? list.name : "..."}
@ -460,17 +374,26 @@ const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
{isOpen && "Theme"} {isOpen && "Theme"}
</div> </div>
{isOpen ? ( {isOpen ? (
<div className="flex flex-row gap-3">
<Image
src="/pengaduan.png"
width={72}
height={72}
alt="profile"
/>
<div className="flex flex-col">
<a className="cursor-pointer">Nama User</a>
<a <a
className={`cursor-pointer flex flex-row ${ className="hover:text-red-600 underline text-sm cursor-pointer"
isOpen ? "justify-start" : "justify-center" onClick={() => onLogout()}
} gap-2 items-center text-zinc-600 dark:text-zinc-400 hover:font-semibold hover:text-zinc-700 dark:hover:text-zinc-300`}
> >
<InfoCircleIcon size={24} /> Logout
{isOpen && "Support"}
</a> </a>
</div>
</div>
) : ( ) : (
<Tooltip <Tooltip
content="Support" content="Profile"
delay={0} delay={0}
closeDelay={0} closeDelay={0}
placement="right" placement="right"
@ -479,65 +402,20 @@ const Sidebar: React.FC<SidebarProps> = ({ updateSidebarData }) => {
className={`cursor-pointer flex flex-row ${ className={`cursor-pointer flex flex-row ${
isOpen ? "justify-start" : "justify-center" isOpen ? "justify-start" : "justify-center"
} gap-2 items-center text-zinc-600 dark:text-zinc-400 hover:font-semibold hover:text-zinc-700 dark:hover:text-zinc-300`} } gap-2 items-center text-zinc-600 dark:text-zinc-400 hover:font-semibold hover:text-zinc-700 dark:hover:text-zinc-300`}
onClick={toggleSidebar}
> >
<InfoCircleIcon size={24} /> <Image
{isOpen && "Support"} src="/pengaduan.png"
</a> width={48}
</Tooltip> height={48}
)} alt="profile"
{isOpen ? ( />
<a
className={`ml-[1px] cursor-pointer flex flex-row ${
isOpen ? "justify-start" : "justify-center"
} gap-2 items-center text-zinc-600 dark:text-zinc-400 hover:font-semibold hover:text-zinc-700 dark:hover:text-zinc-300`}
>
<MinusCircleIcon size={21} />
{isOpen && "Log Out"}
</a>
) : (
<Tooltip
content="Log Out"
delay={0}
closeDelay={0}
placement="right"
>
<a
className={`cursor-pointer flex flex-row ${
isOpen ? "justify-start" : "justify-center"
} gap-2 items-center text-zinc-600 dark:text-zinc-400 hover:font-semibold hover:text-zinc-700 dark:hover:text-zinc-300`}
>
<MinusCircleIcon size={21} />
{isOpen && "Log Out"}
</a> </a>
</Tooltip> </Tooltip>
)} )}
</div> </div>
</div> </div>
</div> </div>
{/* <div className="hidden md:block w-20 bg-gray-100 dark:bg-stone-950 h-full text-black dark:text-white">
<div className={`mt-4 flex justify-center w-full ${isOpen ? "hidden" : ""}`}>
<button
className="w-6 h-6 z-40"
onClick={toggleSidebar}
>
<MenuBurgerIcon />
</button>
</div>
{sideBarDummyData.map((list) => (
<div key={list.id} className='w-full flex justify-center my-2'>
<ClosedSidebarIcon icon={list.icon} isActive={pathname.includes(list.modulePathUrl)} childMenu={list.childMenu} path={list.modulePathUrl} title={list.name} />
</div>
))}
</div>
{isOpen && (
<div
className="block md:hidden fixed top-0 left-0 h-full w-full bg-black opacity-50 z-30"
onClick={toggleSidebar}
></div>
)} */}
</div> </div>
); );
}; };

View File

@ -3,9 +3,10 @@ import {
CreateIconIon, CreateIconIon,
DeleteIcon, DeleteIcon,
DotsYIcon, DotsYIcon,
EyeIconMdi EyeIconMdi,
SearchIcon,
} from "@/components/icons"; } from "@/components/icons";
import { error, success, } from "@/config/swal"; import { error, success } from "@/config/swal";
import { deleteArticle, getListArticle } from "@/service/article"; import { deleteArticle, getListArticle } from "@/service/article";
import { Article } from "@/types/globals"; import { Article } from "@/types/globals";
import { Button } from "@nextui-org/button"; import { Button } from "@nextui-org/button";
@ -16,63 +17,77 @@ import {
DropdownItem, DropdownItem,
DropdownMenu, DropdownMenu,
DropdownTrigger, DropdownTrigger,
Input,
Pagination,
Select,
SelectItem,
Spinner, Spinner,
Table, Table,
TableBody, TableBody,
TableCell, TableCell,
TableColumn, TableColumn,
TableHeader, TableHeader,
TableRow TableRow,
} from "@nextui-org/react"; } from "@nextui-org/react";
import Link from "next/link"; import Link from "next/link";
import { Key, useCallback, useEffect, useState } from "react"; import { Key, useCallback, useEffect, useState } from "react";
import Datepicker from "react-tailwindcss-datepicker";
import Swal from "sweetalert2"; import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content"; import withReactContent from "sweetalert2-react-content";
type UserObject = { const columns = [
id: number;
user: string;
status: string;
projectName: string;
avatar: string;
};
const statusColorMap = {
active: "success",
paused: "danger",
vacation: "warning",
};
export default function ArticleTable() {
const MySwal = withReactContent(Swal);
const [article, setArticle] = useState<Article[]>([]);
useEffect(() => {
async function initState() {
const res = await getListArticle();
setArticle(res.data?.data);
console.log("List Article", res.data.data);
}
initState();
}, []);
type TableRow = (typeof usersTable)[0];
const columns = [
{ name: "No", uid: "no" }, { name: "No", uid: "no" },
{ name: "Judul", uid: "title" }, { name: "Judul", uid: "title" },
{ name: "Kategori", uid: "categoryName" }, { name: "Kategori", uid: "categoryName" },
{ name: "Tanggal Unggah", uid: "createdAt" }, { name: "Tanggal Unggah", uid: "createdAt" },
{ name: "Kreator", uid: "createdByName" }, { name: "Kreator", uid: "createdByName" },
// { name: "Sumber", uid: "source" },
// { name: "Users", uid: "users" },
// { name: "Status", uid: "status" },
{ name: "Aksi", uid: "actions" },
];
{ name: "Aksi", uid: "actions" },
];
type ArticleData = Article & {
no: number;
};
export default function ArticleTable() {
const MySwal = withReactContent(Swal);
const [page, setPage] = useState(1);
const [totalPage, setTotalPage] = useState(1);
const [article, setArticle] = useState<ArticleData[]>([]);
const [showData, setShowData] = useState("10");
const [search, setSearch] = useState("");
const [startDateValue, setStartDateValue] = useState({
startDate: null,
endDate: null,
});
useEffect(() => {
initState();
}, [page, showData, startDateValue]);
async function initState() {
const startDate: string = `${startDateValue.startDate}`;
const req = { limit: showData, page: page, search: search };
const res = await getListArticle(req);
getTableNumber(parseInt(showData), res.data?.data);
console.log("res.data?.data", res.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;
});
console.log("daata", data);
setArticle(newData);
}
};
async function doDelete(id: any) { async function doDelete(id: any) {
// loading(); // loading();
@ -114,55 +129,8 @@ export default function ArticleTable() {
}); });
} }
// const statusOptions = [ const renderCell = useCallback((article: ArticleData, columnKey: Key) => {
// { name: "Active", uid: "active" }, const cellValue = article[columnKey as keyof ArticleData];
// { name: "Paused", uid: "paused" },
// { name: "Vacation", uid: "vacation" },
// ];
const usersTable = [
{
id: 1,
user: "Olivia Rhya",
status: "active",
projectName: "Xtreme admin",
avatar: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSa8Luglga9J2R3Bxt_PsWZISUHQWODD6_ZTAJ5mIQgxYCAE-YbkY81faTqp-hSA_jVPTs&usqp=CAU",
},
{
id: 2,
user: "Barbara Steele",
status: "cancel",
projectName: "Adminpro admin",
avatar: "https://cdn.icon-icons.com/icons2/2859/PNG/512/avatar_face_man_boy_male_profile_smiley_happy_people_icon_181661.png",
},
{
id: 3,
user: "Leonardo Gordon",
status: "pending",
projectName: "Monster admin",
avatar: "https://cdn.icon-icons.com/icons2/2859/PNG/512/avatar_face_man_boy_male_profile_smiley_happy_people_icon_181657.png",
},
{
id: 4,
user: "Evelyn Pope",
status: "cancel",
projectName: "Materialpro admin",
avatar: "https://cdn.icon-icons.com/icons2/3708/PNG/512/man_person_people_avatar_icon_230017.png",
},
{
id: 5,
user: "Tommy Garza",
status: "cancel",
projectName: "Elegant admin",
avatar: "https://cdn.icon-icons.com/icons2/1736/PNG/512/4043275-avatar-man-person-punk_113271.png",
},
];
const renderCell = useCallback(
(article: Article, columnKey: Key) => {
const cellValue = article[columnKey as keyof Article];
const statusColorMap: Record<string, ChipProps["color"]> = { const statusColorMap: Record<string, ChipProps["color"]> = {
active: "primary", active: "primary",
cancel: "danger", cancel: "danger",
@ -170,11 +138,6 @@ export default function ArticleTable() {
}; };
switch (columnKey) { switch (columnKey) {
case "no":
return (
<div>{article.id}</div>
)
case "status": case "status":
return ( return (
<Chip <Chip
@ -199,35 +162,19 @@ export default function ArticleTable() {
</Button> </Button>
</DropdownTrigger> </DropdownTrigger>
<DropdownMenu> <DropdownMenu>
<DropdownItem <DropdownItem>
<Link href={`/admin/article/detail/${article.id}`}>
>
<Link
href={`/admin/article/detail/${article.id}`}
>
<EyeIconMdi className="inline mr-2 mb-1" /> <EyeIconMdi className="inline mr-2 mb-1" />
Detail Detail
</Link> </Link>
</DropdownItem> </DropdownItem>
<DropdownItem <DropdownItem>
<Link href={`/admin/article/edit/${article.id}`}>
>
<Link
href={`/admin/article/edit/${article.id}`}
>
<CreateIconIon className="inline mr-2 mb-1" /> <CreateIconIon className="inline mr-2 mb-1" />
Edit Edit
</Link> </Link>
</DropdownItem> </DropdownItem>
<DropdownItem <DropdownItem onClick={() => handleDelete(article.id)}>
onClick={() => handleDelete(article.id)}
>
<DeleteIcon <DeleteIcon
color="red" color="red"
width={20} width={20}
@ -235,9 +182,7 @@ export default function ArticleTable() {
className="inline mr-2 mb-1" className="inline mr-2 mb-1"
/> />
Delete Delete
</DropdownItem> </DropdownItem>
</DropdownMenu> </DropdownMenu>
</Dropdown> </Dropdown>
</div> </div>
@ -248,19 +193,107 @@ export default function ArticleTable() {
} }
}, []); }, []);
let typingTimer: NodeJS.Timeout;
const doneTypingInterval = 1500;
const handleKeyUp = () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(doneTyping, doneTypingInterval);
};
const handleKeyDown = () => {
clearTimeout(typingTimer);
};
async function doneTyping() {
initState();
}
return ( return (
<> <>
<div className="mx-5 my-5"> <div className="mx-5 my-5">
<div className="flex flex-col items-center rounded-2xl"> <div className="flex flex-col items-start rounded-2xl gap-3">
<div className="flex flex-row gap-3 w-full">
<div className="flex flex-col gap-1 w-1/3">
<p className="font-semibold text-sm">Search</p>
<Input
aria-label="Search"
classNames={{
inputWrapper: "bg-default-100",
input: "text-sm",
}}
labelPlacement="outside"
startContent={
<SearchIcon className="text-base text-default-400 pointer-events-none flex-shrink-0" />
}
type="text"
onChange={(e) => setSearch(e.target.value)}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown}
/>
</div>
<div className="flex flex-col gap-1 w-[72px]">
<p className="font-semibold text-sm">Show</p>
<Select
label=""
variant="bordered"
labelPlacement="outside"
placeholder="Select"
selectedKeys={[showData]}
className="w-full"
onChange={(e) =>
e.target.value === "" ? "" : setShowData(e.target.value)
}
>
<SelectItem key="5" value="5">
5
</SelectItem>
<SelectItem key="10" value="10">
10
</SelectItem>
</Select>
</div>
<div className="flex flex-col gap-1 w-[170px]">
<p className="font-semibold text-sm">Category</p>
<Select
label=""
variant="bordered"
labelPlacement="outside"
placeholder="Select"
selectedKeys={[showData]}
className="w-full"
onChange={(e) =>
e.target.value === "" ? "" : setShowData(e.target.value)
}
>
<SelectItem key="10" value="10">
Polda Metro Jaya
</SelectItem>
<SelectItem key="5" value="5">
Polda Sumatera Utara
</SelectItem>
</Select>
</div>
<div className="flex flex-col gap-1 w-[170px]">
<p className="font-semibold text-sm">Date</p>
<Datepicker
useRange={false}
asSingle={true}
value={startDateValue}
displayFormat="DD/MM/YYYY"
onChange={(e: any) => setStartDateValue(e)}
inputClassName="z-50 w-full text-sm bg-transparent border-1 border-gray-200 px-2 py-[6px] rounded-xl h-[40px]"
/>
</div>
</div>
<Table <Table
// selectionMode="multiple"
aria-label="micro issue table" aria-label="micro issue table"
className="rounded-3xl" className="rounded-3xl"
classNames={{ classNames={{
th: "bg-white dark:bg-black text-black dark:text-white border-b-1 text-md", th: "bg-white dark:bg-black text-black dark:text-white border-b-1 text-md",
base: "bg-white dark:bg-black border", base: "bg-white dark:bg-black border",
wrapper: "min-h-[50px] bg-transpararent text-black dark:text-white ", wrapper:
"min-h-[50px] bg-transpararent text-black dark:text-white ",
}} }}
> >
<TableHeader columns={columns}> <TableHeader columns={columns}>
@ -282,9 +315,23 @@ export default function ArticleTable() {
)} )}
</TableBody> </TableBody>
</Table> </Table>
<div className="my-2 w-full flex justify-center">
<Pagination
isCompact
showControls
showShadow
color="primary"
classNames={{
base: "bg-transparent",
wrapper: "bg-transparent",
}}
page={page}
total={totalPage}
onChange={(page) => setPage(page)}
/>
</div>
</div> </div>
</div> </div>
</> </>
); );
} }

View File

@ -2,7 +2,8 @@ export type SiteConfig = typeof siteConfig;
export const siteConfig = { export const siteConfig = {
name: "DIVISI HUMAS POLRI - Pengelola Informasi dan Dokumentasi Polri", name: "DIVISI HUMAS POLRI - Pengelola Informasi dan Dokumentasi Polri",
description: "DIVISI HUMAS POLRI - Pengelola Informasi dan Dokumentasi Polri.", description:
"DIVISI HUMAS POLRI - Pengelola Informasi dan Dokumentasi Polri.",
navItems: [ navItems: [
{ {
label: "Home", label: "Home",
@ -23,7 +24,7 @@ export const siteConfig = {
{ {
label: "About", label: "About",
href: "/about", href: "/about",
} },
], ],
humasMenuItems: [ humasMenuItems: [
{ {
@ -37,29 +38,29 @@ export const siteConfig = {
submenu: [ submenu: [
{ {
label: "Tentang Humas POLRI", label: "Tentang Humas POLRI",
href: "/tentang-humas-polri" href: "/tentang-humas-polri",
}, },
{ {
label: "Profile Pimpinan POLRI", label: "Profile Pimpinan POLRI",
href: "/profile-pimpinan-polri" href: "/profile-pimpinan-polri",
}, },
{ {
label: "Struktur Organisasi", label: "Struktur Organisasi",
href: "/struktur-organisasi" href: "/struktur-organisasi",
}, },
{ {
label: "Visi dan Misi", label: "Visi dan Misi",
href: "/visi-misi" href: "/visi-misi",
}, },
{ {
label: "Tugas dan Fungsi", label: "Tugas dan Fungsi",
href: "/tugas-dan-fungsi" href: "/tugas-dan-fungsi",
}, },
{ {
label: "Logo", label: "Logo",
href: "#" href: "#",
} },
] ],
}, },
{ {
key: "ppid", key: "ppid",
@ -72,55 +73,60 @@ export const siteConfig = {
submenu: [ submenu: [
{ {
label: "Formulir Permohonan Informasi", label: "Formulir Permohonan Informasi",
href: "/form-permohonan-informasi" href: "/form-permohonan-informasi",
}, },
{ {
label: "Pelayanan SIM", label: "Pelayanan SIM",
href: "https://www.digitalkorlantas.id/sim/" href: "https://www.digitalkorlantas.id/sim/",
}, },
{ {
label: "Pelayanan e-Rikkes SIM", label: "Pelayanan e-Rikkes SIM",
href: "https://erikkes.id/" href: "https://erikkes.id/",
}, },
{ {
label: "Pelayanan Tes Psikologi SIM", label: "Pelayanan Tes Psikologi SIM",
href: "https://eppsi.id/" href: "https://eppsi.id/",
}, },
{ {
label: "Pelayanan e-Avis", label: "Pelayanan e-Avis",
href: "https://e-avis.korlantas.polri.go.id/" href: "https://e-avis.korlantas.polri.go.id/",
}, },
{ {
label: "Pelayanan Samsat Digital", label: "Pelayanan Samsat Digital",
href: "https://samsatdigital.id/" href: "https://samsatdigital.id/",
}, },
{ {
label: "Pelayanan SKCK", label: "Pelayanan SKCK",
href: "https://play.google.com/store/apps/details?id=superapps.polri.presisi.presisi&hl=en_US" href: "https://play.google.com/store/apps/details?id=superapps.polri.presisi.presisi&hl=en_US",
}, },
{ {
label: "Pelayanan Propam Presisi", 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",
}, },
{ {
label: "Pelayanan Dumas Presisi", label: "Pelayanan Dumas Presisi",
href: "https://dumaspresisi.polri.go.id/" href: "https://dumaspresisi.polri.go.id/",
}, },
{ {
label: "Pelayanan Binmas", label: "Pelayanan Binmas",
href: "https://bos.polri.go.id/login" href: "https://bos.polri.go.id/login",
}, },
{ {
label: "Wistle Blower System", label: "Wistle Blower System",
href: "https://play.google.com/store/apps/details?id=id.go.ssdmpolri.pengaduanappsbarupolri2" href: "https://play.google.com/store/apps/details?id=id.go.ssdmpolri.pengaduanappsbarupolri2",
}, },
] ],
}, },
{ {
key: "contact", key: "contact",
label: "Kontak", label: "Kontak",
href: "/kontak-kami", href: "/kontak-kami",
}, },
{
key: "login",
label: "Login",
href: "/auth",
},
], ],
links: { links: {
@ -128,6 +134,6 @@ export const siteConfig = {
twitter: "https://twitter.com/getnextui", twitter: "https://twitter.com/getnextui",
docs: "https://nextui.org", docs: "https://nextui.org",
discord: "https://discord.gg/9b6yyZKmH4", discord: "https://discord.gg/9b6yyZKmH4",
sponsor: "https://patreon.com/jrgarciadev" sponsor: "https://patreon.com/jrgarciadev",
}, },
}; };

9
package-lock.json generated
View File

@ -39,6 +39,7 @@
"html-react-parser": "^5.1.10", "html-react-parser": "^5.1.10",
"intl-messageformat": "^10.5.0", "intl-messageformat": "^10.5.0",
"jodit-react": "^4.0.25", "jodit-react": "^4.0.25",
"js-cookie": "^3.0.5",
"next": "14.0.2", "next": "14.0.2",
"next-themes": "^0.2.1", "next-themes": "^0.2.1",
"postcss": "8.4.31", "postcss": "8.4.31",
@ -4887,6 +4888,14 @@
"react-dom": "~0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" "react-dom": "~0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
} }
}, },
"node_modules/js-cookie": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
"integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
"engines": {
"node": ">=14"
}
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"license": "MIT" "license": "MIT"

View File

@ -40,6 +40,7 @@
"html-react-parser": "^5.1.10", "html-react-parser": "^5.1.10",
"intl-messageformat": "^10.5.0", "intl-messageformat": "^10.5.0",
"jodit-react": "^4.0.25", "jodit-react": "^4.0.25",
"js-cookie": "^3.0.5",
"next": "14.0.2", "next": "14.0.2",
"next-themes": "^0.2.1", "next-themes": "^0.2.1",
"postcss": "8.4.31", "postcss": "8.4.31",

View File

@ -1,10 +1,20 @@
import { httpDeleteInterceptor, httpGet, httpPost, httpPut } from "./http-config/axios-base-service"; import { PaginationRequest } from "@/types/globals";
import {
httpDeleteInterceptor,
httpGet,
httpPost,
httpPut,
} from "./http-config/axios-base-service";
export async function getListArticle() { export async function getListArticle(props: PaginationRequest) {
const { page, limit, search } = props;
const headers = { const headers = {
"content-type": "application/json", "content-type": "application/json",
}; };
return await httpGet(`/articles`, headers); return await httpGet(
`/articles?limit=${limit}&page=${page}&title=${search}`,
headers
);
} }
export async function createArticle(data: any) { export async function createArticle(data: any) {

View File

@ -1,4 +1,8 @@
import { httpDeleteInterceptor, httpGet, httpPost } from "./http-config/axios-base-service"; import {
httpDeleteInterceptor,
httpGet,
httpPost,
} from "./http-config/axios-base-service";
export async function listMasterUsers() { export async function listMasterUsers() {
const headers = { const headers = {
@ -18,3 +22,18 @@ export async function createMasterUser(data: any) {
export async function deleteMasterUser(id: string) { export async function deleteMasterUser(id: string) {
return await httpDeleteInterceptor(`/users/${id}`); return await httpDeleteInterceptor(`/users/${id}`);
} }
export async function postSignIn(data: any) {
const headers = {
"content-type": "application/json",
};
const pathUrl = `/users/login`;
return await httpPost(pathUrl, headers, data);
}
export async function getProfile() {
const headers = {
"content-type": "application/json",
};
return await httpGet(`/users/info`, headers);
}

View File

@ -27,30 +27,36 @@ export type Article = {
export type MasterUser = { export type MasterUser = {
id: number; id: number;
address: string, address: string;
dateOfBirth: string, dateOfBirth: string;
email: string, email: string;
fullname: string, fullname: string;
genderType: string, genderType: string;
identityNumber: string, identityNumber: string;
identityType: string, identityType: string;
lastEducation: string, lastEducation: string;
phoneNumber: string, phoneNumber: string;
userLevelsId: number, userLevelsId: number;
userRoleId: number, userRoleId: number;
username: string, username: string;
workType: string workType: string;
}; };
export type MasterUserRole = { export type MasterUserRole = {
id: number, id: number;
name: string, name: string;
description: string, description: string;
code: string, code: string;
level_number: number, level_number: number;
status_id: number, status_id: number;
created_by_id: string | number, created_by_id: string | number;
is_active: true, is_active: true;
created_at: string, created_at: string;
updated_at: string updated_at: string;
};
export type PaginationRequest = {
limit: string;
page: number;
search: string;
}; };