diff --git a/src/app/(application)/discussion/page.tsx b/src/app/(application)/discussion/page.tsx index 33890bc..de258b1 100644 --- a/src/app/(application)/discussion/page.tsx +++ b/src/app/(application)/discussion/page.tsx @@ -1,11 +1,14 @@ -import { ListDiscussionGeneral, NavbarDiscussionGeneral } from '@/module/discussion_general'; -import React from 'react'; +import { ViewFilter } from '@/module/_global'; +import { NavbarDiscussionGeneral, TabListDiscussionGeneral } from '@/module/discussion_general'; + +function Page({ searchParams }: { searchParams: { page: string } }) { + if (searchParams.page == "filter") + return -function Page() { return (
- +
); } diff --git a/src/app/(application)/division/[id]/(fitur-division)/discussion/page.tsx b/src/app/(application)/division/[id]/(fitur-division)/discussion/page.tsx index 66c768a..5d13b83 100644 --- a/src/app/(application)/division/[id]/(fitur-division)/discussion/page.tsx +++ b/src/app/(application)/division/[id]/(fitur-division)/discussion/page.tsx @@ -1,11 +1,10 @@ -import { ListDiscussion, NavbarListDiscussion } from '@/module/discussion'; -import React from 'react'; +import { NavbarListDiscussion, TabListDiscussion } from '@/module/discussion'; function Page({ params }: { params: { id: string } }) { return (
- +
); } diff --git a/src/app/api/calender/route.ts b/src/app/api/calender/route.ts index 320d328..1ae3295 100644 --- a/src/app/api/calender/route.ts +++ b/src/app/api/calender/route.ts @@ -212,18 +212,85 @@ export async function PUT(request: Request) { } }) - const cek = await prisma.divisionCalendarReminder.count({ + const y = new Date('1970-01-01 ' + timeStart) + const x = new Date('1970-01-01 ' + timeEnd) + const timeStartFix = new Date(y.getTime() - (y.getTimezoneOffset() * 60000)).toISOString() + const timeEndFix = new Date(x.getTime() - (x.getTimezoneOffset() * 60000)).toISOString() + + const cek = await prisma.divisionCalendarReminder.findMany({ where: { isActive: true, - dateStart: new Date(dateStart), Division: { idGroup: division?.idGroup + }, + OR: [ + { + dateStart: new Date(dateStart), + DivisionCalendar: { + title: { + equals: title, + mode: "insensitive" + }, + } + }, + { + dateStart: new Date(dateStart), + OR: [ + { + timeStart: { //13:00 + lte: timeStartFix, //13:30 + }, + timeEnd: { //14:00 + gt: timeStartFix, //13:30 + } + }, + { + timeStart: { //07:00 + lt: timeEndFix, //07:30 + }, + timeEnd: { //08:00 + gt: timeEndFix, //07:30 + } + }, + { + timeStart: { //07:00 + gt: timeStartFix, //06:30 + }, + timeEnd: { //08:00 + lte: timeEndFix, //09:30 + } + } + ] + + } + ] + }, + select: { + id: true, + dateStart: true, + timeStart: true, + timeEnd: true, + DivisionCalendar: { + select: { + title: true, + } + }, + Division: { + select: { + name: true + } } } }) - if (cek > 0) { - return NextResponse.json({ success: false, message: "Tidak dapat membuat acara kalender, acara kalender sudah ada pada tanggal tersebut" }, { status: 400 }); + const dataSama = cek.map((v: any) => ({ + ..._.omit(v, ["DivisionCalendar", "Division"]), + title: v.DivisionCalendar.title, + divisi: v.Division.name + })) + + if (cek.length > 0) { + return NextResponse.json({ success: false, message: "Tidak dapat membuat acara kalender, acara kalender sudah ada pada tanggal tersebut", data: dataSama }, { status: 400 }); } else { return NextResponse.json({ success: true, message: "Berhasil membuat acara kalender" }, { status: 200 }); } diff --git a/src/app/api/discussion-general/[id]/route.ts b/src/app/api/discussion-general/[id]/route.ts index 899097b..7e27184 100644 --- a/src/app/api/discussion-general/[id]/route.ts +++ b/src/app/api/discussion-general/[id]/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"; // GET ONE DETAIL DISKUSI UMUM @@ -22,7 +23,7 @@ export async function GET(request: Request, context: { params: { id: string } }) const cek = await prisma.discussion.count({ where: { id, - isActive: true + // isActive: true } }) @@ -34,9 +35,10 @@ export async function GET(request: Request, context: { params: { id: string } }) const data = await prisma.discussion.findUnique({ where: { id, - isActive: true + // isActive: true }, select: { + isActive: true, id: true, title: true, idGroup: true, @@ -48,7 +50,8 @@ export async function GET(request: Request, context: { params: { id: string } }) dataFix = { id: data?.id, - idGroup:data?.idGroup, + isActive: data?.isActive, + idGroup: data?.idGroup, title: data?.title, desc: data?.desc, status: data?.status, @@ -77,7 +80,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("lll"), + createdAt: moment(v.createdAt).format("lll").replace('pukul', ''), username: v.User.name, img: v.User.img })) @@ -188,6 +191,7 @@ export async function DELETE(request: Request, context: { params: { id: string } return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 401 }); } const { id } = context.params + const { active } = (await request.json()); const cek = await prisma.discussion.count({ where: { @@ -205,19 +209,24 @@ export async function DELETE(request: Request, context: { params: { id: string } id }, data: { - isActive: false + isActive: active } }); // 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 }); + if (active) { + const log = await createLogUser({ act: 'DELETE', desc: 'User mengaktifkan data diskusi umum', table: 'disscussion', data: id }) + return NextResponse.json({ success: true, message: "Berhasil mengaktifkan diskusi umum", user: user.id }, { status: 200 }); + } else { + const log = await createLogUser({ act: 'DELETE', desc: 'User mengarsipkan data diskusi umum', table: 'disscussion', data: id }) + return NextResponse.json({ success: true, message: "Berhasil mengarsipkan 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 }); + return NextResponse.json({ success: false, message: "Gagal mengubah diskusi umum, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); } } diff --git a/src/app/api/discussion-general/route.ts b/src/app/api/discussion-general/route.ts index 5061216..494ea79 100644 --- a/src/app/api/discussion-general/route.ts +++ b/src/app/api/discussion-general/route.ts @@ -24,6 +24,7 @@ export async function GET(request: Request) { const idGroup = searchParams.get("group"); const search = searchParams.get('search'); const page = searchParams.get('page'); + const status = searchParams.get('active'); const dataSkip = Number(page) * 10 - 10; if (idGroup == "null" || idGroup == undefined || idGroup == "") { @@ -43,39 +44,11 @@ export async function GET(request: Request) { 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, + isActive: status == "false" ? false : true, idVillage: String(villageId), idGroup: grup, title: { @@ -112,7 +85,17 @@ export async function GET(request: Request) { createdAt: moment(v.createdAt).format("ll") })) - return NextResponse.json({ success: true, message: "Berhasil mendapatkan diskusi", data: fixData, }, { status: 200 }); + const filter = await prisma.group.findUnique({ + where: { + id: grup + }, + select: { + id: true, + name: true + } + }) + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan diskusi", data: fixData, filter }, { status: 200 }); } catch (error) { console.error(error); diff --git a/src/app/api/discussion/[id]/route.ts b/src/app/api/discussion/[id]/route.ts index febd350..cf7fd56 100644 --- a/src/app/api/discussion/[id]/route.ts +++ b/src/app/api/discussion/[id]/route.ts @@ -36,6 +36,7 @@ export async function GET(request: Request, context: { params: { id: string } }) id: id }, select: { + isActive: true, id: true, title: true, desc: true, @@ -152,7 +153,7 @@ export async function PUT(request: Request, context: { params: { id: string } }) return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 401 }); } const { id } = context.params - + const { active } = (await request.json()); const cek = await prisma.divisionDisscussion.count({ where: { id: id @@ -160,7 +161,7 @@ export async function PUT(request: Request, context: { params: { id: string } }) }); if (cek == 0) { - return NextResponse.json({ success: false, message: "Gagal menghapus diskusi, data tidak ditemukan" }, { status: 404 }); + return NextResponse.json({ success: false, message: "Gagal mengarsipkan diskusi, data tidak ditemukan" }, { status: 404 }); } @@ -169,16 +170,22 @@ export async function PUT(request: Request, context: { params: { id: string } }) id: id }, data: { - isActive: false + isActive: active } }); - // create log user - const log = await createLogUser({ act: 'DELETE', desc: 'User menghapus data diskusi', table: 'divisionDisscussion', data: id }) - return NextResponse.json({ success: true, message: "Berhasil menghapus diskusi", user: user.id }, { status: 200 }); + // create log user + if (active) { + const log = await createLogUser({ act: 'DELETE', desc: 'User mengaktifkan data diskusi', table: 'divisionDisscussion', data: id }) + return NextResponse.json({ success: true, message: "Berhasil mengaktifkan diskusi", user: user.id }, { status: 200 }); + } else { + const log = await createLogUser({ act: 'DELETE', desc: 'User mengarsipkan data diskusi', table: 'divisionDisscussion', data: id }) + return NextResponse.json({ success: true, message: "Berhasil mengarsipkan diskusi", user: user.id }, { status: 200 }); + } + } catch (error) { console.error(error); - return NextResponse.json({ success: false, message: "Gagal menghapus diskusi, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); + return NextResponse.json({ success: false, message: "Gagal mengubah diskusi, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); } } diff --git a/src/app/api/discussion/route.ts b/src/app/api/discussion/route.ts index c1a4660..e8e2d9c 100644 --- a/src/app/api/discussion/route.ts +++ b/src/app/api/discussion/route.ts @@ -19,6 +19,7 @@ export async function GET(request: Request) { const idDivision = searchParams.get("division"); const name = searchParams.get('search'); const page = searchParams.get('page'); + const status = searchParams.get('active'); const dataSkip = Number(page) * 10 - 10; @@ -38,7 +39,7 @@ export async function GET(request: Request) { skip: dataSkip, take: 10, where: { - isActive: true, + isActive: status == "false" ? false : true, idDivision: idDivision, desc: { contains: (name == undefined || name == "null") ? "" : name, diff --git a/src/app/api/division/report/route.ts b/src/app/api/division/report/route.ts index 38c11e3..6c28b1e 100644 --- a/src/app/api/division/report/route.ts +++ b/src/app/api/division/report/route.ts @@ -1,6 +1,6 @@ import { prisma } from "@/module/_global"; import { funGetUserByCookies } from "@/module/auth"; -import _ from "lodash"; +import _, { ceil } from "lodash"; import moment from "moment"; import { NextResponse } from "next/server"; @@ -11,6 +11,8 @@ export async function GET(request: Request) { const idGroup = searchParams.get("group") const division = searchParams.get("division") const date = searchParams.get("date") + const dateAkhir = searchParams.get("date-end") + const kat = searchParams.get("cat") let grup if (user.id == undefined) { @@ -23,178 +25,349 @@ export async function GET(request: Request) { grup = idGroup } - // CHART PROGRESS - let kondisiProgress - if (division == "undefined") { - kondisiProgress = { - isActive: true, - updatedAt: { - lte: new Date(String(date)) - }, - Division: { - idGroup: String(grup) - } - } - } else { - kondisiProgress = { - isActive: true, - idDivision: String(division), - updatedAt: { - lte: new Date(String(date)) - }, - } - } - - const data = await prisma.divisionProject.groupBy({ - where: kondisiProgress, - by: ["status"], - _count: true - }) - - const dataStatus = [{ name: 'Segera dikerjakan', status: 0 }, { name: 'Dikerjakan', status: 1 }, { name: 'Selesai dikerjakan', status: 2 }, { name: 'Dibatalkan', status: 3 }] - const hasilProgres: any[] = [] - let input - for (let index = 0; index < dataStatus.length; index++) { - const cek = data.some((i: any) => i.status == dataStatus[index].status) - if (cek) { - const find = ((Number(data.find((i: any) => i.status == dataStatus[index].status)?._count) * 100) / data.reduce((n, { _count }) => n + _count, 0)).toFixed(2) - const fix = find != "100.00" ? find.substr(-2, 2) == "00" ? find.substr(0, 2) : find : "100" - input = { - name: dataStatus[index].name, - value: fix - } - } else { - input = { - name: dataStatus[index].name, - value: 0 - } - } - hasilProgres.push(input) - } - - - - - // CHART DOKUMEN - let kondisi - if (division == "undefined") { - kondisi = { - isActive: true, - category: 'FILE', - Division: { - idGroup: String(grup) - }, - createdAt: { - lte: new Date(String(date)) - }, - } - } else { - kondisi = { - isActive: true, - category: 'FILE', - idDivision: String(division), - createdAt: { - lte: new Date(String(date)) - }, - } - } - - const dataDokumen = await prisma.divisionDocumentFolderFile.findMany({ - where: kondisi, - }) - - const groupData = _.map(_.groupBy(dataDokumen, "extension"), (v: any) => ({ - file: v[0].extension, - jumlah: v.length, - })) - - const image = ['jpg', 'jpeg', 'png', 'heic'] - - let hasilImage = { - name: 'Gambar', - value: 0 - } - - let hasilFile = { - name: 'Dokumen', - value: 0 - } - - groupData.map((v: any) => { - if (image.some((i: any) => i == v.file)) { - hasilImage = { - name: 'Gambar', - value: hasilImage.value + v.jumlah - } - } else { - hasilFile = { - name: 'Dokumen', - value: hasilFile.value + v.jumlah - } - } - }) - - const hasilDokumen = [hasilImage, hasilFile] - - - - // CHART EVENT - let kondisiEvent - if (division == "undefined") { - kondisiEvent = { - isActive: true, - Division: { - idGroup: String(grup) - }, - dateStart: new Date(String(date)) - } - } else { - kondisiEvent = { - isActive: true, - idDivision: String(division), - dateStart: new Date(String(date)) - } - } - - const dataEvent = await prisma.divisionCalendar.findMany({ - where: kondisiEvent, - select: { - id: true, - idDivision: true, - title: true, - desc: true, - status: true, - timeStart: true, - dateStart: true, - timeEnd: true, - dateEnd: true, - createdAt: true, - User: { - select: { - name: true + if (kat == "table-progress") { + let kondisiProgress + if (division == "undefined") { + kondisiProgress = { + isActive: true, + Division: { + idGroup: String(grup) + }, + DivisionProjectTask: { + some: { + dateStart: { + gte: new Date(String(date)) + }, + dateEnd: { + lte: new Date(String(dateAkhir)) + } + } + } + } + + } else { + kondisiProgress = { + isActive: true, + idDivision: String(division), + DivisionProjectTask: { + some: { + dateStart: { + gte: new Date(String(date)) + }, + dateEnd: { + lte: new Date(String(dateAkhir)) + } + } } } - }, - orderBy: { - createdAt: 'desc' } - }) - - const hasilEvent = dataEvent.map((v: any) => ({ - ..._.omit(v, ["User"]), - user_name: v.User.name, - timeStart: moment.utc(v.timeStart).format('HH:mm'), - timeEnd: moment.utc(v.timeEnd).format('HH:mm') - })) - const allData = { - progress: hasilProgres, - dokumen: hasilDokumen, - event: hasilEvent + const data = await prisma.divisionProject.findMany({ + where: kondisiProgress, + select: { + id: true, + title: true, + status: true, + DivisionProjectTask: { + where: { + isActive: true + }, + select: { + title: true, + status: true + } + } + } + }) + + const dataFix = data.map((v: any) => ({ + ..._.omit(v, ["DivisionProjectTask"]), + progress: ceil((v.DivisionProjectTask.filter((i: any) => i.status == 1).length * 100) / v.DivisionProjectTask.length), + })) + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: dataFix }, { status: 200 }); + + } else { + // CHART PROGRESS + let kondisiProgress + if (division == "undefined") { + kondisiProgress = { + isActive: true, + Division: { + idGroup: String(grup) + }, + DivisionProjectTask: { + some: { + dateStart: { + gte: new Date(String(date)) + }, + dateEnd: { + lte: new Date(String(dateAkhir)) + } + } + } + } + } else { + kondisiProgress = { + isActive: true, + idDivision: String(division), + DivisionProjectTask: { + some: { + dateStart: { + gte: new Date(String(date)) + }, + dateEnd: { + lte: new Date(String(dateAkhir)) + } + } + } + } + } + + const data = await prisma.divisionProject.groupBy({ + where: kondisiProgress, + by: ["status"], + _count: true + }) + + const dataStatus = [{ name: 'Segera', status: 0 }, { name: 'Dikerjakan', status: 1 }, { name: 'Selesai', status: 2 }, { name: 'Dibatalkan', status: 3 }] + const hasilProgres: any[] = [] + let input + for (let index = 0; index < dataStatus.length; index++) { + const cek = data.some((i: any) => i.status == dataStatus[index].status) + if (cek) { + const find = ((Number(data.find((i: any) => i.status == dataStatus[index].status)?._count) * 100) / data.reduce((n, { _count }) => n + _count, 0)).toFixed(2) + const fix = find != "100.00" ? find.substr(-2, 2) == "00" ? find.substr(0, 2) : find : "100" + input = { + name: dataStatus[index].name, + value: fix + } + } else { + input = { + name: dataStatus[index].name, + value: 0 + } + } + hasilProgres.push(input) + } + + + + + // CHART DOKUMEN + let kondisi + if (division == "undefined") { + kondisi = { + isActive: true, + category: 'FILE', + Division: { + idGroup: String(grup) + }, + createdAt: { + gte: new Date(String(date)), + lte: new Date(String(dateAkhir)) + }, + } + } else { + kondisi = { + isActive: true, + category: 'FILE', + idDivision: String(division), + createdAt: { + gte: new Date(String(date)), + lte: new Date(String(dateAkhir)) + }, + } + } + + const dataDokumen = await prisma.divisionDocumentFolderFile.findMany({ + where: kondisi, + }) + + const groupData = _.map(_.groupBy(dataDokumen, "extension"), (v: any) => ({ + file: v[0].extension, + jumlah: v.length, + })) + + const image = ['jpg', 'jpeg', 'png', 'heic'] + + let hasilImage = { + name: 'Gambar', + value: 0 + } + + let hasilFile = { + name: 'Dokumen', + value: 0 + } + + groupData.map((v: any) => { + if (image.some((i: any) => i == v.file)) { + hasilImage = { + name: 'Gambar', + value: hasilImage.value + v.jumlah + } + } else { + hasilFile = { + name: 'Dokumen', + value: hasilFile.value + v.jumlah + } + } + }) + + const hasilDokumen = [hasilImage, hasilFile] + + + + // CHART EVENT + let kondisiEvent, kondisiSelesai, kondisiComingSoon + if (division == "undefined") { + // kondisiEvent = { + // isActive: true, + // Division: { + // idGroup: String(grup) + // }, + // DivisionCalendarReminder: { + // some: { + // dateStart: { + // gte: new Date(String(date)), + // lte: new Date(String(dateAkhir)) + // } + // } + // } + // } + + kondisiSelesai = { + isActive: true, + Division: { + idGroup: String(grup) + }, + DivisionCalendarReminder: { + some: { + dateStart: { + gte: new Date(String(date)), + lte: new Date() + } + } + } + } + + kondisiComingSoon = { + isActive: true, + Division: { + idGroup: String(grup) + }, + DivisionCalendarReminder: { + some: { + dateStart: { + gt: new Date(), + lte: new Date(String(dateAkhir)) + } + } + } + } + } else { + // kondisiEvent = { + // isActive: true, + // idDivision: String(division), + // DivisionCalendarReminder: { + // some: { + // dateStart: { + // gte: new Date(String(date)), + // lte: new Date(String(dateAkhir)) + // } + // } + // } + // } + + kondisiSelesai = { + isActive: true, + idDivision: String(division), + DivisionCalendarReminder: { + some: { + dateStart: { + gte: new Date(String(date)), + lte: new Date() + } + } + } + } + + kondisiComingSoon = { + isActive: true, + idDivision: String(division), + DivisionCalendarReminder: { + some: { + dateStart: { + gt: new Date(), + lte: new Date(String(dateAkhir)) + } + } + } + } + } + + const eventSelesai = await prisma.divisionCalendar.count({ + where: kondisiSelesai + }) + + const eventComingSoon = await prisma.divisionCalendar.count({ + where: kondisiComingSoon + }) + + const hasilEvent = [ + { + name: 'Selesai', + value: eventSelesai + }, + { + name: 'Akan Datang', + value: eventComingSoon + } + ] + + // const dataEvent = await prisma.divisionCalendar.findMany({ + // where: kondisiEvent, + // select: { + // id: true, + // idDivision: true, + // title: true, + // desc: true, + // status: true, + // timeStart: true, + // dateStart: true, + // timeEnd: true, + // dateEnd: true, + // createdAt: true, + // User: { + // select: { + // name: true + // } + // } + // }, + // orderBy: { + // createdAt: 'desc' + // } + // }) + + // const hasilEvent = dataEvent.map((v: any) => ({ + // ..._.omit(v, ["User"]), + // user_name: v.User.name, + // timeStart: moment.utc(v.timeStart).format('HH:mm'), + // timeEnd: moment.utc(v.timeEnd).format('HH:mm') + // })) + + + const allData = { + progress: hasilProgres, + dokumen: hasilDokumen, + event: hasilEvent + } + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: allData }, { status: 200 }); } - return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: allData }, { status: 200 }); } catch (error) { diff --git a/src/app/api/version-app/route.ts b/src/app/api/version-app/route.ts index 1741163..d349d09 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: "1.0.0", tahap: "beta" }, { status: 200 }); + return NextResponse.json({ success: true, version: "1.1.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/_global/index.ts b/src/module/_global/index.ts index a592424..d7ea5e1 100644 --- a/src/module/_global/index.ts +++ b/src/module/_global/index.ts @@ -28,6 +28,7 @@ import SkeletonList from "./components/skeleton_list"; import { funViewDir } from "./fun/view_dir"; import { funSendWebPush } from "./fun/send_web_push"; import ViewFilterData from "./view/view_filter_kategori_data"; +import LayoutModalNew from "./layout/layout_modal_new"; export { WARNA }; export { LayoutLogin }; @@ -65,3 +66,4 @@ export { keyWibu } export { funViewDir } export { funSendWebPush } export { ViewFilterData } +export { LayoutModalNew } diff --git a/src/module/_global/layout/layout_modal_new.tsx b/src/module/_global/layout/layout_modal_new.tsx new file mode 100644 index 0000000..bf80f3c --- /dev/null +++ b/src/module/_global/layout/layout_modal_new.tsx @@ -0,0 +1,39 @@ +import { Button, Flex, Modal, SimpleGrid, Text } from '@mantine/core'; +import { useMediaQuery } from '@mantine/hooks'; +import { IoIosWarning } from 'react-icons/io'; + +export default function LayoutModalNew({ opened, onClose, description, onYes, loading, onCheck }: { opened: boolean, onClose: () => void, loading?: boolean, description: string, onYes: (val: boolean) => void, onCheck: (val: boolean) => void }) { + const isMobile = useMediaQuery('(max-width: 768px)'); + return ( + + + + {description} + + + + {isMobile ? + <> + + + + : + <> + + + + + } + + + ); +} + diff --git a/src/module/banner/ui/create_banner.tsx b/src/module/banner/ui/create_banner.tsx index fbd4afe..8331050 100644 --- a/src/module/banner/ui/create_banner.tsx +++ b/src/module/banner/ui/create_banner.tsx @@ -119,10 +119,10 @@ function CreateBanner() { onValidation('image', files[0]) }} activateOnClick={false} - maxSize={1 * 1024 ** 2} + maxSize={10 * 1024 ** 2} accept={['image/png', 'image/jpeg', 'image/heic']} onReject={(files) => { - return toast.error('File yang diizinkan: .png, .jpg, dan .heic dengan ukuran maksimal 1 MB') + return toast.error('File yang diizinkan: .png, .jpg, dan .heic dengan ukuran maksimal 10 MB') }} onClick={() => openRef.current?.()} > diff --git a/src/module/banner/ui/edit_banner.tsx b/src/module/banner/ui/edit_banner.tsx index 30c5d91..346c30d 100644 --- a/src/module/banner/ui/edit_banner.tsx +++ b/src/module/banner/ui/edit_banner.tsx @@ -120,10 +120,10 @@ export default function EditBanner() { onValidation('image', files[0]) }} activateOnClick={false} - maxSize={1 * 1024 ** 2} + maxSize={10 * 1024 ** 2} accept={['image/png', 'image/jpeg', 'image/heic']} onReject={(files) => { - return toast.error('File yang diizinkan: .png, .jpg, dan .heic dengan ukuran maksimal 1 MB') + return toast.error('File yang diizinkan: .png, .jpg, dan .heic dengan ukuran maksimal 10 MB') }} onClick={() => openRef.current?.()} > diff --git a/src/module/calender/ui/create_calender_division_caleder.tsx b/src/module/calender/ui/create_calender_division_caleder.tsx index 8730faa..f92c985 100644 --- a/src/module/calender/ui/create_calender_division_caleder.tsx +++ b/src/module/calender/ui/create_calender_division_caleder.tsx @@ -1,8 +1,8 @@ 'use client' -import { keyWibu, LayoutNavbarNew, TEMA } from '@/module/_global'; +import { keyWibu, LayoutModalNew, LayoutNavbarNew, TEMA } from '@/module/_global'; import LayoutModal from '@/module/_global/layout/layout_modal'; import { useHookstate } from '@hookstate/core'; -import { Avatar, Box, Button, Divider, Grid, Group, rem, Select, SimpleGrid, Stack, Text, Textarea, TextInput } from '@mantine/core'; +import { Avatar, Box, Button, Divider, Flex, Grid, Group, Modal, rem, Select, SimpleGrid, Stack, Table, Text, Textarea, TextInput } from '@mantine/core'; import { DateInput, TimeInput } from '@mantine/dates'; import { useMediaQuery } from '@mantine/hooks'; import moment from 'moment'; @@ -15,16 +15,19 @@ 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'; +import ViewDataConflict from './create_view_event_conflict'; export default function CreateCalenderDivisionCaleder() { const [value, setValue] = useState(null); const router = useRouter() const [isModal, setModal] = useState(false) const [isModalKonfirmasiTglSama, setModalKonfirmasiTglSama] = useState(false) + const [isModalViewData, setModalViewData] = useState(false) const [loadingModal, setLoadingModal] = useState(false) const [loadingModalKonfirmasiTglSama, setLoadingModalKonfirmasiTglSama] = useState(false) const member = useHookstate(globalCalender) const memberValue = member.get() as IFormMemberCalender[] + const [dataAcaraKonflik, setDataAcaraKonflik] = useState([]) const [openMember, setOpenMember] = useState(false) const param = useParams<{ id: string, detail: string }>() const tema = useHookstate(TEMA) @@ -75,6 +78,7 @@ export default function CreateCalenderDivisionCaleder() { onSubmit() } else { setModalKonfirmasiTglSama(true) + setDataAcaraKonflik(response.data) } } catch (error) { console.error(error) @@ -217,6 +221,7 @@ export default function CreateCalenderDivisionCaleder() { } if (openMember) return setOpenMember(false)} /> + if (isModalViewData) return setModalViewData(false)} data={dataAcaraKonflik} /> return ( @@ -468,7 +473,7 @@ export default function CreateCalenderDivisionCaleder() { }} /> - { val ? setModalViewData(true) : setModalViewData(false) }} onClose={() => { setModalKonfirmasiTglSama(false) setModal(false) diff --git a/src/module/calender/ui/create_view_event_conflict.tsx b/src/module/calender/ui/create_view_event_conflict.tsx new file mode 100644 index 0000000..f6635ce --- /dev/null +++ b/src/module/calender/ui/create_view_event_conflict.tsx @@ -0,0 +1,101 @@ +import { LayoutNavbarNew, TEMA } from '@/module/_global'; +import { useHookstate } from '@hookstate/core'; +import { ActionIcon, Avatar, Box, Button, Divider, Flex, Grid, rem, Stack, Table, Text } from '@mantine/core'; +import moment from 'moment'; +import { HiChevronLeft } from 'react-icons/hi2'; +import "moment/locale/id"; +import { FaCheck } from 'react-icons/fa6'; +import { useMediaQuery } from '@mantine/hooks'; +import { MdOutlineEventNote } from 'react-icons/md'; + +export default function ViewDataConflict({ onClose, data }: { onClose: (val: any) => void, data: any[] }) { + const tema = useHookstate(TEMA) + const isMobile2 = useMediaQuery("(max-width: 438px)"); + + return ( + + + { onClose(true) }} bg={tema.get().bgIcon} size="lg" radius="lg" aria-label="Settings"> + + + + } + menu={<>} + /> + + { + (data.length === 0) ? + + Tidak ada data + + : + + {data.map((v, i) => { + return ( + + + + + + + + + + + {v.title} + {moment.utc(v.timeStart).format("HH:mm")} - {moment.utc(v.timeEnd).format("HH:mm")} + + {/* {isSelected ? : null} */} + + + + + + + + ); + })} + + } + + + + + + ); +} diff --git a/src/module/discussion/index.ts b/src/module/discussion/index.ts index d40c1d6..cda4448 100644 --- a/src/module/discussion/index.ts +++ b/src/module/discussion/index.ts @@ -3,9 +3,11 @@ import FormCreateDiscussion from "./ui/form_create_discussion"; import FormEditDiscussion from "./ui/form_edit_discussion"; import ListDiscussion from "./ui/list_discussion"; import NavbarListDiscussion from "./ui/navbar_list_discussion"; +import TabListDiscussion from "./ui/tab_discussion"; export { ListDiscussion } export { NavbarListDiscussion } export { FormCreateDiscussion } export { DetailDiscussion } -export { FormEditDiscussion } \ No newline at end of file +export { FormEditDiscussion } +export { TabListDiscussion } \ No newline at end of file diff --git a/src/module/discussion/lib/api_discussion.ts b/src/module/discussion/lib/api_discussion.ts index bffbd08..a8c8219 100644 --- a/src/module/discussion/lib/api_discussion.ts +++ b/src/module/discussion/lib/api_discussion.ts @@ -36,9 +36,13 @@ export const funEditStatusDiscussion = async (path: string, data: IStatusDiscuss return await response.json().catch(() => null); } -export const funDeleteDiscussion = async (path: string) => { +export const funDeleteDiscussion = async (path: string, data: { active: boolean }) => { const response = await fetch(`/api/discussion/${path}`, { method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), }); return await response.json().catch(() => null); } diff --git a/src/module/discussion/lib/type_discussion.ts b/src/module/discussion/lib/type_discussion.ts index dcc59d4..d3525cc 100644 --- a/src/module/discussion/lib/type_discussion.ts +++ b/src/module/discussion/lib/type_discussion.ts @@ -30,6 +30,7 @@ export interface IDetailDiscussion { user_img: string totalComments: number createdBy: string + isActive: boolean } export interface IAllComents { diff --git a/src/module/discussion/ui/detail_discussion.tsx b/src/module/discussion/ui/detail_discussion.tsx index 163da33..1b5df7e 100644 --- a/src/module/discussion/ui/detail_discussion.tsx +++ b/src/module/discussion/ui/detail_discussion.tsx @@ -63,10 +63,14 @@ export default function DetailDiscussion({ id, idDivision }: { id: string, idDiv } if (dataRealTime && dataRealTime.some((i: any) => i.category == 'discussion-delete' && i.id == 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) + if ((roleLogin.get() == "user" || roleLogin.get() == "coadmin") && !adminLogin.get()) { + toast.error("Data telah diarsipkan, anda akan beralih ke halaman list diskusi") + setTimeout(() => { + router.push(`/division/${param.id}/discussion`) + }, 1000) + } else { + getData(true) + } } }, [dataRealTime]) @@ -107,7 +111,6 @@ export default function DetailDiscussion({ id, idDivision }: { id: string, idDiv return ( - {/* */} setOpenDrawer(false)}> - setOpenDrawer(false)} id={id} status={Number(isData?.status)} idDivision={idDivision} /> + setOpenDrawer(false)} id={id} status={Number(isData?.status)} idDivision={idDivision} active={isData?.isActive == false ? false : true} /> @@ -159,7 +162,11 @@ export default function DetailDiscussion({ id, idDivision }: { id: string, idDiv {isData?.username} - {isData?.status === 1 ? "BUKA" : "TUTUP"} + {!isData?.isActive ? + ARSIP + : + {isData?.status === 1 ? "BUKA" : "TUTUP"} + } @@ -196,7 +203,11 @@ export default function DetailDiscussion({ id, idDivision }: { id: string, idDiv {isData?.username} - {isData?.status === 1 ? "BUKA" : "TUTUP"} + {!isData?.isActive ? + ARSIP + : + {isData?.status === 1 ? "BUKA" : "TUTUP"} + } @@ -324,7 +335,7 @@ export default function DetailDiscussion({ id, idDivision }: { id: string, idDiv }} size="md" placeholder="Kirim Komentar" - disabled={(isData?.status === 2 || (!memberDivision.get() && (roleLogin.get() == "user" || roleLogin.get() == "coadmin")))} + disabled={(isData?.status === 2 || !isData?.isActive || (!memberDivision.get() && (roleLogin.get() == "user" || roleLogin.get() == "coadmin")))} onChange={(e) => setIsComent(e.target.value)} value={isComent} maxLength={300} @@ -335,7 +346,7 @@ export default function DetailDiscussion({ id, idDivision }: { id: string, idDiv
+ variant="subtle" aria-label="submit" disabled={(isData?.status === 2 || !isData?.isActive || (!memberDivision.get() && (roleLogin.get() == "user" || roleLogin.get() == "coadmin")))}>
diff --git a/src/module/discussion/ui/drawer_detail_discussion.tsx b/src/module/discussion/ui/drawer_detail_discussion.tsx index 053e9d6..ba0de90 100644 --- a/src/module/discussion/ui/drawer_detail_discussion.tsx +++ b/src/module/discussion/ui/drawer_detail_discussion.tsx @@ -5,14 +5,14 @@ import { Box, Flex, SimpleGrid, Stack, Text } from "@mantine/core"; import { useParams, useRouter } from "next/navigation"; import { useState } from "react"; import toast from "react-hot-toast"; -import { BsTrash3 } from "react-icons/bs"; +import { BiArchive } from "react-icons/bi"; import { FaCheck, FaPencil } from "react-icons/fa6"; import { MdClose } from "react-icons/md"; import { useWibuRealtime } from "wibu-realtime"; import { funDeleteDiscussion, funEditStatusDiscussion } from "../lib/api_discussion"; import { globalRefreshDiscussion } from "../lib/val_discussion"; -export default function DrawerDetailDiscussion({ onSuccess, id, status, idDivision }: { onSuccess: (val: boolean) => void, id: string, status: number, idDivision: string }) { +export default function DrawerDetailDiscussion({ onSuccess, id, status, idDivision, active }: { onSuccess: (val: boolean) => void, id: string, status: number, idDivision: string, active: boolean }) { const [isValModal, setValModal] = useState(false) const [isValModalStatus, setValModalStatus] = useState(false) const router = useRouter() @@ -57,28 +57,41 @@ export default function DrawerDetailDiscussion({ onSuccess, id, status, idDivisi try { if (val) { setLoadingDelete(true) - const response = await funDeleteDiscussion(id) + const response = await funDeleteDiscussion(id, { active: !active }) 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`) + if (active) { + setDataRealtime([ + { + category: "discussion-delete", + id: id, + user: response.user + }, + { + category: "division/" + param.id + "/discussion", + } + ]) + router.push(`/division/${param.id}/discussion`) + } else { + refresh.set(!refresh.get()) + setDataRealtime([ + { + category: "discussion-detail", + id: id, + }, + { + category: "division/" + param.id + "/discussion", + } + ]) + } } else { toast.error(response.message) } } } catch (error) { console.error(error); - toast.error("Gagal hapus diskusi, coba lagi nanti"); + toast.error("Gagal mengupdate diskusi, coba lagi nanti"); } finally { setLoadingDelete(false) setValModal(false) @@ -94,52 +107,73 @@ export default function DrawerDetailDiscussion({ onSuccess, id, status, idDivisi spacing={{ base: 'xl', sm: 'xl' }} verticalSpacing={{ base: 'xl', sm: 'xl' }} > - window.location.href = `/division/${param.id}/discussion/update/${param.detail}`} justify={'center'} align={'center'} direction={'column'} > - - - - - Edit - - + { + active && + <> + window.location.href = `/division/${param.id}/discussion/update/${param.detail}`} justify={'center'} align={'center'} direction={'column'} > + + + + + Edit + + - setValModalStatus(true)} > - {status === 1 ? ( - <> - - - - - Tutup Diskusi - - - ) : ( - <> - + setValModalStatus(true)} > + {status === 1 ? ( + <> + + + + + Tutup Diskusi + + + ) : ( + <> + - - - - Buka Diskusi - - - )} - + + + + Buka Diskusi + + + )} + + + + } + + + { + active ? + setValModal(true)} justify={'center'} align={'center'} direction={'column'} > + + + + + Arsipkan + + + : + setValModal(true)} justify={'center'} align={'center'} direction={'column'} > + + + + + Aktifkan Diskusi + + + + } - setValModal(true)} justify={'center'} align={'center'} direction={'column'} > - - - - - Hapus - - setValModal(false)} - description="Apakah Anda yakin ingin menghapus diskusi ini?" + description={`Apakah Anda yakin ingin ${active ? "mengarsipkan" : "mengaktifkan"} diskusi ini?`} onYes={(val) => { fetchDeleteDiscussion(val) }} /> diff --git a/src/module/discussion/ui/list_discussion.tsx b/src/module/discussion/ui/list_discussion.tsx index c8fcac5..cf70e63 100644 --- a/src/module/discussion/ui/list_discussion.tsx +++ b/src/module/discussion/ui/list_discussion.tsx @@ -4,7 +4,7 @@ import { useHookstate } from "@hookstate/core"; import { Avatar, Badge, Box, Divider, Flex, Grid, Group, Skeleton, Spoiler, Text, TextInput } from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; import _ from "lodash"; -import { useParams, useRouter } from "next/navigation"; +import { useParams, useRouter, useSearchParams } from "next/navigation"; import { useEffect, useState } from "react"; import toast from "react-hot-toast"; import { GrChatOption } from "react-icons/gr"; @@ -14,7 +14,7 @@ import { funGetAllDiscussion } from "../lib/api_discussion"; import { IDataDiscussion } from "../lib/type_discussion"; -export default function ListDiscussion({ id }: { id: string }) { +export default function ListDiscussion() { const [isData, setData] = useState([]) const [searchQuery, setSearchQuery] = useState('') const param = useParams<{ id: string }>() @@ -25,6 +25,8 @@ export default function ListDiscussion({ id }: { id: string }) { const [isPage, setPage] = useState(1) const notifLoadPage = useHookstate(globalNotifPage) const [isRefresh, setRefresh] = useState(false) + const searchParams = useSearchParams() + const status = searchParams.get('active') const [dataRealTime, setDataRealtime] = useWibuRealtime({ WIBU_REALTIME_TOKEN: keyWibu, project: "sdm" @@ -33,7 +35,7 @@ export default function ListDiscussion({ id }: { id: string }) { const getData = async (loading: boolean) => { try { setLoading(loading) - const response = await funGetAllDiscussion('?division=' + id + '&search=' + searchQuery + '&page=' + isPage) + const response = await funGetAllDiscussion('?division=' + param.id + '&search=' + searchQuery + '&page=' + isPage + '&active=' + status) if (response.success) { if (isPage == 1) { setData(response.data) @@ -57,7 +59,7 @@ export default function ListDiscussion({ id }: { id: string }) { useShallowEffect(() => { setPage(1) getData(true) - }, [searchQuery]) + }, [searchQuery, status]) useEffect(() => { const handleScroll = async () => { @@ -108,7 +110,7 @@ export default function ListDiscussion({ id }: { id: string }) { return ( - + { isRefresh && {v.user_name} - {v.status === 1 ? "BUKA" : "TUTUP"} + { + status != "false" && {v.status === 1 ? "BUKA" : "TUTUP"} + }
diff --git a/src/module/discussion/ui/navbar_detail_discussion.tsx b/src/module/discussion/ui/navbar_detail_discussion.tsx index 8e948c2..aae37b7 100644 --- a/src/module/discussion/ui/navbar_detail_discussion.tsx +++ b/src/module/discussion/ui/navbar_detail_discussion.tsx @@ -21,7 +21,7 @@ export default function NavbarDetailDiscussion({id, status, idDivision}: {id: st } /> setOpenDrawer(false)}> - setOpenDrawer(false)} id={id} status={status} idDivision={idDivision} /> + setOpenDrawer(false)} id={id} status={status} idDivision={idDivision} active={false}/> ); diff --git a/src/module/discussion/ui/navbar_list_discussion.tsx b/src/module/discussion/ui/navbar_list_discussion.tsx index 9d5112b..cda613f 100644 --- a/src/module/discussion/ui/navbar_list_discussion.tsx +++ b/src/module/discussion/ui/navbar_list_discussion.tsx @@ -6,19 +6,20 @@ import { useState } from "react"; import DrawerListDiscussion from "./drawer_list_discussion"; import { useParams } from "next/navigation"; import { useHookstate } from "@hookstate/core"; -import { globalIsMemberDivision } from "@/module/division_new"; +import { globalIsAdminDivision, globalIsMemberDivision } from "@/module/division_new"; export default function NavbarListDiscussion() { const [openDrawer, setOpenDrawer] = useState(false) const roleLogin = useHookstate(globalRole) const memberDivision = useHookstate(globalIsMemberDivision) + const adminDivision = useHookstate(globalIsAdminDivision) const param = useParams<{ id: string }>() const tema = useHookstate(TEMA) return ( <> + (roleLogin.get() == "user" || roleLogin.get() == "coadmin") && !adminDivision.get() ? <> : setOpenDrawer(true)} bg={tema.get().bgIcon} size="lg" radius="lg" aria-label="Settings"> diff --git a/src/module/discussion/ui/tab_discussion.tsx b/src/module/discussion/ui/tab_discussion.tsx new file mode 100644 index 0000000..89ff6e9 --- /dev/null +++ b/src/module/discussion/ui/tab_discussion.tsx @@ -0,0 +1,68 @@ +"use client"; +import { globalRole, TEMA } from "@/module/_global"; +import { globalIsAdminDivision } from "@/module/division_new"; +import { useHookstate } from "@hookstate/core"; +import { Box, rem, Tabs } from "@mantine/core"; +import { useRouter, useSearchParams } from "next/navigation"; +import { IoMdCheckmarkCircleOutline } from "react-icons/io"; +import { IoCloseCircleOutline } from "react-icons/io5"; +import ListDiscussion from "./list_discussion"; + +export default function TabListDiscussion() { + const iconStyle = { width: rem(20), height: rem(20) }; + const router = useRouter(); + const searchParams = useSearchParams(); + const status = searchParams.get("active"); + const tema = useHookstate(TEMA) + const roleLogin = useHookstate(globalRole) + const adminDivision = useHookstate(globalIsAdminDivision) + + return ( + + + { + roleLogin.get() != '' + ? + (roleLogin.get() == "user" || roleLogin.get() == "coadmin") && !adminDivision.get() ? <> + : + + } + onClick={() => { + router.push("?active=true"); + }} + > + Aktif + + } + onClick={() => { + router.push("?active=false"); + }} + > + Arsip + + + : <> + } + + + + ); +} diff --git a/src/module/discussion_general/index.ts b/src/module/discussion_general/index.ts index bb5e452..2d46292 100644 --- a/src/module/discussion_general/index.ts +++ b/src/module/discussion_general/index.ts @@ -5,6 +5,7 @@ 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"; +import TabListDiscussionGeneral from "./ui/tab_list_discussion_general"; export { ListDiscussionGeneral } export { NavbarDiscussionGeneral } @@ -12,4 +13,5 @@ export { FormCreateDiscussionGeneral } export { DetailDiscussionGeneral } export { MemberDiscussionGeneral } export { FormEditDiscussionGeneral } -export { AddMemberDiscussionGeneral } \ No newline at end of file +export { AddMemberDiscussionGeneral } +export { TabListDiscussionGeneral } \ 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 1d7a9bd..769bce9 100644 --- a/src/module/discussion_general/lib/api_discussion_general.ts +++ b/src/module/discussion_general/lib/api_discussion_general.ts @@ -58,9 +58,13 @@ export const funEditDiscussionGeneral = async (path: string, data: { title: stri } -export const funDeleteDiscussionGeneral = async (path: string) => { +export const funDeleteDiscussionGeneral = async (path: string, data: { active: boolean }) => { const response = await fetch(`/api/discussion-general/${path}`, { method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), }); return await response.json().catch(() => null); } diff --git a/src/module/discussion_general/lib/type_discussion_general.ts b/src/module/discussion_general/lib/type_discussion_general.ts index 62be53f..df2275f 100644 --- a/src/module/discussion_general/lib/type_discussion_general.ts +++ b/src/module/discussion_general/lib/type_discussion_general.ts @@ -6,6 +6,7 @@ export interface IFormMemberDisscussionGeneral { export interface IDetailDiscussionGeneral{ id: string + isActive: boolean title: string desc: string status: number diff --git a/src/module/discussion_general/ui/add_member.tsx b/src/module/discussion_general/ui/add_member.tsx index ab2a6b9..1903eb9 100644 --- a/src/module/discussion_general/ui/add_member.tsx +++ b/src/module/discussion_general/ui/add_member.tsx @@ -184,7 +184,7 @@ export default function AddMemberDiscussionGeneral() { }> + }} src={`https://wibu-storage.wibudev.com/api/files/${v.img}`} alt="it's me" size="lg" /> {v.name} @@ -233,7 +233,7 @@ export default function AddMemberDiscussionGeneral() { xl: 1, }} > - + 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) + if (roleLogin.get() == "user" || roleLogin.get() == "coadmin") { + toast.error("Data telah diarsipkan, anda akan beralih ke halaman list diskusi umum") + setTimeout(() => { + router.push(`/discussion`) + }, 1000) + } else { + getData(false) + } + } }, [dataRealTime]) @@ -139,7 +144,7 @@ export default function DetailDiscussionGeneral() { } /> setOpenDrawer(false)}> - setOpenDrawer(false)} id={param.id} status={Number(isData?.status)} /> + setOpenDrawer(false)} id={param.id} status={Number(isData?.status)} active={isData?.isActive == false ? false : true} /> @@ -188,7 +193,12 @@ export default function DetailDiscussionGeneral() { {isData?.title} - {isData?.status === 1 ? "BUKA" : "TUTUP"} + {!isData?.isActive ? + ARSIP + : + {isData?.status === 1 ? "BUKA" : "TUTUP"} + } + @@ -234,7 +244,12 @@ export default function DetailDiscussionGeneral() { {isData?.title} - {isData?.status === 1 ? "BUKA" : "TUTUP"} + { + !isData?.isActive ? + ARSIP + : + {isData?.status === 1 ? "BUKA" : "TUTUP"} + } @@ -303,7 +318,7 @@ export default function DetailDiscussionGeneral() { - + @@ -313,7 +328,7 @@ export default function DetailDiscussionGeneral() { - {moment(v.createdAt).format('lll').replace('pukul', '')} + {v.createdAt} @@ -362,7 +377,7 @@ export default function DetailDiscussionGeneral() { }} size="md" placeholder="Kirim Komentar" - disabled={(isData?.status === 2 || (!memberDiscussion && (roleLogin.get() == "user" || roleLogin.get() == "coadmin")))} + disabled={(isData?.status === 2 || !isData?.isActive || (!memberDiscussion && (roleLogin.get() == "user" || roleLogin.get() == "coadmin")))} onChange={(e) => setIsComent(e.target.value)} value={isComent} maxLength={300} @@ -373,7 +388,7 @@ export default function DetailDiscussionGeneral() {
+ variant="subtle" aria-label="submit" disabled={(isData?.status === 2 || !isData?.isActive || (!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 c5a656d..becce84 100644 --- a/src/module/discussion_general/ui/drawer_detail_discussion_general.tsx +++ b/src/module/discussion_general/ui/drawer_detail_discussion_general.tsx @@ -4,15 +4,15 @@ import { useHookstate } from "@hookstate/core"; import { Box, Flex, SimpleGrid, Stack, Text } from "@mantine/core"; import { useParams, useRouter } from "next/navigation"; import { useState } from "react"; -import { BsTrash3 } from "react-icons/bs"; +import toast from "react-hot-toast"; +import { BiArchive } from "react-icons/bi"; 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 }) { +export default function DrawerDetailDiscussionGeneral({ onSuccess, id, status, active }: { onSuccess: (val: boolean) => void, id: string, status: number, active: boolean }) { const [isValModal, setValModal] = useState(false) const [isValModalStatus, setValModalStatus] = useState(false) const router = useRouter() @@ -58,28 +58,42 @@ export default function DrawerDetailDiscussionGeneral({ onSuccess, id, status }: try { if (val) { setLoadingDelete(true) - const response = await funDeleteDiscussionGeneral(id) + const response = await funDeleteDiscussionGeneral(id, { active: !active }) if (response.success) { - setDataRealtime([ - { - category: "discussion-general-delete", - id: id, - user: response.user - }, - { - category: "discussion", - } - ]) toast.success(response.message) onSuccess(false) - router.push(`/discussion`) + if (active) { + setDataRealtime([ + { + category: "discussion-general-delete", + id: id, + user: response.user + }, + { + category: "discussion-general", + } + ]) + router.push(`/discussion`) + } else { + refresh.set(!refresh.get()) + setDataRealtime([ + { + category: "discussion-general-detail", + id: id, + }, + { + category: "discussion-general", + } + ]) + } + } else { toast.error(response.message) } } } catch (error) { console.error(error); - toast.error("Gagal hapus diskusi umum, coba lagi nanti"); + toast.error("Gagal mengupdate diskusi umum, coba lagi nanti"); } finally { setLoadingDelete(false) setValModal(false) @@ -108,44 +122,50 @@ export default function DrawerDetailDiscussionGeneral({ onSuccess, id, status }: { (roleLogin.get() != "user" && roleLogin.get() != "coadmin") ? ( <> - window.location.href = `/discussion/${param.id}/edit/`} justify={'center'} align={'center'} direction={'column'} > - - - - - Edit - - - - setValModalStatus(true)} > - {status === 1 ? ( + { + active && ( <> - + window.location.href = `/discussion/${param.id}/edit/`} justify={'center'} align={'center'} direction={'column'} > - + - Tutup Diskusi + + Edit + + + + setValModalStatus(true)} > + {status === 1 ? ( + <> + + + + + Tutup Diskusi + + + ) : ( + <> + + + + + + Buka Diskusi + + + )} - ) : ( - <> - - - - - - Buka Diskusi - - - )} - + ) + } setValModal(true)} justify={'center'} align={'center'} direction={'column'} > - + - Hapus + {active ? "Arsipkan" : "Aktifkan Diskusi"} @@ -157,7 +177,7 @@ export default function DrawerDetailDiscussionGeneral({ onSuccess, id, status }: setValModal(false)} - description="Apakah Anda yakin ingin menghapus diskusi ini?" + description={`Apakah Anda yakin ingin ${active ? "mengarsipkan" : "mengaktifkan"} diskusi ini?`} onYes={(val) => { fetchDeleteDiscussion(val) }} /> diff --git a/src/module/discussion_general/ui/drawer_discussion.tsx b/src/module/discussion_general/ui/drawer_discussion.tsx index 2310337..7fc221a 100644 --- a/src/module/discussion_general/ui/drawer_discussion.tsx +++ b/src/module/discussion_general/ui/drawer_discussion.tsx @@ -1,16 +1,22 @@ -import { TEMA } from "@/module/_global"; +import { globalRole, TEMA } from "@/module/_global"; import { useHookstate } from "@hookstate/core"; import { Box, Flex, SimpleGrid, Stack, Text } from "@mantine/core"; +import { useRouter, useSearchParams } from "next/navigation"; +import { HiOutlineFilter } from "react-icons/hi"; import { IoAddCircle } from "react-icons/io5"; export default function DrawerDiscussionGeneral() { const tema = useHookstate(TEMA) + const roleLogin = useHookstate(globalRole) + const router = useRouter() + const searchParams = useSearchParams() + const group = searchParams.get('group') return ( - window.location.href = "/discussion/create"} justify={'center'} align={'center'} direction={'column'} > + router.push("/discussion/create")} justify={'center'} align={'center'} direction={'column'} > @@ -18,6 +24,19 @@ export default function DrawerDiscussionGeneral() { Tambah Diskusi + { + roleLogin.get() == "supadmin" && + { + router.push('/discussion?page=filter&group=' + group) + }} justify={'center'} align={'center'} direction={'column'} > + + + + + Filter + + + }
diff --git a/src/module/discussion_general/ui/list_discussion.tsx b/src/module/discussion_general/ui/list_discussion.tsx index a5d8b7a..13d3522 100644 --- a/src/module/discussion_general/ui/list_discussion.tsx +++ b/src/module/discussion_general/ui/list_discussion.tsx @@ -1,10 +1,10 @@ 'use client' -import { currentScroll, globalNotifPage, keyWibu, ReloadButtonTop, TEMA } from "@/module/_global"; +import { currentScroll, globalNotifPage, globalRole, keyWibu, ReloadButtonTop, TEMA } from "@/module/_global"; import { useHookstate } from "@hookstate/core"; import { ActionIcon, Badge, Box, Divider, Flex, Grid, Group, Skeleton, Spoiler, Text, TextInput } from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; import _ from "lodash"; -import { useRouter } from "next/navigation"; +import { useRouter, useSearchParams } from "next/navigation"; import { useEffect, useState } from "react"; import toast from "react-hot-toast"; import { BiSolidCommentDetail } from "react-icons/bi"; @@ -20,7 +20,12 @@ export default function ListDiscussionGeneral() { const [loading, setLoading] = useState(true) const tema = useHookstate(TEMA) const router = useRouter() - const { value: containerRef } = useHookstate(currentScroll); + const { value: containerRef } = useHookstate(currentScroll) + const searchParams = useSearchParams() + const status = searchParams.get('active') + const group = searchParams.get('group') + const roleLogin = useHookstate(globalRole) + const [nameGroup, setNameGroup] = useState('') const [isPage, setPage] = useState(1) const notifLoadPage = useHookstate(globalNotifPage) const [isRefresh, setRefresh] = useState(false) @@ -32,8 +37,9 @@ export default function ListDiscussionGeneral() { const getData = async (loading: boolean) => { try { setLoading(loading) - const response = await funGetAllDiscussionGeneral('?search=' + searchQuery + '&page=' + isPage) + const response = await funGetAllDiscussionGeneral('?active=' + status + '&group=' + group + '&search=' + searchQuery + '&page=' + isPage) if (response.success) { + setNameGroup(response.filter.name) if (isPage == 1) { setData(response.data) } else { @@ -56,7 +62,7 @@ export default function ListDiscussionGeneral() { useShallowEffect(() => { setPage(1) getData(true) - }, [searchQuery]) + }, [searchQuery, status]) useEffect(() => { const handleScroll = async () => { @@ -107,7 +113,7 @@ export default function ListDiscussionGeneral() { return ( - + { isRefresh && setSearchQuery(e.target.value)} /> + {roleLogin.get() == 'supadmin' && Filter : {nameGroup}} {loading ? Array(3) .fill(null) @@ -214,7 +221,9 @@ export default function ListDiscussionGeneral() { {v.title} - {v.status === 1 ? "BUKA" : "TUTUP"} + { + status != "false" && {v.status === 1 ? "BUKA" : "TUTUP"} + }
diff --git a/src/module/discussion_general/ui/member_discussion_general.tsx b/src/module/discussion_general/ui/member_discussion_general.tsx index 1613acb..a612951 100644 --- a/src/module/discussion_general/ui/member_discussion_general.tsx +++ b/src/module/discussion_general/ui/member_discussion_general.tsx @@ -122,7 +122,7 @@ export default function MemberDiscussionGeneral() { onClick={() => { onClickMember(v.idUser, v.name) }} > - + diff --git a/src/module/discussion_general/ui/navbar_discussion.tsx b/src/module/discussion_general/ui/navbar_discussion.tsx index 42c1a77..e486ee1 100644 --- a/src/module/discussion_general/ui/navbar_discussion.tsx +++ b/src/module/discussion_general/ui/navbar_discussion.tsx @@ -1,5 +1,5 @@ 'use client' -import { LayoutDrawer, LayoutNavbarNew, TEMA } from "@/module/_global"; +import { globalRole, LayoutDrawer, LayoutNavbarNew, TEMA } from "@/module/_global"; import { useHookstate } from "@hookstate/core"; import { ActionIcon } from "@mantine/core"; import { useState } from "react"; @@ -9,10 +9,13 @@ import DrawerDiscussionGeneral from "./drawer_discussion"; export default function NavbarDiscussionGeneral() { const [openDrawer, setOpenDrawer] = useState(false) const tema = useHookstate(TEMA) + const roleLogin = useHookstate(globalRole) + return ( <> setOpenDrawer(true)} bg={tema.get().bgIcon} size="lg" radius="lg" aria-label="Settings"> diff --git a/src/module/discussion_general/ui/tab_list_discussion_general.tsx b/src/module/discussion_general/ui/tab_list_discussion_general.tsx new file mode 100644 index 0000000..a9855d4 --- /dev/null +++ b/src/module/discussion_general/ui/tab_list_discussion_general.tsx @@ -0,0 +1,68 @@ +"use client"; +import { globalRole, TEMA } from "@/module/_global"; +import { useHookstate } from "@hookstate/core"; +import { Box, rem, Tabs } from "@mantine/core"; +import { useRouter, useSearchParams } from "next/navigation"; +import { IoMdCheckmarkCircleOutline } from "react-icons/io"; +import { IoCloseCircleOutline } from "react-icons/io5"; +import ListDiscussionGeneral from "./list_discussion"; + +export default function TabListDiscussionGeneral() { + const iconStyle = { width: rem(20), height: rem(20) }; + const router = useRouter(); + const searchParams = useSearchParams(); + const status = searchParams.get("active"); + const group = searchParams.get("group"); + const tema = useHookstate(TEMA) + const roleLogin = useHookstate(globalRole) + + return ( + + + { + roleLogin.get() != '' + ? + (roleLogin.get() != "user" && roleLogin.get() != "coadmin") ? + + } + onClick={() => { + router.push("?active=true&group=" + group); + }} + > + Aktif + + } + onClick={() => { + router.push("?active=false&group=" + group); + }} + > + Arsip + + + : + <> + : <> + } + + + + ); +} diff --git a/src/module/division_new/ui/create_report.tsx b/src/module/division_new/ui/create_report.tsx index 59d50fe..8ef8a9e 100644 --- a/src/module/division_new/ui/create_report.tsx +++ b/src/module/division_new/ui/create_report.tsx @@ -2,7 +2,7 @@ import { globalRole, LayoutNavbarNew, TEMA } from "@/module/_global"; import { funGetAllGroup, IDataGroup } from "@/module/group"; import { useHookstate } from "@hookstate/core"; -import { Box, Select, Skeleton, Stack } from "@mantine/core"; +import { Badge, Box, Select, Skeleton, Stack, Table } from "@mantine/core"; import { DateInput } from "@mantine/dates"; import { useShallowEffect } from "@mantine/hooks"; import moment from "moment"; @@ -13,21 +13,32 @@ import { funGetReportDivision } from "../lib/api_division"; import EchartBarReport from "./echart_bar_report"; import EchartPaiReport from "./echart_pai_report"; import EventReport from "./event_report"; +import _ from "lodash"; +import router from "next/router"; +import EchartBarReportCalender from "./echart_bar_calender"; export default function CreateReport() { const [value, setValue] = useState(null); + const [valueAkhir, setValueAkhir] = useState(null); const [dataGroup, setDataGroup] = useState([]); const [loading, setLoading] = useState(false); + const [loadingTable, setLoadingTable] = useState(false); const [tampil, setTampil] = useState(false); const [isGroup, setIsGroup] = useState(""); const param = useParams<{ id: string }>() const tema = useHookstate(TEMA) const roleLogin = useHookstate(globalRole) + const [reportTable, setReportTable] = useState([]) const [report, setReport] = useState({ progress: [], dokumen: [], event: [], }) + const [touched, setTouched] = useState({ + grup: false, + start_date: false, + end_date: false + }) async function loadData() { const loadGroup = await funGetAllGroup('?active=true') @@ -38,7 +49,7 @@ export default function CreateReport() { } } - async function onReport(group: string, date: any) { + async function onReport(group: string, awal: any, akhir: any) { try { setReport({ progress: [], @@ -47,7 +58,7 @@ export default function CreateReport() { }) setTampil(true) setLoading(true) - const res = await funGetReportDivision(`?group=${group}&division=${param.id}&date=${moment(date).format("YYYY-MM-DD")}`) + const res = await funGetReportDivision(`?group=${group}&division=${param.id}&date=${moment(awal).format("YYYY-MM-DD")}&date-end=${moment(akhir).format("YYYY-MM-DD")}`) if (res.success) { setReport(res.data) } else { @@ -61,36 +72,71 @@ export default function CreateReport() { } } - function onChooseGroup(val: any) { - if (val != null && val != "" && value != null && value != undefined) { - onReport(val, value) + async function onReportTable(group: string, awal: any, akhir: any) { + try { + setLoadingTable(true) + const res = await funGetReportDivision(`?cat=table-progress&group=${group}&division=${param.id}&date=${moment(awal).format("YYYY-MM-DD")}&date-end=${moment(akhir).format("YYYY-MM-DD")}`) + if (res.success) { + setReportTable(res.data) + } else { + toast.error(res.message) + } + } catch (error) { + console.error(error) + } finally { + setLoadingTable(false) } - - if (val == null || val == "") { - setTampil(false) - toast.error("Error! harus memilih grup") - } - setIsGroup(String(val)) } - function onChangeDate(val: any) { - if (roleLogin.get() == "supadmin") { - if (val != null && val != "" && isGroup != "" && isGroup != "null") { - onReport(isGroup, val) - } - - if (isGroup == null || isGroup == "") { + function onChangeDate(val: any, kat: string) { + if (kat == "grup") { + setIsGroup(val) + if (val == null || val == "") { setTampil(false) - toast.error("Error! harus memilih grup") + setTouched({ ...touched, grup: true }) + } else { + setIsGroup(String(val)) + setTouched({ ...touched, grup: false }) + + if (value != null && value != undefined && valueAkhir != null && valueAkhir != undefined && !touched.end_date) { + onReport(String(val), value, valueAkhir) + onReportTable(String(val), value, valueAkhir) + } } - } else { - if (val != null && val != "") { - onReport(isGroup, val) + } else if (kat == "start-date") { + setValue(val) + if (valueAkhir != null && valueAkhir != undefined) { + if (val >= valueAkhir) { + setTouched({ ...touched, end_date: true }) + setTampil(false) + } else { + setTouched({ ...touched, end_date: false }) + if (roleLogin.get() == "supadmin" && (isGroup == "" || isGroup == null || isGroup == undefined)) { + setTampil(false) + } else { + onReport(isGroup, val, valueAkhir) + onReportTable(isGroup, val, valueAkhir) + } + } + } + } else if (kat == "end-date") { + setValueAkhir(val) + if (value != null && value != undefined) { + if (value >= val) { + setTouched({ ...touched, end_date: true }) + setTampil(false) + } else { + setTouched({ ...touched, end_date: false }) + if (roleLogin.get() == "supadmin" && (isGroup == "" || isGroup == null || isGroup == undefined)) { + setTampil(false) + } else { + onReport(isGroup, value, val) + onReportTable(isGroup, value, val) + } + } } } - - setValue(val) } useShallowEffect(() => { @@ -113,19 +159,32 @@ export default function CreateReport() { value: String(pro.id), label: pro.name }))} - onChange={(val) => { onChooseGroup(val) }} + onChange={(val) => { onChangeDate(val, 'grup') }} + error={touched.grup && "Grup tidak boleh kosong"} /> } { onChangeDate(val) }} + onChange={(val) => { onChangeDate(val, 'start-date') }} radius={10} size="md" required - label="Tanggal" - placeholder="Tanggal" + label="Tanggal Awal" + placeholder="Tanggal Awal" + /> + + { onChangeDate(val, 'end-date') }} + radius={10} + size="md" + required + label="Tanggal Akhir" + placeholder="Tanggal Akhir" + error={touched.end_date && "Tanggal akhir harus lebih besar dari tanggal awal"} /> { tampil && @@ -158,6 +217,52 @@ export default function CreateReport() { }} > + { + !loadingTable ? + + + Tugas + Progres + Status + + { + reportTable.length == 0 ? + + Data kosong + + : + reportTable.map((item: any, i: number) => { + return ( + { router.push(`/division/${param.id}/task/${item.id}`) }}> + {item.title} + + {_.isNull(item.progress) ? 0 : item.progress}% + + + + { + item.status === 0 ? 'Segera' : + item.status === 1 ? 'Dikerjakan' : + item.status === 2 ? 'Selesai' : + item.status === 3 ? 'Dibatalkan' : + "Segera" + } + + + + ) + }) + } + +
+ : <> + }
@@ -181,7 +286,8 @@ export default function CreateReport() { padding: 10, }} > - + + {/* */} diff --git a/src/module/division_new/ui/echart_bar_calender.tsx b/src/module/division_new/ui/echart_bar_calender.tsx new file mode 100644 index 0000000..2d585ae --- /dev/null +++ b/src/module/division_new/ui/echart_bar_calender.tsx @@ -0,0 +1,93 @@ +import { TEMA, } from '@/module/_global'; +import { useHookstate } from '@hookstate/core'; +import { Box } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { EChartsOption } from "echarts"; +import EChartsReact from "echarts-for-react"; +import { useState } from 'react'; + +export default function EchartBarReportCalender({ data }: { data: any }) { + const [options, setOptions] = useState({}); + const color = ["#5971C0", "#868e96", "#9EC97F"] + const tema = useHookstate(TEMA) + + useShallowEffect(() => { + loadData(data) + }, [data]) + + const loadData = (value: any) => { + const option: EChartsOption = { + title: { + text: "ACARA DIVISI", + top: '2%', + left: 'center', + textStyle: { + color: tema.get().utama + } + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow' + } + }, + grid: { + left: '3%', + right: '4%', + bottom: '3%', + containLabel: true + }, + xAxis: [ + { + type: 'category', + data: value.map(({ name }: any) => name), + axisLabel: { + fontSize: 14 + }, + axisTick: { + alignWithLabel: true + }, + axisLine: { + show: true, + }, + } + ], + yAxis: [ + { + type: 'value', + show: true, + splitLine: { + lineStyle: { + color: "gray", + opacity: 0.1 + } + }, + } + ], + series: [ + { + name: 'Data', + type: 'bar', + barWidth: '70%', + data: value.map( + (v: any, i: any) => + ({ + name: v.name, + value: v.value, + itemStyle: { + color: color[i] + }, + }) + ), + } + ] + }; + setOptions(option); + } + + return ( + + + + ); +} diff --git a/src/module/division_new/ui/echart_bar_report.tsx b/src/module/division_new/ui/echart_bar_report.tsx index b145dc3..87e4e40 100644 --- a/src/module/division_new/ui/echart_bar_report.tsx +++ b/src/module/division_new/ui/echart_bar_report.tsx @@ -1,11 +1,10 @@ -import React, { useState } from 'react'; -import { EChartsOption, color } from "echarts"; -import EChartsReact from "echarts-for-react"; -import { useShallowEffect } from '@mantine/hooks'; -import * as echarts from 'echarts'; -import { Box } from '@mantine/core'; -import { TEMA, } from '@/module/_global'; +import { TEMA, } from '@/module/_global'; import { useHookstate } from '@hookstate/core'; +import { Box } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { EChartsOption } from "echarts"; +import EChartsReact from "echarts-for-react"; +import { useState } from 'react'; export default function EchartBarReport({ data }: { data: any }) { const [options, setOptions] = useState({}); diff --git a/src/module/division_new/ui/report_division_id.tsx b/src/module/division_new/ui/report_division_id.tsx index d495fb8..48d9c00 100644 --- a/src/module/division_new/ui/report_division_id.tsx +++ b/src/module/division_new/ui/report_division_id.tsx @@ -1,32 +1,42 @@ "use client" import { LayoutNavbarNew, TEMA } from '@/module/_global'; import { useHookstate } from '@hookstate/core'; -import { Box, Skeleton, Stack } from '@mantine/core'; +import { Badge, Box, Skeleton, Stack, Table } from '@mantine/core'; import { DateInput } from '@mantine/dates'; +import _ from 'lodash'; import moment from 'moment'; import "moment/locale/id"; -import { useParams } from 'next/navigation'; +import { useParams, useRouter } from 'next/navigation'; import { useState } from 'react'; import toast from 'react-hot-toast'; import { funGetReportDivision } from '../lib/api_division'; import EchartBarReport from './echart_bar_report'; import EchartPaiReport from './echart_pai_report'; import EventReport from './event_report'; +import EchartBarReportCalender from './echart_bar_calender'; export default function ReportDivisionId() { const [value, setValue] = useState(null); + const [valueAkhir, setValueAkhir] = useState(null); const param = useParams<{ id: string }>() const [loading, setLoading] = useState(false); + const [loadingTable, setLoadingTable] = useState(false); const [tampil, setTampil] = useState(false); const tema = useHookstate(TEMA) + const router = useRouter() const [report, setReport] = useState({ progress: [], dokumen: [], event: [], }) + const [reportTable, setReportTable] = useState([]) + const [touched, setTouched] = useState({ + start_date: false, + end_date: false + }) - async function onReport(date: any) { + async function onReport(awal: any, akhir: any) { try { setReport({ progress: [], @@ -35,7 +45,7 @@ export default function ReportDivisionId() { }) setTampil(true) setLoading(true) - const res = await funGetReportDivision(`?division=${param.id}&date=${moment(date).format("YYYY-MM-DD")}`) + const res = await funGetReportDivision(`?division=${param.id}&date=${moment(awal).format("YYYY-MM-DD")}&date-end=${moment(akhir).format("YYYY-MM-DD")}`) if (res.success) { setReport(res.data) } else { @@ -49,11 +59,48 @@ export default function ReportDivisionId() { } } - function onChangeDate(val: any) { - if (val != null && val != "") { - onReport(val) + async function onReportTable(awal: any, akhir: any) { + try { + setLoadingTable(true) + const res = await funGetReportDivision(`?cat=table-progress&division=${param.id}&date=${moment(awal).format("YYYY-MM-DD")}&date-end=${moment(akhir).format("YYYY-MM-DD")}`) + if (res.success) { + setReportTable(res.data) + } else { + toast.error(res.message) + } + } catch (error) { + console.error(error) + } finally { + setLoadingTable(false) + } + } + + function onChangeDate(val: any, kat: string) { + if (kat == "start-date") { + setValue(val) + if (valueAkhir != null && valueAkhir != undefined) { + if (val >= valueAkhir) { + setTouched({ ...touched, end_date: true }) + setTampil(false) + } else { + setTouched({ ...touched, end_date: false }) + onReport(val, valueAkhir) + onReportTable(val, valueAkhir) + } + } + } else if (kat == "end-date") { + setValueAkhir(val) + if (value != null && value != undefined) { + if (value >= val) { + setTouched({ ...touched, end_date: true }) + setTampil(false) + } else { + setTouched({ ...touched, end_date: false }) + onReport(value, val) + onReportTable(value, val) + } + } } - setValue(val) } @@ -65,12 +112,23 @@ export default function ReportDivisionId() { { onChangeDate(val) }} + onChange={(val) => { onChangeDate(val, 'start-date') }} radius={10} size="md" required - label="Tanggal" - placeholder="Tanggal" + label="Tanggal Awal" + placeholder="Tanggal Awal" + /> + { onChangeDate(val, 'end-date') }} + radius={10} + size="md" + required + label="Tanggal Akhir" + placeholder="Tanggal Akhir" + error={touched.end_date && "Tanggal akhir harus lebih besar dari tanggal awal"} /> { tampil && @@ -103,6 +161,53 @@ export default function ReportDivisionId() { }} > + { + !loadingTable ? + + + Tugas + Progres + Status + + { + reportTable.length == 0 ? + + Data kosong + + : + reportTable.map((item: any, i: number) => { + return ( + { router.push(`/division/${param.id}/task/${item.id}`) }}> + {item.title} + + {_.isNull(item.progress) ? 0 : item.progress}% + + + + { + item.status === 0 ? 'Segera' : + item.status === 1 ? 'Dikerjakan' : + item.status === 2 ? 'Selesai' : + item.status === 3 ? 'Dibatalkan' : + "Segera" + } + + + + ) + }) + } + +
+ : <> + } + @@ -126,7 +231,8 @@ export default function ReportDivisionId() { padding: 10, }} > - + + {/* */} @@ -139,4 +245,3 @@ export default function ReportDivisionId() { ); } - diff --git a/src/module/document/ui/drawer_menu_document_division.tsx b/src/module/document/ui/drawer_menu_document_division.tsx index f6de9db..a1d67bf 100644 --- a/src/module/document/ui/drawer_menu_document_division.tsx +++ b/src/module/document/ui/drawer_menu_document_division.tsx @@ -148,13 +148,13 @@ export default function DrawerMenuDocumentDivision() { onUploadFile(files[0]) }} activateOnClick={false} - maxSize={3 * 1024 ** 2} + maxSize={100 * 1024 ** 2} accept={['image/png', 'image/jpeg', 'image/heic', 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']} onReject={(files) => { refresh.set(true) setOpenModal(false) setOpenDrawerDocument(false) - toast.error('File yang diizinkan: .csv, .png, .jpg, .heic, .pdf, .doc, .docx, .xls, .xlsx dengan ukuran maksimal 3 MB') + toast.error('File yang diizinkan: .csv, .png, .jpg, .heic, .pdf, .doc, .docx, .xls, .xlsx dengan ukuran maksimal 100 MB') }} > openRef.current?.()}> diff --git a/src/module/project/ui/add_file_detail_project.tsx b/src/module/project/ui/add_file_detail_project.tsx index b1ceb76..71f3582 100644 --- a/src/module/project/ui/add_file_detail_project.tsx +++ b/src/module/project/ui/add_file_detail_project.tsx @@ -105,10 +105,10 @@ export default function AddFileDetailProject() { cekFileName(files[0]) }} activateOnClick={false} - maxSize={3 * 1024 ** 2} + maxSize={100 * 1024 ** 2} accept={['image/png', 'image/jpeg', 'image/heic', 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']} onReject={(files) => { - return toast.error('File yang diizinkan: .csv, .png, .jpg, .heic, .pdf, .doc, .docx, .xls, .xlsx dengan ukuran maksimal 3 MB') + return toast.error('File yang diizinkan: .csv, .png, .jpg, .heic, .pdf, .doc, .docx, .xls, .xlsx dengan ukuran maksimal 100 MB') }} > diff --git a/src/module/project/ui/create_project.tsx b/src/module/project/ui/create_project.tsx index 1a09ae0..97a2ef3 100644 --- a/src/module/project/ui/create_project.tsx +++ b/src/module/project/ui/create_project.tsx @@ -412,10 +412,10 @@ export default function CreateProject() { setListFile([...listFile, { name: files[0].name, extension: files[0].type.split("/")[1] }]) }} activateOnClick={false} - maxSize={3 * 1024 ** 2} + maxSize={100 * 1024 ** 2} accept={['image/png', 'image/jpeg', 'image/heic', 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']} onReject={(files) => { - return toast.error('File yang diizinkan: .csv, .png, .jpg, .heic, .pdf, .doc, .docx, .xls, .xlsx dengan ukuran maksimal 3 MB') + return toast.error('File yang diizinkan: .csv, .png, .jpg, .heic, .pdf, .doc, .docx, .xls, .xlsx dengan ukuran maksimal 100 MB') }} > @@ -437,10 +437,10 @@ export default function CreateProject() { setListFile([...listFile, { name: files[0].name, extension: files[0].type.split("/")[1] }]) }} activateOnClick={false} - maxSize={3 * 1024 ** 2} + maxSize={100 * 1024 ** 2} accept={['image/png', 'image/jpeg', 'image/heic', 'application/pdf']} onReject={(files) => { - return toast.error('File yang diizinkan: .png, .jpg, .heic, .pdf dengan ukuran maksimal 3 MB') + return toast.error('File yang diizinkan: .png, .jpg, .heic, .pdf dengan ukuran maksimal 100 MB') }} > openRef.current?.()}> diff --git a/src/module/task/ui/add_file_detail_task.tsx b/src/module/task/ui/add_file_detail_task.tsx index a992454..25183e3 100644 --- a/src/module/task/ui/add_file_detail_task.tsx +++ b/src/module/task/ui/add_file_detail_task.tsx @@ -106,10 +106,10 @@ export default function AddFileDetailTask() { cekFileName(files[0]) }} activateOnClick={false} - maxSize={3 * 1024 ** 2} + maxSize={100 * 1024 ** 2} accept={['image/png', 'image/jpeg', 'image/heic', 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']} onReject={(files) => { - return toast.error('File yang diizinkan: .csv, .png, .jpg, .heic, .pdf, .doc, .docx, .xls, .xlsx dengan ukuran maksimal 3 MB') + return toast.error('File yang diizinkan: .csv, .png, .jpg, .heic, .pdf, .doc, .docx, .xls, .xlsx dengan ukuran maksimal 100 MB') }} > diff --git a/src/module/task/ui/create_task.tsx b/src/module/task/ui/create_task.tsx index fc24332..70f4c7a 100644 --- a/src/module/task/ui/create_task.tsx +++ b/src/module/task/ui/create_task.tsx @@ -335,10 +335,10 @@ export default function CreateTask() { setListFile([...listFile, { name: files[0].name, extension: files[0].type.split("/")[1] }]) }} activateOnClick={false} - maxSize={3 * 1024 ** 2} + maxSize={100 * 1024 ** 2} accept={['image/png', 'image/jpeg', 'image/heic', 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']} onReject={(files) => { - return toast.error('File yang diizinkan: .csv, .png, .jpg, .heic, .pdf, .doc, .docx, .xls, .xlsx dengan ukuran maksimal 3 MB') + return toast.error('File yang diizinkan: .csv, .png, .jpg, .heic, .pdf, .doc, .docx, .xls, .xlsx dengan ukuran maksimal 100 MB') }} > diff --git a/src/module/user/member/ui/create_member.tsx b/src/module/user/member/ui/create_member.tsx index c306312..7c78b48 100644 --- a/src/module/user/member/ui/create_member.tsx +++ b/src/module/user/member/ui/create_member.tsx @@ -283,10 +283,10 @@ export default function CreateMember() { setIMG(buffer) }} activateOnClick={false} - maxSize={1 * 1024 ** 2} + maxSize={10 * 1024 ** 2} accept={['image/png', 'image/jpeg', 'image/heic']} onReject={(files) => { - return toast.error('File yang diizinkan: .png, .jpg, dan .heic dengan ukuran maksimal 1 MB') + return toast.error('File yang diizinkan: .png, .jpg, dan .heic dengan ukuran maksimal 10 MB') }} > diff --git a/src/module/user/member/ui/edit_member.tsx b/src/module/user/member/ui/edit_member.tsx index f9acc2e..d47b630 100644 --- a/src/module/user/member/ui/edit_member.tsx +++ b/src/module/user/member/ui/edit_member.tsx @@ -215,10 +215,10 @@ export default function EditMember({ id }: { id: string }) { setIMG(buffer) }} activateOnClick={false} - maxSize={1 * 1024 ** 2} + maxSize={10 * 1024 ** 2} accept={['image/png', 'image/jpeg', 'image/heic']} onReject={(files) => { - return toast.error('File yang diizinkan: .png, .jpg, dan .heic dengan ukuran maksimal 1 MB') + return toast.error('File yang diizinkan: .png, .jpg, dan .heic dengan ukuran maksimal 10 MB') }} > diff --git a/src/module/user/profile/ui/edit_profile.tsx b/src/module/user/profile/ui/edit_profile.tsx index beee85b..e0e6750 100644 --- a/src/module/user/profile/ui/edit_profile.tsx +++ b/src/module/user/profile/ui/edit_profile.tsx @@ -178,11 +178,11 @@ export default function EditProfile() { setIMG(buffer); }} activateOnClick={false} - maxSize={1 * 1024 ** 2} + maxSize={10 * 1024 ** 2} accept={["image/png", "image/jpeg", "image/heic"]} onReject={(files) => { return toast.error( - "File yang diizinkan: .png, .jpg, dan .heic dengan ukuran maksimal 1 MB" + "File yang diizinkan: .png, .jpg, dan .heic dengan ukuran maksimal 10 MB" ); }} >