Merge pull request #210 from bipproduction/amalia/06-september-24

upd: calender
This commit is contained in:
Amalia
2024-09-06 17:42:42 +08:00
committed by GitHub
5 changed files with 179 additions and 129 deletions

View File

@@ -0,0 +1,57 @@
import { prisma } from "@/module/_global";
import { funGetUserByCookies } from "@/module/auth";
import { createLogUser } from "@/module/user";
import _ from "lodash";
import { NextResponse } from "next/server";
// TAMBAH MEMBER KALENDER
export async function POST(request: Request, context: { params: { id: string } }) {
try {
const user = await funGetUserByCookies()
if (user.id == undefined) {
return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 401 });
}
const { id } = context.params
const member = await request.json()
const cek = await prisma.divisionCalendar.count({
where: {
id: id
}
})
if (cek == 0) {
return NextResponse.json(
{
success: false,
message: "Gagal menambahkan anggota, data tidak ditemukan",
},
{ status: 404 }
);
}
if (member.length > 0) {
const dataMember = member.map((v: any) => ({
..._.omit(v, ["idUser", "name", "img"]),
idCalendar: id,
idUser: v.idUser,
}))
const insertMember = await prisma.divisionCalendarMember.createMany({
data: dataMember
})
}
// create log user
const log = await createLogUser({ act: 'CREATE', desc: 'User menambah anggota kalender', table: 'divisionCalendar', data: String(id) })
return NextResponse.json({ success: true, message: "Berhasil menambahkan anggota", }, { status: 200 });
} catch (error) {
console.error(error);
return NextResponse.json({ success: false, message: "Gagal menambah anggota, coba lagi nanti", reason: (error as Error).message, }, { status: 500 });
}
}

View File

@@ -1,4 +1,4 @@
import { IEditCalender, IFormCreateCalender } from "./type_calender"; import { IEditCalender, IFormCreateCalender, IFormMemberCalenderNew } from "./type_calender";
export const funGetAllCalender = async (path?: string) => { export const funGetAllCalender = async (path?: string) => {
const response = await fetch(`/api/calender${(path) ? path : ''}`, { next: { tags: ['calender'] } }); const response = await fetch(`/api/calender${(path) ? path : ''}`, { next: { tags: ['calender'] } });
@@ -48,3 +48,14 @@ export const funGetIndicatorCalender = async (path?: string) => {
const response = await fetch(`/api/calender/indicator${(path) ? path : ''}`, { next: { tags: ['hostory'] } }); const response = await fetch(`/api/calender/indicator${(path) ? path : ''}`, { next: { tags: ['hostory'] } });
return await response.json().catch(() => null); return await response.json().catch(() => null);
} }
export const funAddMemberCalender = async (path: string, data: IFormMemberCalenderNew) => {
const response = await fetch(`/api/calender/${path}/member`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
return await response.json().catch(() => null);
};

View File

@@ -94,3 +94,8 @@ export interface IDetailByIdCalender {
linkMeet?: string linkMeet?: string
repeatEventTyper?: string repeatEventTyper?: string
} }
export interface IFormMemberCalenderNew {
idUser: string
name: string
}

View File

@@ -1,9 +1,7 @@
"use client" "use client"
import React, { useState } from 'react'; import React, { useState } from 'react';
import { globalCalender } from '../lib/val_calender';
import { useParams, useRouter } from 'next/navigation'; import { useParams, useRouter } from 'next/navigation';
import { funGetDivisionById, funGetSearchMemberDivision, IDataMemberDivision } from '@/module/division_new'; import { funGetSearchMemberDivision, IDataMemberDivision } from '@/module/division_new';
import { useHookstate } from '@hookstate/core';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import { useShallowEffect } from '@mantine/hooks'; import { useShallowEffect } from '@mantine/hooks';
import { LayoutNavbarNew, SkeletonSingle, WARNA } from '@/module/_global'; import { LayoutNavbarNew, SkeletonSingle, WARNA } from '@/module/_global';
@@ -12,13 +10,15 @@ import { FaCheck } from 'react-icons/fa6';
import { HiMagnifyingGlass } from 'react-icons/hi2'; import { HiMagnifyingGlass } from 'react-icons/hi2';
import { IoArrowBackOutline, IoClose } from 'react-icons/io5'; import { IoArrowBackOutline, IoClose } from 'react-icons/io5';
import { Carousel } from '@mantine/carousel'; import { Carousel } from '@mantine/carousel';
import { funAddMemberCalender, funGetOneCalender } from '../lib/api_calender';
import { IDataDetailByIdMember } from '../lib/type_calender';
export default function CreateUserDetailCalender() { export default function CreateUserDetailCalender() {
const router = useRouter() const router = useRouter()
const param = useParams<{ id: string }>() const param = useParams<{ id: string, detail: string }>()
const [selectedFiles, setSelectedFiles] = useState<any>([]) const [selectedFiles, setSelectedFiles] = useState<any>([])
const [isData, setData] = useState<IDataMemberDivision[]>([]) const [isData, setData] = useState<IDataMemberDivision[]>([])
const member = useHookstate(globalCalender) const [isDataAnggota, setDataAnggota] = useState<IDataDetailByIdMember[]>([])
const [selectAll, setSelectAll] = useState(false) const [selectAll, setSelectAll] = useState(false)
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const [onClickSearch, setOnClickSearch] = useState(false) const [onClickSearch, setOnClickSearch] = useState(false)
@@ -28,11 +28,10 @@ export default function CreateUserDetailCalender() {
try { try {
setLoading(true) setLoading(true)
const response = await funGetSearchMemberDivision("?search=", param.id) const response = await funGetSearchMemberDivision("?search=", param.id)
const res = await funGetOneCalender(param.detail)
if (response.success) { if (response.success) {
setDataAnggota(res.data.member)
setData(response.data) setData(response.data)
if (member.length > 0) {
setSelectedFiles(JSON.parse(JSON.stringify(member.get())))
}
setLoading(false) setLoading(false)
} else { } else {
toast.error(response.message) toast.error(response.message)
@@ -79,12 +78,23 @@ export default function CreateUserDetailCalender() {
}; };
function onSubmit() { async function onSubmit() {
if (selectedFiles.length == 0) { try {
return toast.error("Error! silahkan pilih anggota") if (selectedFiles.length == 0) {
return toast.error("Error! silahkan pilih anggota")
}
const res = await funAddMemberCalender(param.detail, selectedFiles)
if (res.success) {
toast.success(res.message)
router.push('./')
} else {
toast.error(res.message)
}
} catch (error) {
console.error(error);
toast.error("Gagal menambahkan anggota, coba lagi nanti");
} }
member.set(selectedFiles)
// onClose(true)
} }
const handleSearchClick = () => { const handleSearchClick = () => {
@@ -161,8 +171,8 @@ export default function CreateUserDetailCalender() {
) )
: null : null
} }
{/* Close User */} {/* Close User */}
<Box pos={'fixed'} top={80} pl={rem(20)} pr={rem(20)} pt={rem(20)} pb={rem(5)} w={"100%"} style={{ <Box pos={'fixed'} top={80} pl={rem(20)} pr={rem(20)} pt={rem(20)} pb={rem(5)} w={"100%"} style={{
maxWidth: rem(550), maxWidth: rem(550),
zIndex: 100, zIndex: 100,
backgroundColor: `${WARNA.bgWhite}`, backgroundColor: `${WARNA.bgWhite}`,
@@ -220,8 +230,9 @@ export default function CreateUserDetailCalender() {
<Box mt={20} mb={100}> <Box mt={20} mb={100}>
{isData.map((v, i) => { {isData.map((v, i) => {
const isSelected = selectedFiles.some((i: any) => i?.idUser == v.idUser); const isSelected = selectedFiles.some((i: any) => i?.idUser == v.idUser);
const found = isDataAnggota.some((i: any) => i.idUser == v.idUser)
return ( return (
<Box mb={15} key={i} onClick={() => handleFileClick(i)}> <Box mb={15} key={i} onClick={() => (!found) ? handleFileClick(i) : null}>
<Grid align='center' gutter={{ <Grid align='center' gutter={{
base: 60, base: 60,
xl: "xs" xl: "xs"
@@ -233,6 +244,7 @@ export default function CreateUserDetailCalender() {
<Flex justify='space-between' align={"center"}> <Flex justify='space-between' align={"center"}>
<Flex direction={'column'} align="flex-start" justify="flex-start"> <Flex direction={'column'} align="flex-start" justify="flex-start">
<Text lineClamp={1}>{v.name}</Text> <Text lineClamp={1}>{v.name}</Text>
<Text c={"dimmed"} lineClamp={1}>{(found) ? "sudah menjadi anggota" : ""}</Text>
</Flex> </Flex>
{isSelected ? <FaCheck /> : null} {isSelected ? <FaCheck /> : null}
</Flex> </Flex>

View File

@@ -193,131 +193,96 @@ export default function DetailEventDivision() {
</Stack> </Stack>
</Box> </Box>
} }
{/* {loading ?
<Box pt={20}> {
<Box loading ?
style={{ <Box pt={20}>
border: `1px solid ${"#C7D6E8"}`,
borderRadius: 10,
}}
px={20}
pb={20}
>
{Array(4)
.fill(null)
.map((_, i) => (
<Box
key={i}
>
<SkeletonSingle />
</Box>
))}
</Box>
</Box>
:
<Box pt={20}>
<Group justify='space-between'>
<Text fw={"bold"}>Total Anggota</Text>
<Text>{isLengthMember} Anggota</Text>
</Group>
<Box mb={20}>
<Box <Box
style={{ style={{
border: `1px solid ${"#C7D6E8"}`, border: `1px solid ${"#C7D6E8"}`,
borderRadius: 10, borderRadius: 10,
}} }}
px={20} px={20}
pt={20} pb={20}
> >
{isLengthMember == 0 ? ( {Array(4)
<Box style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '30vh' }}> .fill(null)
<Text c="dimmed" ta={"center"} fs={"italic"}>Tidak ada anggota</Text> .map((_, i) => (
</Box> <Box
) : key={i}
<Box> >
{isDataAnggota.map((v, i) => { <SkeletonSingle />
return ( </Box>
<Box my={10} key={i}> ))}
<Grid align='center' gutter={"lg"}>
<Grid.Col span={{
base: 3,
xl: 2
}}>
<Avatar src={`/api/file/img?jenis=image&cat=user&file=${v.img}`} alt="it's me" size="lg" />
</Grid.Col>
<Grid.Col span={{
base: 9,
xl: 10
}}>
<Flex justify='space-between' align={"center"}>
<Flex direction={'column'} align="flex-start" justify="flex-start">
<Text lineClamp={1}>{v.name}</Text>
<Text c={"#5A687D"} fz={14} lineClamp={1}>
{v.email}
</Text>
</Flex>
</Flex>
</Grid.Col>
</Grid>
<Box mt={10}>
<Divider size={"xs"} />
</Box>
</Box>
);
})}
</Box>
}
</Box> </Box>
</Box> </Box>
</Box> :
} */}
<Box pt={20}> <Box pt={20}>
<Group justify='space-between'> <Group justify='space-between'>
<Text fw={"bold"}>Total Anggota</Text> <Text fw={"bold"}>Total Anggota</Text>
<Text>{isLengthMember} Anggota</Text> <Text>{isLengthMember} Anggota</Text>
</Group> </Group>
<Box mb={20}> <Box mb={20}>
<Box <Box
style={{ style={{
border: `1px solid ${"#C7D6E8"}`, border: `1px solid ${"#C7D6E8"}`,
borderRadius: 10, borderRadius: 10,
}} }}
px={20} px={20}
pt={20} pt={20}
> >
<Box onClick={() => setOpenDrawerUser(true)}> {
<Box my={10}> isLengthMember == 0 ? (
<Grid align='center' gutter={"lg"}> <Box style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '30vh' }}>
<Grid.Col span={{ <Text c="dimmed" ta={"center"} fs={"italic"}>Tidak ada anggota</Text>
base: 3, </Box>
xl: 2 ) :
}}> <Box>
<Avatar src={''} alt="it's me" size="lg" /> {
</Grid.Col> isDataAnggota.map((v, i) => {
<Grid.Col span={{ return (
base: 9, <Box onClick={() => setOpenDrawerUser(true)} key={i}>
xl: 10 <Box my={10}>
}}> <Grid align='center' gutter={"lg"}>
<Flex justify='space-between' align={"center"}> <Grid.Col span={{
<Flex direction={'column'} align="flex-start" justify="flex-start"> base: 3,
<Text lineClamp={1}>Nama</Text> xl: 2
<Text c={"#5A687D"} fz={14} lineClamp={1}> }}>
email.com <Avatar src={`/api/file/img?jenis=image&cat=user&file=${v.img}`} alt="it's me" size="lg" />
</Text> </Grid.Col>
</Flex> <Grid.Col span={{
</Flex> base: 9,
</Grid.Col> xl: 10
</Grid> }}>
<Box mt={10}> <Flex justify='space-between' align={"center"}>
<Divider size={"xs"} /> <Flex direction={'column'} align="flex-start" justify="flex-start">
</Box> <Text lineClamp={1}>{v.name}</Text>
<Text c={"#5A687D"} fz={14} lineClamp={1}>
{v.email}
</Text>
</Flex>
</Flex>
</Grid.Col>
</Grid>
<Box mt={10}>
<Divider size={"xs"} />
</Box>
</Box>
</Box>
)
})
}
</Box>
}
</Box> </Box>
</Box> </Box>
</Box> </Box>
</Box> }
</Box>
</Box> </Box>
<LayoutDrawer opened={openDrawerUser} title={<Text lineClamp={1}>Menu</Text>} onClose={() => setOpenDrawerUser(false)}> <LayoutDrawer opened={openDrawerUser} title={<Text lineClamp={1}>Menu</Text>} onClose={() => setOpenDrawerUser(false)}>
<Box> <Box>
<Stack pt={10}> <Stack pt={10}>