feat : api

Deskripsi:
- add group
- add position

No issue
This commit is contained in:
lukman
2024-07-30 17:45:02 +08:00
parent 2e8d2b3c1f
commit 01016bede9
12 changed files with 214 additions and 224 deletions

View File

@@ -26,4 +26,14 @@ export const API_ADDRESS = {
"apiCreateVillage": "/api/village/post?path=create-village", "apiCreateVillage": "/api/village/post?path=create-village",
"apiUpdateVillage": "/api/village/post?path=update-village", "apiUpdateVillage": "/api/village/post?path=update-village",
"apiDeleteVillage": "/api/village/post?path=delete-village", "apiDeleteVillage": "/api/village/post?path=delete-village",
// Position
"apiGetAllPosition": "/api/position/get?path=get-all-position",
"apiGetOnePosition": "/api/position/get?path=get-one-position",
"apiCreatePosition": "/api/position/post?path=create-position",
"apiUpdatePosition": "/api/position/post?path=update-position",
"apiDeletePosition": "/api/position/post?path=delete-position",
} }

View File

@@ -0,0 +1,33 @@
import { ActionIcon, Box, Group, Skeleton } from '@mantine/core';
import React from 'react';
export default function SkeletonSingle() {
return (
<Box pt={20}>
<Group
align="center"
style={{
border: `1px solid ${"#DCEED8"}`,
padding: 10,
borderRadius: 10,
cursor: "pointer",
}}
>
<Box>
<ActionIcon
variant="light"
bg={"#DCEED8"}
size={50}
radius={100}
aria-label="icon"
>
<Skeleton height={25} width={25} />
</ActionIcon>
</Box>
<Box>
<Skeleton height={20} width={100} />
</Box>
</Group>
</Box>
);
}

View File

@@ -1,6 +1,7 @@
import { API_ADDRESS } from "./bin/api_address"; import { API_ADDRESS } from "./bin/api_address";
import prisma from "./bin/prisma"; import prisma from "./bin/prisma";
import { pwd_key_config } from "./bin/val_global"; import { pwd_key_config } from "./bin/val_global";
import SkeletonSingle from "./components/skeleton_single";
import { WARNA } from "./fun/WARNA"; import { WARNA } from "./fun/WARNA";
import LayoutDrawer from "./layout/layout_drawer"; import LayoutDrawer from "./layout/layout_drawer";
import LayoutIconBack from "./layout/layout_icon_back"; import LayoutIconBack from "./layout/layout_icon_back";
@@ -20,4 +21,5 @@ export { LayoutNavbarNew };
export { ViewFilter }; export { ViewFilter };
export { prisma }; export { prisma };
export { pwd_key_config }; export { pwd_key_config };
export { API_ADDRESS }; export { API_ADDRESS };
export {SkeletonSingle}

View File

@@ -44,6 +44,8 @@ export default function ViewVerification({ phone, otp, user }: IVerification) {
toast.error(setCookies.message) toast.error(setCookies.message)
} }
setLoading(false) setLoading(false)
} else { } else {

View File

@@ -1,4 +1,4 @@
import { API_ADDRESS, LayoutDrawer, WARNA } from "@/module/_global"; import { API_ADDRESS, LayoutDrawer, SkeletonSingle, WARNA } from "@/module/_global";
import { import {
ActionIcon, ActionIcon,
Box, Box,
@@ -74,32 +74,9 @@ export default function ListGroupActive({ status }: { status: boolean }) {
? Array(6) ? Array(6)
.fill(null) .fill(null)
.map((_, i) => ( .map((_, i) => (
<Box pt={20} key={i}> <Box key={i}>
<Group <SkeletonSingle />
align="center" </Box>
style={{
border: `1px solid ${"#DCEED8"}`,
padding: 10,
borderRadius: 10,
cursor: "pointer",
}}
>
<Box>
<ActionIcon
variant="light"
bg={"#DCEED8"}
size={50}
radius={100}
aria-label="icon"
>
<Skeleton height={25} width={25} />
</ActionIcon>
</Box>
<Box>
<Skeleton height={20} width={100} />
</Box>
</Group>
</Box>
)) ))
: isData.map((v, i) => { : isData.map((v, i) => {
return ( return (

View File

@@ -41,11 +41,11 @@ export default function EditDrawerGroup({
}, },
body: JSON.stringify({ body: JSON.stringify({
id: id, id: id,
name : name name: name
}), }),
}); });
setOpenDrawerGroup(false); setOpenDrawerGroup(false);
onUpdated(true); onUpdated(true);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }

View File

@@ -1,22 +1,35 @@
import { prisma } from "@/module/_global"; import { prisma } from "@/module/_global";
import _, { omit } from "lodash";
import { NextRequest } from "next/server"; import { NextRequest } from "next/server";
export async function getAllPosition(req: NextRequest) { export async function getAllPosition(req: NextRequest) {
try { try {
const searchParams = req.nextUrl.searchParams const searchParams = req.nextUrl.searchParams
const groupID = searchParams.get('groupID'); const groupID = "3";
const active = searchParams.get('active');
const positions = await prisma.position.findMany({ const positions = await prisma.position.findMany({
where: { where: {
idGroup: String(groupID), idGroup: String(groupID),
isActive: true, isActive: (active == "true" ? true : false),
}, },
select: { select: {
id: true, id: true,
name: true, name: true,
isActive: true,
Group: {
select: {
name: true
}
}
}, },
}); });
return Response.json(positions); const allData = positions.map((v: any) => ({
..._.omit(v, ["Group"]),
group: v.Group.name
}))
return Response.json(allData);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return Response.json({ success: false, message: "Internal Server Error" }, { status: 500 }); return Response.json({ success: false, message: "Internal Server Error" }, { status: 500 });

View File

@@ -1,18 +1,21 @@
import { prisma } from "@/module/_global"; import { prisma } from "@/module/_global";
import { revalidatePath } from "next/cache";
export async function deletePosition(req: Request) { export async function deletePosition(req: Request) {
try { try {
const data = await req.json(); const data = await req.json();
const active = data.isActive;
const update = await prisma.position.update({ const update = await prisma.position.update({
where: { where: {
id: data.id, id: data.id,
}, },
data: { data: {
isActive: false, isActive: !active,
}, },
}); });
revalidatePath("/position");
return Response.json( return Response.json(
{ success: true, message: "Sukses Delete Position" }, { success: true, message: "Sukses Delete Position" },
{ status: 200 } { status: 200 }

View File

@@ -3,7 +3,6 @@ import { Box, Tabs, rem } from '@mantine/core';
import { IoCloseCircleOutline } from "react-icons/io5" import { IoCloseCircleOutline } from "react-icons/io5"
import { IoMdCheckmarkCircleOutline } from "react-icons/io" import { IoMdCheckmarkCircleOutline } from "react-icons/io"
import ListPositionActive from './ui/list_position_active'; import ListPositionActive from './ui/list_position_active';
import ListPositionNonActive from './ui/list_position_nonactive';
export default function TabListGroup() { export default function TabListGroup() {
const iconStyle = { width: rem(20), height: rem(20) }; const iconStyle = { width: rem(20), height: rem(20) };
@@ -25,11 +24,12 @@ export default function TabListGroup() {
</Tabs.List> </Tabs.List>
<Tabs.Panel value="aktif"> <Tabs.Panel value="aktif">
<ListPositionActive /> <ListPositionActive status={true} />
</Tabs.Panel> </Tabs.Panel>
<Tabs.Panel value="tidak-aktif"> <Tabs.Panel value="tidak-aktif">
<ListPositionNonActive /> <ListPositionActive status={false} />
{/* <ListPositionNonActive /> */}
</Tabs.Panel> </Tabs.Panel>
</Tabs> </Tabs>
</Box> </Box>

View File

@@ -1,10 +1,14 @@
import { LayoutDrawer, WARNA } from "@/module/_global" import { API_ADDRESS, LayoutDrawer, WARNA } from "@/module/_global"
import LayoutModal from "@/module/_global/layout/layout_modal" import LayoutModal from "@/module/_global/layout/layout_modal"
import { Box, Stack, SimpleGrid, Flex, Text, Select, TextInput, Button } from "@mantine/core" import { Box, Stack, SimpleGrid, Flex, Text, Select, TextInput, Button } from "@mantine/core"
import { useState } from "react" import { useState } from "react"
import toast from "react-hot-toast"
import { FaPencil, FaToggleOff } from "react-icons/fa6" import { FaPencil, FaToggleOff } from "react-icons/fa6"
export default function DrawerDetailPosition({ onUpdated }: { onUpdated: (val: boolean) => void }) { export default function DrawerDetailPosition({ onUpdated, id, isActive, }: {
onUpdated: (val: boolean) => void, id: string | null,
isActive: boolean | null;
}) {
const [openDrawerGroup, setOpenDrawerGroup] = useState(false) const [openDrawerGroup, setOpenDrawerGroup] = useState(false)
const [isModal, setModal] = useState(false) const [isModal, setModal] = useState(false)
@@ -20,6 +24,36 @@ export default function DrawerDetailPosition({ onUpdated }: { onUpdated: (val: b
setModal(false) setModal(false)
} }
async function nonActive(val: boolean) {
try {
if (val) {
const res = await fetch(API_ADDRESS.apiDeletePosition, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
id,
isActive,
}),
});
if (res.status == 200) {
onUpdated(true);
} else {
onUpdated(false);
}
}
setModal(false);
} catch (error) {
console.log(error);
setModal(false);
toast.error("Terjadi kesalahan");
onUpdated(false);
}
}
return ( return (
<Box> <Box>
<Stack pt={10}> <Stack pt={10}>
@@ -102,7 +136,7 @@ export default function DrawerDetailPosition({ onUpdated }: { onUpdated: (val: b
<LayoutModal opened={isModal} onClose={() => setModal(false)} <LayoutModal opened={isModal} onClose={() => setModal(false)}
description="Apakah Anda yakin ingin mengubah status aktifasi data?" description="Apakah Anda yakin ingin mengubah status aktifasi data?"
onYes={(val) => { onTrue(val) }} /> onYes={(val) => { nonActive(val) }} />
</Box> </Box>
) )
} }

View File

@@ -1,57 +1,46 @@
import { LayoutDrawer, WARNA } from '@/module/_global'; import { API_ADDRESS, LayoutDrawer, SkeletonSingle, WARNA } from "@/module/_global";
import { ActionIcon, Box, Group, Text, TextInput } from '@mantine/core'; import { ActionIcon, Box, Group, Text, TextInput } from "@mantine/core";
import React, { useState } from 'react'; import React, { useEffect, useState } from "react";
import { FaUserTie } from 'react-icons/fa6'; import { FaUserTie } from "react-icons/fa6";
import { HiMagnifyingGlass } from 'react-icons/hi2'; import { HiMagnifyingGlass } from "react-icons/hi2";
import DrawerDetailPosition from './drawer_detail_position'; import DrawerDetailPosition from "./drawer_detail_position";
import toast from 'react-hot-toast'; import toast from "react-hot-toast";
import _ from "lodash";
const dataGroup = [ type dataPosition = {
{ name: string;
id: 1, idGroup: string;
name: 'Kepala', group: string;
grup: 'Dinas' id: string;
}, isActive: boolean
{ };
id: 2,
name: 'Sekretaris',
grup: 'LPD'
},
{
id: 3,
name: 'Bendahara',
grup: 'Dinas'
},
{
id: 4,
name: 'Anggota',
grup: 'Karang Taruna'
},
{
id: 5,
name: 'Kepala Urusan Kemasyarakatan',
grup: 'Dinas'
},
{
id: 6,
name: 'Kepala Urusan Pemerintahan',
grup: 'Dinas'
},
{
id: 7,
name: 'Kepala Urusan Kependudukan',
grup: 'Dinas'
},
{
id: 8,
name: 'Anggota',
grup: 'Dinas'
},
]
export default function ListPositionActive() { export default function ListPositionActive({ status }: { status: boolean }) {
const [openDrawer, setOpenDrawer] = useState(false) const [openDrawer, setOpenDrawer] = useState(false);
const [isData, setData] = useState("") const [isData, setData] = useState("");
const [isDataPosition, setDataPosition] = useState<dataPosition[]>([]);
const [loading, setLoading] = useState(true);
const [selectId, setSelectId] = useState<string | null>(null);
const [active, setActive] = useState<boolean | null>(null)
async function getAllPosition() {
try {
setDataPosition([]);
setLoading(true)
const res = await fetch(`${API_ADDRESS.apiGetAllPosition}&active=` + status);
const data = await res.json();
setDataPosition(data);
setLoading(false);
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
}
useEffect(() => {
getAllPosition();
}, [status])
return ( return (
<Box pt={20}> <Box pt={20}>
@@ -68,35 +57,65 @@ export default function ListPositionActive() {
leftSection={<HiMagnifyingGlass size={20} />} leftSection={<HiMagnifyingGlass size={20} />}
placeholder="Pencarian" placeholder="Pencarian"
/> />
{dataGroup.map((v, i) => { {loading ? Array(6).fill(null).map((_, i) => (
return ( <Box key={i}>
<Box pt={20} key={i}> <SkeletonSingle />
<Group align='center' style={{ </Box>
border: `1px solid ${"#DCEED8"}`, )) :
padding: 10, isDataPosition.map((v, i) => {
borderRadius: 10 return (
}} onClick={() => { <Box pt={20} key={i}>
setData(v.name) <Group
setOpenDrawer(true) align="center"
}} > style={{
<Box> border: `1px solid ${"#DCEED8"}`,
<ActionIcon variant="light" bg={'#DCEED8'} size={50} radius={100} aria-label="icon"> padding: 10,
<FaUserTie color={WARNA.biruTua} size={25} /> borderRadius: 10,
</ActionIcon> }}
</Box> onClick={() => {
<Box> setData(v.name);
<Text fw={'bold'} c={WARNA.biruTua}>{v.name}</Text> setOpenDrawer(true);
<Text fw={'lighter'} fz={12}>{v.grup}</Text> setSelectId(v.id);
</Box> setActive(v.isActive);
</Group> }}
</Box> >
) <Box>
})} <ActionIcon
<LayoutDrawer opened={openDrawer} onClose={() => setOpenDrawer(false)} title={isData}> variant="light"
<DrawerDetailPosition onUpdated={() => { bg={"#DCEED8"}
setOpenDrawer(false) size={50}
toast.success('Sukses! data tersimpan') radius={100}
}} /> aria-label="icon"
>
<FaUserTie color={WARNA.biruTua} size={25} />
</ActionIcon>
</Box>
<Box>
<Text fw={"bold"} c={WARNA.biruTua}>
{v.name}
</Text>
<Text fw={"lighter"} fz={12}>
{v.group}
</Text>
</Box>
</Group>
</Box>
);
})
}
<LayoutDrawer
opened={openDrawer}
onClose={() => setOpenDrawer(false)}
title={isData}
>
<DrawerDetailPosition
id={selectId}
isActive={active}
onUpdated={() => {
setOpenDrawer(false);
toast.success("Sukses! data tersimpan");
}}
/>
</LayoutDrawer> </LayoutDrawer>
</Box> </Box>
); );

View File

@@ -1,103 +0,0 @@
import { LayoutDrawer, WARNA } from '@/module/_global';
import { ActionIcon, Box, Group, Text, TextInput } from '@mantine/core';
import React, { useState } from 'react';
import { FaUserTie } from 'react-icons/fa6';
import { HiMagnifyingGlass } from 'react-icons/hi2';
import DrawerDetailPosition from './drawer_detail_position';
import toast from 'react-hot-toast';
const dataGroup = [
{
id: 1,
name: 'Kepala',
grup: 'Dinas'
},
{
id: 2,
name: 'Sekretaris',
grup: 'LPD'
},
{
id: 3,
name: 'Bendahara',
grup: 'Dinas'
},
{
id: 4,
name: 'Anggota',
grup: 'Karang Taruna'
},
{
id: 5,
name: 'Kepala Urusan Kemasyarakatan',
grup: 'Dinas'
},
{
id: 6,
name: 'Kepala Urusan Pemerintahan',
grup: 'Dinas'
},
{
id: 7,
name: 'Kepala Urusan Kependudukan',
grup: 'Dinas'
},
{
id: 8,
name: 'Anggota',
grup: 'Dinas'
},
]
export default function ListPositionNonActive() {
const [openDrawer, setOpenDrawer] = useState(false)
const [isData, setData] = useState("")
return (
<Box pt={20}>
<TextInput
styles={{
input: {
color: WARNA.biruTua,
borderRadius: WARNA.biruTua,
borderColor: WARNA.biruTua,
},
}}
size="md"
radius={30}
leftSection={<HiMagnifyingGlass size={20} />}
placeholder="Pencarian"
/>
{dataGroup.map((v, i) => {
return (
<Box pt={20} key={i}>
<Group align='center' style={{
border: `1px solid ${"#DCEED8"}`,
padding: 10,
borderRadius: 10
}} onClick={() => {
setData(v.name)
setOpenDrawer(true)
}}>
<Box>
<ActionIcon variant="light" bg={'#DCEED8'} size={50} radius={100} aria-label="icon">
<FaUserTie color={WARNA.biruTua} size={25} />
</ActionIcon>
</Box>
<Box>
<Text fw={'bold'} c={WARNA.biruTua}>{v.name}</Text>
<Text fw={'lighter'} fz={12}>{v.grup}</Text>
</Box>
</Group>
</Box>
)
})}
<LayoutDrawer opened={openDrawer} onClose={() => setOpenDrawer(false)} title={isData}>
<DrawerDetailPosition onUpdated={() => {
setOpenDrawer(false)
toast.success('Sukses! data tersimpan')
}} />
</LayoutDrawer>
</Box>
);
}