From fdfa526d8c4428d87f64d2ad50bfab4e552d7521 Mon Sep 17 00:00:00 2001 From: amal Date: Mon, 22 Sep 2025 11:43:26 +0800 Subject: [PATCH 1/5] upd: api ai user Deskripsi: - list user - detail user No Issues --- src/app/api/ai/user/[id]/route.ts | 75 ++++++++++++++++++++++++++ src/app/api/ai/user/route.ts | 88 +++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 src/app/api/ai/user/[id]/route.ts create mode 100644 src/app/api/ai/user/route.ts diff --git a/src/app/api/ai/user/[id]/route.ts b/src/app/api/ai/user/[id]/route.ts new file mode 100644 index 0000000..bb8048c --- /dev/null +++ b/src/app/api/ai/user/[id]/route.ts @@ -0,0 +1,75 @@ +import { prisma } from "@/module/_global"; +import _ from "lodash"; +import { NextResponse } from "next/server"; + +// GET ONE MEMBER / USER +export async function GET(request: Request, context: { params: { id: string } }) { + try { + const { id } = context.params; + + const users = await prisma.user.findUnique({ + where: { + id: id, + }, + select: { + id: true, + nik: true, + name: true, + phone: true, + email: true, + gender: true, + img: true, + idGroup: true, + isActive: true, + idPosition: true, + createdAt: true, + updatedAt: true, + UserRole: { + select: { + name: true, + id: true + } + }, + Position: { + select: { + name: true, + id: true + }, + }, + Group: { + select: { + name: true, + id: true + }, + }, + }, + }); + + const { ...userData } = users; + const group = users?.Group.name + const position = users?.Position?.name + const idUserRole = users?.UserRole.id + const phone = '+62' + users?.phone + const role = users?.UserRole.name + const gender = users?.gender == "F" ? "Perempuan" : "Laki-Laki" + + const result = { ...userData, gender, group, position, idUserRole, phone, role }; + + const omitData = _.omit(result, ["Group", "Position", "UserRole"]); + + + + return NextResponse.json( + { + success: true, + message: "Berhasil mendapatkan anggota", + data: omitData, + }, + { status: 200 } + ); + + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan anggota, 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/user/route.ts b/src/app/api/ai/user/route.ts new file mode 100644 index 0000000..0f5908e --- /dev/null +++ b/src/app/api/ai/user/route.ts @@ -0,0 +1,88 @@ +import { prisma } from "@/module/_global"; +import _ from "lodash"; +import { NextResponse } from "next/server"; + +// GET ALL MEMBER / USER +export async function GET(request: Request) { + try { + const { searchParams } = new URL(request.url); + const search = searchParams.get('search') + const idVillage = searchParams.get("desa"); + const idGroup = searchParams.get("group"); + const active = searchParams.get("active"); + const page = searchParams.get('page'); + 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, + idVillage: String(idVillage), + name: { + contains: (search == undefined || search == null) ? "" : search, + mode: "insensitive", + }, + NOT: { + idUserRole: 'developer' + } + } + + if (idGroup != "null" && idGroup != undefined && idGroup != "" && idGroup != null) { + kondisi = { + ...kondisi, + idGroup: String(idGroup) + } + } + + + const users = await prisma.user.findMany({ + skip: dataSkip, + take: getFix, + where: kondisi, + select: { + id: true, + idUserRole: true, + isActive: true, + nik: true, + name: true, + phone: true, + Position: { + select: { + name: true, + }, + }, + Group: { + select: { + name: true, + }, + }, + }, + orderBy: { + name: 'asc' + } + }); + + const allData = users.map((v: any) => ({ + ..._.omit(v, ["phone", "gender", "Group", "Position"]), + gender: v.gender == "F" ? "Perempuan" : "Laki-Laki", + phone: "+" + v.phone, + group: v.Group.name, + position: v?.Position?.name + })) + + return NextResponse.json({ success: true, message: "Berhasil member", data: allData }, { status: 200 }); + + + + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan anggota, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file From 4b1d2e28dc84d9a4b030df389d4d34f63e1b0e92 Mon Sep 17 00:00:00 2001 From: amal Date: Mon, 22 Sep 2025 15:31:02 +0800 Subject: [PATCH 2/5] upd: api ai report divisi Deskripsi: - update api ai laporan divisi NO Issues --- src/app/api/ai/division/report/route.ts | 269 ++++++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 src/app/api/ai/division/report/route.ts diff --git a/src/app/api/ai/division/report/route.ts b/src/app/api/ai/division/report/route.ts new file mode 100644 index 0000000..2d6bbee --- /dev/null +++ b/src/app/api/ai/division/report/route.ts @@ -0,0 +1,269 @@ +import { prisma } from "@/module/_global"; +import _ from "lodash"; +import { NextResponse } from "next/server"; + +export async function GET(request: Request) { + try { + const { searchParams } = new URL(request.url) + const idVillage = searchParams.get("desa") + const idGroup = searchParams.get("group") + const division = searchParams.get("division") + const date = searchParams.get("date-start") + const dateAkhir = searchParams.get("date-end") + const kat = searchParams.get("cat") + + + // CHART PROGRESS + if (kat == "dokumen") { + // CHART DOKUMEN + let kondisi: any = { + isActive: true, + category: 'FILE', + Division: { + idVillage: String(idVillage) + }, + createdAt: { + gte: new Date(String(date)), + lte: new Date(String(dateAkhir)) + }, + } + + if (idGroup != undefined && idGroup != null && idGroup != "") { + kondisi = { + isActive: true, + category: 'FILE', + Division: { + idGroup: String(idGroup) + }, + createdAt: { + gte: new Date(String(date)), + lte: new Date(String(dateAkhir)) + }, + } + } + + + if (division != undefined && division != null && division != "") { + kondisi = { + ...kondisi, + idDivision: String(division) + } + } + + + 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 = { gambar: hasilImage.value, dokumen: hasilFile.value } + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: hasilDokumen }, { status: 200 }); + } else if (kat == "event") { + // CHART EVENT + let kondisiSelesai: any = { + isActive: true, + Division: { + idVillage: String(idVillage) + }, + DivisionCalendarReminder: { + some: { + dateStart: { + gte: new Date(String(date)), + lte: new Date() + } + } + } + } + + let kondisiComingSoon: any = { + isActive: true, + Division: { + idVillage: String(idVillage) + }, + DivisionCalendarReminder: { + some: { + dateStart: { + gt: new Date(), + lte: new Date(String(dateAkhir)) + } + } + } + } + + if (idGroup != undefined && idGroup != null && idGroup != "") { + kondisiSelesai = { + isActive: true, + Division: { + idGroup: String(idGroup) + }, + DivisionCalendarReminder: { + some: { + dateStart: { + gte: new Date(String(date)), + lte: new Date() + } + } + } + } + + kondisiComingSoon = { + isActive: true, + Division: { + idGroup: String(idGroup) + }, + DivisionCalendarReminder: { + some: { + dateStart: { + gt: new Date(), + lte: new Date(String(dateAkhir)) + } + } + } + } + } + + if (division != undefined && division != null && division != "") { + kondisiSelesai = { + ...kondisiSelesai, + idDivision: String(division) + } + + kondisiComingSoon = { + ...kondisiComingSoon, + idDivision: String(division) + } + } + + const eventSelesai = await prisma.divisionCalendar.count({ + where: kondisiSelesai + }) + + const eventComingSoon = await prisma.divisionCalendar.count({ + where: kondisiComingSoon + }) + + const hasilEvent = { + selesai: eventSelesai, + akan_datang: eventComingSoon + } + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: hasilEvent }, { status: 200 }); + + } else { + let kondisiProgress: any = { + isActive: true, + Division: { + idVillage: String(idVillage) + }, + DivisionProjectTask: { + some: { + dateStart: { + gte: new Date(String(date)) + }, + dateEnd: { + lte: new Date(String(dateAkhir)) + } + } + } + } + + if (idGroup != undefined && idGroup != null && idGroup != "") { + kondisiProgress = { + isActive: true, + Division: { + idGroup: String(idGroup) + }, + DivisionProjectTask: { + some: { + dateStart: { + gte: new Date(String(date)) + }, + dateEnd: { + lte: new Date(String(dateAkhir)) + } + } + } + } + } + + if (division != undefined && division != null && division != "") { + kondisiProgress = { + ...kondisiProgress, + idDivision: String(division) + } + } + + 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) + } + + const dataFixProgress = hasilProgres.reduce((acc: any, curr: any) => { + acc[curr.name] = curr.value + return acc + }, {}) + + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: dataFixProgress }, { status: 200 }); + + } + + } + catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan data, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file From 9c07375015645da1b7bf685f5619afb4609ddc9c Mon Sep 17 00:00:00 2001 From: amal Date: Mon, 22 Sep 2025 15:31:23 +0800 Subject: [PATCH 3/5] upd: swagger api ai --- darmasaba-api-ai.yml | 1380 +++++++++++++++++++++++++++++++++--------- 1 file changed, 1079 insertions(+), 301 deletions(-) diff --git a/darmasaba-api-ai.yml b/darmasaba-api-ai.yml index 43ca383..cba55c6 100644 --- a/darmasaba-api-ai.yml +++ b/darmasaba-api-ai.yml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: - title: API AI Desa+ - description: API untuk sistem manajemen AI Desa+ + title: AI Desa+ API + description: API for the AI Desa+ management system, providing endpoints for managing announcements, banners, calendar events, discussions, divisions, documents, groups, positions, projects, tasks, users, and division reports. version: 1.0.0 contact: name: API Support @@ -9,6 +9,8 @@ info: servers: - url: http://localhost:3000/api/ai + description: Development server + - url: https://api.desa-plus.com/api/ai description: Production server components: @@ -27,17 +29,23 @@ components: properties: success: type: boolean + description: Indicates whether the request was successful message: type: string + description: Response message or error description meta: type: object properties: total: type: integer + description: Total number of items page: type: integer - perPage: + description: Current page number + get: type: integer + description: Number of items per page + description: Pagination metadata # Banner BannerBase: @@ -51,40 +59,51 @@ components: properties: id: type: string + description: Unique identifier for the banner idVillage: type: string + description: ID of the village associated with the banner title: type: string - extension: - type: string + description: Title of the banner image: type: string + description: URL or path to the banner image + extension: + type: string + description: File extension of the banner image (e.g., jpg, png) + nullable: true isActive: type: boolean + description: Indicates whether the banner is active createdAt: type: string format: date-time + description: Timestamp when the banner was created updatedAt: type: string format: date-time + description: Timestamp when the banner was last updated BannerListResponse: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: type: array items: - $ref: '#/components/schemas/BannerBase' + $ref: "#/components/schemas/BannerBase" + description: List of banners BannerDetailResponse: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: - $ref: '#/components/schemas/BannerBase' + $ref: "#/components/schemas/BannerBase" + description: Details of a specific banner # Announcement AnnouncementBase: @@ -98,22 +117,30 @@ components: properties: id: type: string + description: Unique identifier for the announcement idVillage: type: string + description: ID of the village associated with the announcement title: type: string + description: Title of the announcement desc: type: string + description: Description or content of the announcement isActive: type: boolean + description: Indicates whether the announcement is active createdBy: type: string + description: ID of the user who created the announcement createdAt: type: string format: date-time + description: Timestamp when the announcement was created updatedAt: type: string format: date-time + description: Timestamp when the announcement was last updated AnnouncementMember: type: object @@ -123,26 +150,31 @@ components: properties: idGroup: type: string + description: ID of the group associated with the announcement idDivision: type: string + description: ID of the division associated with the announcement group: type: string + description: Name of the group division: type: string + description: Name of the division AnnouncementListResponse: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: type: array items: - $ref: '#/components/schemas/AnnouncementBase' + $ref: "#/components/schemas/AnnouncementBase" + description: List of announcements AnnouncementDetailResponse: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: @@ -150,14 +182,33 @@ components: properties: id: type: string + description: Unique identifier for the announcement title: type: string + description: Title of the announcement desc: type: string + description: Description or content of the announcement + isActive: + type: boolean + description: Indicates whether the announcement is active + createdBy: + type: string + description: ID of the user who created the announcement + createdAt: + type: string + format: date-time + description: Timestamp when the announcement was created + updatedAt: + type: string + format: date-time + description: Timestamp when the announcement was last updated member: type: array items: - $ref: '#/components/schemas/AnnouncementMember' + $ref: "#/components/schemas/AnnouncementMember" + description: List of members associated with the announcement + description: Details of a specific announcement # Calendar CalendarBase: @@ -170,24 +221,40 @@ components: properties: id: type: string + description: Unique identifier for the calendar event dateStart: type: string format: date-time + description: Start date and time of the event timeStart: type: string + description: Start time of the event (e.g., HH:MM) + nullable: true timeEnd: type: string + description: End time of the event (e.g., HH:MM) + nullable: true + title: + type: string + description: Title of the event + desc: + type: string + description: Description of the event + nullable: true + createdBy: + type: string + description: ID of the user who created the event + isActive: + type: boolean + description: Indicates whether the event is active createdAt: type: string format: date-time - title: + description: Timestamp when the event was created + updatedAt: type: string - desc: - type: string - createdBy: - type: string - isActive: - type: boolean + format: date-time + description: Timestamp when the event was last updated CalendarMember: type: object @@ -199,29 +266,35 @@ components: properties: id: type: string + description: Unique identifier for the member idUser: type: string + description: ID of the user name: type: string + description: Name of the user email: type: string + description: Email of the user img: type: string + description: URL or path to the user's image nullable: true CalendarListResponse: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: type: array items: - $ref: '#/components/schemas/CalendarBase' + $ref: "#/components/schemas/CalendarBase" + description: List of calendar events CalendarDetailResponse: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: @@ -229,30 +302,58 @@ components: properties: id: type: string - timeStart: - type: string + description: Unique identifier for the event dateStart: type: string format: date-time + description: Start date and time of the event + timeStart: + type: string + description: Start time of the event (e.g., HH:MM) + nullable: true timeEnd: type: string + description: End time of the event (e.g., HH:MM) + nullable: true + title: + type: string + description: Title of the event + desc: + type: string + description: Description of the event + nullable: true + linkMeet: + type: string + description: URL for the meeting (if applicable) + nullable: true + repeatEventType: + type: string + description: Type of event repetition (e.g., daily, weekly) + nullable: true + repeatValue: + type: integer + description: Number of repetitions or interval + nullable: true + createdBy: + type: string + description: ID of the user who created the event + isActive: + type: boolean + description: Indicates whether the event is active createdAt: type: string format: date-time - title: + description: Timestamp when the event was created + updatedAt: type: string - desc: - type: string - linkMeet: - type: string - repeatEventType: - type: string - repeatValue: - type: integer + format: date-time + description: Timestamp when the event was last updated member: type: array items: - $ref: '#/components/schemas/CalendarMember' + $ref: "#/components/schemas/CalendarMember" + description: List of members associated with the event + description: Details of a specific calendar event # Discussion DiscussionBase: @@ -265,20 +366,31 @@ components: properties: id: type: string + description: Unique identifier for the discussion desc: type: string + description: Description of the discussion + idDivision: + type: string + description: ID of the division associated with the discussion + division: + type: string + description: Name of the division + status: + type: string + enum: [open, closed] + description: Status of the discussion + totalKomentar: + type: integer + description: Total number of comments in the discussion createdAt: type: string format: date-time - idDivision: + description: Timestamp when the discussion was created + updatedAt: type: string - division: - type: string - totalKomentar: - type: integer - status: - type: string - enum: [open, close] + format: date-time + description: Timestamp when the discussion was last updated DiscussionComment: type: object @@ -289,30 +401,36 @@ components: properties: id: type: string + description: Unique identifier for the comment comment: type: string + description: Content of the comment + username: + type: string + description: Username of the commenter + userimg: + type: string + description: URL or path to the commenter's image + nullable: true createdAt: type: string format: date-time - username: - type: string - userimg: - type: string - nullable: true + description: Timestamp when the comment was created DiscussionListResponse: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: type: array items: - $ref: '#/components/schemas/DiscussionBase' + $ref: "#/components/schemas/DiscussionBase" + description: List of discussions DiscussionDetailResponse: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: @@ -320,63 +438,92 @@ components: properties: id: type: string + description: Unique identifier for the discussion idDivision: type: string + description: ID of the division associated with the discussion division: type: string - isActive: - type: boolean + description: Name of the division desc: type: string + description: Description of the discussion status: type: string - enum: [open, close] + enum: [open, closed] + description: Status of the discussion + isActive: + type: boolean + description: Indicates whether the discussion is active + createdBy: + type: string + description: ID of the user who created the discussion createdAt: type: string format: date-time - createdBy: + description: Timestamp when the discussion was created + updatedAt: type: string + format: date-time + description: Timestamp when the discussion was last updated komentar: type: array items: - $ref: '#/components/schemas/DiscussionComment' + $ref: "#/components/schemas/DiscussionComment" + description: List of comments in the discussion + description: Details of a specific discussion # Discussion General + DiskusiUmumBase: + type: object + required: + - id + - title + - desc + - status + properties: + id: + type: string + description: Unique identifier for the general discussion + title: + type: string + description: Title of the general discussion + desc: + type: string + description: Description of the general discussion + status: + type: string + enum: [open, closed] + description: Status of the general discussion + group: + type: string + description: Name of the group associated with the discussion + totalKomentar: + type: integer + description: Total number of comments in the discussion + createdAt: + type: string + format: date-time + description: Timestamp when the discussion was created + updatedAt: + type: string + format: date-time + description: Timestamp when the discussion was last updated + DiskusiUmumListResponse: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: type: array items: - type: object - required: - - id - - title - - desc - - status - properties: - id: - type: string - title: - type: string - desc: - type: string - createdAt: - type: string - format: date-time - totalKomentar: - type: integer - status: - type: string - enum: [open, close] - group: - type: string + $ref: "#/components/schemas/DiskusiUmumBase" + description: List of general discussions DiskusiUmumDetailResponse: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: @@ -389,26 +536,39 @@ components: properties: id: type: string - isActive: - type: boolean + description: Unique identifier for the general discussion idGroup: type: string + description: ID of the group associated with the discussion group: type: string + description: Name of the group title: type: string + description: Title of the general discussion desc: type: string + description: Description of the general discussion status: type: string - enum: [open, close] + enum: [open, closed] + description: Status of the general discussion + isActive: + type: boolean + description: Indicates whether the discussion is active createdAt: type: string format: date-time + description: Timestamp when the discussion was created + updatedAt: + type: string + format: date-time + description: Timestamp when the discussion was last updated + description: Details of a specific general discussion DiskusiUmumMemberResponse: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: @@ -421,15 +581,19 @@ components: properties: idUser: type: string + description: ID of the user name: type: string + description: Name of the user img: type: string + description: URL or path to the user's image nullable: true + description: List of members in the general discussion DiskusiUmumCommentResponse: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: @@ -444,49 +608,67 @@ components: properties: id: type: string + description: Unique identifier for the comment comment: type: string + description: Content of the comment + idUser: + type: string + description: ID of the user who made the comment + username: + type: string + description: Username of the commenter + img: + type: string + description: URL or path to the commenter's image + nullable: true createdAt: type: string format: date-time - idUser: - type: string - username: - type: string - img: - type: string - nullable: true + description: Timestamp when the comment was created + description: List of comments in the general discussion # Division + DivisiBase: + type: object + required: + - id + - name + properties: + id: + type: string + description: Unique identifier for the division + name: + type: string + description: Name of the division + desc: + type: string + description: Description of the division + nullable: true + idGroup: + type: string + description: ID of the group associated with the division + group: + type: string + description: Name of the group + jumlahMember: + type: integer + description: Number of members in the division + DivisiListResponse: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: type: array items: - type: object - required: - - id - - name - properties: - id: - type: string - name: - type: string - desc: - type: string - idGroup: - type: string - group: - type: string - jumlahMember: - type: integer + $ref: "#/components/schemas/DivisiBase" + description: List of divisions DivisiDetailResponse: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: @@ -498,24 +680,34 @@ components: properties: id: type: string + description: Unique identifier for the division idVillage: type: string + description: ID of the village associated with the division idGroup: type: string + description: ID of the group associated with the division name: type: string + description: Name of the division desc: type: string + description: Description of the division + nullable: true isActive: type: boolean + description: Indicates whether the division is active createdBy: type: string + description: ID of the user who created the division createdAt: type: string format: date-time + description: Timestamp when the division was created updatedAt: type: string format: date-time + description: Timestamp when the division was last updated member: type: array items: @@ -527,15 +719,76 @@ components: properties: id: type: string - isAdmin: - type: boolean + description: Unique identifier for the member idUser: type: string + description: ID of the user name: type: string + description: Name of the user + isAdmin: + type: boolean + description: Indicates whether the user is an admin img: type: string + description: URL or path to the user's image nullable: true + description: List of members in the division + description: Details of a specific division + + DivisiReportDokumenResponse: + allOf: + - $ref: "#/components/schemas/BaseResponse" + - type: object + properties: + data: + type: object + properties: + gambar: + type: integer + description: Number of images in the division + dokumen: + type: integer + description: Number of documents in the division + description: Document report for the division + + DivisiReportEventResponse: + allOf: + - $ref: "#/components/schemas/BaseResponse" + - type: object + properties: + data: + type: object + properties: + selesai: + type: integer + description: Number of completed events + akan_datang: + type: integer + description: Number of upcoming events + description: Event report for the division + + DivisiReportProgressResponse: + allOf: + - $ref: "#/components/schemas/BaseResponse" + - type: object + properties: + data: + type: object + properties: + Segera: + type: string + description: Percentage of tasks in 'Segera' status + Dikerjakan: + type: string + description: Percentage of tasks in 'Dikerjakan' status + Selesai: + type: integer + description: Percentage of tasks in 'Selesai' status + Dibatalkan: + type: string + description: Percentage of tasks in 'Dibatalkan' status + description: Progress report for the division # Document DocumentItem: @@ -548,38 +801,50 @@ components: properties: id: type: string + description: Unique identifier for the document or folder category: type: string enum: [FILE, FOLDER] + description: Type of item (file or folder) name: type: string + description: Name of the document or folder extension: type: string + description: File extension (e.g., pdf, docx) + nullable: true idStorage: type: string + description: ID of the storage location nullable: true path: type: string + description: Path to the document or folder createdBy: type: string + description: ID of the user who created the document or folder createdAt: type: string format: date-time + description: Timestamp when the document or folder was created updatedAt: type: string format: date-time + description: Timestamp when the document or folder was last updated share: type: boolean + description: Indicates whether the document or folder is shared DocumentListResponse: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: type: array items: - $ref: '#/components/schemas/DocumentItem' + $ref: "#/components/schemas/DocumentItem" + description: List of documents and folders # Group GroupItem: @@ -592,28 +857,35 @@ components: properties: id: type: string + description: Unique identifier for the group idVillage: type: string + description: ID of the village associated with the group name: type: string + description: Name of the group isActive: type: boolean + description: Indicates whether the group is active createdAt: type: string format: date-time + description: Timestamp when the group was created updatedAt: type: string format: date-time + description: Timestamp when the group was last updated GroupListResponse: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: type: array items: - $ref: '#/components/schemas/GroupItem' + $ref: "#/components/schemas/GroupItem" + description: List of groups # Position PositionItem: @@ -626,69 +898,89 @@ components: properties: id: type: string + description: Unique identifier for the position name: type: string + description: Name of the position idGroup: type: string + description: ID of the group associated with the position group: type: string + description: Name of the group isActive: type: boolean + description: Indicates whether the position is active createdAt: type: string format: date-time + description: Timestamp when the position was created updatedAt: type: string format: date-time + description: Timestamp when the position was last updated PositionListResponse: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: type: array items: - $ref: '#/components/schemas/PositionItem' + $ref: "#/components/schemas/PositionItem" + description: List of positions # Project + ProjectBase: + type: object + required: + - id + - idGroup + - title + - status + properties: + id: + type: string + description: Unique identifier for the project + idGroup: + type: string + description: ID of the group associated with the project + title: + type: string + description: Title of the project + desc: + type: string + description: Description of the project + nullable: true + group: + type: string + description: Name of the group + status: + type: string + enum: [segera, dikerjakan, selesai, batal] + description: Status of the project + progress: + type: integer + description: Progress percentage of the project + member: + type: integer + description: Number of members in the project + BaseResponseProjectList: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: type: array items: - type: object - required: - - id - - idGroup - - title - - status - properties: - id: - type: string - idGroup: - type: string - title: - type: string - desc: - type: string - nullable: true - group: - type: string - status: - type: string - enum: [segera, dikerjakan, selesai, batal] - progress: - type: integer - member: - type: integer + $ref: "#/components/schemas/ProjectBase" + description: List of projects BaseResponseProjectDetail: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: @@ -703,68 +995,101 @@ components: properties: id: type: string + description: Unique identifier for the project idVillage: type: string + description: ID of the village associated with the project idGroup: type: string + description: ID of the group associated with the project group: type: string + description: Name of the group title: type: string + description: Title of the project status: type: string enum: [segera, dikerjakan, selesai, batal] + description: Status of the project desc: type: string + description: Description of the project nullable: true reason: type: string + description: Reason for project status (e.g., cancellation) nullable: true report: type: string + description: Project report or summary nullable: true isActive: type: boolean + description: Indicates whether the project is active progress: type: integer + description: Progress percentage of the project + createdAt: + type: string + format: date-time + description: Timestamp when the project was created + updatedAt: + type: string + format: date-time + description: Timestamp when the project was last updated + description: Details of a specific project + + # Task + TaskBase: + type: object + required: + - id + - idDivision + - title + - status + properties: + id: + type: string + description: Unique identifier for the task + idDivision: + type: string + description: ID of the division associated with the task + title: + type: string + description: Title of the task + desc: + type: string + description: Description of the task + nullable: true + division: + type: string + description: Name of the division + status: + type: string + enum: [segera, dikerjakan, selesai, batal] + description: Status of the task + progress: + type: integer + description: Progress percentage of the task + member: + type: integer + description: Number of members assigned to the task BaseResponseTaskList: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: type: array items: - type: object - required: - - id - - idDivision - - title - - status - properties: - id: - type: string - idDivision: - type: string - title: - type: string - desc: - type: string - nullable: true - division: - type: string - status: - type: string - enum: [segera, dikerjakan, selesai, batal] - progress: - type: integer - member: - type: integer + $ref: "#/components/schemas/TaskBase" + description: List of tasks BaseResponseTaskDetail: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: @@ -778,31 +1103,51 @@ components: properties: id: type: string + description: Unique identifier for the task idDivision: type: string + description: ID of the division associated with the task division: type: string + description: Name of the division title: type: string + description: Title of the task status: type: string + enum: [segera, dikerjakan, selesai, batal] + description: Status of the task desc: type: string + description: Description of the task nullable: true reason: type: string + description: Reason for task status (e.g., cancellation) nullable: true report: type: string + description: Task report or summary nullable: true isActive: type: boolean + description: Indicates whether the task is active progress: type: integer + description: Progress percentage of the task + createdAt: + type: string + format: date-time + description: Timestamp when the task was created + updatedAt: + type: string + format: date-time + description: Timestamp when the task was last updated + description: Details of a specific task BaseResponseSubTaskList: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: @@ -816,21 +1161,27 @@ components: properties: id: type: string + description: Unique identifier for the subtask title: type: string + description: Title of the subtask status: type: string - enum: [belum selesai, selesai] + enum: [belum_selesai, selesai] + description: Status of the subtask dateStart: type: string format: date-time + description: Start date and time of the subtask dateEnd: type: string format: date-time + description: End date and time of the subtask + description: List of subtasks BaseResponseMemberList: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: @@ -845,57 +1196,40 @@ components: properties: id: type: string + description: Unique identifier for the member idUser: type: string + description: ID of the user name: type: string + description: Name of the user email: type: string + description: Email of the user img: type: string + description: URL or path to the user's image nullable: true position: type: string + description: Position of the user + nullable: true + description: List of members BaseResponseFileList: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: type: array items: - type: object - required: - - id - - name - - path - properties: - id: - type: string - name: - type: string - extension: - type: string - idStorage: - type: string - nullable: true - path: - type: string - createdBy: - type: string - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time - share: - type: boolean + $ref: "#/components/schemas/DocumentItem" + description: List of files BaseResponseLinkList: allOf: - - $ref: '#/components/schemas/BaseResponse' + - $ref: "#/components/schemas/BaseResponse" - type: object properties: data: @@ -910,18 +1244,153 @@ components: properties: id: type: string + description: Unique identifier for the link idProject: type: string + description: ID of the project associated with the link link: type: string + description: URL of the link isActive: type: boolean + description: Indicates whether the link is active createdAt: type: string format: date-time + description: Timestamp when the link was created updatedAt: type: string format: date-time + description: Timestamp when the link was last updated + description: List of links + + # User + UserBase: + type: object + required: + - id + - idUserRole + - nik + - name + - gender + - phone + - isActive + properties: + id: + type: string + description: Unique identifier for the user + idUserRole: + type: string + description: ID of the user's role + nik: + type: string + description: National identification number of the user + name: + type: string + description: Name of the user + gender: + type: string + description: Gender of the user + phone: + type: string + description: Phone number of the user + position: + type: string + description: Position of the user + nullable: true + group: + type: string + description: Name of the group the user belongs to + nullable: true + isActive: + type: boolean + description: Indicates whether the user is active + + UserListResponse: + allOf: + - $ref: "#/components/schemas/BaseResponse" + - type: object + properties: + data: + type: array + items: + $ref: "#/components/schemas/UserBase" + description: List of users + + UserDetailResponse: + allOf: + - $ref: "#/components/schemas/BaseResponse" + - type: object + properties: + data: + type: object + required: + - id + - idUserRole + - nik + - name + - phone + - email + - gender + - isActive + properties: + id: + type: string + description: Unique identifier for the user + idUserRole: + type: string + description: ID of the user's role + nik: + type: string + description: National identification number of the user + name: + type: string + description: Name of the user + email: + type: string + description: Email of the user + gender: + type: string + description: Gender of the user + phone: + type: string + description: Phone number of the user + img: + type: string + description: URL or path to the user's image + nullable: true + idGroup: + type: string + description: ID of the group the user belongs to + nullable: true + idPosition: + type: string + description: ID of the user's position + nullable: true + role: + type: string + description: Role of the user + nullable: true + position: + type: string + description: Position of the user + nullable: true + group: + type: string + description: Name of the group the user belongs to + nullable: true + isActive: + type: boolean + description: Indicates whether the user is active + createdAt: + type: string + format: date-time + description: Timestamp when the user was created + updatedAt: + type: string + format: date-time + description: Timestamp when the user was last updated + description: Details of a specific user paths: # Announcement @@ -930,7 +1399,7 @@ paths: tags: - Announcement summary: Get list of announcements - description: Retrieves a paginated list of announcements filtered by village + description: Retrieves a paginated list of announcements filtered by village, search term, and active status parameters: - name: desa in: query @@ -943,6 +1412,7 @@ paths: description: Search term for announcement title or description schema: type: string + nullable: true - name: page in: query description: Page number for pagination @@ -950,7 +1420,7 @@ paths: type: integer minimum: 1 default: 1 - - name: get + - name: perPage in: query description: Number of items per page schema: @@ -962,13 +1432,20 @@ paths: description: Filter by active status schema: type: boolean + nullable: true responses: - '200': + "200": description: List of announcements content: application/json: schema: - $ref: '#/components/schemas/AnnouncementListResponse' + $ref: "#/components/schemas/AnnouncementListResponse" + "400": + description: Bad request (e.g., invalid parameters) + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error /announcement/{id}: get: @@ -984,12 +1461,18 @@ paths: schema: type: string responses: - '200': + "200": description: Announcement details content: application/json: schema: - $ref: '#/components/schemas/AnnouncementDetailResponse' + $ref: "#/components/schemas/AnnouncementDetailResponse" + "404": + description: Announcement not found + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error # Banner /banner: @@ -997,7 +1480,7 @@ paths: tags: - Banner summary: Get list of banners - description: Retrieves a paginated list of banners filtered by village + description: Retrieves a paginated list of banners filtered by village, search term, and active status parameters: - name: desa in: query @@ -1010,6 +1493,7 @@ paths: description: Search term for banner title schema: type: string + nullable: true - name: page in: query description: Page number for pagination @@ -1017,7 +1501,7 @@ paths: type: integer minimum: 1 default: 1 - - name: get + - name: perPage in: query description: Number of items per page schema: @@ -1029,13 +1513,20 @@ paths: description: Filter by active status schema: type: boolean + nullable: true responses: - '200': + "200": description: List of banners content: application/json: schema: - $ref: '#/components/schemas/BannerListResponse' + $ref: "#/components/schemas/BannerListResponse" + "400": + description: Bad request (e.g., invalid parameters) + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error /banner/{id}: get: @@ -1051,12 +1542,18 @@ paths: schema: type: string responses: - '200': + "200": description: Banner details content: application/json: schema: - $ref: '#/components/schemas/BannerDetailResponse' + $ref: "#/components/schemas/BannerDetailResponse" + "404": + description: Banner not found + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error # Calendar /calendar: @@ -1064,7 +1561,7 @@ paths: tags: - Calendar summary: Get calendar events - description: Retrieves a paginated list of calendar events filtered by village, date, and division + description: Retrieves a paginated list of calendar events filtered by village, date, division, and active status parameters: - name: desa in: query @@ -1078,21 +1575,25 @@ paths: schema: type: string format: date + nullable: true - name: division in: query description: Filter by division ID schema: type: string + nullable: true - name: active in: query description: Filter by active status schema: type: boolean + nullable: true - name: search in: query description: Search term for event title or description schema: type: string + nullable: true - name: page in: query description: Page number for pagination @@ -1100,7 +1601,7 @@ paths: type: integer minimum: 1 default: 1 - - name: get + - name: perPage in: query description: Number of items per page schema: @@ -1108,12 +1609,18 @@ paths: minimum: 1 default: 10 responses: - '200': + "200": description: List of calendar events content: application/json: schema: - $ref: '#/components/schemas/CalendarListResponse' + $ref: "#/components/schemas/CalendarListResponse" + "400": + description: Bad request (e.g., invalid parameters) + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error /calendar/{id}: get: @@ -1129,12 +1636,18 @@ paths: schema: type: string responses: - '200': + "200": description: Event details content: application/json: schema: - $ref: '#/components/schemas/CalendarDetailResponse' + $ref: "#/components/schemas/CalendarDetailResponse" + "404": + description: Event not found + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error # Discussion /discussion: @@ -1142,7 +1655,7 @@ paths: tags: - Discussion summary: Get division discussions - description: Retrieves a paginated list of discussions filtered by village and division + description: Retrieves a paginated list of discussions filtered by village, division, status, and active status parameters: - name: desa in: query @@ -1155,22 +1668,26 @@ paths: description: Filter by division ID schema: type: string + nullable: true - name: status in: query description: Filter by discussion status schema: type: string - enum: [open, close] + enum: [open, closed] + nullable: true - name: active in: query description: Filter by active status schema: type: boolean + nullable: true - name: search in: query description: Search term for discussion description schema: type: string + nullable: true - name: page in: query description: Page number for pagination @@ -1178,7 +1695,7 @@ paths: type: integer minimum: 1 default: 1 - - name: get + - name: perPage in: query description: Number of items per page schema: @@ -1186,12 +1703,18 @@ paths: minimum: 1 default: 10 responses: - '200': + "200": description: List of discussions content: application/json: schema: - $ref: '#/components/schemas/DiscussionListResponse' + $ref: "#/components/schemas/DiscussionListResponse" + "400": + description: Bad request (e.g., invalid parameters) + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error /discussion/{id}: get: @@ -1207,12 +1730,18 @@ paths: schema: type: string responses: - '200': + "200": description: Discussion details content: application/json: schema: - $ref: '#/components/schemas/DiscussionDetailResponse' + $ref: "#/components/schemas/DiscussionDetailResponse" + "404": + description: Discussion not found + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error # Discussion General /discussion-general: @@ -1220,7 +1749,7 @@ paths: tags: - DiscussionGeneral summary: Get general discussions - description: Retrieves a paginated list of general discussions filtered by village and group + description: Retrieves a paginated list of general discussions filtered by village, group, status, and active status parameters: - name: desa in: query @@ -1233,22 +1762,26 @@ paths: description: Filter by group ID schema: type: string + nullable: true - name: search in: query description: Search term for discussion title or description schema: type: string + nullable: true - name: status in: query description: Filter by discussion status schema: type: string - enum: [open, close] + enum: [open, closed] + nullable: true - name: active in: query description: Filter by active status schema: type: boolean + nullable: true - name: page in: query description: Page number for pagination @@ -1256,7 +1789,7 @@ paths: type: integer minimum: 1 default: 1 - - name: get + - name: perPage in: query description: Number of items per page schema: @@ -1264,12 +1797,18 @@ paths: minimum: 1 default: 10 responses: - '200': + "200": description: List of general discussions content: application/json: schema: - $ref: '#/components/schemas/DiskusiUmumListResponse' + $ref: "#/components/schemas/DiskusiUmumListResponse" + "400": + description: Bad request (e.g., invalid parameters) + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error /discussion-general/{id}: get: @@ -1292,20 +1831,29 @@ paths: type: string - name: cat in: query - description: Category of data to retrieve + description: Category of data to retrieve (detail, member, or comment) schema: type: string enum: [detail, member, comment] + nullable: true responses: - '200': - description: Discussion details + "200": + description: Discussion details, members, or comments content: application/json: schema: oneOf: - - $ref: '#/components/schemas/DiskusiUmumDetailResponse' - - $ref: '#/components/schemas/DiskusiUmumMemberResponse' - - $ref: '#/components/schemas/DiskusiUmumCommentResponse' + - $ref: "#/components/schemas/DiskusiUmumDetailResponse" + - $ref: "#/components/schemas/DiskusiUmumMemberResponse" + - $ref: "#/components/schemas/DiskusiUmumCommentResponse" + "400": + description: Bad request (e.g., invalid parameters) + "404": + description: Discussion not found + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error # Division /division: @@ -1313,7 +1861,7 @@ paths: tags: - Division summary: Get divisions - description: Retrieves a paginated list of divisions filtered by village and group + description: Retrieves a paginated list of divisions filtered by village, group, and active status parameters: - name: desa in: query @@ -1321,21 +1869,24 @@ paths: description: Village ID schema: type: string - - name: active - in: query - description: Filter by active status - schema: - type: boolean - name: group in: query description: Filter by group ID schema: type: string + nullable: true - name: search in: query description: Search term for division name or description schema: type: string + nullable: true + - name: active + in: query + description: Filter by active status + schema: + type: boolean + nullable: true - name: page in: query description: Page number for pagination @@ -1343,7 +1894,7 @@ paths: type: integer minimum: 1 default: 1 - - name: get + - name: perPage in: query description: Number of items per page schema: @@ -1351,12 +1902,18 @@ paths: minimum: 1 default: 10 responses: - '200': + "200": description: List of divisions content: application/json: schema: - $ref: '#/components/schemas/DivisiListResponse' + $ref: "#/components/schemas/DivisiListResponse" + "400": + description: Bad request (e.g., invalid parameters) + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error /division/{id}: get: @@ -1378,12 +1935,83 @@ paths: schema: type: string responses: - '200': + "200": description: Division details content: application/json: schema: - $ref: '#/components/schemas/DivisiDetailResponse' + $ref: "#/components/schemas/DivisiDetailResponse" + "404": + description: Division not found + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error + + /division/report: + get: + tags: + - Division + summary: Get division reports + description: Retrieves reports for a specific division based on category (dokumen, event, or progress) + parameters: + - name: desa + in: query + required: true + description: Village ID + schema: + type: string + - name: cat + in: query + required: true + description: Category of report to retrieve (dokumen, event, or progress) + schema: + type: string + enum: [dokumen, event, progress] + - name: group + in: query + description: Group ID + schema: + type: string + nullable: true + - name: division + in: query + description: Division ID + schema: + type: string + nullable: true + - name: date-start + in: query + required: true + description: Start date for filtering + schema: + type: string + format: date + - name: date-end + in: query + required: true + description: End date for filtering + schema: + type: string + format: date + responses: + "200": + description: Division report data + content: + application/json: + schema: + oneOf: + - $ref: "#/components/schemas/DivisiReportDokumenResponse" + - $ref: "#/components/schemas/DivisiReportEventResponse" + - $ref: "#/components/schemas/DivisiReportProgressResponse" + "400": + description: Bad request (e.g., invalid parameters) + "404": + description: Division not found + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error # Document /document: @@ -1391,7 +2019,7 @@ paths: tags: - Document summary: Get documents - description: Retrieves a paginated list of documents and folders filtered by village and division + description: Retrieves a paginated list of documents and folders filtered by village, division, path, and active status parameters: - name: desa in: query @@ -1404,21 +2032,25 @@ paths: description: Filter by division ID schema: type: string + nullable: true - name: path in: query description: Filter by document path schema: type: string + nullable: true - name: active in: query description: Filter by active status schema: type: boolean + nullable: true - name: search in: query description: Search term for document name schema: type: string + nullable: true - name: page in: query description: Page number for pagination @@ -1426,7 +2058,7 @@ paths: type: integer minimum: 1 default: 1 - - name: get + - name: perPage in: query description: Number of items per page schema: @@ -1434,12 +2066,18 @@ paths: minimum: 1 default: 10 responses: - '200': + "200": description: List of documents and folders content: application/json: schema: - $ref: '#/components/schemas/DocumentListResponse' + $ref: "#/components/schemas/DocumentListResponse" + "400": + description: Bad request (e.g., invalid parameters) + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error # Group /group: @@ -1447,7 +2085,7 @@ paths: tags: - Group summary: Get groups - description: Retrieves a paginated list of groups filtered by village + description: Retrieves a paginated list of groups filtered by village, search term, and active status parameters: - name: desa in: query @@ -1455,16 +2093,18 @@ paths: description: Village ID schema: type: string - - name: active - in: query - description: Filter by active status - schema: - type: boolean - name: search in: query description: Search term for group name schema: type: string + nullable: true + - name: active + in: query + description: Filter by active status + schema: + type: boolean + nullable: true - name: page in: query description: Page number for pagination @@ -1472,7 +2112,7 @@ paths: type: integer minimum: 1 default: 1 - - name: get + - name: perPage in: query description: Number of items per page schema: @@ -1480,12 +2120,18 @@ paths: minimum: 1 default: 10 responses: - '200': + "200": description: List of groups content: application/json: schema: - $ref: '#/components/schemas/GroupListResponse' + $ref: "#/components/schemas/GroupListResponse" + "400": + description: Bad request (e.g., invalid parameters) + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error # Position /position: @@ -1493,7 +2139,7 @@ paths: tags: - Position summary: Get positions - description: Retrieves a paginated list of positions filtered by village and group + description: Retrieves a paginated list of positions filtered by village, group, search term, and active status parameters: - name: desa in: query @@ -1506,16 +2152,19 @@ paths: description: Filter by group ID schema: type: string + nullable: true - name: search in: query description: Search term for position name schema: type: string + nullable: true - name: active in: query description: Filter by active status schema: type: boolean + nullable: true - name: page in: query description: Page number for pagination @@ -1523,7 +2172,7 @@ paths: type: integer minimum: 1 default: 1 - - name: get + - name: perPage in: query description: Number of items per page schema: @@ -1531,12 +2180,18 @@ paths: minimum: 1 default: 10 responses: - '200': + "200": description: List of positions content: application/json: schema: - $ref: '#/components/schemas/PositionListResponse' + $ref: "#/components/schemas/PositionListResponse" + "400": + description: Bad request (e.g., invalid parameters) + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error # Project /project: @@ -1544,7 +2199,7 @@ paths: tags: - Project summary: Get projects - description: Retrieves a paginated list of projects filtered by village, group, and status + description: Retrieves a paginated list of projects filtered by village, group, status, and search term parameters: - name: desa in: query @@ -1552,22 +2207,25 @@ paths: description: Village ID schema: type: string + - name: group + in: query + description: Filter by group ID + schema: + type: string + nullable: true - name: status in: query description: Filter by project status schema: type: string enum: [segera, dikerjakan, selesai, batal] - - name: group - in: query - description: Filter by group ID - schema: - type: string + nullable: true - name: search in: query description: Search term for project title or description schema: type: string + nullable: true - name: page in: query description: Page number for pagination @@ -1575,7 +2233,7 @@ paths: type: integer minimum: 1 default: 1 - - name: get + - name: perPage in: query description: Number of items per page schema: @@ -1583,19 +2241,25 @@ paths: minimum: 1 default: 10 responses: - '200': + "200": description: List of projects content: application/json: schema: - $ref: '#/components/schemas/BaseResponseProjectList' + $ref: "#/components/schemas/BaseResponseProjectList" + "400": + description: Bad request (e.g., invalid parameters) + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error /project/{id}: get: tags: - Project summary: Get project details - description: Retrieves details of a specific project based on category + description: Retrieves details of a specific project based on category (data, task, file, member, or link) parameters: - name: id in: path @@ -1611,17 +2275,25 @@ paths: type: string enum: [data, task, file, member, link] responses: - '200': + "200": description: Project details content: application/json: schema: oneOf: - - $ref: '#/components/schemas/BaseResponseProjectDetail' - - $ref: '#/components/schemas/BaseResponseTaskList' - - $ref: '#/components/schemas/BaseResponseMemberList' - - $ref: '#/components/schemas/BaseResponseFileList' - - $ref: '#/components/schemas/BaseResponseLinkList' + - $ref: "#/components/schemas/BaseResponseProjectDetail" + - $ref: "#/components/schemas/BaseResponseTaskList" + - $ref: "#/components/schemas/BaseResponseFileList" + - $ref: "#/components/schemas/BaseResponseMemberList" + - $ref: "#/components/schemas/BaseResponseLinkList" + "400": + description: Bad request (e.g., invalid parameters) + "404": + description: Project not found + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error # Task /task: @@ -1629,7 +2301,7 @@ paths: tags: - Task summary: Get tasks - description: Retrieves a paginated list of tasks filtered by village, division, and status + description: Retrieves a paginated list of tasks filtered by village, division, status, and search term parameters: - name: desa in: query @@ -1642,17 +2314,20 @@ paths: description: Filter by division ID schema: type: string + nullable: true - name: status in: query description: Filter by task status schema: type: string enum: [segera, dikerjakan, selesai, batal] + nullable: true - name: search in: query description: Search term for task title or description schema: type: string + nullable: true - name: page in: query description: Page number for pagination @@ -1660,7 +2335,7 @@ paths: type: integer minimum: 1 default: 1 - - name: get + - name: perPage in: query description: Number of items per page schema: @@ -1668,19 +2343,25 @@ paths: minimum: 1 default: 10 responses: - '200': + "200": description: List of tasks content: application/json: schema: - $ref: '#/components/schemas/BaseResponseTaskList' + $ref: "#/components/schemas/BaseResponseTaskList" + "400": + description: Bad request (e.g., invalid parameters) + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error /task/{id}: get: tags: - Task summary: Get task details - description: Retrieves details of a specific task based on category + description: Retrieves details of a specific task based on category (data, task, file, member, or link) parameters: - name: id in: path @@ -1696,17 +2377,112 @@ paths: type: string enum: [data, task, file, member, link] responses: - '200': + "200": description: Task details content: application/json: schema: oneOf: - - $ref: '#/components/schemas/BaseResponseTaskDetail' - - $ref: '#/components/schemas/BaseResponseSubTaskList' - - $ref: '#/components/schemas/BaseResponseMemberList' - - $ref: '#/components/schemas/BaseResponseFileList' - - $ref: '#/components/schemas/BaseResponseLinkList' + - $ref: "#/components/schemas/BaseResponseTaskDetail" + - $ref: "#/components/schemas/BaseResponseSubTaskList" + - $ref: "#/components/schemas/BaseResponseFileList" + - $ref: "#/components/schemas/BaseResponseMemberList" + - $ref: "#/components/schemas/BaseResponseLinkList" + "400": + description: Bad request (e.g., invalid parameters) + "404": + description: Task not found + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error + + # User + /user: + get: + tags: + - User + summary: Get users + description: Retrieves a paginated list of users filtered by village, group, search term, and active status + parameters: + - name: desa + in: query + required: true + description: Village ID + schema: + type: string + - name: group + in: query + description: Filter by group ID + schema: + type: string + nullable: true + - name: search + in: query + description: Search term for user name + schema: + type: string + nullable: true + - name: active + in: query + description: Filter by active status + schema: + type: boolean + nullable: true + - name: page + in: query + description: Page number for pagination + schema: + type: integer + minimum: 1 + default: 1 + - name: perPage + in: query + description: Number of items per page + schema: + type: integer + minimum: 1 + default: 10 + responses: + "200": + description: List of users + content: + application/json: + schema: + $ref: "#/components/schemas/UserListResponse" + "400": + description: Bad request (e.g., invalid parameters) + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error + + /user/{id}: + get: + tags: + - User + summary: Get user details + description: Retrieves details of a specific user + parameters: + - name: id + in: path + required: true + description: User ID + schema: + type: string + responses: + "200": + description: User details + content: + application/json: + schema: + $ref: "#/components/schemas/UserDetailResponse" + "404": + description: User not found + "401": + description: Unauthorized (invalid or missing token) + "500": + description: Internal server error security: - bearerAuth: [] @@ -1719,13 +2495,13 @@ tags: - name: Calendar description: Operations related to calendar events - name: Discussion - description: Operations related to division discussions + description: Operations related to division-specific discussions - name: DiscussionGeneral description: Operations related to general discussions - name: Division - description: Operations related to division management + description: Operations related to division management and reports - name: Document - description: Operations related to document management + description: Operations related to document and folder management - name: Group description: Operations related to group management - name: Position @@ -1733,4 +2509,6 @@ tags: - name: Project description: Operations related to project management - name: Task - description: Operations related to task management \ No newline at end of file + description: Operations related to task management + - name: User + description: Operations related to user managementØ From ae3131ecf8bc9eab4f2f82c13e1c12f2cd73300b Mon Sep 17 00:00:00 2001 From: amal Date: Mon, 22 Sep 2025 15:43:11 +0800 Subject: [PATCH 4/5] upd : privacy --- public/util/privacy-policy.html | 253 ++++++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 public/util/privacy-policy.html diff --git a/public/util/privacy-policy.html b/public/util/privacy-policy.html new file mode 100644 index 0000000..be39202 --- /dev/null +++ b/public/util/privacy-policy.html @@ -0,0 +1,253 @@ + + + + + + Privacy Policy — Bali Interaktif Perkasa + + + +
+
+

Privacy Policy

+
Last updated September 01, 2025
+
+
+ +
+

+ This Privacy Notice for Bali Interaktif Perkasa ("we," "us," or "our") describes how and why we might access, collect, store, use, and/or share ("process") your personal information when you use our services ("Services"), including when you: +

+
    +
  • Download and use our mobile application (Darmasaba mobile), or any other application of ours that links to this Privacy Notice.
  • +
  • Use Administration. This mobile application is specifically designed to help village officials manage data and monitor the progress of internal activities. It offers features such as data management by division, general activity monitoring, discussion forums, official announcements, and document folder management.
  • +
  • Engage with us in other related ways, including any sales, marketing, or events.
  • +
+

Questions or concerns? Reading this Privacy Notice will help you understand your privacy rights and choices. We are responsible for making decisions about how your personal information is processed. If you do not agree with our policies and practices, please do not use our Services. If you still have any questions or concerns, please contact us at bip.baliinteraktifperkasa@gmail.com.

+ +
+

Summary of Key Points

+

This summary provides key points from our Privacy Notice, but you can find out more details about any of these topics by using the table of contents below.

+
    +
  • What personal information do we process? We may process personal information depending on how you interact with us and the Services, the choices you make, and the products and features you use.
  • +
  • Do we process any sensitive personal information? We do not process sensitive personal information.
  • +
  • Do we collect any information from third parties? We do not collect any information from third parties.
  • +
  • How do we process your information? To provide, improve, and administer our Services; communicate with you; for security and fraud prevention; and to comply with law. We may also process your information for other purposes with your consent.
  • +
  • In what situations and with which parties do we share personal information? We may share information in specific situations and with specific third parties.
  • +
  • How do we keep your information safe? We have organizational and technical measures to protect your personal information; however, no method is 100% secure.
  • +
  • What are your rights? Depending on your location, you may have certain rights regarding your personal information.
  • +
  • How do you exercise your rights? The easiest way is by emailing bip.baliinteraktifperkasa@gmail.com.
  • +
+

Want to learn more about what we do with information we collect? Review the Privacy Notice in full below.

+
+ + + +
+

1. WHAT INFORMATION DO WE COLLECT?

+

Personal information you disclose to us

+

In Short: We collect personal information that you provide to us.

+

We collect personal information that you voluntarily provide to us when you express an interest in obtaining information about us or our products and Services, when you participate in activities on the Services, or otherwise when you contact us.

+

Personal Information Provided by You

+

The personal information we collect depends on the context of your interactions with us and the Services, the choices you make, and the products and features you use. This may include:

+
    +
  • names
  • +
  • phone numbers
  • +
  • email addresses
  • +
+

Sensitive Information

+

We do not process sensitive information.

+

Application Data

+

If you use our application(s), we may also collect the following information if you choose to provide us with access or permission:

+
    +
  • Mobile Device Access. We may request access or permission to certain features from your mobile device, including your mobile device's camera, and other features. You may change access or permissions in your device's settings.
  • +
  • Push Notifications. We may request to send you push notifications regarding your account or certain features of the application(s). You may opt out in your device's settings.
  • +
+

This information is primarily needed to maintain the security and operation of our application(s), for troubleshooting, and for internal analytics and reporting purposes.

+

All personal information that you provide to us must be true, complete, and accurate, and you must notify us of any changes to such personal information.

+
+ +
+

2. HOW DO WE PROCESS YOUR INFORMATION?

+

In Short: We process your information to provide, improve, and administer our Services, communicate with you, for security and fraud prevention, and to comply with law. We may also process your information for other purposes with your consent.

+

We process your personal information for a variety of reasons, depending on how you interact with our Services, including:

+
    +
  • To deliver and facilitate delivery of services to the user. We may process your information to provide you with the requested service.
  • +
  • To enable user-to-user communications. We may process your information if you choose to use offerings that allow communication with another user.
  • +
  • To evaluate and improve our Services, products, marketing, and your experience. We may process your information to identify usage trends, determine the effectiveness of our promotional campaigns, and to evaluate and improve our Services, products, marketing, and your experience.
  • +
+
+ +
+

3. WHEN AND WITH WHOM DO WE SHARE YOUR PERSONAL INFORMATION?

+

In Short: We may share information in specific situations described in this section and/or with the following third parties.

+

Vendors, Consultants, and Other Third-Party Service Providers. We may share your data with third-party vendors, service providers, contractors, or agents ("third parties") who perform services for us or on our behalf and require access to such information to do that work.

+

The third parties we may share personal information with include:

+
    +
  • Functionality and Infrastructure Optimization: Firebase Realtime Database and Cloud Functions for Firebase
  • +
  • Functionality & Infrastructure Optimization: Expo / EAS Services
  • +
+

We may also need to share your personal information in the following situations:

+
    +
  • Business Transfers. We may share or transfer your information in connection with, or during negotiations of, any merger, sale of company assets, financing, or acquisition of all or a portion of our business to another company.
  • +
  • Other Users. When you share personal information (for example, by posting comments or other content to the Services) or interact with public areas of the Services, such information may be viewed by all users and may be publicly available outside the Services in perpetuity. Other users may view descriptions of your activity, communicate with you, and view your profile.
  • +
+
+ +
+

4. HOW LONG DO WE KEEP YOUR INFORMATION?

+

In Short: We keep your information for as long as necessary to fulfill the purposes outlined in this Privacy Notice unless otherwise required by law.

+

We will only keep your personal information as long as necessary for the purposes set out in this Privacy Notice, unless a longer retention period is required or permitted by law (such as tax, accounting, or other legal requirements).

+

When we have no ongoing legitimate business need to process your personal information, we will delete or anonymize such information. If deletion is not possible (for example, if your personal information is stored in backup archives), we will securely store your personal information and isolate it from any further processing until deletion is possible.

+
+ +
+

5. HOW DO WE KEEP YOUR INFORMATION SAFE?

+

In Short: We aim to protect your personal information through a system of organizational and technical security measures.

+

We have implemented appropriate and reasonable technical and organizational security measures designed to protect the security of any personal information we process. However, despite our safeguards and efforts to secure your information, no electronic transmission over the Internet or information storage technology can be guaranteed to be 100% secure. Transmission of personal information to and from our Services is at your own risk. You should only access the Services within a secure environment.

+
+ +
+

6. DO WE COLLECT INFORMATION FROM MINORS?

+

In Short: We do not knowingly collect data from or market to children under 18 years of age.

+

We do not knowingly collect, solicit data from, or market to children under 18 years of age, nor do we knowingly sell such personal information. By using the Services, you represent that you are at least 18 or that you are the parent or guardian of such a minor and consent to such minor dependent’s use of the Services. If we learn that personal information from users less than 18 years of age has been collected, we will deactivate the account and take reasonable measures to promptly delete such data from our records. If you become aware of any data we may have collected from children under age 18, please contact us at bip.baliinteraktifperkasa@gmail.com.

+
+ +
+

7. WHAT ARE YOUR PRIVACY RIGHTS?

+

In Short: You may review, change, or terminate your account at any time, depending on your country, province, or state of residence.

+

Withdrawing your consent: If we are relying on your consent to process your personal information (which may be express and/or implied consent depending on the applicable law), you have the right to withdraw your consent at any time. You can do so by contacting us using the details in the section "HOW CAN YOU CONTACT US ABOUT THIS NOTICE?" below. This will not affect the lawfulness of the processing before its withdrawal, nor will it affect processing conducted in reliance on lawful processing grounds other than consent where permitted by law.

+

If you have questions or comments about your privacy rights, email us at bip.baliinteraktifperkasa@gmail.com.

+
+ +
+

8. CONTROLS FOR DO-NOT-TRACK FEATURES

+

Most web browsers and some mobile operating systems and applications include a Do-Not-Track ("DNT") setting you can activate to signal your privacy preference not to have data about your online browsing activities monitored and collected. At this stage, no uniform technology standard for recognizing and implementing DNT signals has been finalized. As such, we do not currently respond to DNT browser signals or any other mechanism that automatically communicates your choice not to be tracked online. If a standard for online tracking is adopted that we must follow in the future, we will inform you about that practice in a revised version of this Privacy Notice.

+
+ +
+

9. DO WE MAKE UPDATES TO THIS NOTICE?

+

In Short: Yes, we will update this notice as necessary to stay compliant with relevant laws.

+

We may update this Privacy Notice from time to time. The updated version will be indicated by an updated "Revised" date at the top of this Privacy Notice. If we make material changes, we may notify you by prominently posting a notice of such changes or by directly sending you a notification. We encourage you to review this Privacy Notice frequently to stay informed of how we are protecting your information.

+
+ +
+

10. HOW CAN YOU CONTACT US ABOUT THIS NOTICE?

+
+Bali Interaktif Perkasa +Park23 Creative Hub, Bali Interaktif Perkasa - Private Office +Jl. Kediri 3rd Floor, Number 01 - 02, Tuban +Badung, Bali 80361 +Indonesia +
+

Email: bip.baliinteraktifperkasa@gmail.com

+
+ +
+

11. HOW CAN YOU REVIEW, UPDATE, OR DELETE THE DATA WE COLLECT FROM YOU?

+

You have the right to request access to the personal information we collect from you, details about how we have processed it, correct inaccuracies, or delete your personal information. You may also have the right to withdraw your consent to our processing of your personal information. These rights may be limited in some circumstances by applicable law.

+

To make a request, please contact us at bip.baliinteraktifperkasa@gmail.com.

+
+ +
+

© Bali Interaktif Perkasa. All rights reserved.

+
+
+ + + + From 4064497794397537b421d612121c9c289d5573df Mon Sep 17 00:00:00 2001 From: amal Date: Mon, 22 Sep 2025 15:45:17 +0800 Subject: [PATCH 5/5] upd: api version --- src/app/api/version-app/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/api/version-app/route.ts b/src/app/api/version-app/route.ts index 19f756a..1d58873 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.9.0", tahap: "beta", update: "-api mobile; -login tanpa otp (mobile app); -tambah laporan pada project dan tugas divisi; -tambah upload link pada project dan tugas divisi; -tambah detail tanggal dan jam pada project dan tugas divisi; -api jenna ai" }, { status: 200 }); + return NextResponse.json({ success: true, version: "2.0.0", tahap: "beta", update: "-api mobile; -login tanpa otp (mobile app); -tambah laporan pada project dan tugas divisi; -tambah upload link pada project dan tugas divisi; -tambah detail tanggal dan jam pada project dan tugas divisi; -api jenna ai; -privacy policy" }, { 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 });