diff --git a/src/app/(application)/discussion/[id]/add-member/page.tsx b/src/app/(application)/discussion/[id]/add-member/page.tsx new file mode 100644 index 0000000..7f96823 --- /dev/null +++ b/src/app/(application)/discussion/[id]/add-member/page.tsx @@ -0,0 +1,9 @@ +import { AddMemberDiscussionGeneral } from "@/module/discussion_general"; + +export default function Page() { + return ( + <> + + + ) +} \ No newline at end of file diff --git a/src/app/(application)/discussion/[id]/edit/page.tsx b/src/app/(application)/discussion/[id]/edit/page.tsx new file mode 100644 index 0000000..7164db5 --- /dev/null +++ b/src/app/(application)/discussion/[id]/edit/page.tsx @@ -0,0 +1,14 @@ +import { LayoutNavbarNew } from "@/module/_global"; +import { FormEditDiscussionGeneral } from "@/module/discussion_general"; +import { Box } from "@mantine/core"; + +export default function Page() { + return ( + <> + + } /> + + + + ) +} \ No newline at end of file diff --git a/src/app/api/discussion-general/[id]/comment/route.ts b/src/app/api/discussion-general/[id]/comment/route.ts new file mode 100644 index 0000000..37edf90 --- /dev/null +++ b/src/app/api/discussion-general/[id]/comment/route.ts @@ -0,0 +1,46 @@ +import { prisma } from "@/module/_global"; +import { funGetUserByCookies } from "@/module/auth"; +import { createLogUser } from "@/module/user"; +import { NextResponse } from "next/server"; + + +// KIRIM KOMENTAR DISKUSI UMUM +export async function POST(request: Request, context: { params: { id: string } }) { + try { + const { id } = context.params + const { desc } = (await request.json()); + + const user = await funGetUserByCookies() + if (user.id == undefined) { + return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 401 }); + } + + const cek = await prisma.discussion.count({ + where: { + id, + isActive: true + } + }) + + if (cek == 0) { + return NextResponse.json({ success: false, message: "Gagal menambahkan komentar, data tidak ditemukan" }, { status: 404 }); + } + + + const data = await prisma.discussionComment.create({ + data: { + comment: desc, + idDiscussion: id, + idUser: user.id + } + }) + + // create log user + const log = await createLogUser({ act: 'CREATE', desc: 'User menambah komentar pada diskusi umum', table: 'discussionComment', data: data.id }) + return NextResponse.json({ success: true, message: "Berhasil menambah komentar" }, { status: 200 }); + + } catch (error) { + console.error(error) + return NextResponse.json({ success: false, message: "Gagal menambahkan komentar, coba lagi nanti (error: 500)" }) + } +} \ No newline at end of file diff --git a/src/app/api/discussion-general/[id]/route.ts b/src/app/api/discussion-general/[id]/route.ts index 9aacc5e..899097b 100644 --- a/src/app/api/discussion-general/[id]/route.ts +++ b/src/app/api/discussion-general/[id]/route.ts @@ -1,9 +1,12 @@ import { prisma } from "@/module/_global"; import { funGetUserByCookies } from "@/module/auth"; +import { createLogUser } from "@/module/user"; import _ from "lodash"; import moment from "moment"; import { NextResponse } from "next/server"; + +// GET ONE DETAIL DISKUSI UMUM export async function GET(request: Request, context: { params: { id: string } }) { try { let dataFix @@ -36,6 +39,7 @@ export async function GET(request: Request, context: { params: { id: string } }) select: { id: true, title: true, + idGroup: true, desc: true, status: true, createdAt: true, @@ -44,6 +48,7 @@ export async function GET(request: Request, context: { params: { id: string } }) dataFix = { id: data?.id, + idGroup:data?.idGroup, title: data?.title, desc: data?.desc, status: data?.status, @@ -72,7 +77,7 @@ export async function GET(request: Request, context: { params: { id: string } }) dataFix = data.map((v: any) => ({ ..._.omit(v, ["createdAt", "User",]), - createdAt: moment(v.createdAt).format("ll"), + createdAt: moment(v.createdAt).format("lll"), username: v.User.name, img: v.User.img })) @@ -99,6 +104,20 @@ export async function GET(request: Request, context: { params: { id: string } }) name: v.User.name, img: v.User.img })) + } else if (kategori == "cek-anggota") { + const cek = await prisma.discussionMember.count({ + where: { + idDiscussion: id, + isActive: true, + idUser: user.id + } + }) + + if (cek > 0) { + dataFix = true + } else { + dataFix = false + } } @@ -109,4 +128,136 @@ export async function GET(request: Request, context: { params: { id: string } }) console.error(error); return NextResponse.json({ success: false, message: "Gagal mendapatkan diskusi, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); } +} + + +// OPEN OR CLOSE DISKUSI UMUM +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 { status } = (await request.json()); + let newStatus; + if (status === 1) { + newStatus = 2; + } else if (status === 2) { + newStatus = 1; + } else { + return NextResponse.json({ success: false, message: "Invalid status" }, { status: 400 }); + } + + const data = await prisma.discussion.count({ + where: { + id: id + }, + }); + + if (data == 0) { + return NextResponse.json({ success: false, message: "Gagal mendapatkan diskusi, data tidak ditemukan" }, { status: 404 }); + } + + const result = await prisma.discussion.update({ + where: { + id + }, + data: { + status: newStatus + } + }); + + // create log user + const log = await createLogUser({ act: 'UPDATE', desc: 'User mengupdate status diskusi umum', table: 'discussion', data: id }) + + return NextResponse.json({ success: true, message: "Berhasil mengedit diskusi umum" }, { status: 200 }); + + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mengedit diskusi umum, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); + } +} + + +// DELETE DISCUSSION +export async function DELETE(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 cek = await prisma.discussion.count({ + where: { + id: id + }, + }); + + if (cek == 0) { + return NextResponse.json({ success: false, message: "Gagal menghapus diskusi umum, data tidak ditemukan" }, { status: 404 }); + } + + + const data = await prisma.discussion.update({ + where: { + id + }, + data: { + isActive: false + } + }); + + + // create log user + const log = await createLogUser({ act: 'DELETE', desc: 'User menghapus data diskusi umum', table: 'disscussion', data: id }) + return NextResponse.json({ success: true, message: "Berhasil menghapus diskusi umum", user: user.id }, { status: 200 }); + + + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal menghapus diskusi umum, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); + } +} + + +// EDIT DISCUSSION +export async function PUT(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 { title, desc } = (await request.json()); + + const data = await prisma.discussion.count({ + where: { + id: id + }, + }); + + if (data == 0) { + return NextResponse.json({ success: false, message: "Gagal mengedit diskusi umum, data tidak ditemukan" }, { status: 404 }); + } + + const update = await prisma.discussion.update({ + where: { + id + }, + data: { + desc, + title + } + }); + + // create log user + const log = await createLogUser({ act: 'UPDATE', desc: 'User mengupdate data diskusi umum', table: 'discussion', data: id }) + return NextResponse.json({ success: true, message: "Berhasil mengedit diskusi umum" }, { status: 200 }); + + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mengedit diskusi umum, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); + } } \ No newline at end of file diff --git a/src/app/api/discussion-general/route.ts b/src/app/api/discussion-general/route.ts index e9b01ce..5061216 100644 --- a/src/app/api/discussion-general/route.ts +++ b/src/app/api/discussion-general/route.ts @@ -4,6 +4,7 @@ import { createLogUser } from "@/module/user"; import _ from "lodash"; import moment from "moment"; import { NextResponse } from "next/server"; +import "moment/locale/id"; @@ -82,9 +83,15 @@ export async function GET(request: Request) { mode: "insensitive" }, }, - orderBy: { - status: 'desc' - }, + orderBy: [ + { + status: 'desc' + }, + { + createdAt: 'desc' + } + ], + select: { id: true, title: true, diff --git a/src/module/discussion_general/index.ts b/src/module/discussion_general/index.ts index e612e04..bb5e452 100644 --- a/src/module/discussion_general/index.ts +++ b/src/module/discussion_general/index.ts @@ -1,5 +1,7 @@ +import AddMemberDiscussionGeneral from "./ui/add_member"; import FormCreateDiscussionGeneral from "./ui/create_discussion"; import DetailDiscussionGeneral from "./ui/detail_discussion_general"; +import FormEditDiscussionGeneral from "./ui/edit_discussion_general"; import ListDiscussionGeneral from "./ui/list_discussion"; import MemberDiscussionGeneral from "./ui/member_discussion_general"; import NavbarDiscussionGeneral from "./ui/navbar_discussion"; @@ -8,4 +10,6 @@ export { ListDiscussionGeneral } export { NavbarDiscussionGeneral } export { FormCreateDiscussionGeneral } export { DetailDiscussionGeneral } -export { MemberDiscussionGeneral } \ No newline at end of file +export { MemberDiscussionGeneral } +export { FormEditDiscussionGeneral } +export { AddMemberDiscussionGeneral } \ No newline at end of file diff --git a/src/module/discussion_general/lib/api_discussion_general.ts b/src/module/discussion_general/lib/api_discussion_general.ts index d9cf15b..1d7a9bd 100644 --- a/src/module/discussion_general/lib/api_discussion_general.ts +++ b/src/module/discussion_general/lib/api_discussion_general.ts @@ -20,3 +20,72 @@ export const funGetOneDiscussionGeneral = async (id: string, path: string) => { const response = await fetch(`/api/discussion-general/${id}${(path) ? path : ''}`, { next: { tags: ['discussion-general'] } }); return await response.json().catch(() => null); } + +export const funCreateComentDiscussionGeneral = async (path: string, data: { desc: string }) => { + const response = await fetch(`/api/discussion-general/${path}/comment`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); + return await response.json().catch(() => null); +} + + + +export const funEditStatusDiscussionGeneral = async (path: string, data: { status: number }) => { + const response = await fetch(`/api/discussion-general/${path}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); + return await response.json().catch(() => null); +} + + +export const funEditDiscussionGeneral = async (path: string, data: { title: string, desc: string }) => { + const response = await fetch(`/api/discussion-general/${path}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); + return await response.json().catch(() => null); +} + + +export const funDeleteDiscussionGeneral = async (path: string) => { + const response = await fetch(`/api/discussion-general/${path}`, { + method: "DELETE", + }); + return await response.json().catch(() => null); +} + +export const funAddMemberDiscussionGeneral = async (path: string, data: { member: IFormMemberDisscussionGeneral[] }) => { + const response = await fetch(`/api/discussion-general/${path}/member`, { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(data) + }) + + return await response.json().catch(() => null) +} + + +export const funDelMemberDiscussionGeneral = async (path: string, data: { idUser: string }) => { + const response = await fetch(`/api/discussion-general/${path}/member`, { + method: "DELETE", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(data) + }) + + return await response.json().catch(() => null) +} \ No newline at end of file diff --git a/src/module/discussion_general/lib/type_discussion_general.ts b/src/module/discussion_general/lib/type_discussion_general.ts index 94b130d..62be53f 100644 --- a/src/module/discussion_general/lib/type_discussion_general.ts +++ b/src/module/discussion_general/lib/type_discussion_general.ts @@ -19,4 +19,4 @@ export interface IFormMemberDisscussionGeneral { idUser: string img: string username: string -} \ No newline at end of file +} diff --git a/src/module/discussion_general/ui/add_member.tsx b/src/module/discussion_general/ui/add_member.tsx new file mode 100644 index 0000000..ab2a6b9 --- /dev/null +++ b/src/module/discussion_general/ui/add_member.tsx @@ -0,0 +1,293 @@ +"use client" +import { LayoutNavbarNew, SkeletonList, TEMA } from '@/module/_global'; +import LayoutModal from '@/module/_global/layout/layout_modal'; +import { funGetUserByCookies } from '@/module/auth'; +import { funGetAllmember, TypeUser } from '@/module/user'; +import { useHookstate } from '@hookstate/core'; +import { Carousel } from '@mantine/carousel'; +import { ActionIcon, Avatar, Box, Button, Center, Divider, Flex, Grid, Indicator, rem, Stack, Text, TextInput } from '@mantine/core'; +import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; +import { useParams, useRouter } from 'next/navigation'; +import { useState } from 'react'; +import toast from 'react-hot-toast'; +import { FaCheck } from 'react-icons/fa6'; +import { HiMagnifyingGlass } from 'react-icons/hi2'; +import { IoArrowBackOutline, IoClose } from 'react-icons/io5'; +import { funAddMemberDiscussionGeneral, funGetOneDiscussionGeneral } from '../lib/api_discussion_general'; +import { IFormMemberDisscussionGeneral } from '../lib/type_discussion_general'; + + + +export default function AddMemberDiscussionGeneral() { + const router = useRouter() + const [selectedFiles, setSelectedFiles] = useState([]); + const [dataMember, setDataMember] = useState([]) + const [memberDb, setMemberDb] = useState([]) + const [group, setGroup] = useState("") + const [isOpen, setOpen] = useState(false) + const param = useParams<{ id: string }>() + const [loading, setLoading] = useState(true) + const [onClickSearch, setOnClickSearch] = useState(false) + const tema = useHookstate(TEMA) + const isMobile2 = useMediaQuery("(max-width: 438px)") + const [loadingModal, setLoadingModal] = useState(false) + + const handleFileClick = (index: number) => { + if (selectedFiles.some((i: any) => i.idUser == dataMember[index].id)) { + setSelectedFiles(selectedFiles.filter((i: any) => i.idUser != dataMember[index].id)) + } else { + setSelectedFiles([...selectedFiles, { idUser: dataMember[index].id, name: dataMember[index].name, img: dataMember[index].img }]) + } + }; + + function handleXMember(id: number) { + setSelectedFiles(selectedFiles.filter((i: any) => i.idUser != id)) + } + + + async function loadMember(group: string, search: string) { + try { + setLoading(true) + const res = await funGetAllmember('?active=true&group=' + group + '&search=' + search); + const user = await funGetUserByCookies(); + + if (res.success) { + // const dariUserLogin = res.data.filter((i: any) => i.id != user.id) + // const fixListUser = dariUserLogin.filter((i: any) => i.idUserRole == 'coadmin' || i.idUserRole == 'user') + const fixListUser = res.data.filter((i: any) => i.idUserRole != 'supadmin') + setDataMember(fixListUser) + } else { + toast.error(res.message) + } + } catch (error) { + console.error(error) + toast.error("Gagal memuat data, coba lagi nanti") + } finally { + setLoading(false) + } + + } + + async function loadFirst() { + const respon = await funGetOneDiscussionGeneral(param.id, '?cat=detail'); + const respon2 = await funGetOneDiscussionGeneral(param.id, '?cat=anggota'); + if (respon.success) { + setGroup(respon.data.idGroup) + setMemberDb(respon2.data) + loadMember(respon.data.idGroup, "") + } else { + toast.error(respon.message); + } + } + + async function addMember() { + try { + setLoadingModal(true) + const res = await funAddMemberDiscussionGeneral(param.id, { member: selectedFiles }) + if (res.success) { + toast.success(res.message) + router.push("/discussion/" + param.id + "/member") + } else { + toast.error(res.message) + } + } catch (error) { + console.error(error); + toast.error("Gagal menambahkan anggota diskusi umum, coba lagi nanti"); + } finally { + setLoadingModal(false) + setOpen(false) + } + } + + function onCheck() { + if (selectedFiles.length == 0) { + return toast.error("Error! silahkan pilih anggota") + } + setOpen(true) + } + + + useShallowEffect(() => { + loadFirst() + }, []); + + const handleSearchClick = () => { + setOnClickSearch(true); + }; + + const handleClose = () => { + setOnClickSearch(false); + }; + + return ( + + + + } + /> + {onClickSearch + ? ( + + + + + + + + + loadMember(group, e.target.value)} + /> + + + + ) + : null + } + + {selectedFiles.length > 0 ? ( + + {selectedFiles.map((v: any, i: any) => { + return ( + + { handleXMember(v.idUser) }} + > +
+ }> + + +
+ {v.name} +
+
+ ) + })} +
+ ) : ( + + + Tidak ada anggota yang dipilih + + + )} +
+ + {loading ? + Array(8) + .fill(null) + .map((_, i) => ( + + + + )) + : + (dataMember.length === 0) ? + + Tidak ada anggota + + : + + {dataMember.map((v: any, index: any) => { + const isSelected = selectedFiles.some((i: any) => i.idUser == dataMember[index].id) + const found = memberDb.some((i: any) => i.idUser == v.id) + return ( + (!found) ? handleFileClick(index) : null}> + + + + + + + + {v.name} + {(found) ? "sudah menjadi anggota divisi" : ""} + + {isSelected ? : null} + + + + + + + + ) + })} + + } + + + + + setOpen(false)} + description="Apakah Anda yakin ingin menambahkan anggota diskusi umum?" + onYes={(val) => { + if (val) { + addMember() + } else { + setOpen(false) + } + }} /> +
+ ); +} diff --git a/src/module/discussion_general/ui/detail_discussion_general.tsx b/src/module/discussion_general/ui/detail_discussion_general.tsx index 651b540..5a64475 100644 --- a/src/module/discussion_general/ui/detail_discussion_general.tsx +++ b/src/module/discussion_general/ui/detail_discussion_general.tsx @@ -1,6 +1,5 @@ "use client" import { globalRole, keyWibu, LayoutDrawer, LayoutNavbarNew, TEMA } from "@/module/_global"; -import { globalIsAdminDivision, globalIsMemberDivision } from "@/module/division_new"; import { useHookstate } from "@hookstate/core"; import { ActionIcon, Avatar, Badge, Box, Center, Divider, Flex, Grid, Group, rem, Skeleton, Spoiler, Text, TextInput } from "@mantine/core"; import { useMediaQuery, useShallowEffect } from "@mantine/hooks"; @@ -9,15 +8,16 @@ import "moment/locale/id"; import { useParams, useRouter } from "next/navigation"; import { useState } from "react"; import toast from "react-hot-toast"; +import { BiSolidCommentDetail } from "react-icons/bi"; import { GrChatOption } from "react-icons/gr"; import { HiMenu } from "react-icons/hi"; import { VscSend } from "react-icons/vsc"; import { useWibuRealtime } from "wibu-realtime"; -import { funGetOneDiscussionGeneral } from "../lib/api_discussion_general"; +import { funCreateComentDiscussionGeneral, funGetOneDiscussionGeneral } from "../lib/api_discussion_general"; import { IComentsDisscussionGeneral, IDetailDiscussionGeneral } from "../lib/type_discussion_general"; import { globalRefreshDiscussionGeneral } from "../lib/val_discussion_general"; import DrawerDetailDiscussionGeneral from "./drawer_detail_discussion_general"; -import { BiSolidCommentDetail } from "react-icons/bi"; +import { funGetUserByCookies } from "@/module/auth"; export default function DetailDiscussionGeneral() { const [isData, setData] = useState() @@ -25,15 +25,13 @@ export default function DetailDiscussionGeneral() { const [isComent, setIsComent] = useState("") const param = useParams<{ id: string }>() const [isLoad, setIsLoad] = useState(true) - const [loadingKomentar, setLoadingKomentar] = useState(false) + const [loadingKomentar, setLoadingKomentar] = useState(true) const refresh = useHookstate(globalRefreshDiscussionGeneral) const roleLogin = useHookstate(globalRole) - const [isCreator, setCreator] = useState(false) - const [isUser, setUser] = useState('') - const adminLogin = useHookstate(globalIsAdminDivision) - const memberDivision = useHookstate(globalIsMemberDivision) + const [memberDiscussion, setMemberDiscussion] = useState(false) const tema = useHookstate(TEMA) const router = useRouter() + const [isUser, setUser] = useState('') const [openDrawer, setOpenDrawer] = useState(false) const isMobile = useMediaQuery('(max-width: 369px)') const isMobile2 = useMediaQuery("(max-width: 438px)") @@ -46,8 +44,12 @@ export default function DetailDiscussionGeneral() { try { setIsLoad(loading) const response = await funGetOneDiscussionGeneral(param.id, "?cat=detail") + const cekAnggota = await funGetOneDiscussionGeneral(param.id, "?cat=cek-anggota") + const userLogin = await funGetUserByCookies() if (response.success) { setData(response.data) + setMemberDiscussion(cekAnggota.data) + setUser(String(userLogin?.id)) } else { toast.error(response.message) } @@ -63,25 +65,35 @@ export default function DetailDiscussionGeneral() { getData(true) }, [refresh.get()]) - // useShallowEffect(() => { - // if (dataRealTime && dataRealTime.some((i: any) => i.category == 'discussion-detail' && i.id == param.id)) { - // getData(false) - // } + useShallowEffect(() => { + getKomentar(true) + }, []) - // if (dataRealTime && dataRealTime.some((i: any) => i.category == 'discussion-delete' && i.id == param.id && i.user != isUser)) { - // toast.error("Data telah di hapus, anda akan beralih ke halaman list diskusi") - // setTimeout(() => { - // router.push(`/division/${param.id}/discussion`) - // }, 1000) - // } - // }, [dataRealTime]) + + + useShallowEffect(() => { + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'discussion-general-detail' && i.id == param.id)) { + getData(false) + } + + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'discussion-general-comment' && i.id == param.id)) { + getKomentar(false) + } + + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'discussion-general-delete' && i.id == param.id && i.user != isUser)) { + toast.error("Data telah di hapus, anda akan beralih ke halaman list diskusi umum") + setTimeout(() => { + router.push(`/discussion`) + }, 1000) + } + }, [dataRealTime]) async function getKomentar(loading: boolean) { try { setLoadingKomentar(loading) const response = await funGetOneDiscussionGeneral(param.id, "?cat=komentar") if (response.success) { - setData(response.data) + setDataKomentar(response.data) } else { toast.error(response.message) } @@ -92,27 +104,26 @@ export default function DetailDiscussionGeneral() { } } - // const sendComent = async () => { - // try { - // if (isComent.trim() == "") { - // return toast.error("Masukkan Komentar Anda") - // } - // const response = await funCreateComent(id, { comment: isComent, idDiscussion: param.detail }) + const sendComent = async () => { + try { + if (isComent.trim() == "") { + return toast.error("Masukkan Komentar Anda") + } + const response = await funCreateComentDiscussionGeneral(param.id, { desc: isComent }) - // if (response.success) { - // setIsComent("") - // setDataRealtime([{ - // category: "discussion-detail", - // id: id, - // }]) - // reloadData() - // } else { - // toast.error(response.message) - // } - // } catch (error) { - // console.error(error) - // } - // } + if (response.success) { + setIsComent("") + setDataRealtime([{ + category: "discussion-general-comment", + id: param.id, + }]) + } else { + toast.error(response.message) + } + } catch (error) { + console.error(error) + } + } @@ -122,11 +133,9 @@ export default function DetailDiscussionGeneral() { setOpenDrawer(true)} bg={tema.get().bgIcon} size="lg" radius="lg" aria-label="Settings"> - - - : <> + setOpenDrawer(true)} bg={tema.get().bgIcon} size="lg" radius="lg" aria-label="Settings"> + + } /> setOpenDrawer(false)}> @@ -230,7 +239,7 @@ export default function DetailDiscussionGeneral() { - {isData?.createdAt} + {moment(isData?.createdAt).format('ll')} @@ -258,7 +267,7 @@ export default function DetailDiscussionGeneral() { } - {isLoad ? + {loadingKomentar ? Array(2) .fill(0) .map((_, i) => ( @@ -294,7 +303,7 @@ export default function DetailDiscussionGeneral() { - + @@ -304,7 +313,7 @@ export default function DetailDiscussionGeneral() { - {moment(v.createdAt).format("ll")} + {moment(v.createdAt).format('lll').replace('pukul', '')} @@ -353,7 +362,7 @@ export default function DetailDiscussionGeneral() { }} size="md" placeholder="Kirim Komentar" - disabled={(isData?.status === 2 || (!memberDivision.get() && (roleLogin.get() == "user" || roleLogin.get() == "coadmin")))} + disabled={(isData?.status === 2 || (!memberDiscussion && (roleLogin.get() == "user" || roleLogin.get() == "coadmin")))} onChange={(e) => setIsComent(e.target.value)} value={isComent} maxLength={300} @@ -363,8 +372,8 @@ export default function DetailDiscussionGeneral() {
+ onClick={sendComent} + variant="subtle" aria-label="submit" disabled={(isData?.status === 2 || (!memberDiscussion && (roleLogin.get() == "user" || roleLogin.get() == "coadmin")))}>
diff --git a/src/module/discussion_general/ui/drawer_detail_discussion_general.tsx b/src/module/discussion_general/ui/drawer_detail_discussion_general.tsx index d6eada3..c5a656d 100644 --- a/src/module/discussion_general/ui/drawer_detail_discussion_general.tsx +++ b/src/module/discussion_general/ui/drawer_detail_discussion_general.tsx @@ -1,4 +1,4 @@ -import { keyWibu, TEMA } from "@/module/_global"; +import { globalRole, keyWibu, TEMA } from "@/module/_global"; import LayoutModal from "@/module/_global/layout/layout_modal"; import { useHookstate } from "@hookstate/core"; import { Box, Flex, SimpleGrid, Stack, Text } from "@mantine/core"; @@ -8,13 +8,16 @@ import { BsTrash3 } from "react-icons/bs"; import { FaCheck, FaPencil, FaUsers } from "react-icons/fa6"; import { MdClose } from "react-icons/md"; import { useWibuRealtime } from "wibu-realtime"; +import { funDeleteDiscussionGeneral, funEditStatusDiscussionGeneral } from "../lib/api_discussion_general"; import { globalRefreshDiscussionGeneral } from "../lib/val_discussion_general"; +import toast from "react-hot-toast"; export default function DrawerDetailDiscussionGeneral({ onSuccess, id, status }: { onSuccess: (val: boolean) => void, id: string, status: number }) { const [isValModal, setValModal] = useState(false) const [isValModalStatus, setValModalStatus] = useState(false) const router = useRouter() const param = useParams<{ id: string, detail: string }>() + const roleLogin = useHookstate(globalRole) const refresh = useHookstate(globalRefreshDiscussionGeneral) const tema = useHookstate(TEMA) const [dataRealTime, setDataRealtime] = useWibuRealtime({ @@ -25,63 +28,63 @@ export default function DrawerDetailDiscussionGeneral({ onSuccess, id, status }: const [loadingDelete, setLoadingDelete] = useState(false) - // async function fetchStatusDiscussion(val: boolean) { - // try { - // setLoadingUpdate(true) - // if (val) { - // const response = await funEditStatusDiscussion(id, { status: status }) - // if (response.success) { - // toast.success(response.message) - // refresh.set(!refresh.get()) - // setDataRealtime([{ - // category: "discussion-detail", - // id: id, - // }]) - // onSuccess(false) - // } else { - // toast.error(response.message) - // } - // } - // } catch (error) { - // console.error(error); - // toast.error("Gagal menambahkan diskusi, coba lagi nanti"); - // } finally { - // setLoadingUpdate(false) - // setValModalStatus(false) - // } - // } + async function fetchStatusDiscussion(val: boolean) { + try { + setLoadingUpdate(true) + if (val) { + const response = await funEditStatusDiscussionGeneral(id, { status: status }) + if (response.success) { + toast.success(response.message) + refresh.set(!refresh.get()) + setDataRealtime([{ + category: "discussion-general-detail", + id: id, + }]) + onSuccess(false) + } else { + toast.error(response.message) + } + } + } catch (error) { + console.error(error); + toast.error("Gagal mengupdate diskusi umum, coba lagi nanti"); + } finally { + setLoadingUpdate(false) + setValModalStatus(false) + } + } - // async function fetchDeleteDiscussion(val: boolean) { - // try { - // if (val) { - // setLoadingDelete(true) - // const response = await funDeleteDiscussion(id) - // if (response.success) { - // setDataRealtime([ - // { - // category: "discussion-delete", - // id: id, - // user: response.user - // }, - // { - // category: "division/" + param.id + "/discussion", - // } - // ]) - // toast.success(response.message) - // onSuccess(false) - // router.push(`/division/${param.id}/discussion`) - // } else { - // toast.error(response.message) - // } - // } - // } catch (error) { - // console.error(error); - // toast.error("Gagal hapus diskusi, coba lagi nanti"); - // } finally { - // setLoadingDelete(false) - // setValModal(false) - // } - // } + async function fetchDeleteDiscussion(val: boolean) { + try { + if (val) { + setLoadingDelete(true) + const response = await funDeleteDiscussionGeneral(id) + if (response.success) { + setDataRealtime([ + { + category: "discussion-general-delete", + id: id, + user: response.user + }, + { + category: "discussion", + } + ]) + toast.success(response.message) + onSuccess(false) + router.push(`/discussion`) + } else { + toast.error(response.message) + } + } + } catch (error) { + console.error(error); + toast.error("Gagal hapus diskusi umum, coba lagi nanti"); + } finally { + setLoadingDelete(false) + setValModal(false) + } + } return ( @@ -102,46 +105,53 @@ export default function DrawerDetailDiscussionGeneral({ onSuccess, id, status }:
- window.location.href = `/discussion/${param.id}/update/`} justify={'center'} align={'center'} direction={'column'} > - - - - - Edit - - - - setValModalStatus(true)} > - {status === 1 ? ( + { + (roleLogin.get() != "user" && roleLogin.get() != "coadmin") ? ( <> - + window.location.href = `/discussion/${param.id}/edit/`} justify={'center'} align={'center'} direction={'column'} > - + + + + Edit + + + + setValModalStatus(true)} > + {status === 1 ? ( + <> + + + + + Tutup Diskusi + + + ) : ( + <> + + + + + + Buka Diskusi + + + )} + + + setValModal(true)} justify={'center'} align={'center'} direction={'column'} > + + + + + Hapus - Tutup Diskusi - ) : ( - <> - - - - - - Buka Diskusi - - - )} - - - setValModal(true)} justify={'center'} align={'center'} direction={'column'} > - - - - - Hapus - - + ) + : (<>) + } @@ -149,14 +159,14 @@ export default function DrawerDetailDiscussionGeneral({ onSuccess, id, status }: setValModal(false)} description="Apakah Anda yakin ingin menghapus diskusi ini?" onYes={(val) => { - // fetchDeleteDiscussion(val) + fetchDeleteDiscussion(val) }} /> setValModalStatus(false)} description="Apakah Anda yakin ingin mengubah status diskusi ini?" onYes={(val) => { - // fetchStatusDiscussion(val) + fetchStatusDiscussion(val) }} />
) diff --git a/src/module/discussion_general/ui/edit_discussion_general.tsx b/src/module/discussion_general/ui/edit_discussion_general.tsx new file mode 100644 index 0000000..4bc1945 --- /dev/null +++ b/src/module/discussion_general/ui/edit_discussion_general.tsx @@ -0,0 +1,208 @@ +'use client' +import { keyWibu, TEMA } from "@/module/_global" +import LayoutModal from "@/module/_global/layout/layout_modal" +import { useHookstate } from "@hookstate/core" +import { Box, Button, rem, Skeleton, TextInput } from "@mantine/core" +import { useShallowEffect } from "@mantine/hooks" +import { useParams, useRouter } from "next/navigation" +import { useState } from "react" +import toast from "react-hot-toast" +import { useWibuRealtime } from "wibu-realtime" +import { funEditDiscussionGeneral, funGetOneDiscussionGeneral } from "../lib/api_discussion_general" + +export default function FormEditDiscussionGeneral() { + const [isValModal, setValModal] = useState(false) + const [loadingModal, setLoadingModal] = useState(false) + const router = useRouter() + const param = useParams<{ id: string }>() + const [isDesc, setDesc] = useState("") + const [isTitle, setTitle] = useState("") + const [loading, setLoading] = useState(true) + const tema = useHookstate(TEMA) + const [touched, setTouched] = useState({ + title: false, + desc: false, + }); + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) + + async function fetchGetOneDiscussion() { + try { + setLoading(true) + const response = await funGetOneDiscussionGeneral(param.id, '?cat=detail') + setDesc(response.data.desc) + setTitle(response.data.title) + } catch (error) { + console.error(error); + toast.error("Gagal menampilkan diskusi, coba lagi nanti"); + } finally { + setLoading(false) + } + } + + async function fetchEditDiscussion() { + try { + setLoadingModal(true) + const response = await funEditDiscussionGeneral(param.id, { + title: isTitle, + desc: isDesc + }) + if (response.success) { + toast.success(response.message) + setValModal(false) + setDataRealtime([{ + category: "discussion-general-detail", + id: param.id, + }]) + router.push(`/discussion/${param.id}`) + } else { + toast.error(response.message) + } + + } catch (error) { + console.error(error); + setValModal(false) + toast.error("Gagal mengubah diskusi umum, coba lagi nanti"); + } finally { + setValModal(false) + setLoadingModal(false) + } + } + + useShallowEffect(() => { + fetchGetOneDiscussion() + }, []) + + function onValidation(kategori: string, val: string) { + if (kategori == 'title') { + setTitle(val) + if (val === "") { + setTouched({ ...touched, title: true }) + } else { + setTouched({ ...touched, title: false }) + } + } else if (kategori == 'diskusi') { + setDesc(val) + if (val === "") { + setTouched({ ...touched, desc: true }) + } else { + setTouched({ ...touched, desc: false }) + } + } + } + + + function onCheck() { + const cek = checkAll() + if (!cek) + return false + + setValModal(true) + } + + function checkAll() { + let nilai = true + if (isTitle === "") { + setTouched(touched => ({ ...touched, title: true })) + nilai = false + } + if (isDesc === "") { + setTouched(touched => ({ ...touched, desc: true })) + nilai = false + } + return nilai + } + + + + return ( + + + + {loading ? + Array(2) + .fill(null) + .map((_, i) => ( + + )) + : + + { onValidation('title', e.target.value) }} + error={ + touched.title && ( + isTitle == "" ? "Judul Tidak Boleh Kosong" : null + ) + } + /> + { onValidation('diskusi', e.target.value) }} + error={ + touched.desc && ( + isDesc == "" ? "Diskusi Tidak Boleh Kosong" : null + ) + } + /> + + } + + + + {loading ? + + : + + } + + + setValModal(false)} + description="Apakah Anda yakin ingin mengubah data?" + onYes={(val) => { + if (val) { + fetchEditDiscussion() + } else { + setValModal(false) + } + }} /> + + ) +} \ No newline at end of file diff --git a/src/module/discussion_general/ui/member_discussion_general.tsx b/src/module/discussion_general/ui/member_discussion_general.tsx index ddda736..1613acb 100644 --- a/src/module/discussion_general/ui/member_discussion_general.tsx +++ b/src/module/discussion_general/ui/member_discussion_general.tsx @@ -2,37 +2,31 @@ import { globalRole, LayoutDrawer, LayoutNavbarNew, SkeletonList, TEMA } from '@/module/_global'; import LayoutModal from '@/module/_global/layout/layout_modal'; import { useHookstate } from '@hookstate/core'; -import { ActionIcon, Avatar, Box, Divider, Flex, Grid, Group, SimpleGrid, Stack, Text } from '@mantine/core'; +import { ActionIcon, Avatar, Box, Divider, Grid, Group, Text } from '@mantine/core'; import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; import { useParams, useRouter } from 'next/navigation'; import { useState } from 'react'; import toast from 'react-hot-toast'; import { AiOutlineUserAdd } from 'react-icons/ai'; -import { FaPencil, FaToggleOff, FaUserTie } from 'react-icons/fa6'; +import { FaUserTie } from 'react-icons/fa6'; import { IoIosCloseCircle } from 'react-icons/io'; -import { funGetOneDiscussionGeneral } from '../lib/api_discussion_general'; +import { funDelMemberDiscussionGeneral, funGetOneDiscussionGeneral } from '../lib/api_discussion_general'; import { IFormMemberDisscussionGeneral } from '../lib/type_discussion_general'; export default function MemberDiscussionGeneral() { const router = useRouter() const [openDrawer, setDrawer] = useState(false) - const [openDrawerInfo, setDrawerInfo] = useState(false) - const [valActive, setValActive] = useState(true) const param = useParams<{ id: string }>() const [member, setMember] = useState([]) const [loading, setLoading] = useState(true) - const [valChooseMember, setChooseMember] = useState("") - const [valChooseMemberStatus, setChooseMemberStatus] = useState(false) + const [valChooseMemberId, setChooseMemberId] = useState("") const [valChooseMemberName, setChooseMemberName] = useState("") const [isOpenModal, setOpenModal] = useState(false) - const [isOpenModalStatus, setOpenModalStatus] = useState(false) const roleLogin = useHookstate(globalRole) - const [isAdmin, setAdmin] = useState(false) const isMobile = useMediaQuery('(max-width: 455px)') const isMobile2 = useMediaQuery("(max-width: 438px)") const tema = useHookstate(TEMA) - const [loadingStatus, setLoadingStatus] = useState(false) const [loadingDelete, setLoadingDelete] = useState(false) async function getOneData(loading: boolean) { @@ -57,96 +51,38 @@ export default function MemberDiscussionGeneral() { }, [param.id]) - async function onClickMember(id: string, status: boolean) { - setChooseMember(id) - setChooseMemberStatus(status) + async function onClickMember(id: string, name: string) { + setChooseMemberId(id) + setChooseMemberName(name) setDrawer(true) } - // async function deleteMember() { - // try { - // setLoadingDelete(true) - // const res = await funDeleteMemberDivision(param.id, { id: valChooseMember }) - // if (res.success) { - // toast.success(res.message) - // setDrawer(false) - // getOneData(false) - // } else { - // toast.error(res.message) - // } - // } catch (error) { - // console.error(error); - // toast.error("Gagal mendapatkan divisi, coba lagi nanti"); - // } finally { - // setLoadingDelete(false) - // setOpenModal(false) - // } - // } + async function deleteMember() { + try { + setLoadingDelete(true) + const res = await funDelMemberDiscussionGeneral(param.id, { idUser: valChooseMemberId }) + if (res.success) { + toast.success(res.message) + setDrawer(false) + getOneData(false) + } else { + toast.error(res.message) + } + } catch (error) { + console.error(error); + toast.error("Gagal mendapatkan divisi, coba lagi nanti"); + } finally { + setLoadingDelete(false) + setOpenModal(false) + } + } - // async function editStatusAdmin() { - // try { - // const res = await funEditStatusAdminDivision(param.id, { id: valChooseMember, isAdmin: valChooseMemberStatus }) - // if (res.success) { - // toast.success(res.message) - // getOneData(false) - // } else { - // toast.error(res.message) - // } - // setDrawer(false) - // } catch (error) { - // console.error(error); - // toast.error("Gagal mendapatkan divisi, coba lagi nanti"); - // } - // } - - // async function editStatusDivisi() { - // try { - // setLoadingStatus(true) - // const res = await funUpdateStatusDivision(param.id, { isActive: valActive }) - // if (res.success) { - // toast.success(res.message) - // getOneData(false) - // } else { - // toast.error(res.message) - // } - // } catch (error) { - // console.error(error); - // toast.error("Gagal mendapatkan divisi, coba lagi nanti"); - // } finally { - // setDrawerInfo(false) - // setLoadingStatus(false) - // setOpenModalStatus(false) - // } - // } - return ( } /> - {/* - Deskripsi Divisi - - { - loading ? - Array(3) - .fill(null) - .map((_, i) => ( - - - - )) - : - (deskripsi != null && deskripsi != undefined && deskripsi != "") ? - {deskripsi} - : Tidak ada deskripsi - } - - */} {member.length} Anggota @@ -156,15 +92,17 @@ export default function MemberDiscussionGeneral() { border: `1px solid ${tema.get().bgTotalKegiatan}`, }}> - {loading ? - <> + {!loading ? + roleLogin.get() != "user" && roleLogin.get() != "coadmin" ? + router.push('/discussion/' + param.id + '/add-member/')}> + + + + Tambah Anggota + + : <> : - router.push('/division/add-member/' + param.id)}> - - - - Tambah Anggota - + <> } @@ -181,15 +119,10 @@ export default function MemberDiscussionGeneral() { return ( { - if ((roleLogin.get() != 'user' && roleLogin.get() != 'coadmin') || isAdmin) { - // onClickMember(v.id, (v.isAdmin) ? true : false) - setChooseMemberName(v.name) - } - }} + onClick={() => { onClickMember(v.idUser, v.name) }} > - + @@ -212,66 +145,32 @@ export default function MemberDiscussionGeneral() { setDrawer(false)} title={valChooseMemberName}> - valActive ? editStatusAdmin() : undefined} - > + { router.push('/member/' + valChooseMemberId) }} > - + - {(valChooseMemberStatus == false) ? "Jadikan admin" : "Memberhentikan sebagai admin"} - - valActive ? setOpenModal(true) : undefined}> - - - - Keluarkan dari divisi + Lihat Profil + { + (roleLogin.get() != "user" && roleLogin.get() != "coadmin") && + setOpenModal(true)}> + + + + Keluarkan dari diskusi + + } setOpenModal(false)} description="Apakah Anda yakin ingin mengeluarkan anggota?" onYes={(val) => { - // if (!val) { - // setOpenModal(false) - // } else { - // deleteMember() - // } - }} /> - - setDrawerInfo(false)} title={"Menu"}> - - - - router.push('/division/edit/' + param.id)} justify={'center'} align={'center'} direction={'column'} > - - - - - Edit Divisi - - - { setOpenModalStatus(true) }} justify={'center'} align={'center'} direction={'column'} > - - - - - {valActive ? "Non Aktifkan Divisi" : "Aktifkan Divisi"} - - - - - - - - setOpenModalStatus(false)} - description="Apakah Anda yakin ingin mangubah status aktifasi divisi?" - onYes={(val) => { - // if (!val) { - // setOpenModalStatus(false) - // } else { - // editStatusDivisi() - // } + if (!val) { + setOpenModal(false) + } else { + deleteMember() + } }} /> );