diff --git a/prisma/schema.prisma b/prisma/schema.prisma index a45f6eb..e187a42 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -60,6 +60,7 @@ model Village { Division Division[] ColorTheme ColorTheme[] BannerImage BannerImage[] + Discussion Discussion[] } model Group { @@ -75,6 +76,7 @@ model Group { Project Project[] Division Division[] AnnouncementMember AnnouncementMember[] + Discussion Discussion[] } model Position { @@ -125,6 +127,9 @@ model User { Notifications Notifications[] @relation("UserToUser") Notifications2 Notifications[] @relation("UserFromUser") Subscribe Subscribe? + Discussion Discussion[] + DiscussionMember DiscussionMember[] + DiscussionComment DiscussionComment[] } model UserLog { @@ -213,17 +218,18 @@ model ProjectFile { } model ProjectTask { - id String @id @default(cuid()) - Project Project @relation(fields: [idProject], references: [id]) - idProject String - title String - desc String? - status Int @default(0) // 0 = todo, 1 = done - dateStart DateTime @db.Date - dateEnd DateTime @db.Date - isActive Boolean @default(true) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id String @id @default(cuid()) + Project Project @relation(fields: [idProject], references: [id]) + idProject String + title String + desc String? + status Int @default(0) // 0 = todo, 1 = done + notifikasi Boolean @default(false) + dateStart DateTime @db.Date + dateEnd DateTime @db.Date + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model ProjectComment { @@ -303,6 +309,7 @@ model DivisionProjectTask { title String desc String? @db.Text status Int @default(0) // 0 = todo, 1 = done + notifikasi Boolean @default(false) dateStart DateTime @db.Date dateEnd DateTime @db.Date isActive Boolean @default(true) @@ -519,3 +526,44 @@ model Subscribe { createdAt DateTime? @default(now()) updatedAt DateTime? @updatedAt } + +model Discussion { + id String @id @default(cuid()) + Village Village @relation(fields: [idVillage], references: [id]) + idVillage String + Group Group @relation(fields: [idGroup], references: [id]) + idGroup String + title String? + desc String @db.Text + status Int @default(1) // 1 = open, 2 = close + isActive Boolean @default(true) + User User @relation(fields: [createdBy], references: [id]) + createdBy String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + DiscussionMember DiscussionMember[] + DiscussionComment DiscussionComment[] +} + +model DiscussionMember { + id String @id @default(cuid()) + Discussion Discussion @relation(fields: [idDiscussion], references: [id]) + idDiscussion String + User User @relation(fields: [idUser], references: [id]) + idUser String + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model DiscussionComment { + id String @id @default(cuid()) + Discussion Discussion @relation(fields: [idDiscussion], references: [id]) + idDiscussion String + User User @relation(fields: [idUser], references: [id]) + idUser String + comment String @db.Text + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} 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/(application)/discussion/[id]/member/page.tsx b/src/app/(application)/discussion/[id]/member/page.tsx new file mode 100644 index 0000000..bf272ef --- /dev/null +++ b/src/app/(application)/discussion/[id]/member/page.tsx @@ -0,0 +1,9 @@ +import { MemberDiscussionGeneral } from "@/module/discussion_general"; + +export default function Page() { + return ( + <> + + + ) +} \ No newline at end of file diff --git a/src/app/(application)/discussion/[id]/page.tsx b/src/app/(application)/discussion/[id]/page.tsx new file mode 100644 index 0000000..d179343 --- /dev/null +++ b/src/app/(application)/discussion/[id]/page.tsx @@ -0,0 +1,9 @@ +import { DetailDiscussionGeneral } from "@/module/discussion_general"; + +export default function Page() { + return ( + <> + + + ) +} \ No newline at end of file diff --git a/src/app/(application)/discussion/create/page.tsx b/src/app/(application)/discussion/create/page.tsx new file mode 100644 index 0000000..ac26ec4 --- /dev/null +++ b/src/app/(application)/discussion/create/page.tsx @@ -0,0 +1,9 @@ +import { FormCreateDiscussionGeneral } from "@/module/discussion_general"; + +export default function Page() { + return ( + <> + + + ) +} \ No newline at end of file diff --git a/src/app/(application)/discussion/page.tsx b/src/app/(application)/discussion/page.tsx new file mode 100644 index 0000000..33890bc --- /dev/null +++ b/src/app/(application)/discussion/page.tsx @@ -0,0 +1,13 @@ +import { ListDiscussionGeneral, NavbarDiscussionGeneral } from '@/module/discussion_general'; +import React from 'react'; + +function Page() { + return ( +
+ + +
+ ); +} + +export default Page; diff --git a/src/app/api/calender/route.ts b/src/app/api/calender/route.ts index 37cbc76..320d328 100644 --- a/src/app/api/calender/route.ts +++ b/src/app/api/calender/route.ts @@ -193,4 +193,44 @@ export async function POST(request: Request) { console.error(error); return NextResponse.json({ success: false, message: "Gagal membuat acara kalender, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); } +} + + + +// CEK TGL AVAILABLE +export async function PUT(request: Request) { + try { + const user = await funGetUserByCookies(); + if (user.id == undefined) { + return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 401 }); + } + + const { idDivision, title, desc, timeStart, timeEnd, dateStart, dateEnd, repeatEventTyper, member, linkMeet, repeatValue } = (await request.json()); + const division = await prisma.division.findUnique({ + where: { + id: idDivision + } + }) + + const cek = await prisma.divisionCalendarReminder.count({ + where: { + isActive: true, + dateStart: new Date(dateStart), + Division: { + idGroup: division?.idGroup + } + } + }) + + if (cek > 0) { + return NextResponse.json({ success: false, message: "Tidak dapat membuat acara kalender, acara kalender sudah ada pada tanggal tersebut" }, { status: 400 }); + } else { + return NextResponse.json({ success: true, message: "Berhasil membuat acara kalender" }, { status: 200 }); + } + + + } catch (error) { + console.error(error) + return NextResponse.json({ success: false, message: "Gagal membuat acara kalender, coba lagi nanti (error: 500)" }, { status: 404 }); + } } \ 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]/member/route.ts b/src/app/api/discussion-general/[id]/member/route.ts new file mode 100644 index 0000000..806923d --- /dev/null +++ b/src/app/api/discussion-general/[id]/member/route.ts @@ -0,0 +1,92 @@ +import { prisma } from "@/module/_global"; +import { funGetUserByCookies } from "@/module/auth"; +import { createLogUser } from "@/module/user"; +import _ from "lodash"; +import { NextResponse } from "next/server"; + + +// ADD MEMBER DISCUSSION GENERAL +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.discussion.count({ + where: { + id, + isActive: true + } + }) + + 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"]), + idDiscussion: id, + idUser: v.idUser + })) + + const insertMember = await prisma.discussionMember.createMany({ + data: dataMember + }) + } + + // create log user + const log = await createLogUser({ act: 'CREATE', desc: 'User menambah anggota diskusi umum', table: 'discussion', data: String(id) }) + return NextResponse.json({ success: true, message: "Berhasil menambahkan anggota diskusi umum" }, { status: 200 }); + + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal menambahkan anggota, coba lagi nanti (error : 500)" }, { status: 500 }); + } +} + + +// MENGELUARKAN ANGGOTA +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 { idUser } = (await request.json()); + + + const cek = await prisma.discussion.count({ + where: { + id, + isActive: true + } + }) + + if (cek == 0) { + return NextResponse.json({ success: false, message: "Gagal, data tidak ditemukan" }, { status: 404 }); + } + + const deleteMember = await prisma.discussionMember.deleteMany({ + where: { + idDiscussion: id, + idUser + } + }) + + // create log user + const log = await createLogUser({ act: 'DELETE', desc: 'User mengeluarkan anggota diskusi umum', table: 'discussion', data: String(id) }) + + return NextResponse.json({ success: true, message: "Berhasil mengeluarkan anggota diskusi umum" }, { status: 200 }); + + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mengeluarkan anggota, coba lagi nanti (error : 500)" }, { status: 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 new file mode 100644 index 0000000..899097b --- /dev/null +++ b/src/app/api/discussion-general/[id]/route.ts @@ -0,0 +1,263 @@ +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 + const { id } = context.params + const { searchParams } = new URL(request.url); + const kategori = searchParams.get("cat"); + + 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 mendapatkan diskusi, data tidak ditemukan" }, { status: 404 }); + } + + if (kategori == "detail") { + const data = await prisma.discussion.findUnique({ + where: { + id, + isActive: true + }, + select: { + id: true, + title: true, + idGroup: true, + desc: true, + status: true, + createdAt: true, + } + }) + + dataFix = { + id: data?.id, + idGroup:data?.idGroup, + title: data?.title, + desc: data?.desc, + status: data?.status, + createdAt: moment(data?.createdAt).format("ll"), + } + + } else if (kategori == "komentar") { + const data = await prisma.discussionComment.findMany({ + where: { + idDiscussion: id, + isActive: true + }, + select: { + id: true, + comment: true, + createdAt: true, + idUser: true, + User: { + select: { + name: true, + img: true + } + } + } + }) + + dataFix = data.map((v: any) => ({ + ..._.omit(v, ["createdAt", "User",]), + createdAt: moment(v.createdAt).format("lll"), + username: v.User.name, + img: v.User.img + })) + + } else if (kategori == "anggota") { + const data = await prisma.discussionMember.findMany({ + where: { + idDiscussion: id, + isActive: true + }, + select: { + idUser: true, + User: { + select: { + name: true, + img: true + } + } + } + }) + + dataFix = data.map((v: any) => ({ + ..._.omit(v, ["User",]), + 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 + } + } + + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan diskusi", data: dataFix }, { status: 200 }); + + + } catch (error) { + 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 new file mode 100644 index 0000000..5061216 --- /dev/null +++ b/src/app/api/discussion-general/route.ts @@ -0,0 +1,210 @@ +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"; +import "moment/locale/id"; + + + +// GET ALL DISCUSSION GENERAL +export async function GET(request: Request) { + try { + const user = await funGetUserByCookies() + if (user.id == undefined) { + return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 401 }); + } + + let grup + const roleUser = user.idUserRole + const villageId = user.idVillage + const userId = user.id + const { searchParams } = new URL(request.url); + const idGroup = searchParams.get("group"); + const search = searchParams.get('search'); + const page = searchParams.get('page'); + const dataSkip = Number(page) * 10 - 10; + + if (idGroup == "null" || idGroup == undefined || idGroup == "") { + grup = user.idGroup + } else { + grup = idGroup + } + + const cek = await prisma.group.count({ + where: { + id: grup, + isActive: true + } + }) + + if (cek == 0) { + return NextResponse.json({ success: false, message: "Gagal mendapatkan data kegiatan, data tidak ditemukan", }, { status: 404 }); + } + + + let kondisi: any = { + isActive: true, + idVillage: String(villageId), + idGroup: grup, + title: { + contains: (search == undefined || search == "null") ? "" : search, + mode: "insensitive" + }, + } + + if (roleUser != "supadmin" && roleUser != "cosupadmin" && roleUser != "admin") { + kondisi = { + isActive: true, + idVillage: String(villageId), + idGroup: grup, + title: { + contains: (search == undefined || search == "null") ? "" : search, + mode: "insensitive" + }, + DiscussionMember: { + some: { + idUser: String(userId) + } + } + } + } + + const data = await prisma.discussion.findMany({ + skip: dataSkip, + take: 10, + where: { + isActive: true, + idVillage: String(villageId), + idGroup: grup, + title: { + contains: (search == undefined || search == "null") ? "" : search, + mode: "insensitive" + }, + }, + orderBy: [ + { + status: 'desc' + }, + { + createdAt: 'desc' + } + ], + + select: { + id: true, + title: true, + desc: true, + status: true, + createdAt: true, + DiscussionComment: { + select: { + id: true, + } + } + } + }); + + const fixData = data.map((v: any) => ({ + ..._.omit(v, ["DiscussionComment", "createdAt"]), + total_komentar: v.DiscussionComment.length, + createdAt: moment(v.createdAt).format("ll") + })) + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan diskusi", data: fixData, }, { status: 200 }); + + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan diskusi, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); + } +} + + + +// CREATE DISCUSSION GENERALE +export async function POST(request: Request) { + try { + const user = await funGetUserByCookies() + if (user.id == undefined) { + return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 401 }); + } + + const userId = user.id + const userRoleLogin = user.idUserRole + const { idGroup, title, desc, member } = (await request.json()); + + const data = await prisma.discussion.create({ + data: { + idVillage: String(user.idVillage), + idGroup: idGroup, + title: title, + desc: desc, + createdBy: String(userId), + }, + select: { + id: true + } + }); + + const dataMember = member.map((v: any) => ({ + ..._.omit(v, ["idUser", "name", "img"]), + idDiscussion: data.id, + idUser: v.idUser, + })) + + const insertMember = await prisma.discussionMember.createMany({ + data: dataMember + }) + + const dataNotif = member.map((v: any) => ({ + ..._.omit(v, ["idUser", "name", "img"]), + idUserTo: v.idUser, + idUserFrom: userId, + category: 'discussion-general', + idContent: data.id, + title: 'Diskusi Umum Baru', + desc: 'Terdapat diskusi umum baru. Silahkan periksa detailnya.' + })) + + if (userRoleLogin != "supadmin") { + const perbekel = await prisma.user.findFirst({ + where: { + isActive: true, + idUserRole: "supadmin", + idVillage: user.idVillage + }, + select: { + id: true, + Subscribe: { + select: { + subscription: true + } + } + } + }) + + dataNotif.push({ + idUserTo: perbekel?.id, + idUserFrom: userId, + category: 'discussion-general', + idContent: data.id, + title: 'Diskusi Umum Baru', + desc: 'Terdapat diskusi umum baru. Silahkan periksa detailnya.' + }) + } + + const insertNotif = await prisma.notifications.createMany({ + data: dataNotif + }) + + + // create log user + const log = await createLogUser({ act: 'CREATE', desc: 'User membuat data diskusi umum', table: 'discussion', data: data.id }) + return NextResponse.json({ success: true, message: "Berhasil menambahkan diskusi umum", notif: dataNotif }, { status: 200 }); + + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal menambahkan diskusi, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/home/route.ts b/src/app/api/home/route.ts index f900c8d..d0ef436 100644 --- a/src/app/api/home/route.ts +++ b/src/app/api/home/route.ts @@ -1,3 +1,4 @@ +import { DivisionProject } from './../../../../node_modules/.prisma/client/index.d'; import { prisma } from "@/module/_global"; import { funGetUserByCookies } from "@/module/auth"; import _, { ceil } from "lodash"; @@ -445,6 +446,134 @@ export async function GET(request: Request) { totalNotif: total, village: desa?.name } + } else if (kategori == "check-late-project") { + const supadmin = await prisma.user.findFirst({ + where: { + idUserRole: "supadmin", + idVillage: idVillage + } + }) + + const dataUmum = await prisma.projectTask.findMany({ + where: { + Project: { + idVillage: idVillage, + isActive: true, + status: { + not: { + in: [2, 3] + } + } + }, + notifikasi: false, + isActive: true, + status: 0, + dateEnd: { + lt: new Date() + } + }, + select: { + id: true, + title: true, + dateEnd: true, + Project: { + select: { + id: true, + }, + } + } + }) + + + for (let index = 0; index < dataUmum.length; index++) { + await prisma.projectTask.update({ + where: { + id: dataUmum[index].id + }, + data: { + notifikasi: true + } + }) + } + + const pertama = dataUmum.map((v: any) => ({ + ..._.omit(v, ["Project", "title", "id", "dateEnd"]), + idUserTo: String(supadmin?.id), + idUserFrom: String(user.id), + category: 'project', + idContent: v.Project.id, + title: `Tugas ${v.title} Telah Melewati Batas Waktu`, + desc: `Tugas dengan deadline ${moment(v.dateEnd).format('DD-MM-yyyy')} telah berakhir. Silakan segera melakukan tindakan yang diperlukan.` + })) + + const insertNotif = await prisma.notifications.createMany({ + data: pertama + }) + + const dataDivisi = await prisma.divisionProjectTask.findMany({ + where: { + Division: { + idVillage: idVillage, + isActive: true + }, + DivisionProject: { + isActive: true, + status: { + not: { + in: [2, 3] + } + } + }, + notifikasi: false, + isActive: true, + status: 0, + dateEnd: { + lt: new Date() + } + }, + select: { + id: true, + title: true, + dateEnd: true, + DivisionProject: { + select: { + id: true, + idDivision: true + }, + } + } + }) + + for (let index = 0; index < dataDivisi.length; index++) { + await prisma.divisionProjectTask.update({ + where: { + id: dataDivisi[index].id + }, + data: { + notifikasi: true + } + }) + } + + const kedua = dataDivisi.map((v: any) => ({ + ..._.omit(v, ["DivisionProject", "title", "id", "dateEnd"]), + idUserTo: String(supadmin?.id), + idUserFrom: String(user.id), + category: 'division/' + v.DivisionProject.idDivision + '/task', + idContent: v.DivisionProject.id, + title: `Tugas ${v.title} Telah Melewati Batas Waktu`, + desc: `Tugas dengan deadline ${moment(v.dateEnd).format('DD-MM-yyyy')} telah berakhir. Silakan segera melakukan tindakan yang diperlukan.` + })) + + const insertNotif2 = await prisma.notifications.createMany({ + data: kedua + }) + + + const merge = [...pertama, ...kedua] + + allData = merge + } return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: allData }, { status: 200 }); diff --git a/src/app/api/version-app/route.ts b/src/app/api/version-app/route.ts index 2343ad3..1741163 100644 --- a/src/app/api/version-app/route.ts +++ b/src/app/api/version-app/route.ts @@ -2,7 +2,7 @@ import { NextResponse } from "next/server"; export async function GET(request: Request) { try { - return NextResponse.json({ success: true, version: "0.2.9", tahap: "beta" }, { status: 200 }); + return NextResponse.json({ success: true, version: "1.0.0", tahap: "beta" }, { status: 200 }); } catch (error) { console.error(error); return NextResponse.json({ success: false, version: "Gagal mendapatkan version, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); diff --git a/src/module/calender/lib/api_calender.ts b/src/module/calender/lib/api_calender.ts index 92786d1..8166de4 100644 --- a/src/module/calender/lib/api_calender.ts +++ b/src/module/calender/lib/api_calender.ts @@ -19,6 +19,18 @@ export const funGetOneCalenderByIdCalendar = async (path: string) => { return await response.json().catch(() => null); } + +export const funCheckCalender = async (data: IFormCreateCalender) => { + const response = await fetch("/api/calender", { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); + return await response.json().catch(() => null); +} + export const funCreateCalender = async (data: IFormCreateCalender) => { const response = await fetch("/api/calender", { method: "POST", @@ -67,11 +79,11 @@ export const funAddMemberCalender = async (path: string, data: IFormMemberCalend export const funDeleteMemberCalender = async (path: string, data: { idUser: string }) => { const response = await fetch(`/api/calender/${path}/member`, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(data), + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), }); return await response.json().catch(() => null); - }; \ No newline at end of file +}; \ No newline at end of file diff --git a/src/module/calender/ui/create_calender_division_caleder.tsx b/src/module/calender/ui/create_calender_division_caleder.tsx index fd3f4d0..8730faa 100644 --- a/src/module/calender/ui/create_calender_division_caleder.tsx +++ b/src/module/calender/ui/create_calender_division_caleder.tsx @@ -11,7 +11,7 @@ import { useState } from 'react'; import toast from 'react-hot-toast'; import { IoIosArrowDropright } from 'react-icons/io'; import { useWibuRealtime } from 'wibu-realtime'; -import { funCreateCalender } from '../lib/api_calender'; +import { funCheckCalender, funCreateCalender } from '../lib/api_calender'; import { IFormMemberCalender } from '../lib/type_calender'; import { globalCalender } from '../lib/val_calender'; import CreateUserCalender from './create_user_calender'; @@ -20,7 +20,9 @@ export default function CreateCalenderDivisionCaleder() { const [value, setValue] = useState(null); const router = useRouter() const [isModal, setModal] = useState(false) + const [isModalKonfirmasiTglSama, setModalKonfirmasiTglSama] = useState(false) const [loadingModal, setLoadingModal] = useState(false) + const [loadingModalKonfirmasiTglSama, setLoadingModalKonfirmasiTglSama] = useState(false) const member = useHookstate(globalCalender) const memberValue = member.get() as IFormMemberCalender[] const [openMember, setOpenMember] = useState(false) @@ -53,10 +55,40 @@ export default function CreateCalenderDivisionCaleder() { project: "sdm" }) - - async function onSubmit(val: boolean) { + async function onCheckDate() { try { setLoadingModal(true) + const response = await funCheckCalender({ + idDivision: param.id, + title: isData.title, + dateStart: isData.dateStart, + timeStart: isData.timeStart, + timeEnd: isData.timeEnd, + linkMeet: isData.linkMeet, + repeatEventTyper: isData.repeatEventTyper, + desc: isData.desc, + repeatValue: isData.repeatValue, + member: memberValue + }) + + if (response.success) { + onSubmit() + } else { + setModalKonfirmasiTglSama(true) + } + } catch (error) { + console.error(error) + toast.error("Gagal menambahkan acara, coba lagi nanti"); + } finally { + setLoadingModal(false) + } + } + + + async function onSubmit() { + try { + setLoadingModal(true) + setLoadingModalKonfirmasiTglSama(true) const response = await funCreateCalender({ idDivision: param.id, title: isData.title, @@ -76,20 +108,20 @@ export default function CreateCalenderDivisionCaleder() { division: param.id, date: isData.dateStart }]) - setModal(false) router.push(`/division/${param.id}/calender`) toast.success(response.message) member.set([]) } else { toast.error(response.message) - setModal(false) } } catch (error) { console.error(error) - toast.error("Gagal menambahkan pengumuman, coba lagi nanti"); + toast.error("Gagal menambahkan acara, coba lagi nanti"); } finally { setModal(false) + setModalKonfirmasiTglSama(false) setLoadingModal(false) + setLoadingModalKonfirmasiTglSama(false) } } @@ -381,8 +413,7 @@ export default function CreateCalenderDivisionCaleder() { member.get().map((v: any, i: any) => { return ( - + @@ -430,11 +461,27 @@ export default function CreateCalenderDivisionCaleder() { description="Apakah Anda yakin ingin menambahkan data?" onYes={(val) => { if (val) { - onSubmit(val) + onCheckDate() } else { setModal(false) } }} /> + + + { + setModalKonfirmasiTglSama(false) + setModal(false) + }} + description="Sudah ada acara pada tanggal yang sama. Apakah Anda yakin ingin menambahkan data?" + onYes={(val) => { + if (val) { + onSubmit() + } else { + setModalKonfirmasiTglSama(false) + setModal(false) + } + }} /> ); } \ No newline at end of file diff --git a/src/module/discussion/ui/detail_discussion.tsx b/src/module/discussion/ui/detail_discussion.tsx index 5731dfa..163da33 100644 --- a/src/module/discussion/ui/detail_discussion.tsx +++ b/src/module/discussion/ui/detail_discussion.tsx @@ -275,7 +275,7 @@ export default function DetailDiscussion({ id, idDivision }: { id: string, idDiv - {moment(v.createdAt).format("ll")} + {moment(v.createdAt).format("lll").replace('pukul', '')} diff --git a/src/module/discussion_general/index.ts b/src/module/discussion_general/index.ts new file mode 100644 index 0000000..bb5e452 --- /dev/null +++ b/src/module/discussion_general/index.ts @@ -0,0 +1,15 @@ +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"; + +export { ListDiscussionGeneral } +export { NavbarDiscussionGeneral } +export { FormCreateDiscussionGeneral } +export { DetailDiscussionGeneral } +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 new file mode 100644 index 0000000..1d7a9bd --- /dev/null +++ b/src/module/discussion_general/lib/api_discussion_general.ts @@ -0,0 +1,91 @@ +import { IFormMemberDisscussionGeneral } from "./type_discussion_general"; + +export const funGetAllDiscussionGeneral = async (path?: string) => { + const response = await fetch(`/api/discussion-general${(path) ? path : ''}`, { next: { tags: ['discussion-general'] } }); + return await response.json().catch(() => null); +} + +export const funCreateDiscussionGeneral = async (data: { idGroup: string, title: string, desc: string, member: IFormMemberDisscussionGeneral[] }) => { + const response = await fetch(`/api/discussion-general`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); + return await response.json().catch(() => null); +} + +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 new file mode 100644 index 0000000..62be53f --- /dev/null +++ b/src/module/discussion_general/lib/type_discussion_general.ts @@ -0,0 +1,22 @@ +export interface IFormMemberDisscussionGeneral { + idUser: string, + name: string, + img: string + } + + export interface IDetailDiscussionGeneral{ + id: string + title: string + desc: string + status: number + createdAt: string + } + + export interface IComentsDisscussionGeneral{ + id: string + comment: string + createdAt: string + idUser: string + img: string + username: string +} diff --git a/src/module/discussion_general/lib/val_discussion_general.ts b/src/module/discussion_general/lib/val_discussion_general.ts new file mode 100644 index 0000000..321771d --- /dev/null +++ b/src/module/discussion_general/lib/val_discussion_general.ts @@ -0,0 +1,5 @@ +import { hookstate } from "@hookstate/core"; +import { IFormMemberDisscussionGeneral } from "./type_discussion_general"; + +export const globalMemberDiscussionGeneral = hookstate([]); +export const globalRefreshDiscussionGeneral = hookstate(false); \ 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/choose_user.tsx b/src/module/discussion_general/ui/choose_user.tsx new file mode 100644 index 0000000..e8c7c41 --- /dev/null +++ b/src/module/discussion_general/ui/choose_user.tsx @@ -0,0 +1,249 @@ +"use client" +import { LayoutNavbarNew, SkeletonList, TEMA } from '@/module/_global'; +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 { useState } from 'react'; +import toast from 'react-hot-toast'; +import { FaCheck } from 'react-icons/fa6'; +import { HiChevronLeft, HiMagnifyingGlass } from 'react-icons/hi2'; +import { IoArrowBackOutline, IoClose } from 'react-icons/io5'; +import { globalMemberDiscussionGeneral } from '../lib/val_discussion_general'; + + +export default function ChooseUsersDiscussion({ grup, onClose }: { grup?: string, onClose: (val: any) => void }) { + const member = useHookstate(globalMemberDiscussionGeneral) + const [selectedFiles, setSelectedFiles] = useState([]); + const [dataMember, setDataMember] = useState([]) + const [loading, setLoading] = useState(true) + const [onClickSearch, setOnClickSearch] = useState(false) + const tema = useHookstate(TEMA) + const isMobile2 = useMediaQuery("(max-width: 438px)"); + + 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 }]) + } + }; + + + async function loadData(search: string) { + try { + setLoading(true) + const res = await funGetAllmember('?active=true&group=' + grup + '&search=' + search); + // const user = await funGetUserByCookies(); + if (res.success) { + // setDataMember(res.data.filter((i: any) => i.id != user.id && i.idUserRole != 'supadmin' && i.idUserRole != 'cosupadmin')) + setDataMember(res.data.filter((i: any) => i.idUserRole != 'supadmin')) + // cek data member sebelumnya + if (member.length > 0) { + setSelectedFiles(JSON.parse(JSON.stringify(member.get()))) + } + } else { + toast.error("Gagal mendapatkan data, coba lagi nanti") + } + } catch (error) { + console.error(error) + toast.error("Gagal mendapatkan data, coba lagi nanti") + } finally { + setLoading(false) + } + } + + + function onSubmit() { + if (selectedFiles.length == 0) { + return toast.error("Error! silahkan pilih anggota") + } + member.set(selectedFiles) + onClose(true) + } + + useShallowEffect(() => { + loadData("") + }, []); + + const handleSearchClick = () => { + setOnClickSearch(true); + }; + + const handleClose = () => { + setOnClickSearch(false); + }; + + function handleXMember(id: number) { + setSelectedFiles(selectedFiles.filter((i: any) => i.idUser != id)) + } + + return ( + + + { onClose(true) }} bg={tema.get().bgIcon} size="lg" radius="lg" aria-label="Settings"> + + + + } title="Pilih Anggota" + menu={ + + } + /> + {/* SEARCH */} + {onClickSearch + ? ( + + + + + + + + + loadData(e.target.value)} + /> + + + + ) + : null + } + {/* Close User */} + + {selectedFiles.length > 0 ? ( + + {selectedFiles.map((v: any, i: any) => { + return ( + + { handleXMember(v.idUser) }} + > +
+ }> + + +
+ {v.name} +
+
+ ) + })} +
+ ) : ( + + + Tidak ada anggota yang dipilih + + + )} +
+ + + + {loading ? + Array(6) + .fill(null) + .map((_, i) => ( + + + + )) + : + (dataMember.length === 0) ? + + Tidak ada anggota + + : + dataMember.map((v, index) => { + const isSelected = selectedFiles.some((i: any) => i.idUser == dataMember[index].id); + return ( + handleFileClick(index)}> + + + + + + + + {v.name} + + {isSelected ? : null} + + + + + + + + ); + }) + } + + + + + + +
+ ); +} + diff --git a/src/module/discussion_general/ui/create_discussion.tsx b/src/module/discussion_general/ui/create_discussion.tsx new file mode 100644 index 0000000..bb3161e --- /dev/null +++ b/src/module/discussion_general/ui/create_discussion.tsx @@ -0,0 +1,325 @@ +'use client' +import { funGetAllGroup, IDataGroup } from "@/module/group"; +import { Avatar, Box, Button, Divider, Grid, Group, rem, Select, Text, TextInput } from "@mantine/core"; +import { useMediaQuery, useShallowEffect } from "@mantine/hooks"; +import { useState } from "react"; +import toast from "react-hot-toast"; +import { IoIosArrowDropright } from "react-icons/io"; +import ChooseUsersDiscussion from "./choose_user"; +import { useHookstate } from "@hookstate/core"; +import { globalRole, keyWibu, LayoutNavbarNew, TEMA } from "@/module/_global"; +import { funGetUserByCookies } from "@/module/auth"; +import { globalMemberDiscussionGeneral } from "../lib/val_discussion_general"; +import { IFormMemberDisscussionGeneral } from "../lib/type_discussion_general"; +import LayoutModal from "@/module/_global/layout/layout_modal"; +import { funCreateDiscussionGeneral } from "../lib/api_discussion_general"; +import { useRouter } from "next/navigation"; +import { useWibuRealtime } from "wibu-realtime"; + +export default function FormCreateDiscussionGeneral() { + const router = useRouter() + const isMobile = useMediaQuery('(max-width: 369px)') + const roleLogin = useHookstate(globalRole) + const [isModal, setModal] = useState(false) + const [dataGroup, setDataGroup] = useState([]) + const [isChooseAnggota, setChooseAnggota] = useState(false) + const member = useHookstate(globalMemberDiscussionGeneral) + const memberValue = member.get() as IFormMemberDisscussionGeneral[] + const tema = useHookstate(TEMA) + const [loadingModal, setLoadingModal] = useState(false) + const [data, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) + const [body, setBody] = useState({ + idGroup: "", + title: "", + desc: "" + }); + + const [touched, setTouched] = useState({ + title: false, + idGroup: false, + desc: false + }); + + function onToChooseAnggota() { + if (roleLogin.get() == "supadmin" && body.idGroup == "") + return toast.error("Error! grup harus diisi") + setChooseAnggota(true) + } + + async function loadData() { + const loadGroup = await funGetAllGroup('?active=true') + if (loadGroup.success) { + setDataGroup(loadGroup.data); + } else { + toast.error(loadGroup.message); + } + + if (roleLogin.get() != "supadmin") { + const loadUser = await funGetUserByCookies(); + setBody({ ...body, idGroup: loadUser.idGroup }) + } + } + + useShallowEffect(() => { + loadData(); + }, []); + + + function onCheck() { + const cek = checkAll() + if (!cek) + return false + + if (memberValue.length <= 1) + return toast.error("Error! Silahkan pilih anggota lebih dari 1") + + setModal(true) + } + + function checkAll() { + let nilai = true + if (body.idGroup === "" || String(body.idGroup) == "null") { + setTouched(touched => ({ ...touched, idGroup: true })) + nilai = false + } + if (body.title === "") { + setTouched(touched => ({ ...touched, title: true })) + nilai = false + } + if (body.desc === "") { + setTouched(touched => ({ ...touched, desc: true })) + nilai = false + } + return nilai + } + + + function onValidation(kategori: string, val: string) { + if (kategori == 'idGroup') { + setBody({ ...body, idGroup: val }) + if (val === "" || String(val) == "null") { + setTouched({ ...touched, idGroup: true }) + } else { + setTouched({ ...touched, idGroup: false }) + } + } else if (kategori == 'title') { + setBody({ ...body, title: val }) + if (val === "") { + setTouched({ ...touched, title: true }) + } else { + setTouched({ ...touched, title: false }) + } + } else if (kategori == 'diskusi') { + setBody({ ...body, desc: val }) + if (val === "") { + setTouched({ ...touched, desc: true }) + } else { + setTouched({ ...touched, desc: false }) + } + } + } + + function onChooseGroup(val: any) { + member.set([]) + onValidation('idGroup', val) + } + + async function onSubmit() { + try { + setLoadingModal(true) + const res = await funCreateDiscussionGeneral({ idGroup: body.idGroup, title: body.title, desc: body.desc, member: memberValue }) + if (res.success) { + setDataRealtime(res.notif) + member.set([]) + toast.success(res.message) + router.push('/discussion') + } else { + toast.error(res.message) + } + } catch (error) { + console.error(error) + toast.error("Gagal membuat diskusi umum, coba lagi nanti") + } finally { + setLoadingModal(false) + setModal(false) + } + } + + if (isChooseAnggota) return setChooseAnggota(false)} /> + + return ( + + } /> + + + { + (roleLogin.get() == "supadmin") && ( +