Compare commits

...

5 Commits

Author SHA1 Message Date
e89886e1db feat: Implementasi pagination pada endpoint mobile donation
4
      5 - Menambahkan pagination pada endpoint GET /api/mobile/donation
      6 - Menambahkan pagination pada endpoint GET /api/mobile/donation/[id]/news
      7 - Menambahkan pagination pada endpoint GET /api/mobile/donation/[id]/donatur
      8 - Memperbaiki validasi payload pada endpoint POST /api/mobile/auth/device-tokens
      9 - Menangani struktur payload yang bersarang dan langsung pada device token endpoint
     10 - Menambahkan informasi pagination ke dalam respons API

### NO Issue
2026-02-10 17:31:39 +08:00
934d6a3ef1 feat: update mobile donation API and related dependencies
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-02-09 17:34:49 +08:00
a7694bd7d5 feat: Tambahkan pagination pada API mobile investasi
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

Deskripsi:

- Tambahkan pagination pada endpoint investor/route.ts

- Tambahkan pagination pada endpoint news/route.ts untuk kategori all-news

- Gunakan konstanta PAGINATION_DEFAULT_TAKE untuk jumlah data per halaman

- Tambahkan metadata pagination (currentPage, totalData, totalPage, dataPerPage)

Fixes #issue-number
2026-02-09 15:08:33 +08:00
3b0ea3d847 chore(release): 1.5.40 2026-02-06 17:38:19 +08:00
097758a431 Fix API mobile Investment
API – Investment (Mobile)
- src/app/api/mobile/investment/route.ts
- src/app/api/mobile/investment/[id]/[status]/route.ts
- src/app/api/mobile/investment/[id]/invoice/route.ts
- src/app/api/mobile/investment/[id]/document/route.ts

Docs
- PROMPT-AI.md

### No issue
2026-02-06 17:37:47 +08:00
18 changed files with 477 additions and 23 deletions

View File

@@ -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.40](https://wibugit.wibudev.com/wibu/hipmi/compare/v1.5.39...v1.5.40) (2026-02-06)
## [1.5.39](https://wibugit.wibudev.com/wibu/hipmi/compare/v1.5.38...v1.5.39) (2026-01-30)
## [1.5.38](https://wibugit.wibudev.com/wibu/hipmi/compare/v1.5.37...v1.5.38) (2026-01-27)

View File

@@ -1,5 +1,5 @@
File utama: src/app/api/mobile/voting/[id]/contribution/route.ts
File utama: src/app/api/mobile/donation/[id]/donatur/route.ts
Terapkan pagination pada file "File utama" pada method GET
Analisa juga file "File utama", jika belum memiliki page dari seachParams maka terapkan. Juga pastikan take dan skip sudah sesuai dengan pagination. Buat default nya menjadi 10 untuk take data
@@ -17,4 +17,29 @@ Gunakan bahasa indonesia pada cli agar saya mudah membacanya.
<!-- Additinal prompt -->
File refrensi: src/app/api/mobile/event/[id]/[status]/route.ts
Anda bisa menggunakan refrensi dari "File refrensi" jika butuh pemahaman dengan tipe fitur yang sama
Anda bisa menggunakan refrensi dari "File refrensi" jika butuh pemahaman dengan tipe fitur yang sama
<!-- Auto input promt -->
End-point: src/app/api/mobile/donation/route.ts
Buatkan auto input untuk method POST dengan data yang dibutuhkan sesuai dengan struktur data yang ada di file tersebut, tidak perlu "temporary" cukup data "permanent" dengan ketentuan sebagai berikut:
- authorId: string ( cmha6wb9w0001cfndwl9fcse6 )
- title: string
- target: number
- donasiMaster_DurasiId: number ( 3 )
- donasiMaster_KategoriId: number ( 3 )
- namaBank: string
- rekening: string
- imageId: number ( cm60j9q3m000xc9dc584v8rh8 )
Untuk sisa nya anda bisa bebas mengisi data tersebut.
<!-- COMMIT & PUSH -->
Branch: mobile-api/10-feb-26
Jalankan perintah ini: git checkout -b "Branch"
Setelah itu jalankan perintah ini: git add .
Setelah itu jalankan perintah ini: git commit -m "
<Berikan semua catatan perubahan pada branch ini, tampilan pada saya dan pastikan dalam bahasa indonesia. Saya akan cek baru saya akan berikan perintah push>
"
Setelah itu jalankan perintah ini: git push origin "Branch"

View File

@@ -2,7 +2,7 @@
## Project Overview
HIPMI (Himpunan Pengusaha Muda Indonesia) is a comprehensive Next.js-based web application built for the Indonesian Young Entrepreneurs Association. The project is a sophisticated platform that provides multiple business functionalities including investment management, donations, events, job listings, forums, voting systems, and collaborative projects.
HIPMI (Himpunan Pengusaha Muya Indonesia) is a comprehensive Next.js-based web application built for the Indonesian Young Entrepreneurs Association. The project is a sophisticated platform that provides multiple business functionalities including investment management, donations, events, job listings, forums, voting systems, and collaborative projects.
### Key Technologies
- **Framework**: Next.js 13+ (with App Router)

View File

@@ -39,6 +39,7 @@
"@types/react-dom": "18.2.7",
"@types/uuid": "^9.0.4",
"autoprefixer": "10.4.14",
"axios": "^1.13.5",
"bufferutil": "^4.0.8",
"bun": "^1.1.38",
"caniuse-lite": "^1.0.30001757",
@@ -1324,7 +1325,7 @@
"axe-core": ["axe-core@4.10.2", "", {}, "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w=="],
"axios": ["axios@0.26.1", "", { "dependencies": { "follow-redirects": "^1.14.8" } }, "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA=="],
"axios": ["axios@1.13.5", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q=="],
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
@@ -1882,7 +1883,7 @@
"fn.name": ["fn.name@1.1.0", "", {}, "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="],
"follow-redirects": ["follow-redirects@1.15.9", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="],
"follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="],
"fontfaceobserver": ["fontfaceobserver@2.3.0", "", {}, "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg=="],
@@ -1892,7 +1893,7 @@
"foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
"form-data": ["form-data@4.0.2", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" } }, "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w=="],
"form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
"form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="],
@@ -3618,6 +3619,8 @@
"@types/jsonwebtoken/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="],
"@types/node-fetch/form-data": ["form-data@4.0.2", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" } }, "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w=="],
"@types/request/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="],
"@types/request/form-data": ["form-data@2.5.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.35", "safe-buffer": "^5.2.1" } }, "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A=="],
@@ -3630,6 +3633,8 @@
"ast-types/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"autocannon/form-data": ["form-data@4.0.2", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" } }, "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w=="],
"autoprefixer/caniuse-lite": ["caniuse-lite@1.0.30001701", "", {}, "sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw=="],
"babel-plugin-polyfill-corejs2/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
@@ -3810,6 +3815,8 @@
"metro-file-map/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
"midtrans-client/axios": ["axios@0.26.1", "", { "dependencies": { "follow-redirects": "^1.14.8" } }, "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA=="],
"minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
"minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
@@ -4190,6 +4197,12 @@
"metro/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
"midtrans-client/axios/follow-redirects": ["follow-redirects@1.15.9", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="],
"next-dev/axios/follow-redirects": ["follow-redirects@1.15.9", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="],
"next-dev/axios/form-data": ["form-data@4.0.2", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" } }, "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w=="],
"next-dev/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
"next-scroll-loader/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],

View File

@@ -1,6 +1,6 @@
{
"name": "hipmi",
"version": "1.5.39",
"version": "1.5.40",
"private": true,
"prisma": {
"seed": "bun prisma/seed.ts"
@@ -50,6 +50,7 @@
"@types/react-dom": "18.2.7",
"@types/uuid": "^9.0.4",
"autoprefixer": "10.4.14",
"axios": "^1.13.5",
"bufferutil": "^4.0.8",
"bun": "^1.1.38",
"caniuse-lite": "^1.0.30001757",

View File

@@ -4,13 +4,42 @@ import { prisma } from "@/lib";
export { POST, GET };
async function POST(request: NextRequest) {
const { data } = await request.json();
try {
// Parse the request body - can accept either nested under 'data' or directly
const requestBody = await request.json();
// Check if the data is nested under 'data' property (as described in the issue)
// or if it's directly in the request body (more common pattern)
const payload = requestBody.data ? requestBody.data : requestBody;
const { userId, platform, deviceId, model, appVersion, fcmToken } = payload;
const { userId, platform, deviceId, model, appVersion, fcmToken } = data;
// Validate required fields
if (!fcmToken) {
return NextResponse.json({ error: "Missing Token" }, { status: 400 });
return NextResponse.json(
{ error: "Missing FCM token", field: "fcmToken" },
{ status: 400 }
);
}
if (!userId) {
return NextResponse.json(
{ error: "Missing user ID", field: "userId" },
{ status: 400 }
);
}
// Verify that the user exists before creating/updating the device token
const userExists = await prisma.user.findUnique({
where: { id: userId },
select: { id: true }
});
if (!userExists) {
return NextResponse.json(
{ error: "User not found", field: "userId" },
{ status: 404 }
);
}
const existing = await prisma.tokenUserDevice.findFirst({
@@ -23,7 +52,6 @@ async function POST(request: NextRequest) {
},
});
console.log("✅ EX", existing);
let deviceToken;
@@ -31,7 +59,7 @@ async function POST(request: NextRequest) {
if (existing) {
deviceToken = await prisma.tokenUserDevice.update({
where: {
id: existing?.id,
id: existing.id,
},
data: {
platform,
@@ -43,7 +71,7 @@ async function POST(request: NextRequest) {
},
});
} else {
// Buat baru jika belum ada
// Create new device token record
deviceToken = await prisma.tokenUserDevice.create({
data: {
token: fcmToken,
@@ -58,9 +86,16 @@ async function POST(request: NextRequest) {
}
return NextResponse.json({ success: true, data: deviceToken });
} catch (error) {
} catch (error: any) {
console.error("Error registering device token:", error);
// Return more informative error response
return NextResponse.json(
{ error: (error as Error).message },
{
error: "Internal server error",
message: error.message || "An unexpected error occurred",
field: "server"
},
{ status: 500 }
);
}

View File

@@ -1,6 +1,7 @@
import _ from "lodash";
import { NextResponse } from "next/server";
import prisma from "@/lib/prisma";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
export { GET, PUT };
@@ -10,8 +11,14 @@ async function GET(
) {
const { id, status } = params;
const fixStatus = _.startCase(status);
const { searchParams } = new URL(request.url);
const page = Number(searchParams.get("page")) || 1;
const takeData = PAGINATION_DEFAULT_TAKE
const skipData = page * takeData - takeData;
let fixData;
let meta = null;
try {
const checkStatus = await prisma.donasiMaster_StatusDonasi.findFirst({
where: {
@@ -50,18 +57,38 @@ async function GET(
orderBy: {
updatedAt: "desc",
},
take: takeData,
skip: skipData,
});
const totalData = await prisma.donasi.count({
where: {
authorId: id,
donasiMaster_StatusDonasiId: checkStatus.id,
active: true,
},
});
const totalPages = Math.ceil(totalData / takeData);
fixData = res.map((v: any) => ({
..._.omit(v, ["DonasiMaster_Durasi"]),
nameDonasiDurasi: v.DonasiMaster_Durasi.name,
}));
meta = {
currentPage: page,
totalData: totalData,
totalPage: totalPages,
dataPerPage: takeData,
};
return NextResponse.json({
status: 200,
success: true,
message: "Berhasil mendapatkan data",
data: fixData,
...(meta && { meta }),
});
} catch (error) {
console.log("[ERROR]", error);

View File

@@ -1,5 +1,6 @@
import { NextResponse } from "next/server";
import { prisma } from "@/lib";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
export { GET };
@@ -7,7 +8,7 @@ async function GET(request: Request, { params }: { params: { id: string } }) {
const { id } = params;
const { searchParams } = new URL(request.url);
const page = Number(searchParams.get("page"));
const takeData = 5;
const takeData = PAGINATION_DEFAULT_TAKE;
const skipData = page * takeData - takeData;
try {

View File

@@ -1,3 +1,4 @@
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
import prisma from "@/lib/prisma";
import { NextResponse } from "next/server";
@@ -9,11 +10,12 @@ export async function GET(
let fixData;
const { id } = params;
const { searchParams } = new URL(request.url);
const page = Number(searchParams.get("page"));
const takeData = 10;
const page = Number(searchParams.get("page")) || 1; // Default page 1 jika tidak ada atau invalid
const takeData = PAGINATION_DEFAULT_TAKE;
const skipData = page * takeData - takeData;
fixData = await prisma.donasi_Invoice.findMany({
// Query data dengan pagination
const data = await prisma.donasi_Invoice.findMany({
take: page ? takeData : undefined,
skip: page ? skipData : undefined,
orderBy: {
@@ -59,10 +61,31 @@ export async function GET(
},
});
// Hitung total data untuk pagination
const totalCount = await prisma.donasi_Invoice.count({
where: {
donasiId: id,
DonasiMaster_StatusInvoice: {
name: "Berhasil",
},
},
});
// Hitung total halaman
const totalPages = Math.ceil(totalCount / takeData);
fixData = data;
return NextResponse.json({
success: true,
message: "Data berhasil diambil",
data: fixData,
pagination: {
currentPage: page,
totalPages: totalPages,
totalData: totalCount,
dataPerPage: takeData,
},
});
} catch (error) {
return NextResponse.json({

View File

@@ -8,6 +8,7 @@ import {
} from "../../../../../../../types/type-mobile-notification";
import { routeUserMobile } from "@/lib/mobile/route-page-mobile";
import { funFindDonaturList } from "@/lib/mobile/donation/find-donatur-list";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
export { POST, GET, PUT, DELETE };
@@ -94,11 +95,16 @@ async function GET(
const { id } = params;
const { searchParams } = new URL(request.url);
const category = searchParams.get("category");
const page = Number(searchParams.get("page")) || 1; // Default page 1 jika tidak ada
const takeData = PAGINATION_DEFAULT_TAKE; // Default 10 data per halaman
const skipData = page * takeData - takeData;
let fixData;
let totalCount = 0; // Untuk menghitung total data
try {
if (category === "get-all") {
fixData = await prisma.donasi_Kabar.findMany({
const data = await prisma.donasi_Kabar.findMany({
orderBy: {
updatedAt: "desc",
},
@@ -106,6 +112,8 @@ async function GET(
donasiId: id,
active: true,
},
take: page ? takeData : undefined,
skip: page ? skipData : undefined,
select: {
id: true,
title: true,
@@ -113,6 +121,17 @@ async function GET(
createdAt: true,
},
});
// Hitung total data untuk pagination
totalCount = await prisma.donasi_Kabar.count({
where: {
donasiId: id,
active: true,
},
});
fixData = data;
} else if (category === "get-one") {
const data = await prisma.donasi_Kabar.findUnique({
where: {
@@ -135,11 +154,24 @@ async function GET(
};
}
// Hitung total halaman jika kategori adalah get-all
let pagination = undefined;
if (category === "get-all") {
const totalPages = Math.ceil(totalCount / takeData);
pagination = {
currentPage: page,
totalPages: totalPages,
totalData: totalCount,
dataPerPage: takeData,
};
}
return NextResponse.json({
status: 200,
success: true,
message: "Berhasil mengambil kabar",
data: fixData,
pagination: pagination,
});
} catch (error) {
console.error("[ERROR GET NEWS]", error);

View File

@@ -4,6 +4,7 @@ import _ from "lodash";
import { NextResponse } from "next/server";
import { NotificationMobileBodyType } from "../../../../../types/type-mobile-notification";
import { routeAdminMobile } from "@/lib/mobile/route-page-mobile";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
export { POST, GET };
@@ -125,7 +126,12 @@ async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const category = searchParams.get("category");
const authorId = searchParams.get("authorId");
const page = Number(searchParams.get("page")) || 1; // Default page 1 jika tidak ada
const takeData = PAGINATION_DEFAULT_TAKE; // Default 10 data per halaman
const skipData = page * takeData - takeData;
let fixData;
let totalCount = 0; // Untuk menghitung total data
try {
if (category === "beranda") {
@@ -137,6 +143,8 @@ async function GET(request: Request) {
donasiMaster_StatusDonasiId: "1",
active: true,
},
take: page ? takeData : undefined,
skip: page ? skipData : undefined,
select: {
id: true,
imageId: true,
@@ -152,6 +160,14 @@ async function GET(request: Request) {
},
});
// Hitung total data untuk pagination
totalCount = await prisma.donasi.count({
where: {
donasiMaster_StatusDonasiId: "1",
active: true,
},
});
fixData = data.map((v: any) => ({
..._.omit(v, ["DonasiMaster_Durasi"]),
durasiDonasi: v.DonasiMaster_Durasi.name,
@@ -164,6 +180,8 @@ async function GET(request: Request) {
where: {
authorId: authorId,
},
take: page ? takeData : undefined,
skip: page ? skipData : undefined,
select: {
id: true,
nominal: true,
@@ -190,6 +208,13 @@ async function GET(request: Request) {
},
});
// Hitung total data untuk pagination
totalCount = await prisma.donasi_Invoice.count({
where: {
authorId: authorId,
},
});
fixData = data.map((v: any) => ({
..._.omit(v, ["DonasiMaster_StatusInvoice", "Donasi"]),
statusInvoice: v.DonasiMaster_StatusInvoice.name,
@@ -202,8 +227,21 @@ async function GET(request: Request) {
}));
}
// Hitung total halaman
const totalPages = Math.ceil(totalCount / takeData);
return NextResponse.json(
{ success: true, message: "Data berhasil diambil", data: fixData },
{
success: true,
message: "Data berhasil diambil",
data: fixData,
pagination: {
currentPage: page,
totalPages: totalPages,
totalData: totalCount,
dataPerPage: takeData,
}
},
{ status: 200 }
);
} catch (error) {

View File

@@ -1,6 +1,7 @@
import _ from "lodash";
import { NextResponse } from "next/server";
import prisma from "@/lib/prisma";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
export { GET, PUT };
@@ -9,6 +10,10 @@ async function GET(
{ params }: { params: { id: string; status: string } }
) {
const { id, status } = params;
const { searchParams } = new URL(request.url);
const page = Number(searchParams.get("page"));
const takeData = PAGINATION_DEFAULT_TAKE;
const skipData = page ? page * takeData - takeData : 0;
const fixStatusName = _.startCase(status);
try {
@@ -22,6 +27,8 @@ async function GET(
name: fixStatusName,
},
},
take: page ? takeData : undefined,
skip: page ? skipData : undefined,
select: {
id: true,
title: true,

View File

@@ -6,6 +6,7 @@ import {
NotificationMobileTitleType,
} from "../../../../../../../types/type-mobile-notification";
import { routeAdminMobile, routeUserMobile } from "@/lib/mobile/route-page-mobile";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
export { POST, GET, DELETE };
@@ -98,6 +99,9 @@ async function GET(request: Request, { params }: { params: { id: string } }) {
const { id } = params;
const { searchParams } = new URL(request.url);
const category = searchParams.get("category");
const page = Number(searchParams.get("page"));
const takeData = PAGINATION_DEFAULT_TAKE
const skipData = page * takeData - takeData
try {
let fixData;
@@ -117,6 +121,8 @@ async function GET(request: Request, { params }: { params: { id: string } }) {
investasiId: id,
active: true,
},
take: page ? takeData : undefined,
skip: page ? skipData : undefined,
});
}

View File

@@ -1,10 +1,16 @@
import { NextResponse } from "next/server";
import prisma from "@/lib/prisma";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
export { GET };
async function GET(request: Request, { params }: { params: { id: string } }) {
const { id } = params;
const { searchParams } = new URL(request.url);
const page = Number(searchParams.get("page")) || 1;
const takeData = PAGINATION_DEFAULT_TAKE;
const skipData = page * takeData - takeData;
try {
const data = await prisma.investasi_Invoice.findMany({
@@ -29,13 +35,30 @@ async function GET(request: Request, { params }: { params: { id: string } }) {
},
},
},
take: takeData,
skip: skipData,
});
const totalData = await prisma.investasi_Invoice.count({
where: {
investasiId: id,
statusInvoiceId: "1",
},
});
const totalPages = Math.ceil(totalData / takeData);
return NextResponse.json({
status: 200,
success: true,
message: "Berhasil Mendapatkan Data",
data: data,
meta: {
currentPage: page,
totalData: totalData,
totalPage: totalPages,
dataPerPage: takeData,
},
});
} catch (error) {
return NextResponse.json({

View File

@@ -7,6 +7,7 @@ import {
NotificationMobileTitleType,
NotificationMobileBodyType,
} from "../../../../../../../types/type-mobile-notification";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
export { POST, GET, PUT };
@@ -53,6 +54,9 @@ async function GET(request: Request, { params }: { params: { id: string } }) {
const { searchParams } = new URL(request.url);
const category = searchParams.get("category");
const authorId = searchParams.get("authorId");
const page = Number(searchParams.get("page"));
const takeData = PAGINATION_DEFAULT_TAKE;
const skipData = page ? page * takeData - takeData : 0;
console.log("[ID INVOICE]", id);
@@ -103,6 +107,8 @@ async function GET(request: Request, { params }: { params: { id: string } }) {
statusInvoiceId: "1",
isActive: true,
},
take: page ? takeData : undefined,
skip: page ? skipData : undefined,
select: {
id: true,
nominal: true,
@@ -129,6 +135,8 @@ async function GET(request: Request, { params }: { params: { id: string } }) {
where: {
authorId: authorId,
},
take: page ? takeData : undefined,
skip: page ? skipData : undefined,
select: {
id: true,
statusInvoiceId: true,

View File

@@ -2,6 +2,7 @@ import _ from "lodash";
import { prisma } from "@/lib";
import { NextResponse } from "next/server";
import { sendNotificationInvestmentAddNews } from "@/lib/mobile/notification/notification-add-news-investment";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
export { POST, GET, DELETE };
@@ -88,8 +89,13 @@ async function GET(request: Request, { params }: { params: { id: string } }) {
console.log("id", id);
const { searchParams } = new URL(request.url);
const category = searchParams.get("category");
const page = Number(searchParams.get("page")) || 1;
const takeData = PAGINATION_DEFAULT_TAKE;
const skipData = page * takeData - takeData;
let fixData;
let meta = null;
try {
if (category === "one-news") {
const data = await prisma.beritaInvestasi.findFirst({
@@ -113,7 +119,7 @@ async function GET(request: Request, { params }: { params: { id: string } }) {
fixData = newData;
} else if (category === "all-news") {
fixData = await prisma.beritaInvestasi.findMany({
const newsData = await prisma.beritaInvestasi.findMany({
orderBy: {
updatedAt: "desc",
},
@@ -121,7 +127,27 @@ async function GET(request: Request, { params }: { params: { id: string } }) {
investasiId: id,
active: true,
},
take: takeData,
skip: skipData,
});
const totalData = await prisma.beritaInvestasi.count({
where: {
investasiId: id,
active: true,
},
});
const totalPages = Math.ceil(totalData / takeData);
fixData = newsData;
meta = {
currentPage: page,
totalData: totalData,
totalPage: totalPages,
dataPerPage: takeData,
};
}
return NextResponse.json({
@@ -129,6 +155,7 @@ async function GET(request: Request, { params }: { params: { id: string } }) {
success: true,
message: "Berita berhasil diambil",
data: fixData,
...(meta && { meta }),
});
} catch (error) {
console.log("[ERROR]", error);

View File

@@ -5,6 +5,7 @@ import moment from "moment";
import { sendNotificationMobileToManyUser } from "@/lib/mobile/notification/send-notification";
import { NotificationMobileBodyType } from "../../../../../types/type-mobile-notification";
import { routeAdminMobile } from "@/lib/mobile/route-page-mobile";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
export { POST, GET };
@@ -73,6 +74,9 @@ async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const category = searchParams.get("category");
const authorId = searchParams.get("authorId");
const page = Number(searchParams.get("page"));
const takeData = PAGINATION_DEFAULT_TAKE
const skipData = page ? page * takeData - takeData : 0;
console.log("[CATEGORY]", category);
console.log("[AUTHOR ID]", authorId);
@@ -132,6 +136,8 @@ async function GET(request: Request) {
where: {
masterStatusInvestasiId: "1",
},
take: page ? takeData : undefined,
skip: page ? skipData : undefined,
select: {
id: true,
imageId: true,
@@ -156,6 +162,8 @@ async function GET(request: Request) {
authorId: authorId,
statusInvoiceId: "1",
},
take: page ? takeData : undefined,
skip: page ? skipData : undefined,
select: {
id: true,
investasiId: true,

178
zCoba.js
View File

@@ -1,8 +1,186 @@
const { PrismaClient } = require('@prisma/client')
const axios = require('axios')
const prisma = new PrismaClient()
// Daftar contoh data
const donationDataList = [
{
"data": {
"authorId": "cmha6wb9w0001cfndwl9fcse6",
"title": "Bantuan Pendidikan Anak-anak Kurang Mampu",
"target": 50000000,
"donasiMaster_DurasiId": 3,
"donasiMaster_KategoriId": 3,
"namaBank": "Bank Central Asia",
"rekening": "1234567890",
"imageId": "cm60j9q3m000xc9dc584v8rh8",
"pembukaan": "Kami ingin membantu anak-anak kurang mampu mendapatkan pendidikan yang layak.",
"cerita": "Pendidikan adalah hak dasar setiap anak. Namun, banyak anak-anak di pelosok negeri yang tidak bisa menikmati pendidikan karena keterbatasan ekonomi. Melalui kampanye ini, kami ingin mengumpulkan dana untuk membantu biaya pendidikan mereka.",
"imageCeritaId": "cm60j9q3m000xc9dc584v8rj0"
}
},
{
"data": {
"authorId": "cmha6wb9w0001cfndwl9fcse6",
"title": "Pembangunan Masjid di Desa Terpencil",
"target": 100000000,
"donasiMaster_DurasiId": 3,
"donasiMaster_KategoriId": 3,
"namaBank": "Bank Mandiri",
"rekening": "0987654321",
"imageId": "cm60j9q3m000xc9dc584v8rh8",
"pembukaan": "Membangun masjid untuk masyarakat di daerah terpencil yang belum memiliki tempat ibadah.",
"cerita": "Di sebuah desa terpencil, masyarakat setiap hari harus berjalan jauh untuk bisa melaksanakan sholat berjamaah. Kami ingin membantu membangun masjid di tengah-tengah mereka agar ibadah bisa dilakukan dengan lebih tenang dan khusyuk.",
"imageCeritaId": "cm60j9q3m000xc9dc584v8rj1"
}
},
{
"data": {
"authorId": "cmha6wb9w0001cfndwl9fcse6",
"title": "Bantuan Korban Bencana Alam",
"target": 75000000,
"donasiMaster_DurasiId": 3,
"donasiMaster_KategoriId": 3,
"namaBank": "Bank Rakyat Indonesia",
"rekening": "5678901234",
"imageId": "cm60j9q3m000xc9dc584v8rh8",
"pembukaan": "Membantu meringankan beban korban bencana alam berupa kebutuhan pokok dan kebutuhan darurat.",
"cerita": "Beberapa wilayah dilanda bencana banjir dan tanah longsor. Masyarakat kehilangan harta benda dan membutuhkan bantuan segera. Dana yang terkumpul akan digunakan untuk menyediakan makanan, obat-obatan, dan kebutuhan pokok lainnya.",
"imageCeritaId": "cm60j9q3m000xc9dc584v8rj2"
}
},
{
"data": {
"authorId": "cmha6wb9w0001cfndwl9fcse6",
"title": "Pengadaan Alat Medis Rumah Sakit",
"target": 150000000,
"donasiMaster_DurasiId": 3,
"donasiMaster_KategoriId": 3,
"namaBank": "Bank Negara Indonesia",
"rekening": "4321098765",
"imageId": "cm60j9q3m000xc9dc584v8rh8",
"pembukaan": "Meningkatkan kualitas pelayanan kesehatan dengan menyediakan alat medis yang lebih baik.",
"cerita": "Rumah sakit daerah kekurangan alat medis untuk melayani pasien. Melalui donasi ini, kami ingin membantu pengadaan alat-alat medis penting seperti ventilator, USG, dan alat laboratorium untuk meningkatkan kualitas pelayanan kesehatan.",
"imageCeritaId": "cm60j9q3m000xc9dc584v8rj3"
}
},
{
"data": {
"authorId": "cmha6wb9w0001cfndwl9fcse6",
"title": "Program Beasiswa Mahasiswa Berprestasi",
"target": 80000000,
"donasiMaster_DurasiId": 3,
"donasiMaster_KategoriId": 3,
"namaBank": "Bank Danamon",
"rekening": "1122334455",
"imageId": "cm60j9q3m000xc9dc584v8rh8",
"pembukaan": "Memberikan kesempatan kepada mahasiswa berprestasi untuk melanjutkan pendidikan tanpa beban biaya.",
"cerita": "Banyak mahasiswa berprestasi yang tidak mampu melanjutkan pendidikan karena keterbatasan biaya. Program beasiswa ini akan membantu mereka menyelesaikan kuliah hingga sarjana.",
"imageCeritaId": "cm60j9q3m000xc9dc584v8rj4"
}
},
{
"data": {
"authorId": "cmha6wb9w0001cfndwl9fcse6",
"title": "Pengadaan Air Bersih untuk Desa Kekeringan",
"target": 60000000,
"donasiMaster_DurasiId": 3,
"donasiMaster_KategoriId": 3,
"namaBank": "Bank Permata",
"rekening": "6677889900",
"imageId": "cm60j9q3m000xc9dc584v8rh8",
"pembukaan": "Menyediakan akses air bersih bagi masyarakat yang tinggal di daerah rawan kekeringan.",
"cerita": "Beberapa desa mengalami kekeringan setiap tahunnya, membuat warga kesulitan mendapatkan air bersih. Kami ingin membangun sumur bor dan sistem distribusi air untuk membantu masyarakat setempat.",
"imageCeritaId": "cm60j9q3m000xc9dc584v8rj5"
}
},
{
"data": {
"authorId": "cmha6wb9w0001cfndwl9fcse6",
"title": "Pengobatan Gratis untuk Warga Tidak Mampu",
"target": 40000000,
"donasiMaster_DurasiId": 3,
"donasiMaster_KategoriId": 3,
"namaBank": "Bank Panin",
"rekening": "9988776655",
"imageId": "cm60j9q3m000xc9dc584v8rh8",
"pembukaan": "Memberikan layanan kesehatan gratis bagi warga yang tidak mampu membayar biaya pengobatan.",
"cerita": "Banyak warga yang menunda pengobatan karena keterbatasan biaya. Melalui program ini, kami akan menyelenggarakan pengobatan gratis secara berkala di berbagai wilayah.",
"imageCeritaId": "cm60j9q3m000xc9dc584v8rj6"
}
},
{
"data": {
"authorId": "cmha6wb9w0001cfndwl9fcse6",
"title": "Pembangunan Taman Bacaan Masyarakat",
"target": 35000000,
"donasiMaster_DurasiId": 3,
"donasiMaster_KategoriId": 3,
"namaBank": "Bank Mega",
"rekening": "1357924680",
"imageId": "cm60j9q3m000xc9dc584v8rh8",
"pembukaan": "Membangun taman bacaan untuk meningkatkan minat baca masyarakat di wilayah pedesaan.",
"cerita": "Minat baca masyarakat di pedesaan masih rendah karena keterbatasan akses buku. Taman bacaan ini akan menyediakan ribuan buku gratis dan ruang baca yang nyaman untuk semua usia.",
"imageCeritaId": "cm60j9q3m000xc9dc584v8rj7"
}
},
{
"data": {
"authorId": "cmha6wb9w0001cfndwl9fcse6",
"title": "Pelatihan Keterampilan untuk Pengangguran",
"target": 55000000,
"donasiMaster_DurasiId": 3,
"donasiMaster_KategoriId": 3,
"namaBank": "Bank CIMB Niaga",
"rekening": "2468135790",
"imageId": "cm60j9q3m000xc9dc584v8rh8",
"pembukaan": "Memberikan pelatihan keterampilan untuk membantu pengangguran mendapatkan pekerjaan atau usaha mandiri.",
"cerita": "Angka pengangguran masih tinggi di beberapa wilayah. Program pelatihan ini akan memberikan keterampilan yang dibutuhkan pasar kerja, seperti menjahit, memasak, teknologi informasi, dan lainnya.",
"imageCeritaId": "cm60j9q3m000xc9dc584v8rj8"
}
},
{
"data": {
"authorId": "cmha6wb9w0001cfndwl9fcse6",
"title": "Renovasi Gedung Sekolah Rusak",
"target": 90000000,
"donasiMaster_DurasiId": 3,
"donasiMaster_KategoriId": 3,
"namaBank": "Bank OCBC NISP",
"rekening": "1029384756",
"imageId": "cm60j9q3m000xc9dc584v8rh8",
"pembukaan": "Merestrukturasi gedung sekolah yang rusak agar siswa bisa belajar dengan aman dan nyaman.",
"cerita": "Banyak gedung sekolah yang rusak parah dan membahayakan keselamatan siswa. Dana dari kampanye ini akan digunakan untuk renovasi dan perbaikan gedung sekolah yang membutuhkan.",
"imageCeritaId": "cm60j9q3m000xc9dc584v8rj9"
}
}
];
async function sendDonationData() {
const baseUrl = 'http://localhost:3000/api/mobile/donation'; // Sesuaikan dengan URL server Anda
const headers = {
'Content-Type': 'application/json',
};
for (let i = 0; i < donationDataList.length; i++) {
try {
console.log(`Mengirim data ke-${i + 1}...`);
const response = await axios.post(`${baseUrl}?category=permanent`, donationDataList[i], {
headers: headers
});
console.log(`Data ke-${i + 1} berhasil dikirim:`, response.data);
} catch (error) {
console.error(`Error saat mengirim data ke-${i + 1}:`, error.response?.data || error.message);
}
}
}
async function main() {
// Menjalankan fungsi untuk mengirim data donasi
await sendDonationData();
// Fungsi asli untuk update notifikasi
const result = await prisma.notifikasi.updateMany({
where: {
recipientId: 'cmha7p6yc0000cfoe5w2e7gdr',