diff --git a/.gitignore b/.gitignore index 61b4bc33..72c1174a 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,9 @@ yarn-error.log* # vercel .vercel +# logs +/logs + # typescript *.tsbuildinfo next-env.d.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 30560fe0..c4d9e45d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ 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.32](https://github.com/bipproduction/hipmi/compare/v1.2.31...v1.2.32) (2024-12-19) + + +### Bug Fixes + +* donasi ([72b6d23](https://github.com/bipproduction/hipmi/commit/72b6d239fee3e90e812ab24192154e5e8910fccb)) + +## [1.2.31](https://github.com/bipproduction/hipmi/compare/v1.2.30...v1.2.31) (2024-12-17) + +## [1.2.30](https://github.com/bipproduction/hipmi/compare/v1.2.29...v1.2.30) (2024-12-16) + +## [1.2.29](https://github.com/bipproduction/hipmi/compare/v1.2.28...v1.2.29) (2024-12-13) + +## [1.2.28](https://github.com/bipproduction/hipmi/compare/v1.2.27...v1.2.28) (2024-12-12) + ## [1.2.27](https://github.com/bipproduction/hipmi/compare/v1.2.26...v1.2.27) (2024-12-12) ## [1.2.26](https://github.com/bipproduction/hipmi/compare/v1.2.25...v1.2.26) (2024-12-12) diff --git a/bun.lockb b/bun.lockb index 88eaa6ad..f486e079 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index f18b9dad..b7686848 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hipmi", - "version": "1.2.27", + "version": "1.2.32", "private": true, "prisma": { "seed": "npx tsx prisma/seed.ts --yes" @@ -50,6 +50,7 @@ "autoprefixer": "10.4.14", "bufferutil": "^4.0.8", "bun": "^1.1.38", + "date-fns": "^4.1.0", "dayjs": "^1.11.10", "dotenv": "^16.4.5", "echarts": "^5.4.3", @@ -95,6 +96,8 @@ "wibu": "bipproduction/wibu", "wibu-cli": "^1.0.91", "wibu-pkg": "^1.0.3", + "winston": "^3.17.0", + "winston-daily-rotate-file": "^5.0.0", "yaml": "^2.3.2" } } diff --git a/src/app/(admin)/logs/logs.module.css b/src/app/(admin)/logs/logs.module.css new file mode 100644 index 00000000..42dadbaa --- /dev/null +++ b/src/app/(admin)/logs/logs.module.css @@ -0,0 +1,138 @@ +/* app/admin/logs/logs.module.css */ +.container { + padding: 24px; + max-width: 1200px; + margin: 0 auto; + } + + .title { + font-size: 24px; + font-weight: bold; + margin-bottom: 16px; + } + + .filterContainer { + margin-bottom: 16px; + } + + .select { + padding: 8px; + border: 1px solid #ddd; + border-radius: 4px; + min-width: 200px; + } + + .logsContainer { + display: flex; + flex-direction: column; + gap: 8px; + } + + .logItem { + padding: 16px; + border-radius: 4px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + } + + .errorLog { + background-color: #fef2f2; + } + + .warnLog { + background-color: #fefce8; + } + + .infoLog { + background-color: #ffffff; + } + + .logHeader { + display: flex; + align-items: center; + gap: 8px; + } + + .timestamp { + font-size: 14px; + color: #666; + } + + .level { + padding: 4px 8px; + border-radius: 4px; + font-size: 12px; + font-weight: 500; + } + + .errorLevel { + background-color: #fee2e2; + color: #991b1b; + } + + .warnLevel { + background-color: #fef3c7; + color: #92400e; + } + + .infoLevel { + background-color: #dbeafe; + color: #1e40af; + } + + .message { + margin-top: 8px; + } + + .metadata { + margin-top: 8px; + padding: 8px; + background-color: #f9fafb; + border-radius: 4px; + font-size: 14px; + overflow-x: auto; + white-space: pre-wrap; + } + + .loading { + text-align: center; + padding: 24px; + color: #666; + } + + .error { + color: #dc2626; + text-align: center; + padding: 24px; + } + + /* Hover effects */ + .logItem:hover { + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + } + + .select:hover { + border-color: #999; + } + + /* Focus states */ + .select:focus { + outline: none; + border-color: #2563eb; + box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2); + } + + /* Responsive adjustments */ + @media (max-width: 768px) { + .container { + padding: 16px; + } + + .logHeader { + flex-direction: column; + align-items: flex-start; + } + + .metadata { + font-size: 12px; + } + } \ No newline at end of file diff --git a/src/app/(admin)/logs/page.tsx b/src/app/(admin)/logs/page.tsx new file mode 100644 index 00000000..2ac03d42 --- /dev/null +++ b/src/app/(admin)/logs/page.tsx @@ -0,0 +1,106 @@ +// app/admin/logs/page.tsx +"use client"; + +import { useEffect, useState } from "react"; +import { format } from "date-fns"; +import styles from "./logs.module.css"; + +interface LogEntry { + timestamp: string; + level: string; + message: string; + metadata?: any; +} + +export default function LogsPage() { + const [logs, setLogs] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [filter, setFilter] = useState<"all" | "error" | "info" | "warn">( + "all" + ); + + useEffect(() => { + fetchLogs(); + }, []); + + async function fetchLogs() { + try { + const response = await fetch("/api/logs/view"); + if (!response.ok) throw new Error("Failed to fetch logs"); + + const data = await response.json(); + setLogs(data.logs); + } catch (err) { + setError(err instanceof Error ? err.message : "Error fetching logs"); + } finally { + setLoading(false); + } + } + + const filteredLogs = logs.filter((log) => + filter === "all" ? true : log.level === filter + ); + + if (loading) return
Loading logs...
; + if (error) return
Error: {error}
; + + return ( +
+

System Logs

+ +
+ +
+ +
+ {filteredLogs.map((log, index) => ( +
+
+ + {format(new Date(log.timestamp), "yyyy-MM-dd HH:mm:ss")} + + + {log.level.toUpperCase()} + +
+ +
{log.message}
+ + {log.metadata && ( +
+                {JSON.stringify(log.metadata, null, 2)}
+              
+ )} +
+ ))} +
+
+ ); +} diff --git a/src/app/api/collaboration/master/route.ts b/src/app/api/collaboration/master/route.ts new file mode 100644 index 00000000..fb1b53da --- /dev/null +++ b/src/app/api/collaboration/master/route.ts @@ -0,0 +1,20 @@ +import { prisma } from "@/app/lib"; +import backendLogger from "@/util/backendLogger"; +import { NextResponse } from "next/server"; + +export const dynamic = "force-dynamic"; +export async function GET(request: Request) { + try { + const data = await prisma.projectCollaborationMaster_Industri.findMany({}); + return NextResponse.json( + { success: true, message: "Berhasil mendapatkan data", data: data }, + { status: 200 } + ); + } catch (error) { + backendLogger.error("Master Collaboration:=========>", error); + return NextResponse.json( + { success: false, message: "Gagal mendapatkan data" }, + { status: 500 } + ); + } +} diff --git a/src/app/api/image/delete/route.ts b/src/app/api/image/delete/route.ts new file mode 100644 index 00000000..a38d0e37 --- /dev/null +++ b/src/app/api/image/delete/route.ts @@ -0,0 +1,53 @@ +import { funGetDirectoryNameByValue } from "@/app_modules/_global/fun/get"; +import backendLogger from "@/util/backendLogger"; +import { NextResponse } from "next/server"; + +export async function DELETE(req: Request) { + const data = await req.json(); + const id = data.fileId; + const dirId = data.dirId; + + const keyOfDirectory = await funGetDirectoryNameByValue({ + value: dirId, + }); + + if (req.method === "DELETE") { + try { + const res = await fetch( + `https://wibu-storage.wibudev.com/api/files/${id}/delete`, + { + method: "DELETE", + headers: { + Authorization: `Bearer ${process.env.WS_APIKEY}`, + }, + } + ); + backendLogger.info("Server status code: " + res.status); + const data = await res.json(); + if (res.ok) { + backendLogger.info( + `Success delete ${keyOfDirectory}` + ); + return NextResponse.json({ success: true }); + } else { + const errorText = await res.json(); + backendLogger.error( + `Failed delete ${keyOfDirectory}: ` + errorText.message + ); + return NextResponse.json({ + success: false, + message: errorText.message, + }); + } + } catch (error) { + backendLogger.error(`Delete error ${keyOfDirectory}:`, error); + return NextResponse.json({ + success: false, + message: "An unexpected error occurred", + }); + } + } else { + backendLogger.error(`Error upload ${keyOfDirectory}: Method not allowed`); + return NextResponse.json({ success: false, message: "Method not allowed" }); + } +} diff --git a/src/app/api/image/upload/route.ts b/src/app/api/image/upload/route.ts new file mode 100644 index 00000000..a1004088 --- /dev/null +++ b/src/app/api/image/upload/route.ts @@ -0,0 +1,58 @@ +import { funGetDirectoryNameByValue } from "@/app_modules/_global/fun/get"; +import backendLogger from "@/util/backendLogger"; +import { NextResponse } from "next/server"; +export async function POST(request: Request) { + const formData = await request.formData(); + + const valueOfDir = formData.get("dirId"); + const keyOfDirectory = await funGetDirectoryNameByValue({ + value: valueOfDir as string, + }); + + if (request.method === "POST") { + try { + const res = await fetch("https://wibu-storage.wibudev.com/api/upload", { + method: "POST", + body: formData, + headers: { + Authorization: `Bearer ${process.env.WS_APIKEY}`, + }, + }); + + backendLogger.info("Server status code: " + res.status); + const dataRes = await res.json(); + + if (res.ok) { + backendLogger.info( + `Success upload ${keyOfDirectory}: ${JSON.stringify(dataRes.data)}` + ); + return NextResponse.json( + { success: true, data: dataRes.data }, + { status: 200 } + ); + } else { + const errorText = await res.text(); + backendLogger.error(`Failed upload ${keyOfDirectory}: ${errorText}`); + return NextResponse.json( + { success: false, message: errorText }, + { status: 400 } + ); + } + } catch (error) { + backendLogger.error(`Error upload ${keyOfDirectory}: ${error}`); + return NextResponse.json( + { + success: false, + message: "An unexpected error occurred", + }, + { status: 500 } + ); + } + } else { + backendLogger.error(`Error upload ${keyOfDirectory}: Method not allowed`); + return NextResponse.json( + { success: false, message: "Method not allowed" }, + { status: 405 } + ); + } +} diff --git a/src/app/api/job/get-all/route.ts b/src/app/api/job/get-all/route.ts index 21db076c..1601d2bb 100644 --- a/src/app/api/job/get-all/route.ts +++ b/src/app/api/job/get-all/route.ts @@ -1,16 +1,94 @@ -import { job_getAllListPublish } from "@/app_modules/job/fun/get/get_all_publish"; -import _ from "lodash"; +import { prisma } from "@/app/lib"; import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; -export async function GET(params: Request) { - const { searchParams } = new URL(params.url); - const page = searchParams.get("page"); - const search = searchParams.get("search"); +export async function GET(request: Request) { + try { + const { searchParams } = new URL(request.url); + const search = searchParams.get("search"); + const page = searchParams.get("page"); + const dataTake = 10; + const dataSkip = Number(page) * dataTake - dataTake; - const data = await job_getAllListPublish({ - page: _.toNumber(page), - search: search as string, - }); + if (search != "") { + const data = await prisma.job.findMany({ + take: dataTake, + skip: dataSkip, + orderBy: { + updatedAt: "desc", + }, + where: { + masterStatusId: "1", + isActive: true, + isArsip: false, + title: { + mode: "insensitive", + contains: search as string, + }, + }, + select: { + id: true, + title: true, + Author: { + select: { + id: true, + username: true, + Profile: true, + }, + }, + }, + }); - return NextResponse.json({ data }); + return NextResponse.json( + { + success: true, + message: "Berhasil ambil data", + data: data, + }, + { status: 200 } + ); + } else { + const data = await prisma.job.findMany({ + take: dataTake, + skip: dataSkip, + orderBy: { + updatedAt: "desc", + }, + where: { + masterStatusId: "1", + isActive: true, + isArsip: false, + title: { + mode: "insensitive", + }, + }, + select: { + id: true, + title: true, + Author: { + select: { + id: true, + username: true, + Profile: true, + }, + }, + }, + }); + + return NextResponse.json( + { + success: true, + message: "Berhasil ambil data", + data: data, + }, + { status: 200 } + ); + } + } catch (error) { + console.error(error); + return NextResponse.json({ + success: false, + message: "Gagal ambil data", + }); + } } diff --git a/src/app/api/logs/view/route.ts b/src/app/api/logs/view/route.ts new file mode 100644 index 00000000..b660e323 --- /dev/null +++ b/src/app/api/logs/view/route.ts @@ -0,0 +1,71 @@ +// app/api/logs/view/route.ts +import { NextRequest, NextResponse } from "next/server"; +import fs from "fs/promises"; +import path from "path"; + +interface LogEntry { + timestamp: string; + level: string; + message: string; + metadata?: any; +} + +async function readLogFiles(directory: string): Promise { + try { + const logPath = path.join(process.cwd(), directory); + const files = await fs.readdir(logPath); + const logFiles = files.filter((file) => file.endsWith(".log")); + + const allLogs: LogEntry[] = []; + + for (const file of logFiles) { + const filePath = path.join(logPath, file); + const content = await fs.readFile(filePath, "utf-8"); + + // Parse setiap baris log + const logs = content + .split("\n") + .filter(Boolean) + .map((line) => { + try { + return JSON.parse(line); + } catch (e) { + return null; + } + }) + .filter(Boolean); + + allLogs.push(...logs); + } + + // Sort berdasarkan timestamp, terbaru di atas + return allLogs.sort( + (a, b) => + new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime() + ); + } catch (error) { + console.error("Error reading log files:", error); + return []; + } +} + +export async function GET(request: NextRequest) { + try { + // Baca logs dari frontend dan backend + const frontendLogs = await readLogFiles("logs/frontend"); + const backendLogs = await readLogFiles("logs/backend"); + + // Gabungkan dan sort semua logs + const allLogs = [...frontendLogs, ...backendLogs].sort( + (a, b) => + new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime() + ); + + return NextResponse.json({ logs: allLogs }); + } catch (error) { + return NextResponse.json( + { error: "Failed to fetch logs" }, + { status: 500 } + ); + } +} diff --git a/src/app/api/new/donasi/invoice/route.ts b/src/app/api/new/donasi/invoice/route.ts new file mode 100644 index 00000000..4585a9b9 --- /dev/null +++ b/src/app/api/new/donasi/invoice/route.ts @@ -0,0 +1,77 @@ +import { prisma } from "@/app/lib"; +import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; +import _ from "lodash"; +import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; + + + + +// GET ALL DATA DONASI SAYA (INVOICE) +export async function GET(request: Request) { + try { + const { searchParams } = new URL(request.url) + const page = searchParams.get("page") + const dataSkip = Number(page) * 5 - 5; + + + const userLoginId = await funGetUserIdByToken() + if (userLoginId == null) { + return NextResponse.json({ success: false, message: "Gagal mendapatkan data, user id tidak ada" }, { status: 500 }); + } + + const data = await prisma.donasi_Invoice.findMany({ + take: 5, + skip: dataSkip, + orderBy: { + createdAt: "desc", + }, + where: { + authorId: userLoginId, + }, + select: { + id: true, + nominal: true, + donasiMaster_StatusInvoiceId: true, + DonasiMaster_StatusInvoice: { + select: { + name: true + } + }, + Donasi: { + select: { + id: true, + title: true, + publishTime: true, + progres: true, + imageId: true, + DonasiMaster_Durasi: { + select: { + name: true + } + }, + }, + }, + }, + }); + + const dataFix = data.map((v: any) => ({ + ..._.omit(v, ["DonasiMaster_StatusInvoice", "Donasi"]), + nameStatusInvoice: v.DonasiMaster_StatusInvoice.name, + donasiId: v.Donasi.id, + title: v.Donasi.title, + publishTime: v.Donasi.publishTime, + progres: v.Donasi.progres, + imageId: v.Donasi.imageId, + durasiDonasi: v.Donasi.DonasiMaster_Durasi.name + })) + + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: dataFix }, { status: 200 }); + + } + catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan data, coba lagi nanti ", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/new/donasi/master/route.ts b/src/app/api/new/donasi/master/route.ts new file mode 100644 index 00000000..f42e4ab5 --- /dev/null +++ b/src/app/api/new/donasi/master/route.ts @@ -0,0 +1,33 @@ +import { prisma } from "@/app/lib"; +import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; + + +// GET ALL DATA MASTER UNTUK DONASI +export async function GET(request: Request) { + try { + let dataFix + const { searchParams } = new URL(request.url) + const kategori = searchParams.get("cat") + + if (kategori == "kategori") { + dataFix = await prisma.donasiMaster_Kategori.findMany({ + orderBy: { + createdAt: "asc", + }, + where: { + active: true, + } + }) + } else if (kategori == "durasi") { + dataFix = await prisma.donasiMaster_Durasi.findMany() + } + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: dataFix }, { status: 200 }); + + } + catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan data, coba lagi nanti ", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/new/donasi/route.ts b/src/app/api/new/donasi/route.ts index 2782e7de..22d9449d 100644 --- a/src/app/api/new/donasi/route.ts +++ b/src/app/api/new/donasi/route.ts @@ -46,23 +46,44 @@ export async function GET(request: Request) { ..._.omit(v, ["DonasiMaster_Durasi"]), nameDonasiDurasi: v.DonasiMaster_Durasi.name })) - } else { + } else if (kategori == "galang-dana") { const userLoginId = await funGetUserIdByToken() if (userLoginId == null) { return NextResponse.json({ success: false, message: "Gagal mendapatkan data, user id tidak ada" }, { status: 500 }); } - dataFix = await prisma.donasi.findMany({ + const data = await prisma.donasi.findMany({ take: 5, skip: dataSkip, where: { authorId: userLoginId, donasiMaster_StatusDonasiId: status, + active: true, + }, + select: { + id: true, + title: true, + imagesId: true, + target: true, + progres: true, + publishTime: true, + DonasiMaster_Durasi: { + select: { + name: true + } + }, + terkumpul: true, + imageId: true, }, orderBy: { updatedAt: "desc", }, }); + + dataFix = data.map((v: any) => ({ + ..._.omit(v, ["DonasiMaster_Durasi"]), + nameDonasiDurasi: v.DonasiMaster_Durasi.name + })) } return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: dataFix }, { status: 200 }); diff --git a/src/app/api/new/investasi/[id]/route.ts b/src/app/api/new/investasi/[id]/route.ts new file mode 100644 index 00000000..ef327393 --- /dev/null +++ b/src/app/api/new/investasi/[id]/route.ts @@ -0,0 +1,39 @@ +import { prisma } from "@/app/lib"; +import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; + + + +// GET ONE DATA INVESTASI BY ID +export async function GET(request: Request, context: { params: { id: string } }) { + try { + const { id } = context.params + const data = await prisma.investasi.findUnique({ + where: { + id: id, + }, + select: { + id: true, + title: true, + targetDana: true, + hargaLembar: true, + totalLembar: true, + roi: true, + countDown: true, + catatan: true, + sisaLembar: true, + imageId: true, + masterPencarianInvestorId: true, + masterPeriodeDevidenId: true, + masterPembagianDevidenId: true, + } + }); + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data }, { status: 200 }); + + } + catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan data, coba lagi nanti ", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/new/investasi/invoice/route.ts b/src/app/api/new/investasi/invoice/route.ts new file mode 100644 index 00000000..1f8b4380 --- /dev/null +++ b/src/app/api/new/investasi/invoice/route.ts @@ -0,0 +1,99 @@ +import { prisma } from "@/app/lib"; +import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; +import _ from "lodash"; +import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; + + + +// GET ALL DATA INVESTASI SAYA (INVOICE) +export async function GET(request: Request) { + try { + let dataFix + const { searchParams } = new URL(request.url) + const page = searchParams.get("page") + const dataSkip = Number(page) * 10 - 10; + const kategori = searchParams.get("cat") + + + const userLoginId = await funGetUserIdByToken() + if (userLoginId == null) { + return NextResponse.json({ success: false, message: "Gagal mendapatkan data, user id tidak ada" }, { status: 500 }); + } + + if (kategori == "saham-saya") { + const data = await prisma.investasi_Invoice.findMany({ + take: 10, + skip: dataSkip, + orderBy: { + updatedAt: "desc", + }, + where: { + authorId: userLoginId, + statusInvoiceId: "1", + isActive: true, + }, + select: { + id: true, + nominal: true, + lembarTerbeli: true, + Investasi: { + select: { + title: true, + progress: true + } + } + } + }); + + dataFix = data.map((v: any) => ({ + ..._.omit(v, ["Investasi"]), + title: v.Investasi.title, + progress: v.Investasi.progress + })) + + } else if (kategori == "transaksi") { + const data = await prisma.investasi_Invoice.findMany({ + take: 10, + skip: dataSkip, + orderBy: { + updatedAt: "desc", + }, + where: { + authorId: userLoginId, + }, + select: { + id: true, + statusInvoiceId: true, + nominal: true, + createdAt: true, + StatusInvoice: { + select: { + name: true + } + }, + Investasi: { + select: { + title: true + } + } + } + }); + + + dataFix = data.map((v: any) => ({ + ..._.omit(v, ["Investasi", "StatusInvoice"]), + title: v.Investasi.title, + statusInvoice: v.StatusInvoice.name + })) + } + + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: dataFix }, { status: 200 }); + + } + catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan data, coba lagi nanti ", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/new/investasi/master/route.ts b/src/app/api/new/investasi/master/route.ts new file mode 100644 index 00000000..721e7276 --- /dev/null +++ b/src/app/api/new/investasi/master/route.ts @@ -0,0 +1,46 @@ +import { prisma } from "@/app/lib"; +import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; + + +// GET ALL DATA MASTER UNTUK INVESTASI +export async function GET(request: Request) { + try { + let dataFix + const { searchParams } = new URL(request.url) + const kategori = searchParams.get("cat") + + if (kategori == "pencarian-investor") { + dataFix = await await prisma.masterPencarianInvestor.findMany({ + select: { + id: true, + name: true, + active: true, + }, + }); + } else if (kategori == "periode-deviden") { + dataFix = await prisma.masterPeriodeDeviden.findMany({ + select: { + id: true, + name: true, + active: true, + }, + }); + } else if (kategori == "pembagian-deviden") { + dataFix = await prisma.masterPembagianDeviden.findMany({ + select: { + id: true, + name: true, + active: true, + }, + }); + } + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: dataFix }, { status: 200 }); + + } + catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan data, coba lagi nanti ", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/new/investasi/route.ts b/src/app/api/new/investasi/route.ts new file mode 100644 index 00000000..ea13150c --- /dev/null +++ b/src/app/api/new/investasi/route.ts @@ -0,0 +1,144 @@ +import { prisma } from "@/app/lib"; +import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; +import _ from "lodash"; +import moment from "moment"; +import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; + + +// GET ALL DATA INVESTASI +export async function GET(request: Request) { + try { + let dataFix + const { searchParams } = new URL(request.url) + const kategori = searchParams.get("cat") + const status = searchParams.get("status") + const page = searchParams.get("page") + const dataSkip = Number(page) * 5 - 5; + + if (kategori == "bursa") { + const data = await prisma.investasi.findMany({ + where: { + masterStatusInvestasiId: "1", + masterProgresInvestasiId: "1", + }, + select: { + id: true, + MasterPencarianInvestor: true, + countDown: true, + progress: true, + }, + }); + + for (let a of data) { + if ( + (a.MasterPencarianInvestor?.name as any) - + moment(new Date()).diff(new Date(a.countDown as any), "days") <= + 0 + ) { + await prisma.investasi.update({ + where: { + id: a.id, + }, + data: { + masterProgresInvestasiId: "3", + }, + }); + } + + if (a.progress === "100") { + await prisma.investasi.update({ + where: { + id: a.id, + }, + data: { + masterProgresInvestasiId: "2", + }, + }); + } + } + + // cek data yang lewat + // klo ada, update status + const dataAwal = await prisma.investasi.findMany({ + take: 5, + skip: dataSkip, + orderBy: [ + { + masterProgresInvestasiId: "asc", + }, + { + countDown: "desc", + }, + ], + where: { + masterStatusInvestasiId: "1", + }, + select: { + id: true, + imageId: true, + title: true, + progress: true, + countDown: true, + MasterPencarianInvestor: { + select: { + name: true + } + } + } + }); + + dataFix = dataAwal.map((v: any) => ({ + ..._.omit(v, ["MasterPencarianInvestor"]), + pencarianInvestor: v.MasterPencarianInvestor.name + })) + + } else if (kategori == "portofolio") { + const userLoginId = await funGetUserIdByToken() + if (userLoginId == null) { + return NextResponse.json({ success: false, message: "Gagal mendapatkan data, user id tidak ada" }, { status: 500 }); + } + + const data = await prisma.investasi.findMany({ + take: 5, + skip: dataSkip, + orderBy: { + updatedAt: "desc", + }, + where: { + authorId: userLoginId, + masterStatusInvestasiId: status, + }, + select: { + id: true, + title: true, + targetDana: true, + imageId: true, + countDown: true, + updatedAt: true, + MasterPencarianInvestor: { + select: { + name: true + } + } + } + }); + + dataFix = data.map((v: any) => ({ + ..._.omit(v, ["MasterPencarianInvestor"]), + pencarianInvestor: v.MasterPencarianInvestor.name + })) + + + } + + + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan data", data: dataFix }, { status: 200 }); + + } + catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan data, coba lagi nanti ", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/notifikasi/count/route.ts b/src/app/api/notifikasi/count/route.ts new file mode 100644 index 00000000..c54ea290 --- /dev/null +++ b/src/app/api/notifikasi/count/route.ts @@ -0,0 +1,28 @@ +import { prisma } from "@/app/lib"; +import { newFunGetUserId } from "@/app/lib/new_fun_user_id"; +import backendLogger from "@/util/backendLogger"; +import { NextResponse } from "next/server"; + +export const dynamic = "force-dynamic"; + +export async function GET(request: Request) { + try { + const userLoginId = await newFunGetUserId(); + + const count = await prisma.notifikasi.findMany({ + where: { + userId: userLoginId, + isRead: false, + userRoleId: "1", + }, + }); + + return NextResponse.json({ success: true, data: count.length }); + } catch (error) { + backendLogger.error("Gagal mendapatkan data count notifikasi", error); + return NextResponse.json( + { success: false, message: "Gagal mendapatkan data" }, + { status: 500 } + ); + } +} diff --git a/src/app/api/notifikasi/get-all-by-category/route.ts b/src/app/api/notifikasi/get-all-by-category/route.ts new file mode 100644 index 00000000..bfa9aefd --- /dev/null +++ b/src/app/api/notifikasi/get-all-by-category/route.ts @@ -0,0 +1,65 @@ +import { prisma } from "@/app/lib"; +import { newFunGetUserId } from "@/app/lib/new_fun_user_id"; +import { ICategoryapp } from "@/app_modules/notifikasi/model/interface"; +import backendLogger from "@/util/backendLogger"; +import _ from "lodash"; +import { NextResponse } from "next/server"; +export const dynamic = "force-dynamic"; + +export async function GET(request: Request) { + try { + let fixData; + const { searchParams } = new URL(request.url); + const category = searchParams.get("category") as ICategoryapp; + const page = searchParams.get("page"); + + const userLoginId = await newFunGetUserId(); + const fixPage = _.toNumber(page); + const takeData = 10; + const skipData = fixPage * takeData - takeData; + + if (category === "Semua") { + fixData = await prisma.notifikasi.findMany({ + take: takeData, + skip: skipData, + orderBy: [ + { + isRead: "asc", + }, + { createdAt: "desc" }, + ], + where: { + userId: userLoginId, + userRoleId: "1", + }, + }); + } else { + fixData = await prisma.notifikasi.findMany({ + take: takeData, + skip: skipData, + orderBy: [ + { + isRead: "asc", + }, + { createdAt: "desc" }, + ], + where: { + userId: userLoginId, + userRoleId: "1", + kategoriApp: _.upperCase(category), + }, + }); + } + + return NextResponse.json( + { success: true, data: fixData, message: "Berhasil mendapatkan data" }, + { status: 200 } + ); + } catch (error) { + backendLogger.error("Error get data notifikasi: " + error); + return NextResponse.json( + { success: false, message: "Gagal mendapatkan data" }, + { status: 500 } + ); + } +} diff --git a/src/app/api/notifikasi/master/route.ts b/src/app/api/notifikasi/master/route.ts new file mode 100644 index 00000000..27e4145e --- /dev/null +++ b/src/app/api/notifikasi/master/route.ts @@ -0,0 +1,35 @@ +import { prisma } from "@/app/lib"; +import backendLogger from "@/util/backendLogger"; +import { NextResponse } from "next/server"; + +export const dynamic = "force-dynamic"; + +export async function GET(request: Request) { + try { + const data = await prisma.masterKategoriApp.findMany({ + where: { + isActive: true, + }, + }); + + data.unshift({ + id: "0", + isActive: true, + createdAt: new Date(), + updatedAt: new Date(), + name: "Semua", + value: null, + }); + + return NextResponse.json( + { success: true, data, message: "Berhasil mendapatkan data" }, + { status: 200 } + ); + } catch (error) { + backendLogger.error("Master Notifikasi:", error); + return NextResponse.json( + { success: false, message: "Gagal mendapatkan data" }, + { status: 500 } + ); + } +} diff --git a/src/app/api/upload/route.ts b/src/app/api/upload/route.ts deleted file mode 100644 index 00e2b69c..00000000 --- a/src/app/api/upload/route.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { NextResponse } from "next/server"; -export async function POST(request: Request) { - const WS_APIKEY = process.env.WS_APIKEY; - console.log(WS_APIKEY); - - try { - const formData = await request.formData(); - - const res = await fetch("https://wibu-storage.wibudev.com/api/upload", { - method: "POST", - body: formData, - headers: { - Authorization: `Bearer ${process.env.WS_APIKEY}`, - }, - }); - - // if (res.ok) { - // console.log("Berhasil"); - // const hasil = await res.json(); - // return { success: true, data: hasil.data }; - // } else { - // const errorText = await res.text(); - // return { success: false, data: {} }; - // } - } catch (error) { - console.log(error); - } - - // try { - // const res = await fetch("https://wibu-storage.wibudev.com/api/upload", { - // method: "POST", - // body: formData, - // headers: { - // Authorization: `Bearer ${process.env.WS_APIKEY}`, - // }, - // }); - - // if (res.ok) { - // const hasil = await res.json(); - // return { success: true, data: hasil.data }; - // } else { - // const errorText = await res.text(); - // return { success: false, data: {} }; - // } - // } catch (error) { - // console.error("Upload error:", error); - // return { success: false, data: {} }; - // } - - return NextResponse.json({ success: true }); -} diff --git a/src/app/dev/(user)/home/page.tsx b/src/app/dev/(user)/home/page.tsx index 4bd78e32..675c907a 100644 --- a/src/app/dev/(user)/home/page.tsx +++ b/src/app/dev/(user)/home/page.tsx @@ -1,21 +1,9 @@ import { HomeViewNew } from "@/app_modules/home"; -import notifikasi_countUserNotifikasi from "@/app_modules/notifikasi/fun/count/fun_count_by_id"; export default async function PageHome() { - // const userLoginId = await funGetUserIdByToken(); - // const dataUser = await user_getOneByUserId(userLoginId as string); - // const dataJob = await job_getTwoForHomeView(); - const countNotifikasi = await notifikasi_countUserNotifikasi(); - - return ( <> - {/* */} - + ); } diff --git a/src/app/dev/colab/create/page.tsx b/src/app/dev/colab/create/page.tsx index f1d84a65..6a530d6f 100644 --- a/src/app/dev/colab/create/page.tsx +++ b/src/app/dev/colab/create/page.tsx @@ -1,12 +1,9 @@ import { Colab_Create } from "@/app_modules/colab"; -import colab_funGetMasterIndustri from "@/app_modules/colab/fun/master/fun_get_master_industri"; export default async function Page() { - const listIndustri = await colab_funGetMasterIndustri(); - return ( <> - + ); } diff --git a/src/app/dev/donasi/create/create_donasi/page.tsx b/src/app/dev/donasi/create/create_donasi/page.tsx index 2202dfdf..aef24090 100644 --- a/src/app/dev/donasi/create/create_donasi/page.tsx +++ b/src/app/dev/donasi/create/create_donasi/page.tsx @@ -1,14 +1,12 @@ -import { CreateDonasi } from "@/app_modules/donasi"; -import { - Donasi_getMasterDurasi, - Donasi_getMasterKategori, -} from "@/app_modules/donasi/fun"; +import { CreateDonasiNew } from "@/app_modules/donasi"; + export default async function Page() { - const masterKategori = await Donasi_getMasterKategori(); - const masterDurasi = await Donasi_getMasterDurasi(); + // const masterKategori = await Donasi_getMasterKategori(); + // const masterDurasi = await Donasi_getMasterDurasi(); return ( - + // + ); } diff --git a/src/app/dev/donasi/main/donasi_saya/page.tsx b/src/app/dev/donasi/main/donasi_saya/page.tsx index bbb25e4a..a04c7954 100644 --- a/src/app/dev/donasi/main/donasi_saya/page.tsx +++ b/src/app/dev/donasi/main/donasi_saya/page.tsx @@ -1,8 +1,12 @@ -import { DonasiSayaDonasi } from "@/app_modules/donasi"; -import { donasi_funGetAllInvoiceByAuthorId } from "@/app_modules/donasi/fun/get/get_all_invoice_by_author_id"; +import DonasiSayaNew from "@/app_modules/donasi/main/donasi_saya_new"; export default async function Page() { - const listInvoice = await donasi_funGetAllInvoiceByAuthorId({ page: 1 }); + // const listInvoice = await donasi_funGetAllInvoiceByAuthorId({ page: 1 }); - return ; + return ( + <> + {/* ; */} + + + ) } diff --git a/src/app/dev/donasi/main/galang_dana/[id]/page.tsx b/src/app/dev/donasi/main/galang_dana/[id]/page.tsx index b4ff467c..c3cfa869 100644 --- a/src/app/dev/donasi/main/galang_dana/[id]/page.tsx +++ b/src/app/dev/donasi/main/galang_dana/[id]/page.tsx @@ -1,27 +1,23 @@ -import { GalangDanaDonasiNew, PostingDonasi } from "@/app_modules/donasi"; -import { donasi_funGetAllStatusById } from "@/app_modules/donasi/fun"; -import { donasi_funMasterStatusDonasi } from "@/app_modules/donasi/fun/master/get_status_donasi"; +import { GalangDanaDonasiNew } from "@/app_modules/donasi"; export default async function Page({ params }: { params: { id: string } }) { - const statusId = params.id; + // const statusId = params.id; - const listStatus = await donasi_funMasterStatusDonasi(); - const dataDonasi = await donasi_funGetAllStatusById({ - page: 1, - statusId: statusId, - }); + // const listStatus = await donasi_funMasterStatusDonasi(); + // const dataDonasi = await donasi_funGetAllStatusById({ + // page: 1, + // statusId: statusId, + // }); return ( <> - + /> */} - - {/* NANTI AJA LANJUT -- KEJAR TAYANG :) */} - {/* */} + ); } diff --git a/src/app/dev/investasi/create/investasi/page.tsx b/src/app/dev/investasi/create/investasi/page.tsx index ba8af421..254f9c60 100644 --- a/src/app/dev/investasi/create/investasi/page.tsx +++ b/src/app/dev/investasi/create/investasi/page.tsx @@ -1,23 +1,20 @@ -import { InvestasiCreate } from "@/app_modules/investasi"; -import getPembagianDeviden from "@/app_modules/investasi/fun/master/get_pembagian_deviden"; -import getPencarianInvestor from "@/app_modules/investasi/fun/master/get_pencarian_investor"; -import getPeriodeDeviden from "@/app_modules/investasi/fun/master/get_periode_deviden"; -import getStatusInvestasi from "@/app_modules/investasi/fun/master/get_status_investasi"; +import { InvestasiCreateNew } from "@/app_modules/investasi"; export default async function Page() { - const pencarianInvestor = await getPencarianInvestor(); - const periodeDeviden = await getPeriodeDeviden(); - const pembagianDeviden = await getPembagianDeviden(); - const statusInvestasi = await getStatusInvestasi(); + // const pencarianInvestor = await getPencarianInvestor(); + // const periodeDeviden = await getPeriodeDeviden(); + // const pembagianDeviden = await getPembagianDeviden(); + // const statusInvestasi = await getStatusInvestasi(); return ( <> - + /> */} + ); } diff --git a/src/app/dev/investasi/edit/[id]/page.tsx b/src/app/dev/investasi/edit/[id]/page.tsx index 5d91e4d2..a81633bb 100644 --- a/src/app/dev/investasi/edit/[id]/page.tsx +++ b/src/app/dev/investasi/edit/[id]/page.tsx @@ -1,39 +1,39 @@ import { investasi_funGetOneInvestasiById } from "@/app_modules/investasi/_fun"; -import { Investasi_UiEditInvestasi } from "@/app_modules/investasi/_ui"; +import { Investasi_UiEditInvestasi, Investasi_UiEditInvestasiNew } from "@/app_modules/investasi/_ui"; import getPembagianDeviden from "@/app_modules/investasi/fun/master/get_pembagian_deviden"; import getPencarianInvestor from "@/app_modules/investasi/fun/master/get_pencarian_investor"; import getPeriodeDeviden from "@/app_modules/investasi/fun/master/get_periode_deviden"; import _ from "lodash"; export default async function Page({ params }: { params: { id: string } }) { - const investasiId = params.id; - // console.log(investasiId); + // const investasiId = params.id; - const allData = await investasi_funGetOneInvestasiById({ investasiId }); - const dataInvestasi = _.omit(allData, [ - "BeritaInvestasi", - "DokumenInvestasi", - "MasterPembagianDeviden", - "MasterPencarianInvestor", - "MasterProgresInvestasi", - "MasterStatusInvestasi", - "ProspektusInvestasi", - "MasterPeriodeDeviden", - "author", - ]); + // const allData = await investasi_funGetOneInvestasiById({ investasiId }); + // const dataInvestasi = _.omit(allData, [ + // "BeritaInvestasi", + // "DokumenInvestasi", + // "MasterPembagianDeviden", + // "MasterPencarianInvestor", + // "MasterProgresInvestasi", + // "MasterStatusInvestasi", + // "ProspektusInvestasi", + // "MasterPeriodeDeviden", + // "author", + // ]); - const listPencarian = await getPencarianInvestor(); - const listPeriode = await getPeriodeDeviden(); - const listPembagian = await getPembagianDeviden(); + // const listPencarian = await getPencarianInvestor(); + // const listPeriode = await getPeriodeDeviden(); + // const listPembagian = await getPembagianDeviden(); return ( <> - + /> */} + ); } diff --git a/src/app/dev/investasi/main/page.tsx b/src/app/dev/investasi/main/page.tsx index bc3fe278..7951c323 100644 --- a/src/app/dev/investasi/main/page.tsx +++ b/src/app/dev/investasi/main/page.tsx @@ -1,12 +1,12 @@ -import { Investasi_UiBeranda } from "@/app_modules/investasi/_ui"; -import { investasi_funGetAllPublish } from "@/app_modules/investasi/fun/get_all_investasi"; +import { Investasi_ViewBerandaNew } from "@/app_modules/investasi/_ui"; export default async function Page() { - const allData = await investasi_funGetAllPublish({ page: 1 }); + // const allData = await investasi_funGetAllPublish({ page: 1 }); return ( <> - + {/* */} + ); } diff --git a/src/app/dev/investasi/main/portofolio/[id]/page.tsx b/src/app/dev/investasi/main/portofolio/[id]/page.tsx index b8378bd8..4b135800 100644 --- a/src/app/dev/investasi/main/portofolio/[id]/page.tsx +++ b/src/app/dev/investasi/main/portofolio/[id]/page.tsx @@ -1,22 +1,21 @@ -import { investasi_funGetPortofolioByStatusId } from "@/app_modules/investasi/_fun"; -import { Investasi_UiPortofolio } from "@/app_modules/investasi/_ui"; -import getStatusInvestasi from "@/app_modules/investasi/fun/master/get_status_investasi"; +import { Investasi_UiPortofolioNew } from "@/app_modules/investasi/_ui"; export default async function Page({ params }: { params: { id: string } }) { - const statusId = params.id; - const listStatus = await getStatusInvestasi(); - const dataPortofolio = await investasi_funGetPortofolioByStatusId({ - page: 1, - statusId: statusId, - }); + // const statusId = params.id; + // const listStatus = await getStatusInvestasi(); + // const dataPortofolio = await investasi_funGetPortofolioByStatusId({ + // page: 1, + // statusId: statusId, + // }); return ( <> - + /> */} + ); } diff --git a/src/app/dev/investasi/main/saham_saya/page.tsx b/src/app/dev/investasi/main/saham_saya/page.tsx index fcf6570f..9b675e2c 100644 --- a/src/app/dev/investasi/main/saham_saya/page.tsx +++ b/src/app/dev/investasi/main/saham_saya/page.tsx @@ -1,13 +1,13 @@ -import { investasi_funGetSuccessTransactionById } from "@/app_modules/investasi/_fun"; -import { Investasi_UiSahamSaya } from "@/app_modules/investasi/_ui"; +import { Investasi_ViewSahamSayaNew } from "@/app_modules/investasi/_view/main/view_saham_saya_new"; export default async function Page() { - const dataSaham = await investasi_funGetSuccessTransactionById({ page: 1 }); + // const dataSaham = await investasi_funGetSuccessTransactionById({ page: 1 }); return ( <> {/* */} - + {/* */} + ); } diff --git a/src/app/dev/investasi/main/transaksi/page.tsx b/src/app/dev/investasi/main/transaksi/page.tsx index 78150c82..04b8850f 100644 --- a/src/app/dev/investasi/main/transaksi/page.tsx +++ b/src/app/dev/investasi/main/transaksi/page.tsx @@ -4,6 +4,7 @@ import getMaster_StatusTransaksiInvestasi from "@/app_modules/investasi/fun/mast import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; import { investasi_funGetTransaksiByUserId } from "@/app_modules/investasi/_fun"; import { Investasi_UiDaftarTransaksi } from "@/app_modules/investasi/_ui"; +import { Investasi_ViewDaftarTransaksiNew } from "@/app_modules/investasi/_view/main/view_transaksi_new"; export default async function Page() { const userLoginId = await funGetUserIdByToken(); @@ -22,7 +23,8 @@ export default async function Page() { statusTransaksi={statusTransaksi as any} listTransaksi={listTransaksi as any} /> */} - + {/* */} + ); } diff --git a/src/app/dev/layout.tsx b/src/app/dev/layout.tsx index 49f4a4c7..bfb694c1 100644 --- a/src/app/dev/layout.tsx +++ b/src/app/dev/layout.tsx @@ -7,9 +7,13 @@ export default async function Layout({ }: { children: React.ReactNode; }) { + + const userId = await newFunGetUserId(); + return ( <> - + ); } diff --git a/src/app/dev/notifikasi/donasi/page.tsx b/src/app/dev/notifikasi/donasi/page.tsx index 9af63325..ca9f64eb 100644 --- a/src/app/dev/notifikasi/donasi/page.tsx +++ b/src/app/dev/notifikasi/donasi/page.tsx @@ -1,15 +1,9 @@ -import { Notifikasi_UiDonasi } from "@/app_modules/notifikasi/_ui"; -import notifikasi_getByUserId from "@/app_modules/notifikasi/fun/get/get_notifiaksi_by_id"; +import { Notifikasi_UiMain } from "@/app_modules/notifikasi/_ui"; export default async function Page() { - const listNotifikasi = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Donasi", - }); - return ( <> - + ); } diff --git a/src/app/dev/notifikasi/event/page.tsx b/src/app/dev/notifikasi/event/page.tsx index fa2ce235..d4f0d3a1 100644 --- a/src/app/dev/notifikasi/event/page.tsx +++ b/src/app/dev/notifikasi/event/page.tsx @@ -1,15 +1,9 @@ -import { Notifikasi_UiEvent } from "@/app_modules/notifikasi/_ui"; -import notifikasi_getByUserId from "@/app_modules/notifikasi/fun/get/get_notifiaksi_by_id"; +import Notifikasi_UiMain from "@/app_modules/notifikasi/_ui/ui_notifikasi"; export default async function Page() { - const listNotifikasi = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Event", - }); - return ( <> - + ); } diff --git a/src/app/dev/notifikasi/forum/page.tsx b/src/app/dev/notifikasi/forum/page.tsx index a27ffea2..ca9f64eb 100644 --- a/src/app/dev/notifikasi/forum/page.tsx +++ b/src/app/dev/notifikasi/forum/page.tsx @@ -1,15 +1,9 @@ -import { Notifikasi_UiForum } from "@/app_modules/notifikasi/_ui"; -import notifikasi_getByUserId from "@/app_modules/notifikasi/fun/get/get_notifiaksi_by_id"; +import { Notifikasi_UiMain } from "@/app_modules/notifikasi/_ui"; export default async function Page() { - const listNotifikasi = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Forum", - }); - return ( <> - + ); } diff --git a/src/app/dev/notifikasi/investasi/page.tsx b/src/app/dev/notifikasi/investasi/page.tsx index d5704862..ca9f64eb 100644 --- a/src/app/dev/notifikasi/investasi/page.tsx +++ b/src/app/dev/notifikasi/investasi/page.tsx @@ -1,15 +1,9 @@ -import { Notifikasi_UiDonasi, Notifikasi_UiInvestasi } from "@/app_modules/notifikasi/_ui"; -import notifikasi_getByUserId from "@/app_modules/notifikasi/fun/get/get_notifiaksi_by_id"; +import { Notifikasi_UiMain } from "@/app_modules/notifikasi/_ui"; export default async function Page() { - const listNotifikasi = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Investasi", - }); - return ( <> - + ); } diff --git a/src/app/dev/notifikasi/job/page.tsx b/src/app/dev/notifikasi/job/page.tsx index 029dbb80..ca9f64eb 100644 --- a/src/app/dev/notifikasi/job/page.tsx +++ b/src/app/dev/notifikasi/job/page.tsx @@ -1,15 +1,9 @@ -import { Notifikasi_UiJob } from "@/app_modules/notifikasi/_ui"; -import notifikasi_getByUserId from "@/app_modules/notifikasi/fun/get/get_notifiaksi_by_id"; +import { Notifikasi_UiMain } from "@/app_modules/notifikasi/_ui"; export default async function Page() { - const listNotifikasi = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Job", - }); - return ( <> - + ); } diff --git a/src/app/dev/notifikasi/layout.tsx b/src/app/dev/notifikasi/layout.tsx index 8b1e90fc..9e9d2461 100644 --- a/src/app/dev/notifikasi/layout.tsx +++ b/src/app/dev/notifikasi/layout.tsx @@ -1,19 +1,15 @@ import { UIGlobal_LayoutHeaderTamplate } from "@/app_modules/_global/ui"; import { Notifikasi_UiNewLayout } from "@/app_modules/notifikasi/_ui"; -import { notifikasi_funGetKategoriApp } from "@/app_modules/notifikasi/fun/get/fun_get_kategori_app"; export default async function Layout({ children, }: { children: React.ReactNode; }) { - const masterKategori = await notifikasi_funGetKategoriApp(); - return ( <> } - masterKategori={masterKategori} > {children} diff --git a/src/app/dev/notifikasi/semua/page.tsx b/src/app/dev/notifikasi/semua/page.tsx index f9fad445..ca9f64eb 100644 --- a/src/app/dev/notifikasi/semua/page.tsx +++ b/src/app/dev/notifikasi/semua/page.tsx @@ -1,15 +1,9 @@ -import { Notifikasi_UiAll } from "@/app_modules/notifikasi/_ui"; -import notifikasi_getByUserId from "@/app_modules/notifikasi/fun/get/get_notifiaksi_by_id"; +import { Notifikasi_UiMain } from "@/app_modules/notifikasi/_ui"; export default async function Page() { - const listNotifikasi = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Semua", - }); - return ( <> - + ); } diff --git a/src/app/dev/notifikasi/voting/page.tsx b/src/app/dev/notifikasi/voting/page.tsx index 1dd4b042..ca9f64eb 100644 --- a/src/app/dev/notifikasi/voting/page.tsx +++ b/src/app/dev/notifikasi/voting/page.tsx @@ -1,15 +1,9 @@ -import { Notifikasi_UiVoting } from "@/app_modules/notifikasi/_ui"; -import notifikasi_getByUserId from "@/app_modules/notifikasi/fun/get/get_notifiaksi_by_id"; +import { Notifikasi_UiMain } from "@/app_modules/notifikasi/_ui"; export default async function Page() { - const listNotifikasi = await notifikasi_getByUserId({ - page: 1, - kategoriApp: "Voting", - }); - return ( <> - + ); } diff --git a/src/app/lib/api_user_router/route_api_notifikasi.ts b/src/app/lib/api_user_router/route_api_notifikasi.ts new file mode 100644 index 00000000..0fccc1b0 --- /dev/null +++ b/src/app/lib/api_user_router/route_api_notifikasi.ts @@ -0,0 +1,15 @@ +import { ICategoryapp } from "@/app_modules/notifikasi/model/interface"; + +export const API_RouteNotifikasi = { + get_all_by_category: ({ + category, + page, + }: { + category: ICategoryapp; + page: number; + }) => `/api/notifikasi/get-all-by-category?category=${category}&page=${page}`, + + get_master_kategori: () => `/api/notifikasi/master`, + + get_count_by_id: () => `/api/notifikasi/count`, +}; diff --git a/src/app/lib/global_state.ts b/src/app/lib/global_state.ts index 59259c1c..e49b6643 100644 --- a/src/app/lib/global_state.ts +++ b/src/app/lib/global_state.ts @@ -42,7 +42,7 @@ export type IRealtimeData = { export const gs_realtimeData = atom(null); export const gs_admin_ntf = atom(0); export const gs_user_ntf = atom(0); -export const gs_count_ntf = atom(0); +export const gs_count_ntf = atom(null); // job export const gs_adminJob_triggerReview = atom(false); diff --git a/src/app/lib/prisma.ts b/src/app/lib/prisma.ts index 25a08ef9..101fd93b 100644 --- a/src/app/lib/prisma.ts +++ b/src/app/lib/prisma.ts @@ -1,28 +1,30 @@ -import { PrismaClient } from '@prisma/client'; +import { PrismaClient } from "@prisma/client"; // Singleton PrismaClient untuk pengembangan const globalForPrisma = globalThis as unknown as { __prisma__: PrismaClient | undefined; }; -export const prisma = globalForPrisma.__prisma__ ?? new PrismaClient({ - // log: process.env.NODE_ENV === 'development' ? ['query', 'info', 'warn', 'error'] : [], -}); +export const prisma = + globalForPrisma.__prisma__ ?? + new PrismaClient({ + // log: process.env.NODE_ENV === 'development' ? ['query', 'info', 'warn', 'error'] : [], + }); // Gunakan PrismaClient yang sama jika sudah ada -if (process.env.NODE_ENV !== 'production') { +if (process.env.NODE_ENV !== "production") { if (!globalForPrisma.__prisma__) { - console.log('PrismaClient initialized in development mode'); + console.log("PrismaClient initialized in development mode"); } globalForPrisma.__prisma__ = prisma; } -process.on('SIGINT', async () => { - console.log('Disconnecting PrismaClient...'); - await prisma.$disconnect(); +process.on("SIGINT", async () => { + console.log("Disconnecting PrismaClient..."); + await prisma.$disconnect();3 process.exit(0); }); -// console.log('==> Test prisma'); +// console.log("==> Test prisma"); export default prisma; diff --git a/src/app/lib/realtime_provider.tsx b/src/app/lib/realtime_provider.tsx index 07919a7f..3e25986b 100644 --- a/src/app/lib/realtime_provider.tsx +++ b/src/app/lib/realtime_provider.tsx @@ -17,8 +17,6 @@ import { gs_votingTiggerBeranda, IRealtimeData, } from "./global_state"; -import { newFunGetUserId } from "./new_fun_user_id"; -import { useState } from "react"; // const WIBU_REALTIME_TOKEN: string | undefined = // process.env.NEXT_PUBLIC_WIBU_REALTIME_TOKEN; @@ -28,15 +26,16 @@ export type TypeNotification = { type: "message" | "notification" | "trigger"; pushNotificationTo: "ADMIN" | "USER"; dataMessage?: IRealtimeData; - userLoginId?: string; + userId?: string; }; export default function RealtimeProvider({ + userId, WIBU_REALTIME_TOKEN, }: { + userId: string; WIBU_REALTIME_TOKEN: string; }) { - const [userLoginId, setUserLoginId] = useState(""); const [dataRealtime, setDataRealtime] = useAtom(gs_realtimeData); const [newAdminNtf, setNewAdminNtf] = useAtom(gs_admin_ntf); const [newUserNtf, setNewUserNtf] = useAtom(gs_user_ntf); @@ -72,15 +71,7 @@ export default function RealtimeProvider({ gs_donasiTriggerBeranda ); - async function loadUserId() { - const userId = await newFunGetUserId(); - - setUserLoginId(userId as string); - } - useShallowEffect(() => { - loadUserId(); - try { WibuRealtime.init({ project: "hipmi", @@ -97,7 +88,7 @@ export default function RealtimeProvider({ if ( data.type == "notification" && data.pushNotificationTo == "USER" && - data.dataMessage?.userId == userLoginId + data.dataMessage?.userId == userId ) { setNewUserNtf((e) => e + 1); setDataRealtime(data.dataMessage as any); @@ -144,7 +135,7 @@ export default function RealtimeProvider({ data.type == "notification" && data.pushNotificationTo == "USER" && data.dataMessage?.status == "Peserta Event" && - userLoginId !== data.dataMessage?.userId + userId !== data.dataMessage?.userId ) { setNewUserNtf((e) => e + 1); } @@ -172,7 +163,7 @@ export default function RealtimeProvider({ data.type == "notification" && data.pushNotificationTo == "USER" && data.dataMessage?.status == "Voting Masuk" && - userLoginId !== data.dataMessage?.userId + userId !== data.dataMessage?.userId ) { setNewUserNtf((e) => e + 1); } @@ -200,9 +191,9 @@ export default function RealtimeProvider({ // data.type == "notification" && // data.pushNotificationTo == "ADMIN" && // data.dataMessage?.status == "Menunggu" && - // userLoginId !== data.dataMessage?.userId + // userId !== data.dataMessage?.userId // ) { - // console.log("yes"); + // } // ---------------------- DONASI ------------------------- // diff --git a/src/app/page.tsx b/src/app/page.tsx index 3dc0d2d4..6fc3a06d 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -5,11 +5,11 @@ import { useShallowEffect } from "@mantine/hooks"; import { useRouter } from "next/navigation"; export default function Page() { - const router = useRouter() + const router = useRouter(); useShallowEffect(() => { setTimeout(() => { // window.location.replace("/dev/home"); - router.replace("/dev/home"); + router.replace("/dev/home", { scroll: false }); }, 1000); }, []); diff --git a/src/app/zCoba/page.tsx b/src/app/zCoba/page.tsx index 8df38920..dd5cb2f4 100644 --- a/src/app/zCoba/page.tsx +++ b/src/app/zCoba/page.tsx @@ -33,7 +33,7 @@ export default function Page() { const formData = new FormData(); formData.append("file", filePP as any); - const res = await fetch("/api/upload", { + const res = await fetch("/api/image/upload", { method: "POST", body: formData, }); diff --git a/src/app_modules/_global/button/comp_button_upload_photo.tsx b/src/app_modules/_global/button/comp_button_upload_photo.tsx index 0a28c023..378e7126 100644 --- a/src/app_modules/_global/button/comp_button_upload_photo.tsx +++ b/src/app_modules/_global/button/comp_button_upload_photo.tsx @@ -6,6 +6,7 @@ import { MainColor } from "../color"; import { MAX_SIZE } from "../lib"; import { PemberitahuanMaksimalFile } from "../lib/max_size"; import { ComponentGlobal_NotifikasiPeringatan } from "../notif_global"; +import { clientLogger } from "@/util/clientLogger"; export function ComponentGlobal_ButtonUploadFileImage({ onSetFile, @@ -24,12 +25,13 @@ export function ComponentGlobal_ButtonUploadFileImage({ if (files.size > MAX_SIZE) { ComponentGlobal_NotifikasiPeringatan(PemberitahuanMaksimalFile); + return; } else { onSetFile(files); onSetImage(buffer); } } catch (error) { - console.log(error); + clientLogger.error("Upload error:", error); } }} accept="image/png,image/jpeg" diff --git a/src/app_modules/_global/fun/delete/fun_delete_file_by_id.tsx b/src/app_modules/_global/fun/delete/fun_delete_file_by_id.tsx index a31df82d..cb3d48be 100644 --- a/src/app_modules/_global/fun/delete/fun_delete_file_by_id.tsx +++ b/src/app_modules/_global/fun/delete/fun_delete_file_by_id.tsx @@ -1,24 +1,50 @@ -export async function funGlobal_DeleteFileById({ fileId }: { fileId: string }) { - try { - const res = await fetch( - `https://wibu-storage.wibudev.com/api/files/${fileId}/delete`, - { - method: "DELETE", - headers: { - Authorization: `Bearer ${process.env.WS_APIKEY}`, - }, - } - ); +import { clientLogger } from "@/util/clientLogger"; - if (res.ok) { - const hasil = await res.json(); - return { success: true }; +export async function funGlobal_DeleteFileById({ + fileId, + dirId, +}: { + fileId: string; + dirId?: string; +}) { + try { + const res = await fetch("/api/image/delete", { + method: "DELETE", + body: JSON.stringify({ fileId, dirId }), + }); + + const data = await res.json(); + + if (data.success) { + clientLogger.info(`File ${fileId} deleted successfully`); + return { success: true, message: "File berhasil dihapus" }; } else { - const errorText = await res.json(); - return { success: false }; + return { success: false, message: data.message }; } } catch (error) { - return { success: false }; console.error("Upload error:", error); + return { success: false, message: "An unexpected error occurred" }; } + // try { + // const res = await fetch( + // `https://wibu-storage.wibudev.com/api/files/${fileId}/delete`, + // { + // method: "DELETE", + // headers: { + // Authorization: `Bearer ${process.env.WS_APIKEY}`, + // }, + // } + // ); + + // if (res.ok) { + // const hasil = await res.json(); + // return { success: true, message: "File berhasil dihapus" }; + // } else { + // const errorText = await res.json(); + // return { success: false, message: errorText.message }; + // } + // } catch (error) { + // console.error("Upload error:", error); + // return { success: false, message: "An unexpected error occurred" }; + // } } diff --git a/src/app_modules/_global/fun/get/fun_get_directory_name.ts b/src/app_modules/_global/fun/get/fun_get_directory_name.ts new file mode 100644 index 00000000..fbacfec2 --- /dev/null +++ b/src/app_modules/_global/fun/get/fun_get_directory_name.ts @@ -0,0 +1,11 @@ +import { DIRECTORY_ID } from "@/app/lib"; + +export async function funGetDirectoryNameByValue({ + value, +}: { + value?: string | null; +}) { + if (!value) return null; + const object: any = DIRECTORY_ID; + return Object.keys(object).find((key) => object[key] === value); +} diff --git a/src/app_modules/_global/fun/get/index.ts b/src/app_modules/_global/fun/get/index.ts index b71aacbe..25d8ace9 100644 --- a/src/app_modules/_global/fun/get/index.ts +++ b/src/app_modules/_global/fun/get/index.ts @@ -1,5 +1,5 @@ - import { funGlobal_CheckProfile } from "./fun_check_profile"; +import { funGetDirectoryNameByValue } from "./fun_get_directory_name"; import { funGlobal_getNomorAdmin } from "./fun_get_nomor_admin"; import { funGetUserIdByToken } from "./fun_get_user_id_by_token"; import { funGlobal_getMasterKategoriApp } from "./fun_master_kategori_app"; @@ -8,3 +8,4 @@ export { funGlobal_getMasterKategoriApp }; export { funGlobal_getNomorAdmin }; export { funGetUserIdByToken }; export { funGlobal_CheckProfile }; +export { funGetDirectoryNameByValue }; diff --git a/src/app_modules/_global/fun/upload/fun_upload_to_storage.ts b/src/app_modules/_global/fun/upload/fun_upload_to_storage.ts index 06ed38f4..4af61f50 100644 --- a/src/app_modules/_global/fun/upload/fun_upload_to_storage.ts +++ b/src/app_modules/_global/fun/upload/fun_upload_to_storage.ts @@ -32,38 +32,20 @@ export async function funGlobal_UploadToStorage({ console.error("File terlalu besar"); return { success: false, message: "File size exceeds limit" }; } - - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), 30000); // Timeout 30 detik - const formData = new FormData(); formData.append("file", file); formData.append("dirId", dirId); - try { - const res = await fetch("https://wibu-storage.wibudev.com/api/upload", { - method: "POST", - body: formData, - headers: { - Authorization: `Bearer ${Env_WS_APIKEY}`, - }, - signal: controller.signal, - }); + const upload = await fetch("/api/image/upload", { + method: "POST", + body: formData, + }); - clearTimeout(timeoutId); // Bersihkan timeout jika selesai tepat waktu + const res = await upload.json(); - if (res.ok) { - const dataRes = await res.json(); - return { success: true, data: dataRes.data }; - } else { - const errorText = await res.text(); - console.error("Error:", errorText); - return { success: false, message: errorText }; - } - } catch (error) { - clearTimeout(timeoutId); // - - console.error("Error:", error); - return { success: false, message: "An unexpected error occurred" }; + if (upload.ok) { + return { success: true, data: res.data, message: res.message }; + } else { + return { success: false, data: {}, message: res.message }; } } diff --git a/src/app_modules/_global/ui/ui_image_preview.tsx b/src/app_modules/_global/ui/ui_image_preview.tsx index a2057b2d..fce91d58 100644 --- a/src/app_modules/_global/ui/ui_image_preview.tsx +++ b/src/app_modules/_global/ui/ui_image_preview.tsx @@ -10,18 +10,16 @@ import { Image, rem, ScrollArea, - Skeleton, - Text, - Title, + Skeleton } from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; import { IconX } from "@tabler/icons-react"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { MainColor } from "../color"; +import ComponentGlobal_Loader from "../component/loader"; import UIGlobal_LayoutHeaderTamplate from "./ui_header_tamplate"; import { UIHeader } from "./ui_layout_tamplate"; -import ComponentGlobal_Loader from "../component/loader"; export function UIGlobal_ImagePreview({ fileId }: { fileId: string }) { const router = useRouter(); diff --git a/src/app_modules/admin/job/child/reject/index.tsx b/src/app_modules/admin/job/child/reject/index.tsx index 8ba05154..ec3364a9 100644 --- a/src/app_modules/admin/job/child/reject/index.tsx +++ b/src/app_modules/admin/job/child/reject/index.tsx @@ -1,5 +1,6 @@ "use client"; +import { IRealtimeData } from "@/app/lib/global_state"; import { RouterAdminJob } from "@/app/lib/router_admin/router_admin_job"; import ComponentGlobal_InputCountDown from "@/app_modules/_global/component/input_countdown"; import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/_global/notif_global/notifikasi_berhasil"; @@ -8,7 +9,6 @@ import { ComponentAdminGlobal_TitlePage } from "@/app_modules/admin/_admin_globa import ComponentAdminGlobal_HeaderTamplate from "@/app_modules/admin/_admin_global/header_tamplate"; import adminNotifikasi_funCreateToUser from "@/app_modules/admin/notifikasi/fun/create/fun_create_notif_user"; import { MODEL_JOB } from "@/app_modules/job/model/interface"; -import mqtt_client from "@/util/mqtt_client"; import { Button, Center, @@ -22,11 +22,12 @@ import { Table, Text, TextInput, - Textarea + Textarea, } from "@mantine/core"; import { IconBan, IconPhotoCheck, IconSearch } from "@tabler/icons-react"; import { useRouter } from "next/navigation"; import { useState } from "react"; +import { WibuRealtime } from "wibu-pkg"; import { AdminJob_funEditCatatanById } from "../../fun/edit/fun_edit_catatan_by_id"; import adminJob_getListReject from "../../fun/get/get_list_reject"; @@ -311,7 +312,7 @@ async function onReject({ const loadData = await adminJob_getListReject({ page: 1 }); onSetData(loadData); - const dataNotif = { + const dataNotifikasi: IRealtimeData = { appId: reject.data?.id as any, status: reject.data?.MasterStatus?.name as any, userId: reject.data?.authorId as any, @@ -321,14 +322,15 @@ async function onReject({ }; const notif = await adminNotifikasi_funCreateToUser({ - data: dataNotif as any, + data: dataNotifikasi as any, }); if (notif.status === 201) { - mqtt_client.publish( - "USER", - JSON.stringify({ userId: reject?.data?.authorId, count: 1 }) - ); + WibuRealtime.setData({ + type: "notification", + pushNotificationTo: "USER", + dataMessage: dataNotifikasi, + }); } ComponentGlobal_NotifikasiBerhasil(reject.message); diff --git a/src/app_modules/auth/register/view.tsx b/src/app_modules/auth/register/view.tsx index 79d4b59a..8e6f84b3 100644 --- a/src/app_modules/auth/register/view.tsx +++ b/src/app_modules/auth/register/view.tsx @@ -65,13 +65,13 @@ export default function Register() { const result = await res.json(); if (res.status === 200) { - localStorage.removeItem("hipmi_auth_code_id"); ComponentGlobal_NotifikasiBerhasil(result.message); - router.push("/dev/home", { scroll: false }); - + localStorage.removeItem("hipmi_auth_code_id"); await auth_funDeleteAktivasiKodeOtpByNomor({ nomor: data.nomor, }); + router.push("/dev/home", { scroll: false }); + return; } if (res.status === 400) { diff --git a/src/app_modules/auth/validasi/view.tsx b/src/app_modules/auth/validasi/view.tsx index d0b39c01..83894734 100644 --- a/src/app_modules/auth/validasi/view.tsx +++ b/src/app_modules/auth/validasi/view.tsx @@ -59,11 +59,7 @@ export default function Validasi() { } }, [triggerOtp]); - async function onCheckAuthCode({ - kodeId, - }: { - kodeId: string; - }) { + async function onCheckAuthCode({ kodeId }: { kodeId: string }) { const res = await fetch(`/api/auth/check?id=${kodeId}`); const result = await res.json(); @@ -97,37 +93,40 @@ export default function Validasi() { const result = await res.json(); - if (res.status === 200) { + if (res.status === 200 && result.roleId == "1") { + ComponentGlobal_NotifikasiBerhasil(result.message); localStorage.removeItem("hipmi_auth_code_id"); - - if (result.roleId === "1") { - ComponentGlobal_NotifikasiBerhasil(result.message); - router.push(RouterHome.main_home, { scroll: false }); - // if (result.active === true) { - // } else { - // ComponentGlobal_NotifikasiBerhasil(result.message); - // router.push("/waiting-room", { scroll: false }); - // } - } else { - ComponentGlobal_NotifikasiBerhasil("Admin Logged in"); - router.push(RouterAdminDashboard.splash_admin, { scroll: false }); - } - await auth_funDeleteAktivasiKodeOtpByNomor({ nomor: data.nomor, }); + router.push(RouterHome.main_home, { scroll: false }); + return; + } + + if (res.status === 200 && result.roleId != "1") { + ComponentGlobal_NotifikasiBerhasil("Admin Logged in"); + localStorage.removeItem("hipmi_auth_code_id"); + await auth_funDeleteAktivasiKodeOtpByNomor({ + nomor: data.nomor, + }); + router.push(RouterAdminDashboard.splash_admin, { scroll: false }); + return; } if (res.status === 404) { - ComponentGlobal_NotifikasiBerhasil(result.message); router.push("/register", { scroll: false }); + ComponentGlobal_NotifikasiBerhasil(result.message); + return; } if (res.status === 400) { ComponentGlobal_NotifikasiPeringatan(result.message); + return; } } catch (error) { console.error(error); + } finally { + setLoading(false); } } diff --git a/src/app_modules/colab/component/index.ts b/src/app_modules/colab/component/index.ts new file mode 100644 index 00000000..e17745cf --- /dev/null +++ b/src/app_modules/colab/component/index.ts @@ -0,0 +1,3 @@ +import { Collaboration_SkeletonCreate } from "./skeleton_view"; + +export { Collaboration_SkeletonCreate }; diff --git a/src/app_modules/colab/component/lib/api_collaboration.ts b/src/app_modules/colab/component/lib/api_collaboration.ts new file mode 100644 index 00000000..bfffe8d5 --- /dev/null +++ b/src/app_modules/colab/component/lib/api_collaboration.ts @@ -0,0 +1,4 @@ +export async function apiGetMasterCollaboration() { + const data = await fetch(`/api/collaboration/master`); + return await data.json().catch(() => null); +} diff --git a/src/app_modules/colab/component/skeleton_view.tsx b/src/app_modules/colab/component/skeleton_view.tsx new file mode 100644 index 00000000..1061a1af --- /dev/null +++ b/src/app_modules/colab/component/skeleton_view.tsx @@ -0,0 +1,28 @@ +import { Skeleton, Stack } from "@mantine/core"; + +export function Collaboration_SkeletonCreate() { + return ( + <> + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/app_modules/colab/create/index.tsx b/src/app_modules/colab/create/index.tsx index 32c32541..b28e3236 100644 --- a/src/app_modules/colab/create/index.tsx +++ b/src/app_modules/colab/create/index.tsx @@ -5,7 +5,15 @@ import ComponentGlobal_InputCountDown from "@/app_modules/_global/component/inpu import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/_global/notif_global/notifikasi_berhasil"; import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/_global/notif_global/notifikasi_gagal"; import { ComponentGlobal_NotifikasiPeringatan } from "@/app_modules/_global/notif_global/notifikasi_peringatan"; -import { Button, Select, Stack, TextInput, Textarea } from "@mantine/core"; +import { + Button, + Center, + Select, + Stack, + TextInput, + Textarea, + Loader, +} from "@mantine/core"; import { useRouter } from "next/navigation"; import { useState } from "react"; import colab_funCreateProyek from "../fun/create/fun_create_proyek"; @@ -13,12 +21,12 @@ import { MODEL_COLLABORATION_MASTER } from "../model/interface"; import mqtt_client from "@/util/mqtt_client"; import { useHookstate } from "@hookstate/core"; import { useGsCollabCreate } from "../global_state/state"; +import { useShallowEffect } from "@mantine/hooks"; +import { apiGetMasterCollaboration } from "../component/lib/api_collaboration"; +import { clientLogger } from "@/util/clientLogger"; +import { Collaboration_SkeletonCreate } from "../component"; -export default function Colab_Create({ - listIndustri, -}: { - listIndustri: MODEL_COLLABORATION_MASTER[]; -}) { +export default function Colab_Create() { const [value, setValue] = useState({ title: "", lokasi: "", @@ -27,9 +35,38 @@ export default function Colab_Create({ projectCollaborationMaster_IndustriId: 0, // jumlah_partisipan: 0, }); + + const [listIndustri, setListIndustri] = useState< + MODEL_COLLABORATION_MASTER[] | null + >(null); + + useShallowEffect(() => { + onLoadMaster(); + }, []); + + async function onLoadMaster() { + try { + const respone = await apiGetMasterCollaboration(); + if (respone.success) { + setListIndustri(respone.data); + } + } catch (error) { + clientLogger.error("Error get master collaboration", error); + } + } + + + if (listIndustri == null) { + return ( + <> + + + ); + } + return ( <> - + - ({ + value: e.id, + label: e.name, + }))} + onChange={(val: string) => + setData({ + ...data, + kategoriId: val, + }) + } + /> + + + { + setData({ ...data, title: val.target.value }); + }} + /> + Rp.} + min={0} + withAsterisk + label="Target Dana" + placeholder="0" + value={data.target} + onChange={(val) => { + const match = val.currentTarget.value + .replace(/\./g, "") + .match(/^[0-9]+$/); + + if (val.currentTarget.value === "") + return setData({ + ...data, + target: 0 + "", + }); + if (!match?.[0]) return null; + + const nilai = val.currentTarget.value.replace(/\./g, ""); + const target = Intl.NumberFormat("id-ID").format(+nilai); + + setTargetDana(nilai); + setData({ + ...data, + target, + }); + }} + /> + ({ + value: e.id, + label: e.name + " " + "hari", + }))} + value={data?.masterPencarianInvestorId} + onChange={(val) => { + setData({ + ...(data as any), + masterPencarianInvestorId: val, + }); + }} + /> + + ({ + value: e.id, + label: e.name + " " + "bulan", + }))} + value={data?.masterPembagianDevidenId} + onChange={(val) => { + setData({ + ...(data as any), + masterPembagianDevidenId: val, + }); + }} + /> + + + + } + + + ); +} diff --git a/src/app_modules/investasi/_view/main/portofolio/skeleton_portofolio.tsx b/src/app_modules/investasi/_view/main/portofolio/skeleton_portofolio.tsx new file mode 100644 index 00000000..8fc3463b --- /dev/null +++ b/src/app_modules/investasi/_view/main/portofolio/skeleton_portofolio.tsx @@ -0,0 +1,31 @@ +import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; +import { Box, Grid, Skeleton } from "@mantine/core"; + +export default function SkeletonInvestasiPortofolio() { + return ( + <> + {[...Array(4)].map((_, index) => ( + + + + + {[...Array(3)].map((_, i) => ( + + + + + + + + ))} + + + + + + + + ))} + + ); +} \ No newline at end of file diff --git a/src/app_modules/investasi/_view/main/portofolio/view_portofolio_new.tsx b/src/app_modules/investasi/_view/main/portofolio/view_portofolio_new.tsx new file mode 100644 index 00000000..5c0a76c0 --- /dev/null +++ b/src/app_modules/investasi/_view/main/portofolio/view_portofolio_new.tsx @@ -0,0 +1,79 @@ +import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; +import ComponentGlobal_Loader from "@/app_modules/_global/component/loader"; +import { Investasi_ComponentCardPortofolio_NotPublishNew } from "@/app_modules/investasi/_component/main/comp_card_portofolio_not_publish_new"; +import { Investasi_ComponentCardPortofolioPublishNew } from "@/app_modules/investasi/_component/main/comp_card_portofolio_publish_new"; +import { apiGetAllInvestasi } from "@/app_modules/investasi/_lib/api_interface"; +import { IDataInvestasiBursa } from "@/app_modules/investasi/_lib/type_investasi"; +import { Box, Center } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import _ from "lodash"; +import { ScrollOnly } from "next-scroll-loader"; +import { useParams } from "next/navigation"; +import { useState } from "react"; +import SkeletonInvestasiPortofolio from "./skeleton_portofolio"; + +export function Investasi_ViewPortofolioNew() { + const param = useParams<{ id: string }>(); + const [data, setData] = useState([]); + const [activePage, setActivePage] = useState(1); + const [loading, setLoading] = useState(true) + + async function getDataInvestasi() { + try { + setLoading(true) + const response = await apiGetAllInvestasi(`?cat=portofolio&status=${param.id}&page=1`) + if (response.success) { + setData(response.data); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + + useShallowEffect(() => { + getDataInvestasi() + }, []); + + return ( + <> + + { + loading ? + + : + _.isEmpty(data) ? ( + + ) : ( + ( +
+ +
+ )} + data={data} + setData={setData} + moreData={async () => { + const pageNew = activePage + 1 + const loadData = await apiGetAllInvestasi(`?cat=portofolio&status=${param.id}&page=${pageNew}`) + setActivePage((val) => val + 1); + + return loadData.data; + }} + > + { + param.id == "1" ? + (item) => () + : + (item) => () + } +
+ ) + } +
+ + ); +} diff --git a/src/app_modules/investasi/_view/main/skeleton_beranda.tsx b/src/app_modules/investasi/_view/main/skeleton_beranda.tsx new file mode 100644 index 00000000..cc16d4bb --- /dev/null +++ b/src/app_modules/investasi/_view/main/skeleton_beranda.tsx @@ -0,0 +1,31 @@ +import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; +import { Box, Grid, Skeleton } from "@mantine/core"; + +export default function SkeletonInvestasiBursa() { + return ( + <> + {[...Array(4)].map((_, index) => ( + + + + + + + + {[...Array(3)].map((_, i) => ( + + + + + + + + ))} + + + + + ))} + + ); +} \ No newline at end of file diff --git a/src/app_modules/investasi/_view/main/skeleton_saham_saya.tsx b/src/app_modules/investasi/_view/main/skeleton_saham_saya.tsx new file mode 100644 index 00000000..1aada275 --- /dev/null +++ b/src/app_modules/investasi/_view/main/skeleton_saham_saya.tsx @@ -0,0 +1,18 @@ +import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; +import { Grid, Skeleton } from "@mantine/core"; + +export default function SkeletonInvestasiSahamSaya() { + return ( + <> + {[...Array(4)].map((_, index) => ( + + + + + + + + ))} + + ); +} \ No newline at end of file diff --git a/src/app_modules/investasi/_view/main/view_beranda_new.tsx b/src/app_modules/investasi/_view/main/view_beranda_new.tsx new file mode 100644 index 00000000..4ee233a5 --- /dev/null +++ b/src/app_modules/investasi/_view/main/view_beranda_new.tsx @@ -0,0 +1,97 @@ +'use client' +import { RouterInvestasi_OLD } from "@/app/lib/router_hipmi/router_investasi"; +import ComponentGlobal_CreateButton from "@/app_modules/_global/component/button_create"; +import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; +import ComponentGlobal_Loader from "@/app_modules/_global/component/loader"; +import mqtt_client from "@/util/mqtt_client"; +import { Box, Center } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import _ from "lodash"; +import { ScrollOnly } from "next-scroll-loader"; +import { useState } from "react"; +import { Investasi_ComponentButtonUpdateBeranda } from "../../_component"; +import { Investasi_ComponentCardBerandaNew } from "../../_component/main/com_card_beranda_new"; +import { apiGetAllInvestasi } from "../../_lib/api_interface"; +import { IDataInvestasiBursa } from "../../_lib/type_investasi"; +import SkeletonInvestasiBursa from "./skeleton_beranda"; + +export function Investasi_ViewBerandaNew() { + const [data, setData] = useState([]); + const [activePage, setActivePage] = useState(1); + const [isNewPost, setIsNewPost] = useState(false); + const [loading, setLoading] = useState(true) + + useShallowEffect(() => { + mqtt_client.subscribe("Beranda_Investasi"); + + mqtt_client.on("message", (topic, message) => { + const newPost = JSON.parse(message.toString()); + setIsNewPost(newPost); + }); + }, []); + + + async function getDataInvestasi() { + try { + setLoading(true) + const response = await apiGetAllInvestasi(`?cat=bursa&page=1`) + if (response.success) { + setData(response.data); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + + useShallowEffect(() => { + getDataInvestasi() + }, []); + + return ( + <> + {isNewPost && ( + { + setData(val.data); + setIsNewPost(val.isNewPost); + }} + /> + )} + + + + { + loading ? + : + _.isEmpty(data) ? ( + + ) : ( + ( +
+ +
+ )} + data={data} + setData={setData} + moreData={async () => { + const pageNew = activePage + 1 + const loadData = await apiGetAllInvestasi(`?cat=bursa&page=${pageNew}`); + setActivePage((val) => val + 1); + + return loadData; + }} + > + {(item) => } +
+ ) + + } +
+ + ); +} diff --git a/src/app_modules/investasi/_view/main/view_saham_saya_new.tsx b/src/app_modules/investasi/_view/main/view_saham_saya_new.tsx new file mode 100644 index 00000000..280f9990 --- /dev/null +++ b/src/app_modules/investasi/_view/main/view_saham_saya_new.tsx @@ -0,0 +1,73 @@ +'use client' +import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; +import ComponentGlobal_Loader from "@/app_modules/_global/component/loader"; +import { Box, Center } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import _ from "lodash"; +import { ScrollOnly } from "next-scroll-loader"; +import { useState } from "react"; +import { Investasi_ComponentSahamSayaNew } from "../../_component/main/comp_card_saham_saya_new"; +import { apiGetAllSahamSaya } from "../../_lib/api_interface"; +import { IDataSahamSaya } from "../../_lib/type_investasi"; +import SkeletonInvestasiSahamSaya from "./skeleton_saham_saya"; + +export function Investasi_ViewSahamSayaNew() { + const [data, setData] = useState([]); + const [activePage, setActivePage] = useState(1); + const [loading, setLoading] = useState(true) + + + async function getDataSahamSaya() { + try { + setLoading(true) + const response = await apiGetAllSahamSaya(`?cat=saham-saya&page=1`) + if (response.success) { + setData(response.data); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + + useShallowEffect(() => { + getDataSahamSaya() + }, []); + + return ( + <> + + { + loading ? + + : + _.isEmpty(data) ? ( + + ) : ( + ( +
+ +
+ )} + data={data} + setData={setData} + moreData={async () => { + const pageNew = activePage + 1 + const loadData = await apiGetAllSahamSaya(`?cat=saham-saya&page=${pageNew}`) + setActivePage((val) => val + 1); + + return loadData.data; + }} + > + {(item) => } +
+ ) + } +
+ + ); +} diff --git a/src/app_modules/investasi/_view/main/view_transaksi_new.tsx b/src/app_modules/investasi/_view/main/view_transaksi_new.tsx new file mode 100644 index 00000000..87726193 --- /dev/null +++ b/src/app_modules/investasi/_view/main/view_transaksi_new.tsx @@ -0,0 +1,72 @@ +'use client' +import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; +import { Box, Center, Loader } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import _ from "lodash"; +import { ScrollOnly } from "next-scroll-loader"; +import { useState } from "react"; +import { Investasi_ComponentCardDaftarTransaksiNew } from "../../_component/main/comp_card_daftar_transaksi_new"; +import { apiGetAllSahamSaya } from "../../_lib/api_interface"; +import { IDataSahamSaya } from "../../_lib/type_investasi"; +import SkeletonInvestasiSahamSaya from "./skeleton_saham_saya"; + +export function Investasi_ViewDaftarTransaksiNew() { + const [data, setData] = useState([]); + const [activePage, setActivePage] = useState(1); + const [loading, setLoading] = useState(true) + + + async function getDataTransaksi() { + try { + setLoading(true) + const response = await apiGetAllSahamSaya(`?cat=transaksi&page=1`) + if (response.success) { + setData(response.data); + } + } catch (error) { + console.error(error); + } finally { + setLoading(false) + } + } + + + useShallowEffect(() => { + getDataTransaksi() + }, []); + + return ( + <> + { + loading ? + + : + _.isEmpty(data) ? ( + + ) : ( + + ( +
+ +
+ )} + data={data} + setData={setData} + moreData={async () => { + const pageNew = activePage + 1 + const loadData = await apiGetAllSahamSaya(`?cat=transaksi&page=${pageNew}`) + setActivePage((val) => val + 1); + + return loadData.data; + }} + > + {(item) => } +
+
+ ) + } + + ); +} diff --git a/src/app_modules/investasi/create/view_new.tsx b/src/app_modules/investasi/create/view_new.tsx new file mode 100644 index 00000000..8eb154db --- /dev/null +++ b/src/app_modules/investasi/create/view_new.tsx @@ -0,0 +1,413 @@ +"use client"; +import { MainColor } from "@/app_modules/_global/color/color_pallet"; +import { ComponentGlobal_BoxInformation, ComponentGlobal_BoxUploadImage, ComponentGlobal_CardStyles, } from "@/app_modules/_global/component"; +import { AspectRatio, Box, Button, Center, FileButton, Grid, Group, Image, Select, Stack, Text, TextInput, } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import { IconCamera, IconCircleCheck, IconFileTypePdf, IconUpload } from "@tabler/icons-react"; +import _ from "lodash"; +import { useState } from "react"; +import { Investasi_ComponentButtonCreateNewInvestasi } from "../_component"; +import { apiGetMasterInvestasi } from "../_lib/api_interface"; + +export default function InvestasiCreateNew() { + const [loadingMasterInvestor, setLoadingMasterInvestor] = useState(true) + const [loadingMasterPeriodeDeviden, setLoadingMasterPeriodeDeviden] = useState(true) + const [loadingMasterPembagianDeviden, setLoadingMasterPembagianDeviden] = useState(true) + const [periodeDeviden, setPeriodeDeviden] = useState([]); + const [pembagianDeviden, setPembagianDeviden] = useState([]); + const [pencarianInvestor, setPencarianInvestor] = useState([]); + const [fileImage, setFileImage] = useState(null); + const [img, setImg] = useState(); + const [filePdf, setFilePdf] = useState(null); + const [fPdf, setFPdf] = useState(null); + const [totalLembar, setTotalLembar] = useState(0); + const [value, setValue] = useState({ + title: "", + targetDana: 0, + hargaLembar: 0, + roi: 0, + pencarianInvestorId: "", + periodeDevidenId: "", + pembagianDevidenId: "", + }); + const [target, setTarget] = useState(""); + const [harga, setHarga] = useState(""); + + async function onTotalLembar({ target, harga, }: { target?: number | any; harga?: number | any; }) { + if (target !== 0 && harga !== 0) { + const hasil: any = target / harga; + setTotalLembar(_.floor(hasil === Infinity ? 0 : hasil)); + } + } + + async function onGetMasterInvestor() { + try { + setLoadingMasterInvestor(true) + const response = await apiGetMasterInvestasi("?cat=pencarian-investor") + if (response.success) { + setPencarianInvestor(response.data) + } + } catch (error) { + console.log(error); + } finally { + setLoadingMasterInvestor(false) + } + } + + async function onGetMasterPeriodeDeviden() { + try { + setLoadingMasterPeriodeDeviden(true) + const response = await apiGetMasterInvestasi("?cat=periode-deviden") + if (response.success) { + setPeriodeDeviden(response.data) + } + } catch (error) { + console.log(error); + } finally { + setLoadingMasterPeriodeDeviden(false) + } + } + + + async function onGetMasterPembagianDeviden() { + try { + setLoadingMasterPembagianDeviden(true) + const response = await apiGetMasterInvestasi("?cat=pembagian-deviden") + if (response.success) { + setPembagianDeviden(response.data) + } + } catch (error) { + console.log(error); + } finally { + setLoadingMasterPembagianDeviden(false) + } + } + + useShallowEffect(() => { + onGetMasterInvestor() + onGetMasterPeriodeDeviden() + onGetMasterPembagianDeviden() + }, []) + + return ( + <> + + {/* Upload Image */} + + + + + + {img ? ( + + Foto + + ) : ( + + + + Upload Gambar + + + )} + + + {/* Upload Foto */} + + { + try { + const buffer = URL.createObjectURL( + new Blob([new Uint8Array(await files.arrayBuffer())]) + ); + + setImg(buffer); + setFileImage(files); + } catch (error) { + console.log(error); + } + }} + accept="image/png,image/jpeg" + > + {(props) => ( + + )} + + + + + {/* Upload File */} + + + + {!filePdf ? ( + + Upload File Prospektus + + ) : ( + + + + + {filePdf.name} + + + +
+ +
+
+
+ )} +
+ + + { + try { + const buffer = URL.createObjectURL( + new Blob([new Uint8Array(await files.arrayBuffer())]) + ); + setFPdf(buffer); + setFilePdf(files); + } catch (error) { + console.log(error); + } + }} + > + {(props) => ( + + )} + + +
+ + + { + setValue({ + ...value, + title: val.target.value, + }); + }} + /> + + Rp.} + min={0} + withAsterisk + label="Dana Dibutuhkan" + placeholder="0" + value={target} + onChange={(val) => { + // console.log(typeof val) + const match = val.currentTarget.value + .replace(/\./g, "") + .match(/^[0-9]+$/); + + if (val.currentTarget.value === "") return setTarget(0 + ""); + if (!match?.[0]) return null; + + const nilai = val.currentTarget.value.replace(/\./g, ""); + const targetNilai = Intl.NumberFormat("id-ID").format(+nilai); + + onTotalLembar({ + target: +nilai, + harga: +value.hargaLembar, + }); + + setTarget(targetNilai); + setValue({ + ...value, + targetDana: +nilai, + }); + }} + /> + + Rp.} + min={0} + withAsterisk + label="Harga Per Lembar" + placeholder="0" + value={harga} + onChange={(val) => { + try { + // console.log(typeof +val.currentTarget.value); + + const match = val.currentTarget.value + .replace(/\./g, "") + .match(/^[0-9]+$/); + + if (val.currentTarget.value === "") return setHarga(0 + ""); + + if (!match?.[0]) return null; + + const nilai = val.currentTarget.value.replace(/\./g, ""); + const targetNilai = Intl.NumberFormat("id-ID").format(+nilai); + + onTotalLembar({ + harga: +nilai, + target: +value.targetDana, + }); + + setHarga(targetNilai); + setValue({ + ...value, + hargaLembar: +nilai, + }); + } catch (error) { + console.log(error); + } + }} + /> + + + + + % + + } + withAsterisk + type="number" + label={"Rasio Keuntungan / ROI %"} + placeholder="Masukan rasio keuntungan" + onChange={(val) => { + setValue({ + ...value, + roi: _.toNumber(val.target.value), + }); + }} + /> + + ({ value: e.id, label: e.name }))} + onChange={(val) => { + setValue({ + ...(value as any), + periodeDevidenId: val, + }); + }} + /> + +