From 20d05c1cc7c2a4f4e452a9c91bc10461bfb7c72e Mon Sep 17 00:00:00 2001 From: bagasbanuna Date: Thu, 22 Jan 2026 11:58:37 +0800 Subject: [PATCH 1/5] component sender wa to all device ( apps & web ) --- src/app/api/auth/login/route.ts | 31 ++++++++++---------------- src/app/api/auth/mobile-login/route.ts | 30 +++++++++---------------- src/app/api/auth/resend/route.ts | 30 ++++++++++--------------- src/lib/code-otp-sender.ts | 28 +++++++++++++++++++++++ 4 files changed, 63 insertions(+), 56 deletions(-) create mode 100644 src/lib/code-otp-sender.ts diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts index ae5526f6..a53bc710 100644 --- a/src/app/api/auth/login/route.ts +++ b/src/app/api/auth/login/route.ts @@ -2,12 +2,13 @@ import { prisma } from "@/lib"; import { randomOTP } from "@/app_modules/auth/fun/rondom_otp"; import backendLogger from "@/util/backendLogger"; import { NextResponse } from "next/server"; +import { sendCodeOtp } from "@/lib/code-otp-sender"; export async function POST(req: Request) { if (req.method !== "POST") { return NextResponse.json( { success: false, message: "Method Not Allowed" }, - { status: 405 } + { status: 405 }, ); } @@ -26,29 +27,21 @@ export async function POST(req: Request) { if (!createOtpId) return NextResponse.json( { success: false, message: "Gagal mengirim kode OTP" }, - { status: 400 } + { status: 400 }, ); - const msg = `HIPMI%20-%20Kode%20ini%20bersifat%20RAHASIA%20dan%20JANGAN%20DI%20BAGIKAN%20KEPADA%20SIAPAPUN%2C%20termasuk%20anggota%20ataupun%20pengurus%20HIPMI%20lainnya.%5Cn%5Cn%3E%3E%20Kode%20OTP%20anda%3A%20${codeOtp}.`; - // const encodedMsg = encodeURIComponent(msg); + const resSendCode = await sendCodeOtp({ + nomor, + codeOtp: codeOtp.toString(), + }); - const res = await fetch( - `https://cld-dkr-prod-wajs-server.wibudev.com/api/wa/code?nom=${nomor}&text=${msg}`, - { - cache: "no-cache", - headers: { - Authorization: `Bearer ${process.env.WA_SERVER_TOKEN}`, - }, - } - ); - - if (res.status !== 200) + if (resSendCode.status !== 200) return NextResponse.json( { success: false, message: "Nomor Whatsapp Tidak Aktif" }, - { status: 400 } + { status: 400 }, ); - const sendWa = await res.text(); + const sendWa = await resSendCode.text(); console.log("WA Response:", sendWa); return NextResponse.json( @@ -57,7 +50,7 @@ export async function POST(req: Request) { message: "Kode verifikasi terkirim", kodeId: createOtpId.id, }, - { status: 200 } + { status: 200 }, ); } catch (error) { backendLogger.log("Error Login", error); @@ -67,7 +60,7 @@ export async function POST(req: Request) { message: "Terjadi masalah saat login", reason: error as Error, }, - { status: 500 } + { status: 500 }, ); } } diff --git a/src/app/api/auth/mobile-login/route.ts b/src/app/api/auth/mobile-login/route.ts index 7bcf5bd1..7b1c4847 100644 --- a/src/app/api/auth/mobile-login/route.ts +++ b/src/app/api/auth/mobile-login/route.ts @@ -1,6 +1,7 @@ import { prisma } from "@/lib"; import { randomOTP } from "@/app_modules/auth/fun/rondom_otp"; import { NextResponse } from "next/server"; +import { sendCodeOtp } from "@/lib/code-otp-sender"; export async function POST(req: Request) { try { @@ -31,30 +32,21 @@ export async function POST(req: Request) { if (!createOtpId) return NextResponse.json( { success: false, message: "Gagal mengirim kode OTP" }, - { status: 400 } + { status: 400 }, ); - // const msg = `HIPMI - Kode ini bersifat RAHASIA dan JANGAN DI BAGIKAN KEPAADA SIAPAPUN, termasuk anggota ataupun pengurus HIPMI lainnya.\n\n\n> Kode OTP anda: ${codeOtp}.`; - // const encodedMsg = encodeURIComponent(msg); - const msg = `HIPMI%20-%20Kode%20ini%20bersifat%20RAHASIA%20dan%20JANGAN%20DI%20BAGIKAN%20KEPADA%20SIAPAPUN%2C%20termasuk%20anggota%20ataupun%20pengurus%20HIPMI%20lainnya.%20Kode%20OTP%20anda%3A%20${codeOtp}.`; + const resSendCode = await sendCodeOtp({ + nomor, + codeOtp: codeOtp.toString(), + }); - const res = await fetch( - `https://cld-dkr-prod-wajs-server.wibudev.com/api/wa/code?nom=${nomor}&text=${msg}`, - { - cache: "no-cache", - headers: { - Authorization: `Bearer ${process.env.WA_SERVER_TOKEN}`, - }, - } - ); - - if (res.status !== 200) + if (resSendCode.status !== 200) return NextResponse.json( { success: false, message: "Nomor Whatsapp Tidak Aktif" }, - { status: 400 } + { status: 400 }, ); - const sendWa = await res.text(); + const sendWa = await resSendCode.text(); console.log("WA Response:", sendWa); return NextResponse.json( @@ -64,7 +56,7 @@ export async function POST(req: Request) { kodeId: createOtpId.id, isAcceptTerms: user.termsOfServiceAccepted, }, - { status: 200 } + { status: 200 }, ); } catch (error) { return NextResponse.json( @@ -73,7 +65,7 @@ export async function POST(req: Request) { message: "Terjadi masalah saat login", reason: error as Error, }, - { status: 500 } + { status: 500 }, ); } } diff --git a/src/app/api/auth/resend/route.ts b/src/app/api/auth/resend/route.ts index a6378462..eb64a8f5 100644 --- a/src/app/api/auth/resend/route.ts +++ b/src/app/api/auth/resend/route.ts @@ -2,12 +2,13 @@ import { prisma } from "@/lib"; import { randomOTP } from "@/app_modules/auth/fun/rondom_otp"; import backendLogger from "@/util/backendLogger"; import { NextResponse } from "next/server"; +import { sendCodeOtp } from "@/lib/code-otp-sender"; export async function POST(req: Request) { if (req.method !== "POST") { return NextResponse.json( { success: false, message: "Method Not Allowed" }, - { status: 405 } + { status: 405 }, ); } @@ -16,25 +17,18 @@ export async function POST(req: Request) { const body = await req.json(); const { nomor } = body; - const msg = `HIPMI%20-%20Kode%20ini%20bersifat%20RAHASIA%20dan%20JANGAN%20DI%20BAGIKAN%20KEPADA%20SIAPAPUN%2C%20termasuk%20anggota%20ataupun%20pengurus%20HIPMI%20lainnya.%5Cn%5Cn%3E%3E%20Kode%20OTP%20anda%3A%20${codeOtp}.`; + const resSendCode = await sendCodeOtp({ + nomor, + codeOtp: codeOtp.toString(), + }); - const res = await fetch( - `https://cld-dkr-prod-wajs-server.wibudev.com/api/wa/code?nom=${nomor}&text=${msg}`, - { - cache: "no-cache", - headers: { - Authorization: `Bearer ${process.env.WA_SERVER_TOKEN}`, - }, - } - ); - - if (res.status !== 200) + if (resSendCode.status !== 200) return NextResponse.json( { success: false, message: "Nomor Whatsapp Tidak Aktif" }, - { status: 400 } + { status: 400 }, ); - const sendWa = await res.text(); + const sendWa = await resSendCode.text(); console.log("WA Response:", sendWa); const createOtpId = await prisma.kodeOtp.create({ @@ -50,7 +44,7 @@ export async function POST(req: Request) { success: false, message: "Gagal Membuat Kode OTP", }, - { status: 400 } + { status: 400 }, ); return NextResponse.json( @@ -59,7 +53,7 @@ export async function POST(req: Request) { message: "Kode Verifikasi Dikirim", kodeId: createOtpId.id, }, - { status: 200 } + { status: 200 }, ); } catch (error) { backendLogger.error(" Error Resend OTP", error); @@ -68,7 +62,7 @@ export async function POST(req: Request) { success: false, message: "Server Whatsapp Error !!", }, - { status: 500 } + { status: 500 }, ); } finally { await prisma.$disconnect(); diff --git a/src/lib/code-otp-sender.ts b/src/lib/code-otp-sender.ts new file mode 100644 index 00000000..a5cd885d --- /dev/null +++ b/src/lib/code-otp-sender.ts @@ -0,0 +1,28 @@ +const sendCodeOtp = async ({ + nomor, + codeOtp, +}: { + nomor: string; + codeOtp: string; +}) => { + // const msg = `HIPMI%20-%20Kode%20ini%20bersifat%20RAHASIA%20dan%20JANGAN%20DI%20BAGIKAN%20KEPADA%20SIAPAPUN%2C%20termasuk%20anggota%20ataupun%20pengurus%20HIPMI%20lainnya.%20Kode%20OTP%20anda%3A%20${codeOtp}.`; + // const res = await fetch( + // `https://cld-dkr-prod-wajs-server.wibudev.com/api/wa/code?nom=${nomor}&text=${msg}`, + // { + // cache: "no-cache", + // headers: { + // Authorization: `Bearer ${process.env.WA_SERVER_TOKEN}`, + // }, + // } + // ); + const res = await fetch( + `https://wa.wibudev.com/code?nom=${nomor}&text=HIPMI - Kode ini bersifat RAHASIA dan JANGAN DI BAGIKAN KEPADA SIAPAPUN, termasuk anggota ataupun pengurus HIPMI lainnya. + \n + >> Kode OTP anda: ${codeOtp}. + `, + ); + + return res; +}; + +export { sendCodeOtp }; From fc23e01275fdc3a3c0b9d4ce091aaeef6e6a5222 Mon Sep 17 00:00:00 2001 From: bagasbanuna Date: Thu, 22 Jan 2026 17:53:35 +0800 Subject: [PATCH 2/5] Notification investasi mobile done ### No Issue --- .../admin/investment/[id]/invoice/route.ts | 6 +-- .../api/mobile/admin/investment/[id]/route.ts | 2 +- .../mobile/investment/[id]/document/route.ts | 36 +++++++++------ .../api/mobile/investment/[id]/news/route.ts | 33 ++++++++++++- src/lib/code-otp-sender.ts | 28 +++++------ .../notification-add-news-investment.ts | 46 +++++++++++++++++++ src/lib/mobile/route-page-mobile.ts | 4 +- 7 files changed, 120 insertions(+), 35 deletions(-) create mode 100644 src/lib/mobile/notification/notification-add-news-investment.ts diff --git a/src/app/api/mobile/admin/investment/[id]/invoice/route.ts b/src/app/api/mobile/admin/investment/[id]/invoice/route.ts index 3018a513..5b13c0ab 100644 --- a/src/app/api/mobile/admin/investment/[id]/invoice/route.ts +++ b/src/app/api/mobile/admin/investment/[id]/invoice/route.ts @@ -90,7 +90,7 @@ async function PUT(req: Request, { params }: { params: { id: string } }) { body: `Maaf transaksi kamu telah ditolak ! ${updt?.Investasi?.title}` as NotificationMobileBodyType, type: "announcement", kategoriApp: "INVESTASI", - deepLink: routeUserMobile.investasiTransaction, + deepLink: routeUserMobile.investmentTransaction, }, }); @@ -182,7 +182,7 @@ async function PUT(req: Request, { params }: { params: { id: string } }) { body: `Selamat anda menjadi investor pada investasi ${findInvestasi?.title}` as NotificationMobileBodyType, type: "announcement", kategoriApp: "INVESTASI", - deepLink: routeUserMobile.investasiTransaction, + deepLink: routeUserMobile.investmentTransaction, }, }); @@ -195,7 +195,7 @@ async function PUT(req: Request, { params }: { params: { id: string } }) { body: `Cek daftar investor pada ${findInvestasi?.title}` as NotificationMobileBodyType, type: "announcement", kategoriApp: "INVESTASI", - deepLink: routeUserMobile.investasiDetailPublish({ + deepLink: routeUserMobile.investmentDetailPublish({ id: findInvestasi?.id as string, }), }, diff --git a/src/app/api/mobile/admin/investment/[id]/route.ts b/src/app/api/mobile/admin/investment/[id]/route.ts index 3d5913bb..15e2bbcb 100644 --- a/src/app/api/mobile/admin/investment/[id]/route.ts +++ b/src/app/api/mobile/admin/investment/[id]/route.ts @@ -174,7 +174,7 @@ async function PUT(request: Request, { params }: { params: { id: string } }) { body: `${updatedData.title}` as NotificationMobileBodyType, type: "announcement", kategoriApp: "INVESTASI", - deepLink: routeUserMobile.investasiDetailPublish({ id: id }), + deepLink: routeUserMobile.investmentDetailPublish({ id: id }), }, }); diff --git a/src/app/api/mobile/investment/[id]/document/route.ts b/src/app/api/mobile/investment/[id]/document/route.ts index 684a40fe..ae0a2e82 100644 --- a/src/app/api/mobile/investment/[id]/document/route.ts +++ b/src/app/api/mobile/investment/[id]/document/route.ts @@ -5,6 +5,7 @@ import { NotificationMobileBodyType, NotificationMobileTitleType, } from "../../../../../../../types/type-mobile-notification"; +import { routeAdminMobile, routeUserMobile } from "@/lib/mobile/route-page-mobile"; export { POST, GET, DELETE }; @@ -33,11 +34,14 @@ async function POST(request: Request, { params }: { params: { id: string } }) { investasi: { select: { title: true, + authorId: true, }, }, }, }); + console.log("[CREATED DOCS]", createdDocs); + const findInvestor = await prisma.investasi_Invoice.findMany({ where: { investasiId: id, @@ -45,23 +49,27 @@ async function POST(request: Request, { params }: { params: { id: string } }) { name: "Berhasil", }, }, + select: { + authorId: true, + }, }); + console.log("[FIND INVESTOR]", findInvestor); + // SEND NOTIFICATION - // await sendNotificationMobileToManyUser({ - // recipientIds: findInvestor.map((user) => user.id), - // senderId: data.authorId, - // payload: { - // title: "Cek Dokumen" as NotificationMobileTitleType, - // body: `Ada informasi dokumen yang di\\\ ${createdDocs.investasi?.title}` as NotificationMobileBodyType, - // type: "announcement", - // kategoriApp: "INVESTASI", - // deepLink: routeAdminMobile.investmentDetailPublish({ - // id: update.investasiId as string, - // status: "publish", - // }), - // }, - // }); + await sendNotificationMobileToManyUser({ + recipientIds: findInvestor.map((e) => e.authorId!), + senderId: createdDocs.investasi?.authorId as string, + payload: { + title: "Cek Dokumen" as NotificationMobileTitleType, + body: `Ada dokumen terupdate pada ${createdDocs.investasi?.title}` as NotificationMobileBodyType, + type: "announcement", + kategoriApp: "INVESTASI", + deepLink: routeUserMobile.investmentDetailPublish({ + id: createdDocs.investasiId as string, + }), + }, + }); if (!createdDocs) return NextResponse.json({ diff --git a/src/app/api/mobile/investment/[id]/news/route.ts b/src/app/api/mobile/investment/[id]/news/route.ts index 8571686f..7faa94d6 100644 --- a/src/app/api/mobile/investment/[id]/news/route.ts +++ b/src/app/api/mobile/investment/[id]/news/route.ts @@ -1,6 +1,7 @@ import _ from "lodash"; import { prisma } from "@/lib"; import { NextResponse } from "next/server"; +import { sendNotificationInvestmentAddNews } from "@/lib/mobile/notification/notification-add-news-investment"; export { POST, GET, DELETE }; @@ -21,6 +22,21 @@ async function POST(request: Request, { params }: { params: { id: string } }) { deskripsi: data.deskripsi, imageId: data.imageId, }, + select: { + investasiId: true, + investasi: { + select: { + title: true, + authorId: true, + }, + }, + }, + }); + + await sendNotificationInvestmentAddNews({ + invesmentId: createWithFile.investasiId, + senderId: createWithFile.investasi.authorId as string, + title: createWithFile.investasi.title, }); fixData = createWithFile; @@ -31,6 +47,21 @@ async function POST(request: Request, { params }: { params: { id: string } }) { title: _.startCase(data.title), deskripsi: data.deskripsi, }, + select: { + investasiId: true, + investasi: { + select: { + title: true, + authorId: true, + }, + }, + }, + }); + + await sendNotificationInvestmentAddNews({ + invesmentId: createWitOutFile.investasiId, + senderId: createWitOutFile.investasi.authorId as string, + title: createWitOutFile.investasi.title, }); fixData = createWitOutFile; @@ -111,7 +142,7 @@ async function GET(request: Request, { params }: { params: { id: string } }) { async function DELETE( request: Request, - { params }: { params: { id: string } } + { params }: { params: { id: string } }, ) { const { id } = params; console.log("id", id); diff --git a/src/lib/code-otp-sender.ts b/src/lib/code-otp-sender.ts index a5cd885d..134e20b5 100644 --- a/src/lib/code-otp-sender.ts +++ b/src/lib/code-otp-sender.ts @@ -5,22 +5,22 @@ const sendCodeOtp = async ({ nomor: string; codeOtp: string; }) => { - // const msg = `HIPMI%20-%20Kode%20ini%20bersifat%20RAHASIA%20dan%20JANGAN%20DI%20BAGIKAN%20KEPADA%20SIAPAPUN%2C%20termasuk%20anggota%20ataupun%20pengurus%20HIPMI%20lainnya.%20Kode%20OTP%20anda%3A%20${codeOtp}.`; - // const res = await fetch( - // `https://cld-dkr-prod-wajs-server.wibudev.com/api/wa/code?nom=${nomor}&text=${msg}`, - // { - // cache: "no-cache", - // headers: { - // Authorization: `Bearer ${process.env.WA_SERVER_TOKEN}`, - // }, - // } - // ); + const msg = `HIPMI%20-%20Kode%20ini%20bersifat%20RAHASIA%20dan%20JANGAN%20DI%20BAGIKAN%20KEPADA%20SIAPAPUN%2C%20termasuk%20anggota%20ataupun%20pengurus%20HIPMI%20lainnya.%20Kode%20OTP%20anda%3A%20${codeOtp}.`; const res = await fetch( - `https://wa.wibudev.com/code?nom=${nomor}&text=HIPMI - Kode ini bersifat RAHASIA dan JANGAN DI BAGIKAN KEPADA SIAPAPUN, termasuk anggota ataupun pengurus HIPMI lainnya. - \n - >> Kode OTP anda: ${codeOtp}. - `, + `https://cld-dkr-prod-wajs-server.wibudev.com/api/wa/code?nom=${nomor}&text=${msg}`, + { + cache: "no-cache", + headers: { + Authorization: `Bearer ${process.env.WA_SERVER_TOKEN}`, + }, + }, ); + // const res = await fetch( + // `https://wa.wibudev.com/code?nom=${nomor}&text=HIPMI - Kode ini bersifat RAHASIA dan JANGAN DI BAGIKAN KEPADA SIAPAPUN, termasuk anggota ataupun pengurus HIPMI lainnya. + // \n + // >> Kode OTP anda: ${codeOtp}. + // `, + // ); return res; }; diff --git a/src/lib/mobile/notification/notification-add-news-investment.ts b/src/lib/mobile/notification/notification-add-news-investment.ts new file mode 100644 index 00000000..7bf1d819 --- /dev/null +++ b/src/lib/mobile/notification/notification-add-news-investment.ts @@ -0,0 +1,46 @@ +import { + NotificationMobileBodyType, + NotificationMobileTitleType, +} from "../../../../types/type-mobile-notification"; +import { sendNotificationMobileToManyUser } from "./send-notification"; +import { routeUserMobile } from "../route-page-mobile"; +import prisma from "@/lib/prisma"; + +export const sendNotificationInvestmentAddNews = async ({ + invesmentId, + senderId, + title, +}: { + invesmentId: string; + senderId: string; + title: string; +}) => { + const findInvestor = await prisma.investasi_Invoice.findMany({ + where: { + investasiId: invesmentId, + StatusInvoice: { + name: "Berhasil", + }, + }, + select: { + authorId: true, + }, + }); + + console.log("[FIND INVESTOR]", findInvestor); + + // SEND NOTIFICATION + await sendNotificationMobileToManyUser({ + recipientIds: findInvestor.map((e) => e.authorId!), + senderId: senderId, + payload: { + title: "Berita terbaru" as NotificationMobileTitleType, + body: `Ada berita yang terupdate pada ${title}` as NotificationMobileBodyType, + type: "announcement", + kategoriApp: "INVESTASI", + deepLink: routeUserMobile.investmentDetailPublish({ + id: invesmentId, + }), + }, + }); +}; diff --git a/src/lib/mobile/route-page-mobile.ts b/src/lib/mobile/route-page-mobile.ts index 1679f2c9..9c5749e0 100644 --- a/src/lib/mobile/route-page-mobile.ts +++ b/src/lib/mobile/route-page-mobile.ts @@ -59,6 +59,6 @@ const routeUserMobile = { // INVESTMENT investmentPortofolioByStatus: ({ status }: { status?: StatusApp }) => `/investment/(tabs)/portofolio?status=${status}`, - investasiDetailPublish: ({ id }: { id: string }) => `/investment/${id}`, - investasiTransaction: `/investment/(tabs)/transaction` + investmentDetailPublish: ({ id }: { id: string }) => `/investment/${id}`, + investmentTransaction: `/investment/(tabs)/transaction` }; From 048b7b60943b4fa5f02df651ee6d633ebfa60660 Mon Sep 17 00:00:00 2001 From: bagasbanuna Date: Fri, 23 Jan 2026 14:46:44 +0800 Subject: [PATCH 3/5] API Rgister component sender Legal Documents & Registration - public/privacy-policy.html - public/terms-of-service.html - src/app/api/auth/mobile-register/route.ts ### No Issue; --- public/privacy-policy.html | 2 +- public/terms-of-service.html | 2 +- src/app/api/auth/mobile-register/route.ts | 23 +++++++++++------------ 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/public/privacy-policy.html b/public/privacy-policy.html index d80db54d..9603f572 100644 --- a/public/privacy-policy.html +++ b/public/privacy-policy.html @@ -295,7 +295,7 @@

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.


-

© 2025 Bali Interaktif Perkasa. All rights reserved.

+

© 2026 Bali Interaktif Perkasa. All rights reserved.

\ No newline at end of file diff --git a/public/terms-of-service.html b/public/terms-of-service.html index e28fd62b..2664ec5f 100644 --- a/public/terms-of-service.html +++ b/public/terms-of-service.html @@ -104,7 +104,7 @@

-

© 2025 Bali Interaktif Perkasa. All rights reserved.

+

© 2026 Bali Interaktif Perkasa. All rights reserved.

\ No newline at end of file diff --git a/src/app/api/auth/mobile-register/route.ts b/src/app/api/auth/mobile-register/route.ts index da1e8ab4..9f3b74e0 100644 --- a/src/app/api/auth/mobile-register/route.ts +++ b/src/app/api/auth/mobile-register/route.ts @@ -7,6 +7,7 @@ import { NotificationMobileBodyType, NotificationMobileTitleType, } from "../../../../../types/type-mobile-notification"; +import { sendCodeOtp } from "@/lib/code-otp-sender"; export async function POST(req: Request) { if (req.method !== "POST") { @@ -69,23 +70,21 @@ export async function POST(req: Request) { { status: 400 } ); - // const msg = `HIPMI - Kode ini bersifat RAHASIA dan JANGAN DI BAGIKAN KEPAADA SIAPAPUN, termasuk anggota ataupun pengurus HIPMI lainnya.\n\n\n> Kode OTP anda: ${codeOtp}.`; - const msg = `HIPMI%20-%20Kode%20ini%20bersifat%20RAHASIA%20dan%20JANGAN%20DI%20BAGIKAN%20KEPADA%20SIAPAPUN%2C%20termasuk%20anggota%20ataupun%20pengurus%20HIPMI%20lainnya.%20Kode%20OTP%20anda%3A%20${codeOtp}.`; - // // const encodedMsg = encodeURIComponent(msg); + const resSendCode = await sendCodeOtp({ + nomor: data.nomor, + codeOtp: codeOtp.toString(), + }); - const res = await fetch( - `https://wa.wibudev.com/code?nom=${data.nomor}&text=${msg}`, - { cache: "no-cache" } - ); - - const sendWa = await res.json(); - - if (sendWa.status !== "success") + if (resSendCode.status !== 200) return NextResponse.json( { success: false, message: "Nomor Whatsapp Tidak Aktif" }, - { status: 400 } + { status: 400 }, ); + const sendWa = await resSendCode.text(); + console.log("WA Response:", sendWa); + + // =========== START SEND NOTIFICATION =========== // const adminUsers = await prisma.user.findMany({ From 8ccf1f2b6e47c288a7df9a0c1d41e4fcdcd0ce3e Mon Sep 17 00:00:00 2001 From: bagasbanuna Date: Fri, 23 Jan 2026 17:04:43 +0800 Subject: [PATCH 4/5] =?UTF-8?q?API=20=E2=80=93=20Donation=20(Admin=20&=20U?= =?UTF-8?q?ser)=20-=20src/app/api/mobile/admin/donation/[id]/route.ts=20-?= =?UTF-8?q?=20src/app/api/mobile/admin/donation/[id]/invoice/route.ts=20-?= =?UTF-8?q?=20src/app/api/mobile/donation/route.ts=20-=20src/app/api/mobil?= =?UTF-8?q?e/donation/[id]/invoice/route.ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Routing Helper - src/lib/mobile/route-page-mobile.ts ### No Issue --- .../admin/donation/[id]/invoice/route.ts | 39 ++++++++++ .../api/mobile/admin/donation/[id]/route.ts | 73 +++++++++++++++++-- .../api/mobile/donation/[id]/invoice/route.ts | 55 +++++++++++++- src/app/api/mobile/donation/route.ts | 23 ++++++ src/lib/mobile/route-page-mobile.ts | 19 ++++- 5 files changed, 197 insertions(+), 12 deletions(-) diff --git a/src/app/api/mobile/admin/donation/[id]/invoice/route.ts b/src/app/api/mobile/admin/donation/[id]/invoice/route.ts index 1627bf56..48d85a99 100644 --- a/src/app/api/mobile/admin/donation/[id]/invoice/route.ts +++ b/src/app/api/mobile/admin/donation/[id]/invoice/route.ts @@ -1,6 +1,9 @@ import { NextResponse } from "next/server"; import { prisma } from "@/lib"; import _ from "lodash"; +import { sendNotificationMobileToOneUser } from "@/lib/mobile/notification/send-notification"; +import { NotificationMobileBodyType, NotificationMobileTitleType } from "../../../../../../../../types/type-mobile-notification"; +import { routeUserMobile } from "@/lib/mobile/route-page-mobile"; export { GET, PUT }; @@ -50,6 +53,7 @@ async function GET(req: Request, { params }: { params: { id: string } }) { interface DataType { donationId: string; nominal: number; + senderId: string; } async function PUT(req: Request, { params }: { params: { id: string } }) { @@ -111,6 +115,9 @@ async function PUT(req: Request, { params }: { params: { id: string } }) { data: { donasiMaster_StatusInvoiceId: checkStatusTransaksi.id, }, + select: { + authorId: true, + }, }); if (!updateInvoice) { @@ -154,6 +161,38 @@ async function PUT(req: Request, { params }: { params: { id: string } }) { ); } + // SEND NOTIFICATION: to donatur + await sendNotificationMobileToOneUser({ + recipientId: updateInvoice?.authorId as string, + senderId: data?.senderId || "", + payload: { + title: "Transaksi Berhasil" as NotificationMobileTitleType, + body: `Selamat anda menjadi donatur pada ${updateDonasi?.title}` as NotificationMobileBodyType, + type: "announcement", + kategoriApp: "DONASI", + deepLink: routeUserMobile.donationTransaction, + }, + }); + + // SEND NOTIFICATION: to creator + await sendNotificationMobileToOneUser({ + recipientId: updateDonasi?.authorId as any, + senderId: data?.senderId || "", + payload: { + title: "Ada Donatur Baru !" as NotificationMobileTitleType, + body: `Cek daftar donatur pada ${updateDonasi?.title}` as NotificationMobileBodyType, + type: "announcement", + kategoriApp: "DONASI", + deepLink: routeUserMobile.donationDetailPublish({ + id: updateDonasi?.id as string, + }), + }, + }); + + + + + return NextResponse.json( { success: true, diff --git a/src/app/api/mobile/admin/donation/[id]/route.ts b/src/app/api/mobile/admin/donation/[id]/route.ts index ab040081..557f8cb0 100644 --- a/src/app/api/mobile/admin/donation/[id]/route.ts +++ b/src/app/api/mobile/admin/donation/[id]/route.ts @@ -1,6 +1,15 @@ +import { + sendNotificationMobileToManyUser, + sendNotificationMobileToOneUser, +} from "@/lib/mobile/notification/send-notification"; +import { routeUserMobile } from "@/lib/mobile/route-page-mobile"; import prisma from "@/lib/prisma"; import _ from "lodash"; import { NextResponse } from "next/server"; +import { + NotificationMobileBodyType, + NotificationMobileTitleType, +} from "../../../../../../../types/type-mobile-notification"; export { GET, PUT }; @@ -48,7 +57,7 @@ async function GET(request: Request, { params }: { params: { id: string } }) { DonasiMaster_StatusInvoice: { name: "Berhasil", }, - }, + }, }); return NextResponse.json( @@ -60,7 +69,7 @@ async function GET(request: Request, { params }: { params: { id: string } }) { donatur: successInvoice, }, }, - { status: 200 } + { status: 200 }, ); } catch (error) { return NextResponse.json( @@ -69,7 +78,7 @@ async function GET(request: Request, { params }: { params: { id: string } }) { message: "Error get detail Investasi", reason: (error as Error).message, }, - { status: 500 } + { status: 500 }, ); } } @@ -77,6 +86,10 @@ async function GET(request: Request, { params }: { params: { id: string } }) { async function PUT(request: Request, { params }: { params: { id: string } }) { const { id } = params; const { data } = await request.json(); + const { catatan, senderId } = data; + console.log("[PUT CATATAN]", catatan); + console.log("[PUT SENDER ID]", senderId); + const { searchParams } = new URL(request.url); const status = searchParams.get("status"); const fixStatus = _.startCase(status as string); @@ -102,7 +115,7 @@ async function PUT(request: Request, { params }: { params: { id: string } }) { message: "Error update data event", reason: "Status not found", }, - { status: 500 } + { status: 500 }, ); if (fixStatus === "Reject") { @@ -111,11 +124,24 @@ async function PUT(request: Request, { params }: { params: { id: string } }) { id: id, }, data: { - catatan: data, + catatan: catatan, donasiMaster_StatusDonasiId: checkStatus.id, }, }); + // SEND NOTIFICATION + await sendNotificationMobileToOneUser({ + recipientId: updateData.authorId as any, + senderId: senderId, + payload: { + title: "Pengajuan Review Ditolak", + body: "Mohon perbaiki data sesuai catatan penolakan !", + type: "announcement", + kategoriApp: "DONASI", + deepLink: routeUserMobile.donationByStatus({ status: "reject" }), + }, + }); + fixData = updateData; } else if (fixStatus === "Publish") { const updateData = await prisma.donasi.update({ @@ -128,6 +154,39 @@ async function PUT(request: Request, { params }: { params: { id: string } }) { }, }); + // SEND NOTIFICAtION + await sendNotificationMobileToOneUser({ + recipientId: updateData.authorId as any, + senderId: senderId, + payload: { + title: "Review Selesai", + body: `Donasi kamu telah dipublikasikan ! ${updateData.title}` as NotificationMobileBodyType, + type: "announcement", + kategoriApp: "DONASI", + deepLink: routeUserMobile.donationByStatus({ status: "publish" }), + }, + }); + + const allUsers = await prisma.user.findMany({ + where: { + NOT: { id: updateData.authorId as any }, + active: true, + }, + select: { id: true }, + }); + + await sendNotificationMobileToManyUser({ + recipientIds: allUsers.map((user) => user.id), + senderId: senderId, + payload: { + title: "Ayo Cek Donasi Terbaru" as NotificationMobileTitleType, + body: `${updateData.title}` as NotificationMobileBodyType, + type: "announcement", + kategoriApp: "DONASI", + deepLink: routeUserMobile.donationDetailPublish({ id: id }), + }, + }); + fixData = updateData; } @@ -137,7 +196,7 @@ async function PUT(request: Request, { params }: { params: { id: string } }) { message: "Data Donasi Berhasil Diambil", data: data, }, - { status: 200 } + { status: 200 }, ); } catch (error) { return NextResponse.json( @@ -146,7 +205,7 @@ async function PUT(request: Request, { params }: { params: { id: string } }) { message: "Error get detail Investasi", reason: (error as Error).message, }, - { status: 500 } + { status: 500 }, ); } } diff --git a/src/app/api/mobile/donation/[id]/invoice/route.ts b/src/app/api/mobile/donation/[id]/invoice/route.ts index 56840a33..da00d061 100644 --- a/src/app/api/mobile/donation/[id]/invoice/route.ts +++ b/src/app/api/mobile/donation/[id]/invoice/route.ts @@ -1,6 +1,12 @@ import _ from "lodash"; import { NextResponse } from "next/server"; import prisma from "@/lib/prisma"; +import { sendNotificationMobileToManyUser } from "@/lib/mobile/notification/send-notification"; +import { + NotificationMobileBodyType, + NotificationMobileTitleType, +} from "../../../../../../../types/type-mobile-notification"; +import { routeAdminMobile } from "@/lib/mobile/route-page-mobile"; export { POST, GET, PUT }; @@ -33,6 +39,14 @@ async function POST(request: Request, { params }: { params: { id: string } }) { }, }); + if (!create) { + return NextResponse.json({ + status: 500, + success: false, + message: "Gagal membuat invoice", + }); + } + return NextResponse.json({ status: 201, success: true, @@ -48,7 +62,7 @@ async function POST(request: Request, { params }: { params: { id: string } }) { reason: (error as Error).message, }); } -} +} async function GET(request: Request, { params }: { params: { id: string } }) { try { @@ -65,7 +79,7 @@ async function GET(request: Request, { params }: { params: { id: string } }) { createdAt: true, donasiMaster_BankId: true, donasiMaster_StatusInvoiceId: true, -MasterBank: true, + MasterBank: true, Donasi: { select: { id: true, @@ -139,7 +153,7 @@ async function PUT(request: Request, { params }: { params: { id: string } }) { }); } - const update = await prisma.donasi_Invoice.update({ + const updated = await prisma.donasi_Invoice.update({ where: { id: id, }, @@ -164,7 +178,40 @@ async function PUT(request: Request, { params }: { params: { id: string } }) { }, }); - console.log("[UPDATE INVOICE]", update); + if (!updated) { + return NextResponse.json({ + status: 500, + success: false, + message: "Gagal memperbarui data", + }); + } + + const findUsers = await prisma.user.findMany({ + where: { + masterUserRoleId: "2", + active: true, + NOT: { id: updated?.Donasi?.authorId as string }, + }, + select: { id: true }, + }); + + // SEND NOTIFICATION + await sendNotificationMobileToManyUser({ + recipientIds: findUsers.map((user) => user.id), + senderId: data.authorId, + payload: { + title: "Ada Donasi Baru !" as NotificationMobileTitleType, + body: `Cek data investor pada ${updated?.Donasi?.title}` as NotificationMobileBodyType, + type: "announcement", + kategoriApp: "DONASI", + deepLink: routeAdminMobile.donationDetailPublish({ + id: updated?.Donasi?.id as string, + status: "publish", + }), + }, + }); + + console.log("[UPDATE INVOICE]", updated); return NextResponse.json({ status: 200, diff --git a/src/app/api/mobile/donation/route.ts b/src/app/api/mobile/donation/route.ts index a31d2194..4e7ece15 100644 --- a/src/app/api/mobile/donation/route.ts +++ b/src/app/api/mobile/donation/route.ts @@ -1,6 +1,9 @@ +import { sendNotificationMobileToManyUser } from "@/lib/mobile/notification/send-notification"; import prisma from "@/lib/prisma"; import _ from "lodash"; import { NextResponse } from "next/server"; +import { NotificationMobileBodyType } from "../../../../../types/type-mobile-notification"; +import { routeAdminMobile } from "@/lib/mobile/route-page-mobile"; export { POST }; @@ -49,6 +52,26 @@ async function POST(request: Request) { console.log("[DATA DONASI]", dataDonasi); + const adminUsers = await prisma.user.findMany({ + where: { masterUserRoleId: "2", NOT: { id: data.authorId } }, + select: { id: true }, + }); + + // SEND NOTIFICATION + await sendNotificationMobileToManyUser({ + recipientIds: adminUsers.map((user) => user.id), + senderId: data.authorId, + payload: { + title: "Pengajuan Review Baru", + body: data.title as NotificationMobileBodyType, + type: "announcement", + deepLink: routeAdminMobile.donationByStatus({ status: "review" }), + kategoriApp: "DONASI", + }, + }); + + + if (!dataDonasi) return NextResponse.json({ status: 400, diff --git a/src/lib/mobile/route-page-mobile.ts b/src/lib/mobile/route-page-mobile.ts index 9c5749e0..9f5f42dd 100644 --- a/src/lib/mobile/route-page-mobile.ts +++ b/src/lib/mobile/route-page-mobile.ts @@ -29,8 +29,19 @@ const routeAdminMobile = { id: string; status: StatusApp; }) => `/admin/investment/${id}/${status}`, + + // DONATION + donationByStatus: ({ status }: { status: StatusApp }) => `/admin/donation/${status}/status`, + donationDetailPublish: ({ + id, + status, + }: { + id: string; + status: StatusApp; + }) => `/admin/donation/${id}/${status}`, }; +// ================ ROUTER USER ================= const routeUserMobile = { home: `/(user)/home`, // JOB @@ -60,5 +71,11 @@ const routeUserMobile = { investmentPortofolioByStatus: ({ status }: { status?: StatusApp }) => `/investment/(tabs)/portofolio?status=${status}`, investmentDetailPublish: ({ id }: { id: string }) => `/investment/${id}`, - investmentTransaction: `/investment/(tabs)/transaction` + investmentTransaction: `/investment/(tabs)/transaction`, + + // DONATION + donationByStatus: ({ status }: { status?: StatusApp }) => + `/donation/(tabs)/status?status=${status}`, + donationDetailPublish: ({ id }: { id: string }) => `/donation/${id}`, + donationTransaction: `/donation/(tabs)/my-donation`, }; From 70db97f5bbf956906e34c343a8a6b2845643febe Mon Sep 17 00:00:00 2001 From: bagasbanuna Date: Fri, 23 Jan 2026 17:05:04 +0800 Subject: [PATCH 5/5] chore(release): 1.5.37 --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efc63381..0c703fb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. +## [1.5.37](https://wibugit.wibudev.com/wibu/hipmi/compare/v1.5.36...v1.5.37) (2026-01-23) + ## [1.5.36](https://wibugit.wibudev.com/wibu/hipmi/compare/v1.5.35...v1.5.36) (2026-01-13) ## [1.5.35](https://wibugit.wibudev.com/wibu/hipmi/compare/v1.5.34...v1.5.35) (2026-01-12) diff --git a/package.json b/package.json index 64915a43..4444cb40 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.5.36", + "version": "1.5.37", "private": true, "prisma": { "seed": "bun prisma/seed.ts"