From 863b8bec5409d2db1121c9b64b07a0d47fb2db65 Mon Sep 17 00:00:00 2001 From: amaliadwiy Date: Mon, 11 May 2026 15:19:29 +0800 Subject: [PATCH] feat: tambah endpoint kalender umum village - GET /mobile/village-calendar: ambil acara divisi dan kegiatan se-village per tanggal - GET /mobile/village-calendar/indicator: dot indikator per bulan, task di-expand per hari dalam range dateStart-dateEnd --- .../village-calendar/indicator/route.ts | 83 ++++++++++++ src/app/api/mobile/village-calendar/route.ts | 122 ++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 src/app/api/mobile/village-calendar/indicator/route.ts create mode 100644 src/app/api/mobile/village-calendar/route.ts diff --git a/src/app/api/mobile/village-calendar/indicator/route.ts b/src/app/api/mobile/village-calendar/indicator/route.ts new file mode 100644 index 0000000..d196ace --- /dev/null +++ b/src/app/api/mobile/village-calendar/indicator/route.ts @@ -0,0 +1,83 @@ +import { prisma } from "@/module/_global"; +import { funGetUserById } from "@/module/auth"; +import _ from "lodash"; +import moment from "moment"; +import { NextResponse } from "next/server"; + +// GET indicator dot per bulan (tanggal mana saja yang ada event) +export async function GET(request: Request) { + try { + const { searchParams } = new URL(request.url); + const user = searchParams.get("user"); + const date = searchParams.get("date"); + + const userMobile = await funGetUserById({ id: String(user) }); + if (!userMobile.id || userMobile.id === "null") { + return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 200 }); + } + + const idVillage = userMobile.idVillage; + const awalDate = moment(date).format("YYYY-MM") + "-01"; + const akhirDate = moment(awalDate).add(1, "M").format("YYYY-MM-DD"); + + // Tanggal dengan event kalender divisi + const calendarDates = await prisma.divisionCalendarReminder.findMany({ + where: { + isActive: true, + dateStart: { + gte: new Date(awalDate), + lt: new Date(akhirDate), + }, + Division: { + idVillage, + isActive: true, + }, + DivisionCalendar: { + isActive: true, + }, + }, + select: { dateStart: true }, + }); + + // Tanggal dengan task project se-village (ambil semua task yang overlap dengan bulan ini) + const taskDates = await prisma.projectTask.findMany({ + where: { + isActive: true, + dateStart: { lt: new Date(akhirDate) }, + dateEnd: { gte: new Date(awalDate) }, + Project: { + isActive: true, + idVillage: String(idVillage), + }, + }, + select: { dateStart: true, dateEnd: true }, + }); + + // Expand setiap task menjadi array tanggal per hari dalam range bulan + const taskExpandedDates: string[] = []; + for (const task of taskDates) { + let cur = moment(task.dateStart); + const end = moment(task.dateEnd); + const monthStart = moment(awalDate); + const monthEnd = moment(akhirDate); + while (cur.isSameOrBefore(end)) { + if (cur.isSameOrAfter(monthStart) && cur.isBefore(monthEnd)) { + taskExpandedDates.push(cur.format("YYYY-MM-DD")); + } + cur.add(1, 'day'); + } + } + + const calendarResult = _.uniq(calendarDates.map((v) => moment(v.dateStart).format("YYYY-MM-DD"))).sort(); + const taskResult = _.uniq(taskExpandedDates).sort(); + + return NextResponse.json({ + success: true, + message: "Berhasil mendapatkan indicator kalender", + data: { calendar: calendarResult, task: taskResult } + }, { status: 200 }); + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan indicator kalender (error: 500)" }, { status: 500 }); + } +} diff --git a/src/app/api/mobile/village-calendar/route.ts b/src/app/api/mobile/village-calendar/route.ts new file mode 100644 index 0000000..cacf321 --- /dev/null +++ b/src/app/api/mobile/village-calendar/route.ts @@ -0,0 +1,122 @@ +import { prisma } from "@/module/_global"; +import { funGetUserById } from "@/module/auth"; +import _ from "lodash"; +import moment from "moment"; +import "moment/locale/id"; +import { NextResponse } from "next/server"; + +// GET events per tanggal (DivisionCalendarReminder + ProjectTask) +export async function GET(request: Request) { + try { + const { searchParams } = new URL(request.url); + const user = searchParams.get("user"); + const date = searchParams.get("date"); + + const userMobile = await funGetUserById({ id: String(user) }); + if (!userMobile.id || userMobile.id === "null") { + return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 200 }); + } + + const targetDate = new Date(String(date)); + const idVillage = userMobile.idVillage; + + // Ambil semua event kalender divisi se-village pada tanggal tersebut + const calendarData = await prisma.divisionCalendarReminder.findMany({ + where: { + isActive: true, + dateStart: targetDate, + Division: { + idVillage, + isActive: true, + }, + DivisionCalendar: { + isActive: true, + }, + }, + select: { + id: true, + idCalendar: true, + dateStart: true, + dateEnd: true, + timeStart: true, + timeEnd: true, + Division: { + select: { + id: true, + name: true, + }, + }, + DivisionCalendar: { + select: { + title: true, + desc: true, + }, + }, + }, + orderBy: [{ timeStart: "asc" }, { timeEnd: "asc" }], + }); + + // Ambil semua task project se-village yang mencakup tanggal tersebut (dateStart <= target <= dateEnd) + const taskData = await prisma.projectTask.findMany({ + where: { + isActive: true, + dateStart: { lte: targetDate }, + dateEnd: { gte: targetDate }, + Project: { + isActive: true, + idVillage: String(idVillage), + }, + }, + select: { + id: true, + title: true, + desc: true, + dateStart: true, + dateEnd: true, + Project: { + select: { + id: true, + title: true, + }, + }, + }, + orderBy: { dateStart: "asc" }, + }); + + const calendarResult = calendarData.map((v) => ({ + id: v.id, + type: "calendar", + title: v.DivisionCalendar.title, + desc: v.DivisionCalendar.desc, + dateStart: moment(v.dateStart).format("YYYY-MM-DD"), + dateEnd: v.dateEnd ? moment(v.dateEnd).format("YYYY-MM-DD") : null, + timeStart: moment.utc(v.timeStart).format("HH:mm"), + timeEnd: moment.utc(v.timeEnd).format("HH:mm"), + divisionName: v.Division.name, + projectName: null, + idDivision: v.Division.id, + idRef: v.Division.id, + })); + + const taskResult = taskData.map((v) => ({ + id: v.id, + type: "task", + title: v.title, + desc: v.desc ?? "", + dateStart: moment(v.dateStart).format("YYYY-MM-DD"), + dateEnd: moment(v.dateEnd).format("YYYY-MM-DD"), + timeStart: null, + timeEnd: null, + divisionName: null, + projectName: v.Project.title, + idRef: v.Project.id, + })); + + const data = [...calendarResult, ...taskResult]; + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan kalender umum", data }, { status: 200 }); + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan kalender umum (error: 500)" }, { status: 500 }); + } +}