diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e93ff79..b08dbab9 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.2.42](https://github.com/bipproduction/hipmi/compare/v1.2.41...v1.2.42) (2025-01-30) + ## [1.2.41](https://github.com/bipproduction/hipmi/compare/v1.2.40...v1.2.41) (2025-01-21) ## [1.2.40](https://github.com/bipproduction/hipmi/compare/v1.2.39...v1.2.40) (2025-01-16) diff --git a/package.json b/package.json index 81b515cf..20f4c0f2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.41", + "version": "1.2.42", "private": true, "prisma": { "seed": "bun prisma/seed.ts" diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 8fcbf136..eab897c0 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -11,7 +11,6 @@ datasource db { url = env("DATABASE_URL") } - model User { id String @id @default(cuid()) username String @unique @@ -182,6 +181,15 @@ model MasterStatus { Job Job[] } +model MasterStatusTransaksi { + id String @id @default(cuid()) + name String + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + EventTransaksi EventTransaksi[] +} + // -------------------- INVESTASI --------------------- // // Table investasi / saham model Investasi { @@ -983,16 +991,17 @@ model EventSponsor { } model EventTransaksi { - id String @id @default(cuid()) - isActive Boolean @default(true) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id String @id @default(cuid()) + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt nominal Int - MasterBank MasterBank? @relation(fields: [masterBankId], references: [id]) - masterBankId String? status String transferImageId String? + MasterBank MasterBank? @relation(fields: [masterBankId], references: [id]) + masterBankId String? + AuthorId User? @relation(fields: [authorId], references: [id]) authorId String? @@ -1001,4 +1010,7 @@ model EventTransaksi { EventSponsor EventSponsor? @relation(fields: [eventSponsorId], references: [id]) eventSponsorId String? @unique + + MasterStatusTransaksi MasterStatusTransaksi? @relation(fields: [masterStatusTransaksiId], references: [id]) + masterStatusTransaksiId String? } diff --git a/src/app/api/admin/donasi/dashboard/[name]/route.ts b/src/app/api/admin/donasi/dashboard/[name]/route.ts new file mode 100644 index 00000000..16a64c4a --- /dev/null +++ b/src/app/api/admin/donasi/dashboard/[name]/route.ts @@ -0,0 +1,50 @@ +import { prisma } from "@/app/lib"; +import backendLogger from "@/util/backendLogger"; +import { data } from "autoprefixer"; +import _ from "lodash"; +import { NextResponse } from "next/server"; + +export async function GET(request: Request, { params }: { + params: { name: string } +}) { + const method = request.method; + if (method !== "GET") { + return NextResponse.json({ + success: false, + message: "Method not allowed", + }, + { status: 405 } + ); + } + + const { name } = params; + try { + let fixData; + const fixStatus = _.startCase(name); + fixData = await prisma.donasi.count({ + where: { + DonasiMaster_Status: { + name: fixStatus + } + } + }); + return NextResponse.json({ + success: true, + message: "Success get data donasi dashboard", + data: fixData + }, + { status: 200 } + ) + } catch (error) { + backendLogger.error("Error get data donasi dashboard >>", error); + return NextResponse.json({ + success: false, + message: "Failed to get data donasi dashboard", + reason: (error as Error).message + }, + { status: 500 } + ) + } finally { + await prisma.$disconnect(); + } +} \ No newline at end of file diff --git a/src/app/api/admin/event/dashboard/[name]/route.ts b/src/app/api/admin/event/dashboard/[name]/route.ts new file mode 100644 index 00000000..9ebc3310 --- /dev/null +++ b/src/app/api/admin/event/dashboard/[name]/route.ts @@ -0,0 +1,53 @@ +import { prisma } from "@/app/lib"; +import backendLogger from "@/util/backendLogger"; +import _ from "lodash"; +import { NextResponse } from "next/server"; + +export async function GET( + request: Request, + { params }: { params: { name: string } } +) { + const method = request.method; + if (method !== "GET") { + return NextResponse.json( + { success: false, message: "Method not allowed" }, + { status: 405 } + ); + } + + const { name } = params; + + try { + let fixData; + const fixStatus = _.startCase(name); + fixData = await prisma.event.count({ + where: { + EventMaster_Status: { + name: fixStatus, + }, + isArsip: false, + }, + }); + + return NextResponse.json( + { + success: true, + message: "Success get data event dashboard", + data: fixData, + }, + { status: 200 } + ); + } catch (error) { + backendLogger.error("Error get data event dashboard >>", error); + return NextResponse.json( + { + success: false, + message: "Failed to get data", + reason: (error as Error).message, + }, + { status: 500 } + ); + } finally { + await prisma.$disconnect(); + } +} diff --git a/src/app/api/admin/event/dashboard/riwayat/route.ts b/src/app/api/admin/event/dashboard/riwayat/route.ts new file mode 100644 index 00000000..377d371d --- /dev/null +++ b/src/app/api/admin/event/dashboard/riwayat/route.ts @@ -0,0 +1,47 @@ +import { prisma } from "@/app/lib"; +import backendLogger from "@/util/backendLogger"; +import _ from "lodash"; +import { NextResponse } from "next/server"; + +export async function GET(request: Request) { + const method = request.method; + if (method !== "GET") { + return NextResponse.json( + { success: false, message: "Method not allowed" }, + { status: 405 } + ); + } + + try { + let fixData; + fixData = await prisma.event.count({ + where: { + EventMaster_Status: { + name: "Publish", + }, + isArsip: true, + }, + }); + + return NextResponse.json( + { + success: true, + message: "Success get data riwayat event dashboard", + data: fixData, + }, + { status: 200 } + ); + } catch (error) { + backendLogger.error("Error get data riwayat event dashboard >>", error); + return NextResponse.json( + { + success: false, + message: "Failed to get data", + reason: (error as Error).message, + }, + { status: 500 } + ); + } finally { + await prisma.$disconnect(); + } +} diff --git a/src/app/api/admin/event/dashboard/tipe-acara/route.ts b/src/app/api/admin/event/dashboard/tipe-acara/route.ts new file mode 100644 index 00000000..e4d22fdf --- /dev/null +++ b/src/app/api/admin/event/dashboard/tipe-acara/route.ts @@ -0,0 +1,44 @@ +import { prisma } from "@/app/lib"; +import backendLogger from "@/util/backendLogger"; +import _ from "lodash"; +import { NextResponse } from "next/server"; + +export async function GET(request: Request) { + const method = request.method; + if (method !== "GET") { + return NextResponse.json( + { success: false, message: "Method not allowed" }, + { status: 405 } + ); + } + + try { + let fixData; + fixData = await prisma.eventMaster_TipeAcara.count({ + where: { + active: true, + }, + }); + + return NextResponse.json( + { + success: true, + message: "Success get data riwayat event dashboard", + data: fixData, + }, + { status: 200 } + ); + } catch (error) { + backendLogger.error("Error get data riwayat event dashboard >>", error); + return NextResponse.json( + { + success: false, + message: "Failed to get data", + reason: (error as Error).message, + }, + { status: 500 } + ); + } finally { + await prisma.$disconnect(); + } +} diff --git a/src/app/api/admin/investasi/dashboard/[name]/route.ts b/src/app/api/admin/investasi/dashboard/[name]/route.ts index 8ed70f14..c577c34b 100644 --- a/src/app/api/admin/investasi/dashboard/[name]/route.ts +++ b/src/app/api/admin/investasi/dashboard/[name]/route.ts @@ -1,24 +1,41 @@ import { prisma } from "@/app/lib"; import backendLogger from "@/util/backendLogger"; +import _ from "lodash"; import { NextResponse } from "next/server"; -export async function GET(request: Request) { +export async function GET(request: Request, { params }: { params: { name: string } }) { + const method = request.method; + if (method !== "GET") { + return NextResponse.json({ + success: false, + message: "Method not allowed", + }, + { status: 405 } + ); + } + const { name } = params; try { - const data = await prisma.investasi.count({ + let fixData; + const fixStatus = _.startCase(name); + fixData = await prisma.investasi.count({ where: { - active: true - }, + MasterStatusInvestasi: { + name: fixStatus + }, + } }) return NextResponse.json({ - message: "Data Investasi", - data: data, + success: true, + message: "Success get data investasi dashboard", + data: fixData, }, { status: 200 } ) } catch (error) { - backendLogger.error("Error Get Count Investasi Main Dashboard") + backendLogger.error("Error get data investasi dashboard >>", error); return NextResponse.json({ - message: "Error Get Count Investasi Main Dashboard", + success: false, + message: "Error get data investasi dashboard", reason: (error as Error).message }, { status: 500 } diff --git a/src/app/api/event/[id]/route.ts b/src/app/api/event/[id]/route.ts index cde5cdba..0d7214c7 100644 --- a/src/app/api/event/[id]/route.ts +++ b/src/app/api/event/[id]/route.ts @@ -32,19 +32,17 @@ export async function GET( }, }); - await prisma.$disconnect(); return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: fixData, }); } catch (error) { - await prisma.$disconnect(); return NextResponse.json( { success: false, message: "Gagal mendapatkan data" }, { status: 500 } ); + } finally { + await prisma.$disconnect(); } } - - diff --git a/src/app/api/master/bank/route.ts b/src/app/api/master/bank/route.ts new file mode 100644 index 00000000..2ce3228b --- /dev/null +++ b/src/app/api/master/bank/route.ts @@ -0,0 +1,41 @@ +import { prisma } from "@/app/lib"; +import backendLogger from "@/util/backendLogger"; +import { NextResponse } from "next/server"; + +export async function GET(request: Request) { + const method = request.method; + if (method !== "GET") { + return NextResponse.json( + { success: false, message: "Method not allowed" }, + { status: 405 } + ); + } + + try { + const res = await prisma.masterBank.findMany({ + orderBy: { + updatedAt: "asc", + }, + where: { + isActive: true, + }, + }); + + await prisma.$disconnect(); + return NextResponse.json( + { success: true, message: "Berhasil mendapatkan data", data: res }, + { status: 200 } + ); + } catch (error) { + await prisma.$disconnect(); + backendLogger.error("Error Get Master Bank >>", error); + return NextResponse.json( + { + success: false, + message: "API Error Get Data", + reason: (error as Error).message, + }, + { status: 500 } + ); + } +} diff --git a/src/app/api/master/status_transaksi/route.ts b/src/app/api/master/status_transaksi/route.ts new file mode 100644 index 00000000..e7abd5e0 --- /dev/null +++ b/src/app/api/master/status_transaksi/route.ts @@ -0,0 +1,41 @@ +import { prisma } from "@/app/lib"; +import backendLogger from "@/util/backendLogger"; +import { NextResponse } from "next/server"; + +export async function GET(request: Request) { + const method = request.method; + if (method !== "GET") { + return NextResponse.json( + { success: false, message: "Method not allowed" }, + { status: 405 } + ); + } + + try { + const res = await prisma.masterStatusTransaksi.findMany({ + orderBy: { + updatedAt: "asc", + }, + where: { + isActive: true, + }, + }); + + await prisma.$disconnect(); + return NextResponse.json( + { success: true, message: "Berhasil mendapatkan data", data: res }, + { status: 200 } + ); + } catch (error) { + await prisma.$disconnect(); + backendLogger.error("Error Get Master Status Transaksi >>", error); + return NextResponse.json( + { + success: false, + message: "API Error Get Data", + reason: (error as Error).message, + }, + { status: 500 } + ); + } +} diff --git a/src/app/dev/admin/event/_lib/api_fecth_admin_event.ts b/src/app/dev/admin/event/_lib/api_fecth_admin_event.ts new file mode 100644 index 00000000..0fe61529 --- /dev/null +++ b/src/app/dev/admin/event/_lib/api_fecth_admin_event.ts @@ -0,0 +1,60 @@ +export { + apiGetEventStatusCountDashboard, + apiGetEventTipeAcara, + apiGetEventRiwayatCount, +}; + +const apiGetEventStatusCountDashboard = async ({ + name, +}: { + name: "Publish" | "Review" | "Reject"; +}) => { + const { token } = await fetch("/api/get-cookie").then((res) => res.json()); + if (!token) return await token.json().catch(() => null); + + const response = await fetch(`/api/admin/event/dashboard/${name}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + "Access-Control-Allow-Origin": "*", + Authorization: `Bearer ${token}`, + }, + }); + + return await response.json().catch(() => null); +}; + +const apiGetEventRiwayatCount = async () => { + const { token } = await fetch("/api/get-cookie").then((res) => res.json()); + if (!token) return await token.json().catch(() => null); + + const response = await fetch(`/api/admin/event/dashboard/riwayat`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + "Access-Control-Allow-Origin": "*", + Authorization: `Bearer ${token}`, + }, + }); + + return await response.json().catch(() => null); +} + +const apiGetEventTipeAcara = async () => { + const { token } = await fetch("/api/get-cookie").then((res) => res.json()); + if (!token) return await token.json().catch(() => null); + + const response = await fetch(`/api/admin/event/dashboard/tipe-acara`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + "Access-Control-Allow-Origin": "*", + Authorization: `Bearer ${token}`, + }, + }); + + return await response.json().catch(() => null); +}; diff --git a/src/app/dev/admin/event/main/page.tsx b/src/app/dev/admin/event/main/page.tsx index abd71c1e..21e83625 100644 --- a/src/app/dev/admin/event/main/page.tsx +++ b/src/app/dev/admin/event/main/page.tsx @@ -1,26 +1,9 @@ import { AdminEvent_Main } from "@/app_modules/admin/event"; -import AdminEvent_funCountByStatusId from "@/app_modules/admin/event/fun/count/fun_count_event_by_status_id"; -import { AdminEvent_funCountRiwayat } from "@/app_modules/admin/event/fun/count/fun_count_riwayat"; -import { AdminEvent_funCountTipeAcara } from "@/app_modules/admin/event/fun/count/fun_count_tipe_acara"; export default async function Page() { - const countPublish = await AdminEvent_funCountByStatusId("1"); - const countReview = await AdminEvent_funCountByStatusId("2"); - const countDraft = await AdminEvent_funCountByStatusId("3"); - const countReject = await AdminEvent_funCountByStatusId("4"); - const countTipeAcara = await AdminEvent_funCountTipeAcara(); - const countRiwayat = await AdminEvent_funCountRiwayat(); - return ( <> - + ); } diff --git a/src/app/dev/admin/investasi/main/page.tsx b/src/app/dev/admin/investasi/main/page.tsx index fdeee026..cf6eb9a9 100644 --- a/src/app/dev/admin/investasi/main/page.tsx +++ b/src/app/dev/admin/investasi/main/page.tsx @@ -6,10 +6,10 @@ import Admin_getTotalInvestasiByUser from "@/app_modules/admin/investasi/fun/get export default async function Page() { const listInvestasi = await Admin_funGetAllInvestasi(); - const countDraft = await Admin_CountStatusInvestasi(1); - const countReview = await Admin_CountStatusInvestasi(2); - const countPublish = await Admin_CountStatusInvestasi(3); - const countReject = await Admin_CountStatusInvestasi(4); + // const countDraft = await Admin_CountStatusInvestasi(1); + // const countReview = await Admin_CountStatusInvestasi(2); + // const countPublish = await Admin_CountStatusInvestasi(3); + // const countReject = await Admin_CountStatusInvestasi(4); const totalInvestasiByUser = await Admin_getTotalInvestasiByUser() const publishProgres = await Admin_getPublishProgresInvestasi() // console.log(targetTerbesar) @@ -18,10 +18,10 @@ export default async function Page() { <> + + + ); +} + +export default Page; diff --git a/src/app/dev/event/invoice/page.tsx b/src/app/dev/event/invoice/page.tsx deleted file mode 100644 index 9c6a990b..00000000 --- a/src/app/dev/event/invoice/page.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import Event_Invoice from '@/app_modules/event/detail/invoice'; -import React from 'react'; - -function Page() { - return ( - <> - - - ); -} - -export default Page; diff --git a/src/app/lib/limit.ts b/src/app/lib/limit.ts new file mode 100644 index 00000000..1be48f04 --- /dev/null +++ b/src/app/lib/limit.ts @@ -0,0 +1,5 @@ +import pLimit from "p-limit"; + +const global_limit = pLimit(1); + +export default global_limit; \ No newline at end of file diff --git a/src/app/lib/new_fun_user_id.ts b/src/app/lib/new_fun_user_id.ts deleted file mode 100644 index c147ff22..00000000 --- a/src/app/lib/new_fun_user_id.ts +++ /dev/null @@ -1,28 +0,0 @@ -"use server"; - -import _ from "lodash"; -import { cookies } from "next/headers"; -import { decrypt } from "../auth/_lib/decrypt"; -import backendLogger from "@/util/backendLogger"; - -export async function newFunGetUserId() { - try { - const key = process.env.NEXT_PUBLIC_BASE_SESSION_KEY; - const c = cookies().get("hipmi-key"); - - if (!c || !c?.value || _.isEmpty(c?.value) || _.isUndefined(c?.value)) { - return null; - } - - const token = c.value; - const dataUser = await decrypt({ - token: token, - encodedKey: process.env.NEXT_PUBLIC_BASE_TOKEN_KEY!, - }); - - return dataUser?.id; - } catch (error) { - backendLogger.log("Gagal mendapatkan user id", error); - return null; - } -} diff --git a/src/app/lib/router_hipmi/router_event.ts b/src/app/lib/router_hipmi/router_event.ts index c98a3b9e..41154204 100644 --- a/src/app/lib/router_hipmi/router_event.ts +++ b/src/app/lib/router_hipmi/router_event.ts @@ -43,7 +43,11 @@ export const RouterEvent = { `/dev/event/detail/sponsor/tambah_sponsor/${id}`, detail_sponsor: ({ id }: { id: string }) => `/dev/event/detail/detail_sponsor/${id}`, - nominal_sponsor: ({ id }: { id: string }) => `/dev/event/detail/sponsor/nominal_sponsor/${id}`, + + metode_pembayaran: ({ id }: { id: string }) => + `/dev/event/detail/sponsor/metode_pembayaran/${id}`, + invoice: ({ id }: { id: string }) => + `/dev/event/invoice/${id}`, }; diff --git a/src/app_modules/_global/fun/generate_seeder.ts b/src/app_modules/_global/fun/generate_seeder.ts index 3704edf4..3e549c78 100644 --- a/src/app_modules/_global/fun/generate_seeder.ts +++ b/src/app_modules/_global/fun/generate_seeder.ts @@ -25,6 +25,7 @@ import voting_status from "../../../bin/seeder/voting/master_status.json"; import { master_kategori_app } from "@/bin/seeder/master"; import { new_status_transaksi_investasi } from "@/bin/seeder/investasi"; import { master_nama_bank } from "@/bin/seeder/master"; +import { master_status_transaksi } from "@/bin/seeder/master"; import pLimit from "p-limit"; async function masterUserRole() { @@ -543,6 +544,26 @@ async function masterInvestasiNewTransaksiStatus() { console.log("masterInvestasiNewTransaksiStatus success"); } +async function masterStatusTransaksi() { + for (let a of master_status_transaksi) { + await prisma.masterStatusTransaksi.upsert({ + where: { + id: a.id, + }, + create: { + id: a.id, + name: a.name, + }, + update: { + id: a.id, + name: a.name, + }, + }); + } + + console.log("masterStatusTransaksi success"); +} + const listSeederQueue = [ masterUserRole, seederUser, @@ -570,6 +591,7 @@ const listSeederQueue = [ seederNomorAdmin, masterKategoriApp, masterInvestasiNewTransaksiStatus, + masterStatusTransaksi, ]; const limit = pLimit(1); diff --git a/src/app_modules/_global/lib/api_master.ts b/src/app_modules/_global/lib/api_master.ts new file mode 100644 index 00000000..7443c12e --- /dev/null +++ b/src/app_modules/_global/lib/api_master.ts @@ -0,0 +1,19 @@ +export { apiGetMasterBank }; + +const apiGetMasterBank = async () => { + const { token } = await fetch("/api/get-cookie").then((res) => res.json()); + if (!token) return await token.json().catch(() => null); + + const respone = await fetch(`/api/master/bank`, { + headers: { + "Content-Type": "application/json", + Accept: "application/json", + "Access-Control-Allow-Origin": "*", + Authorization: `Bearer ${token}`, + }, + }); + return await respone.json().catch(() => null); +}; + + + diff --git a/src/app_modules/admin/donasi/lib/api_fetch_admin_donasi.ts b/src/app_modules/admin/donasi/lib/api_fetch_admin_donasi.ts new file mode 100644 index 00000000..f1852371 --- /dev/null +++ b/src/app_modules/admin/donasi/lib/api_fetch_admin_donasi.ts @@ -0,0 +1,16 @@ +const apiGetDonasiStatusCountDashboard = async ({ name }: + { name: "Publish" | "Review" | "Reject" }) => { + const { token } = await fetch("/api/get-cookie").then((res) => res.json()); + if (!token) return await token.json().catch(() => null); + + const response = await fetch(`api/admin/donasi/dashboard/${name}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + "Access-Control-Allow-Origin": "*", + Authorization: `Bearer ${token}`, + } + }); + return await response.json().catch(() => null); +}; \ No newline at end of file diff --git a/src/app_modules/admin/event/main/index.tsx b/src/app_modules/admin/event/main/index.tsx index 0f4db2b9..468d25d3 100644 --- a/src/app_modules/admin/event/main/index.tsx +++ b/src/app_modules/admin/event/main/index.tsx @@ -2,7 +2,16 @@ import { RouterAdminEvent } from "@/app/lib/router_admin/router_admin_event"; +import { + apiGetEventRiwayatCount, + apiGetEventStatusCountDashboard, + apiGetEventTipeAcara, +} from "@/app/dev/admin/event/_lib/api_fecth_admin_event"; +import global_limit from "@/app/lib/limit"; import { AccentColor, MainColor } from "@/app_modules/_global/color"; +import { AdminColor } from "@/app_modules/_global/color/color_pallet"; +import CustomSkeleton from "@/app_modules/components/CustomSkeleton"; +import { clientLogger } from "@/util/clientLogger"; import { Flex, Paper, @@ -10,81 +19,192 @@ import { Stack, Text, ThemeIcon, - Title + Title, } from "@mantine/core"; -import { IconAlertTriangle, IconBookmark, IconBriefcase, IconHistory, IconUpload } from "@tabler/icons-react"; +import { useShallowEffect } from "@mantine/hooks"; +import { + IconAlertTriangle, + IconBookmark, + IconBriefcase, + IconHistory, + IconUpload, +} from "@tabler/icons-react"; import { useRouter } from "next/navigation"; +import { useState } from "react"; import ComponentAdminGlobal_HeaderTamplate from "../../_admin_global/header_tamplate"; -import { AdminColor } from "@/app_modules/_global/color/color_pallet"; -export default function AdminEvent_Main({ - countPublish, - countReview, - countDraft, - countReject, - countTipeAcara, - countRiwayat, -}: { - countPublish: number; - countReview: number; - countDraft: number; - countReject: number; - countTipeAcara: number; - countRiwayat: number -}) { +export default function AdminEvent_Main() { const router = useRouter(); + const [countPublish, setCountPublish] = useState(null); + const [countReview, setCountReview] = useState(null); + const [countReject, setCountReject] = useState(null); + const [countTipeAcara, setCountTipeAcara] = useState(null); + const [countRiwayat, setCountRiwayat] = useState(null); + + useShallowEffect(() => { + handlerLoadData(); + }, []); + + async function handlerLoadData() { + try { + const listLoadData = [ + global_limit(() => onLoadCountPublish()), + global_limit(() => onLoadCountReview()), + global_limit(() => onLoadCountReject()), + global_limit(() => onLoadCountRiwayat()), + global_limit(() => onLoadCountTipeAcara()), + ]; + const result = await Promise.all(listLoadData); + } catch (error) { + clientLogger.error("Error handler load data", error); + } + } + + async function onLoadCountPublish() { + try { + const respone = await apiGetEventStatusCountDashboard({ + name: "Publish", + }); + + if (respone) { + setCountPublish(respone.data); + } + } catch (error) { + clientLogger.error("Error get count publish", error); + } + } + + async function onLoadCountReview() { + try { + const respone = await apiGetEventStatusCountDashboard({ + name: "Review", + }); + + if (respone) { + setCountReview(respone.data); + } + } catch (error) { + clientLogger.error("Error get count review", error); + } + } + + async function onLoadCountReject() { + try { + const respone = await apiGetEventStatusCountDashboard({ + name: "Reject", + }); + + if (respone) { + setCountReject(respone.data); + } + } catch (error) { + clientLogger.error("Error get count reject", error); + } + } + + async function onLoadCountRiwayat() { + try { + const respone = await apiGetEventRiwayatCount(); + + if (respone) { + setCountRiwayat(respone.data); + } + } catch (error) { + clientLogger.error("Error get count riwayat", error); + } + } + + async function onLoadCountTipeAcara() { + try { + const respone = await apiGetEventTipeAcara(); + + if (respone) { + setCountTipeAcara(respone.data); + } + } catch (error) { + clientLogger.error("Error get count tipe acara", error); + } + } const listStatus = [ { id: 1, name: "Publish", - jumlah: countPublish, + jumlah: + countPublish == null ? ( + + ) : countPublish ? ( + countPublish + ) : ( + "-" + ), path: RouterAdminEvent.table_publish, color: MainColor.green, - icon: , + icon: , }, { id: 2, name: "Review", - jumlah: countReview, + jumlah: + countReview == null ? ( + + ) : countReview ? ( + countReview + ) : ( + "-" + ), path: RouterAdminEvent.table_review, color: MainColor.orange, - icon: + icon: , }, - // { - // id: 3, - // name: "Draft", - // jumlah: countDraft, - // path: "", - // color: "yellow", - // }, + { id: 3, name: "Reject", - jumlah: countReject, + jumlah: + countReject == null ? ( + + ) : countReject ? ( + countReject + ) : ( + "-" + ), path: RouterAdminEvent.table_reject, color: MainColor.red, - icon: + icon: , }, { id: 4, name: "Riwayat Event", - jumlah: countRiwayat, + jumlah: + countRiwayat == null ? ( + + ) : countRiwayat ? ( + countRiwayat + ) : ( + "-" + ), path: RouterAdminEvent.table_publish, color: AccentColor.softblue, - icon: + icon: , }, ]; const listBox2 = [ - { id: 1, name: "Tipe Acara", - jumlah: countTipeAcara, + jumlah: + countTipeAcara == null ? ( + + ) : countTipeAcara ? ( + countTipeAcara + ) : ( + "-" + ), path: RouterAdminEvent.table_publish, color: "#A888E2", - icon: + icon: , }, ]; @@ -109,19 +229,23 @@ export default function AdminEvent_Main({ shadow="md" radius="md" p="md" - // sx={{ borderColor: e.color, borderStyle: "solid" }} + // sx={{ borderColor: e.color, borderStyle: "solid" }} > - - {e.name} + + {e.name} + {e.jumlah} - + {e.icon} - ))} @@ -141,17 +265,19 @@ export default function AdminEvent_Main({ shadow="md" radius="md" p="md" - // sx={{ borderColor: e.color, borderStyle: "solid" }} + // sx={{ borderColor: e.color, borderStyle: "solid" }} > - - {e.name} - - {e.jumlah} - - {e.icon} - - - + + + {e.name} + + + {e.jumlah} + + {e.icon} + + + ))} diff --git a/src/app_modules/admin/investasi/_lib/api_fetch_count_status.ts b/src/app_modules/admin/investasi/_lib/api_fetch_count_status.ts new file mode 100644 index 00000000..ed19bd40 --- /dev/null +++ b/src/app_modules/admin/investasi/_lib/api_fetch_count_status.ts @@ -0,0 +1,22 @@ +const apiGetInvestasiCountDashboard = async ({ name }: + { name: "Publish" | "Review" | "Reject" }) => { + + + const { token } = await fetch("/api/get-cookie").then((res) => res.json()); + if (!token) return await token.json().catch(() => null); + + + const response = await fetch(`/api/admin/investasi/dashboard/${name}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + "Access-Control-Allow-Origin": "*", + Authorization: `Bearer ${token}`, + }, + }) + + return await response.json().catch(() => null); +}; + +export default apiGetInvestasiCountDashboard; \ No newline at end of file diff --git a/src/app_modules/admin/investasi/main/view.tsx b/src/app_modules/admin/investasi/main/view.tsx index 5f9c3c8a..c27312dc 100644 --- a/src/app_modules/admin/investasi/main/view.tsx +++ b/src/app_modules/admin/investasi/main/view.tsx @@ -46,49 +46,129 @@ import TablePublikasiProgresInvestasi from "./table_publikasi_progres"; import ComponentAdminGlobal_HeaderTamplate from "../../_admin_global/header_tamplate"; import { AccentColor, MainColor } from "@/app_modules/_global/color"; import { AdminColor } from "@/app_modules/_global/color/color_pallet"; +import { clientLogger } from "@/util/clientLogger"; +import CustomSkeleton from "@/app_modules/components/CustomSkeleton"; +import global_limit from "@/app/lib/limit"; +import { useShallowEffect } from "@mantine/hooks"; +import apiGetInvestasiCountDashboard from "../_lib/api_fetch_count_status"; export default function Admin_Investasi({ listInvestasi, - countDraft, - countReview, - countPublish, - countReject, totalInvestasiByUser, publishProgres, }: { listInvestasi: MODEL_INVESTASI[]; - countDraft: number | any; - countReview: number | any; - countPublish: number | any; - countReject: number | any; totalInvestasiByUser: any[]; publishProgres: any[]; }) { const [investasi, setInvestasi] = useState(listInvestasi); const router = useRouter(); + const [countPublish, setCountPublish] = useState(null); + const [countReview, setCountReview] = useState(null); + const [countReject, setCountReject] = useState(null); + + useShallowEffect(() => { + handlerLoadData(); + }, []) + async function handlerLoadData() { + try { + const listLoadData = [ + global_limit(() => onLoadCountPublish()), + global_limit(() => onLoadCountReview()), + global_limit(() => onLoadCountReject()), + ]; + + + const result = await Promise.all(listLoadData); + } catch (error) { + clientLogger.error("Error handler load data", error); + } + } + + async function onLoadCountPublish() { + try { + + const response = await apiGetInvestasiCountDashboard({ + name: "Publish", + }); + + console.log("Response Publish", response); + + if (response) { + setCountPublish(response.data); + } + } catch (error) { + clientLogger.error("Error get count publish", error); + } + } + async function onLoadCountReview() { + try { + const response = await apiGetInvestasiCountDashboard({ + name: "Review", + }); + + if (response) { + setCountReview(response.data); + } + } catch (error) { + clientLogger.error("Error get count review", error); + } + } + async function onLoadCountReject() { + try { + const response = await apiGetInvestasiCountDashboard({ + name: "Reject", + }); + + if (response) { + setCountReject(response.data); + } + } catch (error) { + clientLogger.error("Error get count reject", error); + } + } const listBox = [ { id: 1, name: "Publish", - jumlah: countPublish, - link: RouterAdminInvestasi_OLD.table_status_publish, + jumlah: + countPublish == null ? ( + + ) : countPublish ? ( + countPublish + ) : ( + "-" + ), + path: RouterAdminInvestasi_OLD.table_status_publish, color: MainColor.green, icon: , }, { id: 2, name: "Review", - jumlah: countReview, - link: RouterAdminInvestasi_OLD.table_status_review, + jumlah: countReview == null ? ( + + ) : countReview ? ( + countReview + ) : ( + "-" + ), + path: RouterAdminInvestasi_OLD.table_status_review, color: MainColor.orange, icon: }, { id: 3, name: "Reject", - jumlah: countReject, - link: RouterAdminInvestasi_OLD.table_status_reject, + jumlah: countReject == null ? ( + + ) : countReject ? ( + countReject + ) : ( + "-" + ), + path: RouterAdminInvestasi_OLD.table_status_reject, color: MainColor.red, icon: diff --git a/src/app_modules/admin/main_dashboard/main/view.tsx b/src/app_modules/admin/main_dashboard/main/view.tsx index 2d8fc67c..8acc4b4c 100644 --- a/src/app_modules/admin/main_dashboard/main/view.tsx +++ b/src/app_modules/admin/main_dashboard/main/view.tsx @@ -10,6 +10,7 @@ import { apiGetCountPortofolio, apiGetCountUserActive } from "../lib/api_fetch_m import { NextResponse } from "next/server"; import { clientLogger } from "@/util/clientLogger"; import MainDashboardSkeleton from "../../_admin_global/_component/skeleton/main_dashboard_skeleton"; +import CustomSkeleton from "@/app_modules/components/CustomSkeleton"; export default function AdminMain() { const [countUser, setCountUser] = useState(null); @@ -34,7 +35,7 @@ export default function AdminMain() { clientLogger.error("Error get count user data", error); } } - + async function onLoadDataPortofolio() { try { const response = await apiGetCountPortofolio(); @@ -52,14 +53,26 @@ export default function AdminMain() { { id: 1, name: "User", - jumlah: countUser, + jumlah: + countUser == null ? ( + ) : countUser ? ( + countUser + ) : ( + "-" + ), link: "", icon: }, { id: 2, name: "Portofolio", - jumlah: countPortofolio, + jumlah: + countPortofolio == null ? ( + ) : countPortofolio ? ( + countPortofolio + ) : ( + "-" + ), link: "", icon: }, @@ -68,35 +81,31 @@ export default function AdminMain() { return ( <> - {!countUser && !countPortofolio ? ( - - ) : ( - - - {/* Main Dashboard + + + {/* Main Dashboard */} - - {listBox.map((e) => ( - - - - {e.name} - - {e.jumlah} - {e.icon} - - - - - ))} - - - {/* */} + + {listBox.map((e) => ( + + + + {e.name} + + {e.jumlah} + {e.icon} + + + - - - )} + ))} + + + {/* */} + + + ); } diff --git a/src/app_modules/event/detail/invoice/index.tsx b/src/app_modules/event/detail/invoice/index.tsx index 127b1fd5..d3a4ee70 100644 --- a/src/app_modules/event/detail/invoice/index.tsx +++ b/src/app_modules/event/detail/invoice/index.tsx @@ -1,10 +1,23 @@ -'use client'; -import { AccentColor, MainColor } from '@/app_modules/_global/color'; -import { ActionIcon, Button, Grid, Group, Paper, Stack, Text, Title } from '@mantine/core'; -import { IconCamera } from '@tabler/icons-react'; -import React from 'react'; +"use client"; + +import { AccentColor, MainColor } from "@/app_modules/_global/color"; +import { Button, Grid, Group, Paper, Stack, Text, Title } from "@mantine/core"; +import { IconCamera } from "@tabler/icons-react"; +import { useAtom } from "jotai"; +import { useParams, useRouter } from "next/navigation"; +import { gs_nominal_sponsor, gs_event_bank_id } from "../../global_state"; + +function Event_Invoice({ userLoginId }: { userLoginId: string }) { + const router = useRouter(); + const params = useParams<{ id: string }>(); + const sponsorId = params.id; + + const [nominal, setNominal] = useAtom(gs_nominal_sponsor); + const [bankId, setBankId] = useAtom(gs_event_bank_id); + + console.log("nominal >>", nominal); + console.log("bankId >>", bankId); -function Event_Invoice() { return ( <> @@ -54,17 +67,19 @@ function Event_Invoice() { > - + 9065456754325643 - - @@ -101,15 +116,19 @@ function Event_Invoice() { > - + Rp. 100.000 - - @@ -131,15 +150,22 @@ function Event_Invoice() { }} > - - - Upload bukti transfer anda + + Upload bukti transfer anda + - diff --git a/src/app_modules/event/detail/sponsor/metode_pembayaran/index.tsx b/src/app_modules/event/detail/sponsor/metode_pembayaran/index.tsx index 365af7dc..18ad624e 100644 --- a/src/app_modules/event/detail/sponsor/metode_pembayaran/index.tsx +++ b/src/app_modules/event/detail/sponsor/metode_pembayaran/index.tsx @@ -1,84 +1,130 @@ -'use client'; +"use client"; -import { AccentColor, MainColor } from '@/app_modules/_global/color'; -import { MODEL_MASTER_BANK } from '@/app_modules/investasi/_lib/interface'; -import { Button, Paper, Radio, Stack, Title } from '@mantine/core'; -import { useRouter } from 'next/navigation'; -import React, { useState } from 'react'; - -const bank = [ - { - id: 1, - namaBank: "BRI", - }, - { - id: 2, - namaBank: "BCA", - }, - { - id: 3, - namaBank: "BNI", - }, - { - id: 4, - namaBank: "BSI", - } -] +import { RouterEvent } from "@/app/lib/router_hipmi/router_event"; +import { AccentColor, MainColor } from "@/app_modules/_global/color"; +import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; +import { apiGetMasterBank } from "@/app_modules/_global/lib/api_master"; +import CustomSkeleton from "@/app_modules/components/CustomSkeleton"; +import { + gs_event_bank_id, + gs_nominal_sponsor, +} from "@/app_modules/event/global_state"; +import { MODEL_MASTER_BANK } from "@/app_modules/investasi/_lib/interface"; +import { clientLogger } from "@/util/clientLogger"; +import { Button, Paper, Radio, Stack, Title } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import { useAtom } from "jotai"; +import _ from "lodash"; +import { useParams, useRouter } from "next/navigation"; +import { useState } from "react"; function Event_MetodePembayaran() { + const params = useParams<{ id: string }>(); const [pilihBank, setPilihBank] = useState(""); const router = useRouter(); + const [nominal, setNominal] = useAtom(gs_nominal_sponsor); + const [bankId, setBankId] = useAtom(gs_event_bank_id); + const [isLoading, setIsLoading] = useState(false); + console.log("nominal >>", nominal); + + const [data, setData] = useState(null); + useShallowEffect(() => { + onLoadBank(); + }, []); + + async function onLoadBank() { + try { + const respone = await apiGetMasterBank(); + + if (respone) { + setData(respone.data); + } + } catch (error) { + clientLogger.error("Error get data bank", error); + } + } + + if (!data) { + return ( + <> + + {Array.from({ length: 2 }).map((e, i) => ( + + ))} + + + ); + } + return ( <> - - - {bank.map((e) => ( - + ) : ( + + + {data.map((e) => ( + + + {e.namaBank} + + } + /> + + ))} + + + - + Pilih + + + )} ); } -export default Event_MetodePembayaran; \ No newline at end of file +export default Event_MetodePembayaran; diff --git a/src/app_modules/event/detail/sponsor/nominal_sponsor/index.tsx b/src/app_modules/event/detail/sponsor/nominal_sponsor/index.tsx index 846ec486..3f437159 100644 --- a/src/app_modules/event/detail/sponsor/nominal_sponsor/index.tsx +++ b/src/app_modules/event/detail/sponsor/nominal_sponsor/index.tsx @@ -1,5 +1,7 @@ "use client"; +import { RouterEvent } from "@/app/lib/router_hipmi/router_event"; import { AccentColor, MainColor } from "@/app_modules/_global/color"; +import { gs_nominal_sponsor } from "@/app_modules/event/global_state"; import { Box, Button, @@ -9,6 +11,7 @@ import { Text, TextInput, Title, + Loader, } from "@mantine/core"; import { IconChevronRight, @@ -17,35 +20,42 @@ import { IconMoodSmileDizzy, IconMoodXd, } from "@tabler/icons-react"; +import { useAtom } from "jotai"; import { useParams, useRouter } from "next/navigation"; -import React from "react"; +import { useState } from "react"; const listNominal = [ { id: 1, - jumlah: " 25.000", + value: 25000, icon: , }, { id: 2, - jumlah: " 50.000", + value: 50000, icon: , }, { id: 3, - jumlah: " 75.000", + value: 75000, icon: , }, { id: 4, - jumlah: " 100.000", + value: 100000, icon: , }, ]; function Event_PilihNominalSponsor() { const params = useParams<{ id: string }>(); const router = useRouter(); - + const [inputNominal, setInputNominal] = useState(""); + const [valueNominal, setValueNominal] = useState(0); + const [fixNominal, setFixNominal] = useAtom(gs_nominal_sponsor); + const [isLoading, setLoading] = useState(false); + const [isLoadingPaper, setLoadingPaper] = useState(false); + const [chooseId, setChooseId] = useState(0); + return ( <> @@ -54,6 +64,7 @@ function Event_PilihNominalSponsor() { { + try { + setChooseId(e.id); + setLoadingPaper(true); + setFixNominal(e.value); + router.push(RouterEvent.metode_pembayaran({ id: params.id })); + } catch (error) { + console.error(error); + } + }} > {e.icon} - Rp.{e.jumlah} + + Rp. + {new Intl.NumberFormat("id-ID", { currency: "IDR" }).format( + e.value + )} + - + {isLoadingPaper && e.id === chooseId ? ( + + ) : ( + + )} ))} @@ -89,21 +119,41 @@ function Event_PilihNominalSponsor() { icon={Rp.} placeholder="0" min={0} + value={inputNominal} + onChange={(val) => { + const match = val.currentTarget.value + .replace(/\./g, "") + .match(/^[0-9]+$/); + + if (val.currentTarget.value === "") + return setInputNominal(0 + ""); + + if (!match?.[0]) return null; + + const nilai = val.currentTarget.value.replace(/\./g, ""); + const target = Intl.NumberFormat("id-ID").format(+nilai); + + setValueNominal(+nilai); + setInputNominal(target); + }} /> - - Minimal Donasi Rp. 10.000 - + diff --git a/src/app_modules/event/global_state/index.ts b/src/app_modules/event/global_state/index.ts index 310e4a91..2574ed23 100644 --- a/src/app_modules/event/global_state/index.ts +++ b/src/app_modules/event/global_state/index.ts @@ -1,13 +1,24 @@ +import { atom } from "jotai"; import { atomWithStorage } from "jotai/utils"; /** * @param index | 0 - 3 | 0: Beranda, 1: Status, 2: Kontibusi, 3: Riwayat * @type number */ -export const gs_event_hotMenu = atomWithStorage("gs_event_hotMenu", 0) +export const gs_event_hotMenu = atomWithStorage("gs_event_hotMenu", 0); /** * @param status | "Publish", "Review", "Draft", "Reject" * @type string */ -export const gs_event_status = atomWithStorage("gs_status_event", "Publish") +export const gs_event_status = atomWithStorage( + "gs_status_event", + "Publish" +); + +export const gs_nominal_sponsor = atomWithStorage( + "gs_nominal_sponsor", + 0 +); + +export const gs_event_bank_id = atomWithStorage("gs_event_bank_id", ""); diff --git a/src/bin/seeder/master/index.ts b/src/bin/seeder/master/index.ts index b2a301c4..8cb000b1 100644 --- a/src/bin/seeder/master/index.ts +++ b/src/bin/seeder/master/index.ts @@ -1,5 +1,7 @@ import master_kategori_app from "./master_kategori_app.json"; import master_nama_bank from "./master_nama_bank.json"; +import master_status_transaksi from "./master_status_transaksi.json"; export { master_kategori_app }; export { master_nama_bank }; +export { master_status_transaksi }; diff --git a/src/bin/seeder/master/master_status_transaksi.json b/src/bin/seeder/master/master_status_transaksi.json new file mode 100644 index 00000000..6093bdd0 --- /dev/null +++ b/src/bin/seeder/master/master_status_transaksi.json @@ -0,0 +1,18 @@ +[ + { + "id": "1", + "name": "Berhasil" + }, + { + "id": "2", + "name": "Proses" + }, + { + "id": "3", + "name": "Menunggu" + }, + { + "id": "4", + "name": "Gagal" + } +] diff --git a/src/middleware.ts b/src/middleware.ts index dc993068..b20103f3 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -32,9 +32,15 @@ const middlewareConfig: MiddlewareConfig = { "/api/auth/*", "/api/origin-url", "/api/event/*", + "/api/master/*", // "/api/image/*", // "/api/user/*", // "/api/new/*", + // ADMIN API + // "/api/admin/event/*", + // "/api/admin/investasi/*", + + // Akses awal "/api/get-cookie", "/api/user/activation",