From 83085418eda0b54ef69de139b89e93714950cbcb Mon Sep 17 00:00:00 2001 From: amal Date: Tue, 16 Sep 2025 14:23:15 +0800 Subject: [PATCH 1/6] upd: pengumuman deskripsiL : - update api ai pengumuman No Issues --- src/app/api/ai/announcement/route.ts | 36 +++++++++++++--------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/app/api/ai/announcement/route.ts b/src/app/api/ai/announcement/route.ts index 15b27db..a4a1d4a 100644 --- a/src/app/api/ai/announcement/route.ts +++ b/src/app/api/ai/announcement/route.ts @@ -1,6 +1,5 @@ import { prisma } from "@/module/_global"; import _ from "lodash"; -import moment from "moment"; import "moment/locale/id"; import { NextResponse } from "next/server"; export const dynamic = 'force-dynamic' @@ -11,42 +10,41 @@ export const dynamic = 'force-dynamic' export async function GET(request: Request) { try { const { searchParams } = new URL(request.url); - const judul = searchParams.get('q'); - const page = searchParams.get('p'); + const judul = searchParams.get('search'); + const page = searchParams.get('page'); const get = searchParams.get('get'); const villageId = searchParams.get('desa'); - const dataSkip = page == null || page == undefined ? 0 : Number(page) * 10 - 10; + const active = searchParams.get('active'); + + let getFix = 0; + if (get == null || get == undefined || get == "" || _.isNaN(Number(get))) { + getFix = 10; + } else { + getFix = Number(get); + } + + const dataSkip = page == null || page == undefined ? 0 : Number(page) * getFix - getFix; + let kondisi: any = { idVillage: String(villageId), - isActive: true, + isActive: (active == "false" || active == undefined) ? false : true, title: { contains: (judul == undefined || judul == null) ? "" : judul, mode: "insensitive" } } - const announcements = await prisma.announcement.findMany({ + const data = await prisma.announcement.findMany({ skip: dataSkip, - take: (get == null || get == undefined || get == "" || _.isNaN(Number(get))) ? 10 : Number(get), + take: getFix, where: kondisi, - select: { - id: true, - title: true, - desc: true, - createdAt: true, - }, orderBy: { createdAt: 'desc' } }); - const allData = announcements.map((v: any) => ({ - ..._.omit(v, ["createdAt"]), - createdAt: moment(v.createdAt).format("ll") - })) - - return NextResponse.json({ success: true, message: "Berhasil mendapatkan pengumuman", data: allData, }, { status: 200 }); + return NextResponse.json({ success: true, message: "Berhasil mendapatkan pengumuman", data, }, { status: 200 }); } catch (error) { console.error(error); return NextResponse.json({ success: false, message: "Gagal mendapatkan pengumuman, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); From f87b99f70424a933e517c33510e10f99bb69575c Mon Sep 17 00:00:00 2001 From: amal Date: Tue, 16 Sep 2025 14:24:58 +0800 Subject: [PATCH 2/6] upd: api ai Deskripsi : - update banner api ai No Issues --- src/app/api/ai/banner/[id]/route.ts | 21 +++++++++++++++++++++ src/app/api/ai/banner/route.ts | 25 +++++++++++++++++-------- 2 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 src/app/api/ai/banner/[id]/route.ts diff --git a/src/app/api/ai/banner/[id]/route.ts b/src/app/api/ai/banner/[id]/route.ts new file mode 100644 index 0000000..01f91ac --- /dev/null +++ b/src/app/api/ai/banner/[id]/route.ts @@ -0,0 +1,21 @@ +import { prisma } from "@/module/_global"; +import { NextResponse } from "next/server"; + + +// GET ONE BANNER +export async function GET(request: Request, context: { params: { id: string } }) { + try { + const { id } = context.params; + + const data = await prisma.bannerImage.findUnique({ + where: { + id: String(id) + } + }) + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan banner", data }, { status: 200 }); + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan banner, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/ai/banner/route.ts b/src/app/api/ai/banner/route.ts index ccbefea..93704b4 100644 --- a/src/app/api/ai/banner/route.ts +++ b/src/app/api/ai/banner/route.ts @@ -1,4 +1,5 @@ import { prisma } from "@/module/_global"; +import _ from "lodash"; import { NextResponse } from "next/server"; @@ -6,15 +7,24 @@ import { NextResponse } from "next/server"; export async function GET(request: Request) { try { const { searchParams } = new URL(request.url); - const judul = searchParams.get('q'); - const page = searchParams.get('p'); + const judul = searchParams.get('search'); + const page = searchParams.get('page'); const get = searchParams.get('get'); const villageId = searchParams.get('desa'); - const dataSkip = page == null || page == undefined ? 0 : Number(page) * 10 - 10; + const active = searchParams.get('active'); + + let getFix = 0; + if (get == null || get == undefined || get == "" || _.isNaN(Number(get))) { + getFix = 10; + } else { + getFix = Number(get); + } + + const dataSkip = page == null || page == undefined ? 0 : Number(page) * getFix - getFix; let kondisi: any = { idVillage: String(villageId), - isActive: true, + isActive: (active == "false" || active == undefined) ? false : true, title: { contains: (judul == undefined || judul == null) ? "" : judul, mode: "insensitive" @@ -22,10 +32,9 @@ export async function GET(request: Request) { } const data = await prisma.bannerImage.findMany({ - where: { - isActive: true, - idVillage: String(villageId) - }, + skip: dataSkip, + take: getFix, + where: kondisi, orderBy: { createdAt: 'desc' } From 2e4eaaec9537c7833f76c1463b93c9273398865b Mon Sep 17 00:00:00 2001 From: amal Date: Tue, 16 Sep 2025 15:35:47 +0800 Subject: [PATCH 3/6] api ai Deskripsi: - api get list calendar - api get detail calendar No Issues --- src/app/api/ai/calendar/[id]/route.ts | 99 +++++++++++++++++ src/app/api/ai/calendar/route.ts | 149 ++++++++++++++++++++++++++ 2 files changed, 248 insertions(+) create mode 100644 src/app/api/ai/calendar/[id]/route.ts create mode 100644 src/app/api/ai/calendar/route.ts diff --git a/src/app/api/ai/calendar/[id]/route.ts b/src/app/api/ai/calendar/[id]/route.ts new file mode 100644 index 0000000..9d43c90 --- /dev/null +++ b/src/app/api/ai/calendar/[id]/route.ts @@ -0,0 +1,99 @@ +import { prisma } from "@/module/_global"; +import _ from "lodash"; +import moment from "moment"; +import { NextResponse } from "next/server"; + +// GET ONE CALENDER BY ID KALENDER REMINDER +export async function GET(request: Request, context: { params: { id: string } }) { + try { + const { id } = context.params + + const cek = await prisma.divisionCalendarReminder.count({ + where: { + id: id + } + }) + + if (cek == 0) { + return NextResponse.json( + { + success: false, + message: "Gagal mendapatkan acara, data tidak ditemukan", + }, + { status: 404 } + ); + } + + const data: any = await prisma.divisionCalendarReminder.findUnique({ + where: { + id: id + }, + select: { + id: true, + timeStart: true, + dateStart: true, + timeEnd: true, + createdAt: true, + DivisionCalendar: { + select: { + id: true, + title: true, + desc: true, + linkMeet: true, + repeatEventTyper: true, + repeatValue: true, + } + } + } + }); + const { DivisionCalendar, ...dataCalender } = data + const timeStart = moment.utc(dataCalender?.timeStart).format("HH:mm") + const timeEnd = moment.utc(dataCalender?.timeEnd).format("HH:mm") + const idCalendar = data?.DivisionCalendar.id + const title = data?.DivisionCalendar?.title + const desc = data?.DivisionCalendar?.desc + const linkMeet = data?.DivisionCalendar?.linkMeet + const repeatEventTyper = data?.DivisionCalendar?.repeatEventTyper + const repeatValue = data?.DivisionCalendar?.repeatValue + + + const result = { ...dataCalender, timeStart, timeEnd, title, desc, linkMeet, repeatEventTyper, repeatValue } + + + const member = await prisma.divisionCalendarMember.findMany({ + where: { + idCalendar + }, + select: { + id: true, + idUser: true, + User: { + select: { + id: true, + name: true, + email: true, + img: true + } + } + } + }) + const fixMember = member.map((v: any) => ({ + ..._.omit(v, ["User"]), + name: v.User.name, + email: v.User.email, + img: v.User.img + })) + + + const dataFix = { + ...result, + member: fixMember, + } + + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan kalender", data: dataFix }, { status: 200 }); + + } catch (error) { + return NextResponse.json({ success: false, message: "Gagal mendapatkan kalender, data tidak ditemukan (error: 500)", }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/ai/calendar/route.ts b/src/app/api/ai/calendar/route.ts new file mode 100644 index 0000000..c07d011 --- /dev/null +++ b/src/app/api/ai/calendar/route.ts @@ -0,0 +1,149 @@ +import { prisma } from "@/module/_global"; +import _ from "lodash"; +import moment from "moment"; +import "moment/locale/id"; +import { NextResponse } from "next/server"; + +//GET ALL CALENDER +export async function GET(request: Request) { + try { + + const { searchParams } = new URL(request.url); + const idDivision = searchParams.get("division"); + const isDate = searchParams.get("date") + const villageId = searchParams.get("desa") + const active = searchParams.get("active") + const search = searchParams.get("search") + const page = searchParams.get("page") + const get = searchParams.get("get") + + let getFix = 0; + if (get == null || get == undefined || get == "" || _.isNaN(Number(get))) { + getFix = 10; + } else { + getFix = Number(get); + } + + + + const dataSkip = page == null || page == undefined ? 0 : Number(page) * getFix - getFix; + + + let kondisi: any = {} + + if (idDivision != "" && idDivision != null && idDivision != undefined) { + if (isDate != null && isDate != undefined && isDate != "") { + kondisi = { + idDivision: String(idDivision), + dateStart: new Date(String(isDate)), + DivisionCalendar: { + title: { + contains: (search == undefined || search == null) ? "" : search, + mode: "insensitive" + }, + isActive: (active == "false" || active == undefined) ? false : true, + Division: { + idVillage: String(villageId) + } + } + } + } else { + kondisi = { + idDivision: String(idDivision), + DivisionCalendar: { + title: { + contains: (search == undefined || search == null) ? "" : search, + mode: "insensitive" + }, + isActive: (active == "false" || active == undefined) ? false : true, + Division: { + idVillage: String(villageId) + } + } + } + } + } else { + if (isDate != null && isDate != undefined && isDate != "") { + kondisi = { + dateStart: new Date(String(isDate)), + DivisionCalendar: { + title: { + contains: (search == undefined || search == null) ? "" : search, + mode: "insensitive" + }, + isActive: (active == "false" || active == undefined) ? false : true, + Division: { + idVillage: String(villageId) + } + } + } + } else { + kondisi = { + DivisionCalendar: { + title: { + contains: (search == undefined || search == null) ? "" : search, + mode: "insensitive" + }, + isActive: (active == "false" || active == undefined) ? false : true, + Division: { + idVillage: String(villageId) + } + } + } + } + } + + const data = await prisma.divisionCalendarReminder.findMany({ + where: kondisi, + skip: dataSkip, + take: getFix, + select: { + id: true, + dateStart: true, + timeStart: true, + timeEnd: true, + createdAt: true, + DivisionCalendar: { + select: { + isActive: true, + title: true, + desc: true, + User: { + select: { + name: true + } + } + } + } + }, + orderBy: [ + { + dateStart: 'asc' + }, + { + timeStart: 'asc' + }, + { + timeEnd: 'asc' + } + ] + }); + + const allOmit = data.map((v: any) => ({ + ..._.omit(v, ["DivisionCalendar", "User"]), + title: v.DivisionCalendar.title, + desc: v.DivisionCalendar.desc, + createdBy: v.DivisionCalendar.User.name, + isActive: v.DivisionCalendar.isActive, + timeStart: moment.utc(v.timeStart).format('HH:mm'), + timeEnd: moment.utc(v.timeEnd).format('HH:mm') + })) + + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan kalender", data: allOmit }, { status: 200 }); + + } catch (error) { + console.error(error) + return NextResponse.json({ success: false, message: "Gagal mendapatkan kalender, data tidak ditemukan (error: 500)" }, { status: 404 }); + } +} \ No newline at end of file From f10b87f0f264bbae4a790e9ecb2596cce7882421 Mon Sep 17 00:00:00 2001 From: amal Date: Tue, 16 Sep 2025 17:10:03 +0800 Subject: [PATCH 4/6] upd : api ai diskusi divisi Deskripsi: - list diskusi divisi - detail diskusi divisi No Issues" git status --- src/app/api/ai/discussion/[id]/route.ts | 92 ++++++++++++++++++++++++ src/app/api/ai/discussion/route.ts | 95 +++++++++++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 src/app/api/ai/discussion/[id]/route.ts create mode 100644 src/app/api/ai/discussion/route.ts diff --git a/src/app/api/ai/discussion/[id]/route.ts b/src/app/api/ai/discussion/[id]/route.ts new file mode 100644 index 0000000..c7d9876 --- /dev/null +++ b/src/app/api/ai/discussion/[id]/route.ts @@ -0,0 +1,92 @@ +import { prisma } from "@/module/_global"; +import { NextResponse } from "next/server"; + +// GET ONE DISCUSSION BY ID +export async function GET(request: Request, context: { params: { id: string } }) { + try { + const { id } = context.params + + const cek = await prisma.divisionDisscussion.count({ + where: { id } + }) + + if (cek == 0) { + return NextResponse.json( + { success: false, message: "Gagal mendapatkan diskusi, data tidak ditemukan" }, + { status: 404 } + ); + } + + const data = await prisma.divisionDisscussion.findUnique({ + where: { id }, + select: { + isActive: true, + id: true, + desc: true, + status: true, + createdAt: true, + idDivision: true, + Division: { + select: { + name: true, + } + }, + User: { select: { name: true, img: true } }, + DivisionDisscussionComment: { + select: { + id: true, + comment: true, + createdAt: true, + User: { select: { name: true, img: true } } + } + }, + } + }); + + if (!data) { + return NextResponse.json( + { success: false, message: "Diskusi tidak ditemukan" }, + { status: 404 } + ); + } + + // ambil nama creator + const createdBy = data.User.name; + const status = data.status == 1 ? "Open" : "Close" + const division = data.Division.name + + // mapping komentar → hilangkan nested User + const komentar = data.DivisionDisscussionComment.map((comment: any) => ({ + id: comment.id, + comment: comment.comment, + createdAt: comment.createdAt, + username: comment.User.name, + userimg: comment.User.img, + })); + + // bentuk hasil akhir sesuai request + const result = { + id: data.id, + idDivision: data.idDivision, + division, + isActive: data.isActive, + desc: data.desc, + status, + createdAt: data.createdAt, + createdBy, + komentar, + }; + + return NextResponse.json( + { success: true, message: "Berhasil mendapatkan diskusi", data: result }, + { 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 } + ); + } +} diff --git a/src/app/api/ai/discussion/route.ts b/src/app/api/ai/discussion/route.ts new file mode 100644 index 0000000..a37f0b9 --- /dev/null +++ b/src/app/api/ai/discussion/route.ts @@ -0,0 +1,95 @@ +import { prisma } from "@/module/_global"; +import _ from "lodash"; +import "moment/locale/id"; +import { NextResponse } from "next/server"; + + +// GET ALL DISCUSSION DIVISION ACTIVE = TRUE +export async function GET(request: Request) { + try { + const { searchParams } = new URL(request.url); + const idDivision = searchParams.get("division"); + const search = searchParams.get('search'); + const page = searchParams.get('page'); + const status = searchParams.get('status'); + const isActive = searchParams.get('active'); + const villageId = searchParams.get('desa'); + const get = searchParams.get('get'); + + let getFix = 0; + if (get == null || get == undefined || get == "" || _.isNaN(Number(get))) { + getFix = 10; + } else { + getFix = Number(get); + } + + const dataSkip = page == null || page == undefined ? 0 : Number(page) * getFix - getFix; + + let kondisi: any = { + isActive: isActive == "false" ? false : true, + status: status == "close" ? 2 : 1, + Division: { + idVillage: String(villageId) + }, + desc: { + contains: (search == undefined || search == "null") ? "" : search, + mode: "insensitive" + }, + } + + + if (idDivision != "null" && idDivision != null && idDivision != undefined) { + kondisi = { + isActive: isActive == "false" ? false : true, + status: status == "close" ? 2 : 1, + idDivision: idDivision, + Division: { + idVillage: String(villageId) + }, + desc: { + contains: (search == undefined || search == "null") ? "" : search, + mode: "insensitive" + }, + } + } + + const data = await prisma.divisionDisscussion.findMany({ + skip: dataSkip, + take: getFix, + where: kondisi, + orderBy: { + createdAt: 'desc' + }, + select: { + id: true, + desc: true, + status: true, + createdAt: true, + idDivision: true, + Division: { + select: { + name: true, + } + }, + DivisionDisscussionComment: { + select: { + id: true, + } + } + } + }); + + const fixData = data.map((v: any) => ({ + ..._.omit(v, ["DivisionDisscussionComment", "status", "Division"]), + totalKomentar: v.DivisionDisscussionComment.length, + status: v.status == 1 ? "Open" : "Close", + division: v.Division.name + })) + + 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 }); + } +} \ No newline at end of file From bef4dd1969c3c64cdde43f9f8ad1c0a5df5bf64e Mon Sep 17 00:00:00 2001 From: amal Date: Tue, 16 Sep 2025 17:31:46 +0800 Subject: [PATCH 5/6] upd: api ai Deskripsi: - api ai get list discussion general - api ai detail discussion general nb : belm selesai No Issues --- .../api/ai/discussion-general/[id]/route.ts | 111 ++++++++++++++++++ src/app/api/ai/discussion-general/route.ts | 92 +++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 src/app/api/ai/discussion-general/[id]/route.ts create mode 100644 src/app/api/ai/discussion-general/route.ts diff --git a/src/app/api/ai/discussion-general/[id]/route.ts b/src/app/api/ai/discussion-general/[id]/route.ts new file mode 100644 index 0000000..3df7047 --- /dev/null +++ b/src/app/api/ai/discussion-general/[id]/route.ts @@ -0,0 +1,111 @@ +import { prisma } from "@/module/_global"; +import _ from "lodash"; +import moment from "moment"; +import "moment/locale/id"; +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 cek = await prisma.discussion.count({ + where: { + id, + } + }) + + 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, + }, + select: { + isActive: true, + id: true, + title: true, + idGroup: true, + desc: true, + status: true, + createdAt: true, + } + }) + + dataFix = { + id: data?.id, + isActive: data?.isActive, + 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").replace('pukul', ''), + 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 + })) + } + + + 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 }); + } +} diff --git a/src/app/api/ai/discussion-general/route.ts b/src/app/api/ai/discussion-general/route.ts new file mode 100644 index 0000000..1e26ea7 --- /dev/null +++ b/src/app/api/ai/discussion-general/route.ts @@ -0,0 +1,92 @@ +import { prisma } from "@/module/_global"; +import _ from "lodash"; +import "moment/locale/id"; +import { NextResponse } from "next/server"; + + +// GET ALL DISCUSSION GENERAL +export async function GET(request: Request) { + try { + const { searchParams } = new URL(request.url); + const idGroup = searchParams.get("group"); + const idVillage = searchParams.get("desa"); + const search = searchParams.get('search'); + const page = searchParams.get('page'); + const status = searchParams.get('status'); + const active = searchParams.get('active'); + const get = searchParams.get('get') + + + let getFix = 10; + if (get == null || get == undefined || get == "" || _.isNaN(Number(get))) { + getFix = 10; + } else { + getFix = Number(get); + } + + const dataSkip = page == null || page == undefined ? 0 : Number(page) * getFix - getFix; + + let kondisi: any = { + isActive: active == "false" ? false : true, + status: status == "close" ? 2 : 1, + idVillage: String(idVillage), + title: { + contains: (search == undefined || search == "null") ? "" : search, + mode: "insensitive" + }, + } + + if (idGroup != "null" && idGroup != undefined && idGroup != "") { + kondisi = { + ...kondisi, + idGroup: String(idGroup) + } + } + + + const data = await prisma.discussion.findMany({ + skip: dataSkip, + take: getFix, + where: kondisi, + orderBy: [ + { + status: 'desc' + }, + { + createdAt: 'desc' + } + ], + + select: { + id: true, + title: true, + desc: true, + status: true, + createdAt: true, + DiscussionComment: { + select: { + id: true, + } + }, + Group: { + select: { + name: true, + } + } + } + }); + + const fixData = data.map((v: any) => ({ + ..._.omit(v, ["DiscussionComment", "status", "Group"]), + totalKomentar: v.DiscussionComment.length, + status: v.status == 1 ? "Open" : "Close", + group: v.Group.name, + })) + + 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 }); + } +} From b78013c160a1d33c58eb3b5e1abf44f39748a3a6 Mon Sep 17 00:00:00 2001 From: amal Date: Tue, 16 Sep 2025 17:32:55 +0800 Subject: [PATCH 6/6] upd: api ai swager --- darmasaba-api-ai.yml | 1472 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1472 insertions(+) create mode 100644 darmasaba-api-ai.yml diff --git a/darmasaba-api-ai.yml b/darmasaba-api-ai.yml new file mode 100644 index 0000000..9ae1e25 --- /dev/null +++ b/darmasaba-api-ai.yml @@ -0,0 +1,1472 @@ +openapi: 3.0.3 +info: + title: API AI Desa+ + description: API untuk AI Desa+ + version: 1.0.0 + contact: + name: API Support + email: support@desa-plus.com + +servers: + - url: http://localhost:3000/api/ai + description: Production server + +components: + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + + schemas: + # Common Schemas + User: + type: object + properties: + id: + type: string + name: + type: string + phone: + type: string + # Tambahkan properties lain sesuai kebutuhan + required: + - id + + Error: + type: object + properties: + message: + type: string + status: + type: integer + + + + + # Banner + BannerBase: + type: object + properties: + id: + type: string + idVillage: + type: string + title: + type: string + extension: + type: string + image: + type: string + isActive: + type: boolean + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + + BannerListResponse: + type: object + properties: + success: + type: boolean + message: + type: string + data: + type: array + items: + $ref: '#/components/schemas/BannerBase' + + BannerDetailResponse: + type: object + properties: + success: + type: boolean + message: + type: string + data: + $ref: '#/components/schemas/BannerBase' + + + # Announcement + AnnouncementBase: + type: object + properties: + id: + type: string + idVillage: + type: string + title: + type: string + desc: + type: string + isActive: + type: boolean + createdBy: + type: string + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + + AnnouncementMember: + type: object + properties: + idGroup: + type: string + idDivision: + type: string + group: + type: string + division: + type: string + + AnnouncementListResponse: + type: object + properties: + success: + type: boolean + message: + type: string + data: + type: array + items: + $ref: '#/components/schemas/AnnouncementBase' + + AnnouncementDetailResponse: + type: object + properties: + success: + type: boolean + message: + type: string + data: + type: object + properties: + id: + type: string + title: + type: string + desc: + type: string + member: + type: array + items: + $ref: '#/components/schemas/AnnouncementMember' + + + # Calendar + CalendarBase: + type: object + properties: + id: + type: string + dateStart: + type: string + format: date-time + timeStart: + type: string + timeEnd: + type: string + createdAt: + type: string + format: date-time + title: + type: string + desc: + type: string + createdBy: + type: string + isActive: + type: boolean + + CalendarMember: + type: object + properties: + id: + type: string + idUser: + type: string + name: + type: string + email: + type: string + img: + type: string + nullable: true + + CalendarListResponse: + type: object + properties: + success: + type: boolean + message: + type: string + data: + type: array + items: + $ref: '#/components/schemas/CalendarBase' + + CalendarDetailResponse: + type: object + properties: + success: + type: boolean + message: + type: string + data: + type: object + properties: + id: + type: string + timeStart: + type: string + dateStart: + type: string + format: date-time + timeEnd: + type: string + createdAt: + type: string + format: date-time + title: + type: string + desc: + type: string + linkMeet: + type: string + repeatEventTyper: + type: string + repeatValue: + type: integer + member: + type: array + items: + $ref: '#/components/schemas/CalendarMember' + + + # Discussion + DiscussionBase: + type: object + properties: + id: + type: string + desc: + type: string + createdAt: + type: string + format: date-time + idDivision: + type: string + division: + type: string + totalKomentar: + type: integer + status: + type: string + + DiscussionComment: + type: object + properties: + id: + type: string + comment: + type: string + createdAt: + type: string + format: date-time + username: + type: string + userimg: + type: string + + DiscussionListResponse: + type: object + properties: + success: + type: boolean + message: + type: string + data: + type: array + items: + $ref: '#/components/schemas/DiscussionBase' + + DiscussionDetailResponse: + type: object + properties: + success: + type: boolean + message: + type: string + data: + type: object + properties: + id: + type: string + idDivision: + type: string + division: + type: string + isActive: + type: boolean + desc: + type: string + status: + type: string + createdAt: + type: string + format: date-time + createdBy: + type: string + komentar: + type: array + items: + $ref: '#/components/schemas/DiscussionComment' + + + # Group + Group: + type: object + properties: + id: + type: string + name: + type: string + user: + type: string + isActive: + type: boolean + required: + - id + - name + + # Position + Position: + type: object + properties: + id: + type: string + name: + type: string + idGroup: + type: string + user: + type: string + isActive: + type: boolean + required: + - id + - name + + # Discussion General + DiscussionGeneral: + type: object + properties: + id: + type: string + title: + type: string + desc: + type: string + user: + type: string + status: + type: integer + member: + type: array + items: + $ref: '#/components/schemas/User' + required: + - id + - title + + # Project + Project: + type: object + properties: + id: + type: string + name: + type: string + user: + type: string + status: + type: integer + member: + type: array + items: + $ref: '#/components/schemas/User' + required: + - id + - name + + # Division (mirip Project, disederhanakan) + Division: + type: object + properties: + id: + type: string + name: + type: string + desc: + type: string + user: + type: string + isActive: + type: boolean + member: + type: array + items: + $ref: '#/components/schemas/User' + required: + - id + - name + + # Task (mirip Project) + Task: + type: object + properties: + id: + type: string + title: + type: string + user: + type: string + status: + type: integer + idDivision: + type: string + required: + - id + - title + + # Document + + # Notification + NotificationReadBody: + type: object + properties: + user: + type: string + id: + type: string + required: + - user + - id + + # Generic Response + ApiResponse: + type: object + properties: + data: + type: object + success: + type: boolean + required: + - data + + + + + + +paths: + # Announcement + /announcement: + get: + tags: + - Announcement + summary: Get announcements + parameters: + - name: desa + in: query + required: true + schema: + type: string + - name: searh + in: query + schema: + type: string + - name: page + in: query + schema: + type: integer + - name: get + in: query + schema: + type: integer + - name: active + in: query + schema: + type: boolean + responses: + '200': + description: List of announcements + content: + application/json: + schema: + $ref: '#/components/schemas/AnnouncementListResponse' + + /announcement/{id}: + get: + tags: + - Announcement + summary: Get one announcement + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: Announcement details + content: + application/json: + schema: + $ref: '#/components/schemas/AnnouncementDetailResponse' + + + # Banner + /banner: + get: + tags: + - Banner + summary: Get banners + parameters: + - name: desa + in: query + required: true + schema: + type: string + - name: page + in: query + schema: + type: integer + - name: get + in: query + schema: + type: integer + - name: search + in: query + schema: + type: string + - name: active + in: query + schema: + type: boolean + responses: + '200': + description: List of banners + content: + application/json: + schema: + $ref: '#/components/schemas/BannerListResponse' + + /banner/{id}: + get: + tags: + - Banner + summary: Get one banner + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: Banner details + content: + application/json: + schema: + $ref: '#/components/schemas/BannerDetailResponse' + + + # Calendar + /calendar: + get: + tags: + - Calendar + summary: Get calendar by date and division + parameters: + - name: desa + in: query + required: true + schema: + type: string + - name: date + in: query + schema: + type: string + - name: division + in: query + schema: + type: string + - name: active + in: query + schema: + type: boolean + - name: search + in: query + schema: + type: string + - name: page + in: query + schema: + type: integer + - name: get + in: query + schema: + type: integer + responses: + '200': + description: Calendar events + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/CalendarListResponse' + /calendar/{id}: + get: + tags: + - Calendar + summary: Get one calendar event + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: Event details + content: + application/json: + schema: + $ref: '#/components/schemas/CalendarDetailResponse' + + + # Discussion + /discussion: + get: + tags: + - Discussion + summary: Get discussions division + parameters: + - name: desa + in: query + required: true + schema: + type: string + - name: division + in: query + schema: + type: string + - name: status + in: query + schema: + type: string + enum: [open, close] + - name: active + in: query + schema: + type: boolean + - name: search + in: query + schema: + type: string + - name: page + in: query + schema: + type: integer + - name: get + in: query + schema: + type: integer + responses: + '200': + description: List of discussions + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/DiscussionListResponse' + + /discussion/{id}: + get: + tags: + - Discussion + summary: Get one discussion division + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: Discussion details + content: + application/json: + schema: + $ref: '#/components/schemas/DiscussionDetailResponse' + + + # Home Data + /home: + get: + tags: + - Home + summary: Get home data by category + parameters: + - name: user + in: query + required: true + schema: + type: string + - name: cat + in: query + required: true + schema: + type: string + enum: [kegiatan, division, progress, dokumen, event, discussion, header, check-late-project] + responses: + '200': + description: Home data + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + + /home/search: + get: + tags: + - Home + summary: Search home + parameters: + - name: search + in: query + required: true + schema: + type: string + - name: user + in: query + required: true + schema: + type: string + responses: + '200': + description: Search results + content: + application/json: + schema: + type: array + items: + type: object + + /home/notification: + get: + tags: + - Notification + summary: Get notifications + parameters: + - name: user + in: query + required: true + schema: + type: string + - name: page + in: query + schema: + type: integer + responses: + '200': + description: List of notifications + content: + application/json: + schema: + type: array + items: + type: object + + # Group + /group: + get: + tags: + - Group + summary: Get groups + parameters: + - name: user + in: query + required: true + schema: + type: string + - name: active + in: query + required: true + schema: + type: string + - name: search + in: query + required: true + schema: + type: string + responses: + '200': + description: List of groups + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Group' + + # Position + /position: + get: + tags: + - Position + summary: Get positions + parameters: + - name: user + in: query + required: true + schema: + type: string + - name: active + in: query + required: true + schema: + type: string + - name: group + in: query + schema: + type: string + - name: search + in: query + required: true + schema: + type: string + responses: + '200': + description: List of positions + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Position' + + # User + /user: + get: + tags: + - User + summary: Get users + parameters: + - name: user + in: query + required: true + schema: + type: string + - name: active + in: query + required: true + schema: + type: string + - name: group + in: query + schema: + type: string + - name: search + in: query + required: true + schema: + type: string + - name: page + in: query + schema: + type: integer + responses: + '200': + description: List of users + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + + /user/{id}: + get: + tags: + - User + summary: Get profile + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: User profile + content: + application/json: + schema: + $ref: '#/components/schemas/User' + + # Discussion General + /discussion-general: + get: + tags: + - DiscussionGeneral + summary: Get discussion general + parameters: + - name: user + in: query + required: true + schema: + type: string + - name: active + in: query + required: true + schema: + type: string + - name: group + in: query + schema: + type: string + - name: search + in: query + required: true + schema: + type: string + - name: page + in: query + schema: + type: integer + responses: + '200': + description: List of discussions + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/DiscussionGeneral' + + /discussion-general/{id}: + get: + tags: + - DiscussionGeneral + summary: Get one discussion general + parameters: + - name: id + in: path + required: true + schema: + type: string + - name: user + in: query + required: true + schema: + type: string + - name: cat + in: query + required: true + schema: + type: string + responses: + '200': + description: Discussion details + content: + application/json: + schema: + $ref: '#/components/schemas/DiscussionGeneral' + + # Project + /project: + get: + tags: + - Project + summary: Get projects + parameters: + - name: user + in: query + required: true + schema: + type: string + - name: status + in: query + required: true + schema: + type: string + - name: group + in: query + schema: + type: string + - name: search + in: query + required: true + schema: + type: string + - name: cat + in: query + schema: + type: string + - name: page + in: query + schema: + type: integer + responses: + '200': + description: List of projects + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Project' + + /project/{id}: + get: + tags: + - Project + summary: Get one project + parameters: + - name: id + in: path + required: true + schema: + type: string + - name: user + in: query + required: true + schema: + type: string + - name: cat + in: query + required: true + schema: + type: string + enum: [data, progress, task, file, member, link] + responses: + '200': + description: Project details + content: + application/json: + schema: + $ref: '#/components/schemas/Project' + + # Division + /division: + get: + tags: + - Division + summary: Get divisions + parameters: + - name: user + in: query + required: true + schema: + type: string + - name: active + in: query + schema: + type: string + - name: group + in: query + schema: + type: string + - name: search + in: query + required: true + schema: + type: string + - name: cat + in: query + schema: + type: string + - name: page + in: query + schema: + type: integer + responses: + '200': + description: List of divisions + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Division' + /division/{id}: + get: + tags: + - Division + summary: Get one division detail + parameters: + - name: id + in: path + required: true + schema: + type: string + - name: user + in: query + required: true + schema: + type: string + responses: + '200': + description: Division details + content: + application/json: + schema: + $ref: '#/components/schemas/Division' + + /division/{id}/detail: + get: + tags: + - Division + summary: Get division one feature + parameters: + - name: id + in: path + required: true + schema: + type: string + - name: user + in: query + required: true + schema: + type: string + - name: cat + in: query + required: true + schema: + type: string + enum: [jumlah, today-task, new-file, new-discussion, check-member, check-admin] + responses: + '200': + description: Feature data + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + + /division/{id}/member: + get: + tags: + - Division + summary: Get division members + parameters: + - name: id + in: path + required: true + schema: + type: string + - name: user + in: query + required: true + schema: + type: string + - name: search + in: query + required: true + schema: + type: string + responses: + '200': + description: List of members + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + + /division/report: + get: + tags: + - Division + summary: Get division report + parameters: + - name: user + in: query + required: true + schema: + type: string + - name: cat + in: query + required: true + schema: + type: string + enum: [table-progress, lainnya] + - name: date + in: query + required: true + schema: + type: string + - name: date-end + in: query + required: true + schema: + type: string + - name: division + in: query + required: true + schema: + type: string + - name: group + in: query + schema: + type: string + responses: + '200': + description: Report data + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + + /division/more: + get: + tags: + - Division + summary: Get list division by id division + parameters: + - name: user + in: query + required: true + schema: + type: string + - name: search + in: query + required: true + schema: + type: string + - name: division + in: query + required: true + schema: + type: string + responses: + '200': + description: List of divisions + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Division' + + # Task + /task: + get: + tags: + - Task + summary: Get tasks + parameters: + - name: user + in: query + required: true + schema: + type: string + - name: status + in: query + required: true + schema: + type: string + - name: division + in: query + required: true + schema: + type: string + - name: search + in: query + required: true + schema: + type: string + - name: page + in: query + schema: + type: integer + responses: + '200': + description: List of tasks + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Task' + + /task/{id}: + get: + tags: + - Task + summary: Get one task + parameters: + - name: id + in: path + required: true + schema: + type: string + - name: user + in: query + required: true + schema: + type: string + - name: cat + in: query + required: true + schema: + type: string + enum: [data, progress, task, file, member, link] + responses: + '200': + description: Task details + content: + application/json: + schema: + $ref: '#/components/schemas/Task' + + /task/detail/{id}: + get: + tags: + - Task + summary: Get task tugas + parameters: + - name: id + in: path + required: true + schema: + type: string + - name: user + in: query + required: true + schema: + type: string + - name: cat + in: query + schema: + type: string + responses: + '200': + description: Tugas details + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + + # Document + /document: + get: + tags: + - Document + summary: Get documents + parameters: + - name: user + in: query + required: true + schema: + type: string + - name: path + in: query + required: true + schema: + type: string + - name: division + in: query + required: true + schema: + type: string + - name: category + in: query + required: true + schema: + type: string + enum: [all, folder] + responses: + '200': + description: List of documents + content: + application/json: + schema: + type: array + items: + type: object + + /document/more: + get: + tags: + - Document + summary: Get document info + parameters: + - name: user + in: query + required: true + schema: + type: string + - name: item + in: query + required: true + schema: + type: string + - name: cat + in: query + required: true + schema: + type: string + enum: [share, lainnya] + responses: + '200': + description: Info + content: + application/json: + schema: + $ref: '#/components/schemas/ApiResponse' + + +security: + - bearerAuth: [] + +tags: + - name: Announcement + description: Announcements + - name: Banner + description: Banner management + - name: Calendar + description: Calendar events + - name: Discussion + description: Division discussions + + - name: Home + description: Home and search + - name: Group + description: Group management + - name: Position + description: Position management + - name: User + description: User management + - name: DiscussionGeneral + description: General discussions + - name: Project + description: Project management + - name: Division + description: Division management + - name: Task + description: Task management + - name: Document + description: Document management + - name: Notification + description: Notifications